1<?php
2/**
3 * GeSHi example script
4 *
5 * Just point your browser at this script (with geshi.php in the parent directory,
6 * and the language files in subdirectory "../geshi/")
7 *
8 * @author  Nigel McNie
9 * @version $Id: langcheck.php 1971 2008-12-25 15:14:14Z benbe $
10 */
11header('Content-Type: text/html; charset=utf-8');
12
13set_time_limit(0);
14error_reporting(E_ALL);
15$time_start = explode(' ', microtime());
16
17define ('TYPE_NOTICE', 0);
18define ('TYPE_WARNING', 1);
19define ('TYPE_ERROR', 2);
20
21$error_abort = false;
22$error_cache = array();
23function output_error_cache(){
24    global $error_cache, $error_abort;
25
26    if(count($error_cache)) {
27        echo "<span style=\"color: #F00; font-weight: bold;\">Failed</span><br />";
28        echo "<ol>\n";
29        foreach($error_cache as $error_msg) {
30            echo "<li>";
31            switch($error_msg['t']) {
32                case TYPE_NOTICE:
33                    echo "<span style=\"color: #080; font-weight: bold;\">NOTICE:</span>";
34                    break;
35                case TYPE_WARNING:
36                    echo "<span style=\"color: #CC0; font-weight: bold;\">WARNING:</span>";
37                    break;
38                case TYPE_ERROR:
39                    echo "<span style=\"color: #F00; font-weight: bold;\">ERROR:</span>";
40                    break;
41            }
42            echo " " . $error_msg['m'] . "</li>";
43        }
44        echo "</ol>\n";
45    } else {
46        echo "<span style=\"color: #080; font-weight: bold;\">OK</span><br />";
47    }
48    echo "\n";
49
50    $error_cache = array();
51}
52
53function report_error($type, $message) {
54    global $error_cache, $error_abort;
55
56    $error_cache[] = array('t' => $type, 'm' => $message);
57    if(TYPE_ERROR == $type) {
58        $error_abort = true;
59    }
60}
61
62?>
63<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
64     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
65<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
66<head>
67    <title>GeSHi Language File Validation Script</title>
68    <style type="text/css">
69    <!--
70    html {
71        background-color: #f0f0f0;
72    }
73    body {
74        font-family: Verdana, Arial, sans-serif;
75        margin: 10px;
76        border: 2px solid #e0e0e0;
77        background-color: #fcfcfc;
78        padding: 5px;
79        font-size: 10pt;
80    }
81    h2 {
82        margin: .1em 0 .2em .5em;
83        border-bottom: 1px solid #b0b0b0;
84        color: #b0b0b0;
85        font-weight: normal;
86        font-size: 150%;
87    }
88    h3 {
89        margin: .1em 0 .2em .5em;
90        color: #b0b0b0;
91        font-weight: normal;
92        font-size: 120%;
93    }
94    #footer {
95        text-align: center;
96        font-size: 80%;
97        color: #a9a9a9;
98    }
99    #footer a {
100        color: #9999ff;
101    }
102    textarea {
103        border: 1px solid #b0b0b0;
104        font-size: 90%;
105        color: #333;
106        margin-left: 20px;
107    }
108    select, input {
109        margin-left: 20px;
110    }
111    p {
112        font-size: 90%;
113        margin-left: .5em;
114    }
115    -->
116    </style>
117</head>
118<body>
119<h2>GeSHi Language File Validation Script</h2>
120<p>To use this script, make sure that <strong>geshi.php</strong> is in the
121parent directory or in your include_path, and that the language files are in a
122subdirectory of GeSHi's directory called <strong>geshi/</strong>.</p>
123<p>Everything else will be done by this script automatically. After the script
124finished you should see messages of what could cause trouble with GeSHi or where
125your language files can be improved. Please be patient, as this might take some time.</p>
126
127<ol>
128<li>Checking where to find GeSHi installation ... <?php
129// Rudimentary checking of where GeSHi is. In a default install it will be in ../, but
130// it could be in the current directory if the include_path is set. There's nowhere else
131// we can reasonably guess.
132if (is_readable('../geshi.php')) {
133    $path = '../';
134} elseif (is_readable('geshi.php')) {
135    $path = './';
136} else {
137    report_error(TYPE_ERROR, 'Could not find geshi.php - make sure it is in your include path!');
138}
139
140if(!$error_abort) {
141    require $path . 'geshi.php';
142
143    if(!class_exists('GeSHi')) {
144        report_error(TYPE_ERROR, 'The GeSHi class was not found, although it seemed we loaded the correct file!');
145    }
146}
147
148if(!$error_abort) {
149    if(!defined('GESHI_LANG_ROOT')) {
150        report_error(TYPE_ERROR, 'There\'s no information present on where to find the language files!');
151    } else if(!is_dir(GESHI_LANG_ROOT)) {
152        report_error(TYPE_ERROR, 'The path "'.GESHI_LANG_ROOT.'" given, does not ressemble a directory!');
153    } else if(!is_readable(GESHI_LANG_ROOT)) {
154        report_error(TYPE_ERROR, 'The path "'.GESHI_LANG_ROOT.'" is not readable to this script!');
155    }
156}
157
158output_error_cache();
159
160if(!$error_abort) {
161    echo "</li>\n<li>Listing available language files ... ";
162
163    if (!($dir = @opendir(GESHI_LANG_ROOT))) {
164        report_error(TYPE_ERROR, 'Error requesting listing for available language files!');
165    }
166
167    $languages = array();
168
169    if(!$error_abort) {
170        while ($file = readdir($dir)) {
171            if (!$file || $file[0] == '.' || strpos($file, '.') === false) {
172                continue;
173            }
174            $lang = substr($file, 0,  strpos($file, '.'));
175            $languages[] = $lang;
176        }
177        closedir($dir);
178    }
179
180    $languages = array_unique($languages);
181    sort($languages);
182
183    if(!count($languages)) {
184        report_error(TYPE_WARNING, 'Unable to locate any usable language files in "'.GESHI_LANG_ROOT.'"!');
185    }
186
187    output_error_cache();
188}
189
190if (isset($_REQUEST['show']) && in_array($_REQUEST['show'], $languages)) {
191    $languages = array($_REQUEST['show']);
192}
193
194if(!$error_abort) {
195    foreach ($languages as $lang) {
196        echo "</li>\n<li>Validating language file for '$lang' ... ";
197
198        $langfile = GESHI_LANG_ROOT . $lang . '.php';
199
200        unset($language_data);
201
202        if(!is_file($langfile)) {
203            report_error(TYPE_ERROR, 'The path "' .$langfile. '" does not ressemble a regular file!');
204        } else if(!is_readable($langfile)) {
205            report_error(TYPE_ERROR, 'Cannot read file "' .$langfile. '"!');
206        } else {
207            $langfile_content = file_get_contents($langfile);
208            if(preg_match("/\?>(?:\r?\n|\r(?!\n)){2,}\Z/", $langfile_content)) {
209                report_error(TYPE_ERROR, 'Language file contains trailing empty lines at EOF!');
210            }
211            if(!preg_match("/\?>(?:\r?\n|\r(?!\n))?\Z/", $langfile_content)) {
212                report_error(TYPE_ERROR, 'Language file contains no PHP end marker at EOF!');
213            }
214            if(preg_match("/\t/", $langfile_content)) {
215                report_error(TYPE_NOTICE, 'Language file contains unescaped tabulator chars (probably for indentation)!');
216            }
217            if(preg_match('/^(?:    )*(?!    )(?! \*) /m', $langfile_content)) {
218                report_error(TYPE_NOTICE, 'Language file contains irregular indentation (other than 4 spaces per indentation level)!');
219            }
220
221            if(!preg_match("/\/\*\*((?!\*\/).)*?Author:((?!\*\/).)*?\*\//s", $langfile_content)) {
222                report_error(TYPE_WARNING, 'Language file does not contain a specification of an author!');
223            }
224            if(!preg_match("/\/\*\*((?!\*\/).)*?Copyright:((?!\*\/).)*?\*\//s", $langfile_content)) {
225                report_error(TYPE_WARNING, 'Language file does not contain a specification of the copyright!');
226            }
227            if(!preg_match("/\/\*\*((?!\*\/).)*?Release Version:((?!\*\/).)*?\*\//s", $langfile_content)) {
228                report_error(TYPE_WARNING, 'Language file does not contain a specification of the release version!');
229            }
230            if(!preg_match("/\/\*\*((?!\*\/).)*?Date Started:((?!\*\/).)*?\*\//s", $langfile_content)) {
231                report_error(TYPE_WARNING, 'Language file does not contain a specification of the date it was started!');
232            }
233            if(!preg_match("/\/\*\*((?!\*\/).)*?This file is part of GeSHi\.((?!\*\/).)*?\*\//s", $langfile_content)) {
234                report_error(TYPE_WARNING, 'Language file does not state that it belongs to GeSHi!');
235            }
236            if(!preg_match("/\/\*\*((?!\*\/).)*?language file for GeSHi\.((?!\*\/).)*?\*\//s", $langfile_content)) {
237                report_error(TYPE_WARNING, 'Language file does not state that it is a language file for GeSHi!');
238            }
239            if(!preg_match("/\/\*\*((?!\*\/).)*?GNU General Public License((?!\*\/).)*?\*\//s", $langfile_content)) {
240                report_error(TYPE_WARNING, 'Language file does not state that it is provided under the terms of the GNU GPL!');
241            }
242
243            unset($langfile_content);
244
245            include $langfile;
246
247            if(!isset($language_data)) {
248                report_error(TYPE_ERROR, 'Language file does not contain a $language_data structure to check!');
249            } else if (!is_array($language_data)) {
250                report_error(TYPE_ERROR, 'Language file contains a $language_data structure which is not an array!');
251            }
252        }
253
254        if(!$error_abort) {
255            if(!isset($language_data['LANG_NAME'])) {
256                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'LANG_NAME\'] specification!');
257            } else if (!is_string($language_data['LANG_NAME'])) {
258                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'LANG_NAME\'] specification which is not a string!');
259            }
260
261            if(!isset($language_data['COMMENT_SINGLE'])) {
262                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'COMMENT_SIGNLE\'] structure to check!');
263            } else if (!is_array($language_data['COMMENT_SINGLE'])) {
264                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'COMMENT_SINGLE\'] structure which is not an array!');
265            }
266
267            if(!isset($language_data['COMMENT_MULTI'])) {
268                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'COMMENT_MULTI\'] structure to check!');
269            } else if (!is_array($language_data['COMMENT_MULTI'])) {
270                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'COMMENT_MULTI\'] structure which is not an array!');
271            }
272
273            if(isset($language_data['COMMENT_REGEXP'])) {
274                if (!is_array($language_data['COMMENT_REGEXP'])) {
275                    report_error(TYPE_ERROR, 'Language file contains a $language_data[\'COMMENT_REGEXP\'] structure which is not an array!');
276                }
277            }
278
279            if(!isset($language_data['QUOTEMARKS'])) {
280                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'QUOTEMARKS\'] structure to check!');
281            } else if (!is_array($language_data['QUOTEMARKS'])) {
282                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'QUOTEMARKS\'] structure which is not an array!');
283            }
284
285            if(isset($language_data['HARDQUOTE'])) {
286                if (!is_array($language_data['HARDQUOTE'])) {
287                    report_error(TYPE_ERROR, 'Language file contains a $language_data[\'HARDQUOTE\'] structure which is not an array!');
288                }
289            }
290
291            if(!isset($language_data['ESCAPE_CHAR'])) {
292                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'ESCAPE_CHAR\'] specification to check!');
293            } else if (!is_string($language_data['ESCAPE_CHAR'])) {
294                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'ESCAPE_CHAR\'] specification which is not a string!');
295            } else if (1 < strlen($language_data['ESCAPE_CHAR'])) {
296                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'ESCAPE_CHAR\'] specification is not empty or exactly one char!');
297            }
298
299            if(!isset($language_data['CASE_KEYWORDS'])) {
300                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'CASE_KEYWORDS\'] specification!');
301            } else if (!is_int($language_data['CASE_KEYWORDS'])) {
302                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'CASE_KEYWORDS\'] specification which is not an integer!');
303            } else if (GESHI_CAPS_NO_CHANGE != $language_data['CASE_KEYWORDS'] &&
304                GESHI_CAPS_LOWER != $language_data['CASE_KEYWORDS'] &&
305                GESHI_CAPS_UPPER != $language_data['CASE_KEYWORDS']) {
306                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'CASE_KEYWORDS\'] specification which is neither of GESHI_CAPS_NO_CHANGE, GESHI_CAPS_LOWER nor GESHI_CAPS_UPPER!');
307            }
308
309            if(!isset($language_data['KEYWORDS'])) {
310                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'KEYWORDS\'] structure to check!');
311            } else if (!is_array($language_data['KEYWORDS'])) {
312                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'KEYWORDS\'] structure which is not an array!');
313            } else {
314                foreach($language_data['KEYWORDS'] as $kw_key => $kw_value) {
315                    if(!is_integer($kw_key)) {
316                        report_error(TYPE_WARNING, "Language file contains an key '$kw_key' in \$language_data['KEYWORDS'] that is not integer!");
317                    } else if (!is_array($kw_value)) {
318                        report_error(TYPE_ERROR, "Language file contains a \$language_data['CASE_SENSITIVE']['$kw_value'] structure which is not an array!");
319                    }
320                }
321            }
322
323            if(!isset($language_data['SYMBOLS'])) {
324                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'SYMBOLS\'] structure to check!');
325            } else if (!is_array($language_data['SYMBOLS'])) {
326                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'SYMBOLS\'] structure which is not an array!');
327            }
328
329            if(!isset($language_data['CASE_SENSITIVE'])) {
330                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'CASE_SENSITIVE\'] structure to check!');
331            } else if (!is_array($language_data['CASE_SENSITIVE'])) {
332                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'CASE_SENSITIVE\'] structure which is not an array!');
333            } else {
334                foreach($language_data['CASE_SENSITIVE'] as $cs_key => $cs_value) {
335                    if(!is_integer($cs_key)) {
336                        report_error(TYPE_WARNING, "Language file contains an key '$cs_key' in \$language_data['CASE_SENSITIVE'] that is not integer!");
337                    } else if (!is_bool($cs_value)) {
338                        report_error(TYPE_ERROR, "Language file contains a Case Sensitivity specification for \$language_data['CASE_SENSITIVE']['$cs_value'] which is not a boolean!");
339                    }
340                }
341            }
342
343            if(!isset($language_data['URLS'])) {
344                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'URLS\'] structure to check!');
345            } else if (!is_array($language_data['URLS'])) {
346                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'URLS\'] structure which is not an array!');
347            } else {
348                foreach($language_data['URLS'] as $url_key => $url_value) {
349                    if(!is_integer($url_key)) {
350                        report_error(TYPE_WARNING, "Language file contains an key '$url_key' in \$language_data['URLS'] that is not integer!");
351                    } else if (!is_string($url_value)) {
352                        report_error(TYPE_ERROR, "Language file contains a Documentation URL specification for \$language_data['URLS']['$url_value'] which is not a string!");
353                    } else if (preg_match('#&([^;]*(=|$))#U', $url_value)) {
354                        report_error(TYPE_ERROR, "Language file contains unescaped ampersands (&amp;) in \$language_data['URLS']!");
355                    }
356                }
357            }
358
359            if(!isset($language_data['OOLANG'])) {
360                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'OOLANG\'] specification!');
361            } else if (!is_int($language_data['OOLANG']) && !is_bool($language_data['OOLANG'])) {
362                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'OOLANG\'] specification which is neither boolean nor integer!');
363            } else if (false !== $language_data['OOLANG'] &&
364                true !== $language_data['OOLANG'] &&
365                2 !== $language_data['OOLANG']) {
366                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'OOLANG\'] specification which is neither of false, true or 2!');
367            }
368
369            if(!isset($language_data['OBJECT_SPLITTERS'])) {
370                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'OBJECT_SPLITTERS\'] structure to check!');
371            } else if (!is_array($language_data['OBJECT_SPLITTERS'])) {
372                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'OBJECT_SPLITTERS\'] structure which is not an array!');
373            }
374
375            if(!isset($language_data['REGEXPS'])) {
376                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'REGEXPS\'] structure to check!');
377            } else if (!is_array($language_data['REGEXPS'])) {
378                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'REGEXPS\'] structure which is not an array!');
379            }
380
381            if(!isset($language_data['STRICT_MODE_APPLIES'])) {
382                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'STRICT_MODE_APPLIES\'] specification!');
383            } else if (!is_int($language_data['STRICT_MODE_APPLIES'])) {
384                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'STRICT_MODE_APPLIES\'] specification which is not an integer!');
385            } else if (GESHI_MAYBE != $language_data['STRICT_MODE_APPLIES'] &&
386                GESHI_ALWAYS != $language_data['STRICT_MODE_APPLIES'] &&
387                GESHI_NEVER != $language_data['STRICT_MODE_APPLIES']) {
388                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'STRICT_MODE_APPLIES\'] specification which is neither of GESHI_MAYBE, GESHI_ALWAYS nor GESHI_NEVER!');
389            }
390
391            if(!isset($language_data['SCRIPT_DELIMITERS'])) {
392                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'SCRIPT_DELIMITERS\'] structure to check!');
393            } else if (!is_array($language_data['SCRIPT_DELIMITERS'])) {
394                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'SCRIPT_DELIMITERS\'] structure which is not an array!');
395            }
396
397            if(!isset($language_data['HIGHLIGHT_STRICT_BLOCK'])) {
398                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'HIGHLIGHT_STRICT_BLOCK\'] structure to check!');
399            } else if (!is_array($language_data['HIGHLIGHT_STRICT_BLOCK'])) {
400                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'HIGHLIGHT_STRICT_BLOCK\'] structure which is not an array!');
401            }
402
403            if(isset($language_data['TAB_WIDTH'])) {
404                if (!is_int($language_data['TAB_WIDTH'])) {
405                    report_error(TYPE_ERROR, 'Language file contains a $language_data[\'TAB_WIDTH\'] specification which is not an integer!');
406                } else if (1 > $language_data['TAB_WIDTH']) {
407                    report_error(TYPE_ERROR, 'Language file contains a $language_data[\'TAB_WIDTH\'] specification which is less than 1!');
408                }
409            }
410
411            if(isset($language_data['PARSER_CONTROL'])) {
412                if (!is_array($language_data['PARSER_CONTROL'])) {
413                    report_error(TYPE_ERROR, 'Language file contains a $language_data[\'PARSER_CONTROL\'] structure which is not an array!');
414                }
415            }
416
417            if(!isset($language_data['STYLES'])) {
418                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'STYLES\'] structure to check!');
419            } else if (!is_array($language_data['STYLES'])) {
420                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'STYLES\'] structure which is not an array!');
421            } else {
422                $style_arrays = array('KEYWORDS', 'COMMENTS', 'ESCAPE_CHAR',
423                    'BRACKETS', 'STRINGS', 'NUMBERS', 'METHODS', 'SYMBOLS',
424                    'REGEXPS', 'SCRIPT');
425                foreach($style_arrays as $style_kind) {
426                    if(!isset($language_data['STYLES'][$style_kind])) {
427                        report_error(TYPE_ERROR, "Language file contains no \$language_data['STYLES']['$style_kind'] structure to check!");
428                    } else if (!is_array($language_data['STYLES'][$style_kind])) {
429                        report_error(TYPE_ERROR, "Language file contains a \$language_data['STYLES\']['$style_kind'] structure which is not an array!");
430                    } else {
431                        foreach($language_data['STYLES'][$style_kind] as $sk_key => $sk_value) {
432                            if(!is_int($sk_key) && ('COMMENTS' != $style_kind && 'MULTI' != $sk_key)
433                                && !(('STRINGS' == $style_kind || 'ESCAPE_CHAR' == $style_kind) && 'HARD' == $sk_key)) {
434                                report_error(TYPE_WARNING, "Language file contains an key '$sk_key' in \$language_data['STYLES']['$style_kind'] that is not integer!");
435                            } else if (!is_string($sk_value)) {
436                                report_error(TYPE_WARNING, "Language file contains a CSS specification for \$language_data['STYLES']['$style_kind'][$key] which is not a string!");
437                            }
438                        }
439                    }
440                }
441
442                unset($style_arrays);
443            }
444        }
445
446        if(!$error_abort) {
447            //Initial sanity checks survived? --> Let's dig deeper!
448            foreach($language_data['KEYWORDS'] as $key => $keywords) {
449                if(!isset($language_data['CASE_SENSITIVE'][$key])) {
450                    report_error(TYPE_ERROR, "Language file contains no \$language_data['CASE_SENSITIVE'] specification for keyword group $key!");
451                }
452                if(!isset($language_data['URLS'][$key])) {
453                    report_error(TYPE_ERROR, "Language file contains no \$language_data['URLS'] specification for keyword group $key!");
454                }
455                if(empty($keywords)) {
456                    report_error(TYPE_WARNING, "Language file contains an empty keyword list in \$language_data['KEYWORDS'] for group $key!");
457                }
458                foreach($keywords as $id => $kw) {
459                    if(!is_string($kw)) {
460                        report_error(TYPE_WARNING, "Language file contains an non-string entry at \$language_data['KEYWORDS'][$key][$id]!");
461                    } else if (!strlen($kw)) {
462                        report_error(TYPE_ERROR, "Language file contains an empty string entry at \$language_data['KEYWORDS'][$key][$id]!");
463                    } else if (preg_match('/^([\(\)\{\}\[\]\^=.,:;\-+\*\/%\$\"\'\?]|&[\w#]\w*;)+$/i', $kw)) {
464                        report_error(TYPE_NOTICE, "Language file contains an keyword ('$kw') at \$language_data['KEYWORDS'][$key][$id] which seems to be better suited for the symbols section!");
465                    }
466                }
467                if(count($keywords) != count(array_unique($keywords))) {
468                    $kw_diffs = array_count_values($keywords);
469                    foreach($kw_diffs as $kw => $kw_count) {
470                        if($kw_count > 1) {
471                            report_error(TYPE_WARNING, "Language file contains per-group duplicate keyword '$kw' in \$language_data['KEYWORDS'][$key]!");
472                        }
473                    }
474                }
475            }
476
477            $disallowed_before = "(?<![a-zA-Z0-9\$_\|\#;>|^&";
478            $disallowed_after = "(?![a-zA-Z0-9_\|%\\-&;";
479
480            foreach($language_data['KEYWORDS'] as $key => $keywords) {
481                foreach($language_data['KEYWORDS'] as $key2 => $keywords2) {
482                    if($key2 <= $key) {
483                        continue;
484                    }
485                    $kw_diffs = array_intersect($keywords, $keywords2);
486                    foreach($kw_diffs as $kw) {
487                        if(isset($language_data['PARSER_CONTROL']['KEYWORDS'])) {
488                            //Check the precondition\post-cindition for the involved keyword groups
489                            $g1_pre = $disallowed_before;
490                            $g2_pre = $disallowed_before;
491                            $g1_post = $disallowed_after;
492                            $g2_post = $disallowed_after;
493                            if(isset($language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_BEFORE'])) {
494                                $g1_pre = $language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_BEFORE'];
495                                $g2_pre = $language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_BEFORE'];
496                            }
497                            if(isset($language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_AFTER'])) {
498                                $g1_post = $language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_AFTER'];
499                                $g2_post = $language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_AFTER'];
500                            }
501
502                            if(isset($language_data['PARSER_CONTROL']['KEYWORDS'][$key]['DISALLOWED_BEFORE'])) {
503                                $g1_pre = $language_data['PARSER_CONTROL']['KEYWORDS'][$key]['DISALLOWED_BEFORE'];
504                            }
505                            if(isset($language_data['PARSER_CONTROL']['KEYWORDS'][$key]['DISALLOWED_AFTER'])) {
506                                $g1_post = $language_data['PARSER_CONTROL']['KEYWORDS'][$key]['DISALLOWED_AFTER'];
507                            }
508
509                            if(isset($language_data['PARSER_CONTROL']['KEYWORDS'][$key2]['DISALLOWED_BEFORE'])) {
510                                $g2_pre = $language_data['PARSER_CONTROL']['KEYWORDS'][$key2]['DISALLOWED_BEFORE'];
511                            }
512                            if(isset($language_data['PARSER_CONTROL']['KEYWORDS'][$key2]['DISALLOWED_AFTER'])) {
513                                $g2_post = $language_data['PARSER_CONTROL']['KEYWORDS'][$key2]['DISALLOWED_AFTER'];
514                            }
515
516                            if($g1_pre != $g2_pre || $g1_post != $g2_post) {
517                                continue;
518                            }
519                        }
520                        report_error(TYPE_WARNING, "Language file contains cross-group duplicate keyword '$kw' in \$language_data['KEYWORDS'][$key] and \$language_data['KEYWORDS'][$key2]!");
521                    }
522                }
523            }
524            foreach($language_data['CASE_SENSITIVE'] as $key => $keywords) {
525                if(!isset($language_data['KEYWORDS'][$key]) && $key != GESHI_COMMENTS) {
526                    report_error(TYPE_WARNING, "Language file contains an superfluous \$language_data['CASE_SENSITIVE'] specification for non-existing keyword group $key!");
527                }
528            }
529            foreach($language_data['URLS'] as $key => $keywords) {
530                if(!isset($language_data['KEYWORDS'][$key])) {
531                    report_error(TYPE_WARNING, "Language file contains an superfluous \$language_data['URLS'] specification for non-existing keyword group $key!");
532                }
533            }
534            foreach($language_data['STYLES']['KEYWORDS'] as $key => $keywords) {
535                if(!isset($language_data['KEYWORDS'][$key])) {
536                    report_error(TYPE_WARNING, "Language file contains an superfluous \$language_data['STYLES']['KEYWORDS'] specification for non-existing keyword group $key!");
537                }
538            }
539
540            foreach($language_data['COMMENT_SINGLE'] as $ck => $cv) {
541                if(!is_int($ck)) {
542                    report_error(TYPE_WARNING, "Language file contains an key '$ck' in \$language_data['COMMENT_SINGLE'] that is not integer!");
543                }
544                if(!is_string($cv)) {
545                    report_error(TYPE_WARNING, "Language file contains an non-string entry at \$language_data['COMMENT_SINGLE'][$ck]!");
546                }
547                if(!isset($language_data['STYLES']['COMMENTS'][$ck])) {
548                    report_error(TYPE_WARNING, "Language file contains no \$language_data['STYLES']['COMMENTS'] specification for comment group $ck!");
549                }
550            }
551            if(isset($language_data['COMMENT_REGEXP'])) {
552                foreach($language_data['COMMENT_REGEXP'] as $ck => $cv) {
553                    if(!is_int($ck)) {
554                        report_error(TYPE_WARNING, "Language file contains an key '$ck' in \$language_data['COMMENT_REGEXP'] that is not integer!");
555                    }
556                    if(!is_string($cv)) {
557                        report_error(TYPE_WARNING, "Language file contains an non-string entry at \$language_data['COMMENT_REGEXP'][$ck]!");
558                    }
559                    if(!isset($language_data['STYLES']['COMMENTS'][$ck])) {
560                        report_error(TYPE_WARNING, "Language file contains no \$language_data['STYLES']['COMMENTS'] specification for comment group $ck!");
561                    }
562                }
563            }
564            foreach($language_data['STYLES']['COMMENTS'] as $ck => $cv) {
565                if($ck != 'MULTI' && !isset($language_data['COMMENT_SINGLE'][$ck]) &&
566                    !isset($language_data['COMMENT_REGEXP'][$ck])) {
567                    report_error(TYPE_NOTICE, "Language file contains an superfluous \$language_data['STYLES']['COMMENTS'] specification for Single Line or Regular-Expression Comment key $ck!");
568                }
569            }
570            if (isset($language_data['STYLES']['STRINGS']['HARD'])) {
571                if (empty($language_data['HARDQUOTE'])) {
572                    report_error(TYPE_NOTICE, "Language file contains superfluous \$language_data['STYLES']['STRINGS'] specification for key 'HARD', but no 'HARDQUOTE's are defined!");
573                }
574                unset($language_data['STYLES']['STRINGS']['HARD']);
575            }
576            foreach($language_data['STYLES']['STRINGS'] as $sk => $sv) {
577                if($sk && !isset($language_data['QUOTEMARKS'][$sk])) {
578                    report_error(TYPE_NOTICE, "Language file contains an superfluous \$language_data['STYLES']['STRINGS'] specification for non-existing quotemark key $sk!");
579                }
580            }
581
582            foreach($language_data['REGEXPS'] as $rk => $rv) {
583                if(!is_int($rk)) {
584                    report_error(TYPE_WARNING, "Language file contains an key '$rk' in \$language_data['REGEXPS'] that is not integer!");
585                }
586                if(is_string($rv)) {
587                    //Check for unmasked / in regular expressions ...
588                    if(empty($rv)) {
589                        report_error(TYPE_WARNING, "Language file contains an empty regular expression at \$language_data['REGEXPS'][$rk]!");
590                    } else {
591                        if(preg_match("/(?<!\\\\)\//s", $rv)) {
592                            report_error(TYPE_WARNING, "Language file contains a regular expression with an unmasked / character at \$language_data['REGEXPS'][$rk]!");
593                        } elseif (preg_match("/(?<!<)(\\\\\\\\)*\\\\\|(?!>)/s", $rv)) {
594                            report_error(TYPE_WARNING, "Language file contains a regular expression with an unescaped match for a pipe character '|' which needs escaping as '&lt;PIPE&gt;' instead at \$language_data['REGEXPS'][$rk]!");
595                        }
596                    }
597                } elseif(is_array($rv)) {
598                    if(!isset($rv[GESHI_SEARCH])) {
599                        report_error(TYPE_ERROR, "Language file contains no GESHI_SEARCH entry in extended regular expression at \$language_data['REGEXPS'][$rk]!");
600                    } elseif(!is_string($rv[GESHI_SEARCH])) {
601                        report_error(TYPE_ERROR, "Language file contains a GESHI_SEARCH entry in extended regular expression at \$language_data['REGEXPS'][$rk] which is not a string!");
602                    } else {
603                        if(preg_match("/(?<!\\\\)\//s", $rv[GESHI_SEARCH])) {
604                            report_error(TYPE_WARNING, "Language file contains a regular expression with an unmasked / character at \$language_data['REGEXPS'][$rk]!");
605                        } elseif (preg_match("/(?<!<)(\\\\\\\\)*\\\\\|(?!>)/s", $rv[GESHI_SEARCH])) {
606                            report_error(TYPE_WARNING, "Language file contains a regular expression with an unescaped match for a pipe character '|' which needs escaping as '&lt;PIPE&gt;' instead at \$language_data['REGEXPS'][$rk]!");
607                        }
608                    }
609                    if(!isset($rv[GESHI_REPLACE])) {
610                        report_error(TYPE_WARNING, "Language file contains no GESHI_REPLACE entry in extended regular expression at \$language_data['REGEXPS'][$rk]!");
611                    } elseif(!is_string($rv[GESHI_REPLACE])) {
612                        report_error(TYPE_ERROR, "Language file contains a GESHI_REPLACE entry in extended regular expression at \$language_data['REGEXPS'][$rk] which is not a string!");
613                    }
614                    if(!isset($rv[GESHI_MODIFIERS])) {
615                        report_error(TYPE_WARNING, "Language file contains no GESHI_MODIFIERS entry in extended regular expression at \$language_data['REGEXPS'][$rk]!");
616                    } elseif(!is_string($rv[GESHI_MODIFIERS])) {
617                        report_error(TYPE_ERROR, "Language file contains a GESHI_MODIFIERS entry in extended regular expression at \$language_data['REGEXPS'][$rk] which is not a string!");
618                    }
619                    if(!isset($rv[GESHI_BEFORE])) {
620                        report_error(TYPE_WARNING, "Language file contains no GESHI_BEFORE entry in extended regular expression at \$language_data['REGEXPS'][$rk]!");
621                    } elseif(!is_string($rv[GESHI_BEFORE])) {
622                        report_error(TYPE_ERROR, "Language file contains a GESHI_BEFORE entry in extended regular expression at \$language_data['REGEXPS'][$rk] which is not a string!");
623                    }
624                    if(!isset($rv[GESHI_AFTER])) {
625                        report_error(TYPE_WARNING, "Language file contains no GESHI_AFTER entry in extended regular expression at \$language_data['REGEXPS'][$rk]!");
626                    } elseif(!is_string($rv[GESHI_AFTER])) {
627                        report_error(TYPE_ERROR, "Language file contains a GESHI_AFTER entry in extended regular expression at \$language_data['REGEXPS'][$rk] which is not a string!");
628                    }
629                } else {
630                    report_error(TYPE_WARNING, "Language file contains an non-string and non-array entry at \$language_data['REGEXPS'][$rk]!");
631                }
632                if(!isset($language_data['STYLES']['REGEXPS'][$rk])) {
633                    report_error(TYPE_WARNING, "Language file contains no \$language_data['STYLES']['REGEXPS'] specification for regexp group $rk!");
634                }
635            }
636            foreach($language_data['STYLES']['REGEXPS'] as $rk => $rv) {
637                if(!isset($language_data['REGEXPS'][$rk])) {
638                    report_error(TYPE_NOTICE, "Language file contains an superfluous \$language_data['STYLES']['REGEXPS'] specification for regexp key $rk!");
639                }
640            }
641
642
643        }
644
645        output_error_cache();
646
647        flush();
648
649        if($error_abort) {
650            break;
651        }
652    }
653}
654?></li>
655</ol>
656
657<p>Validation process completed in <?
658$time_end = explode(' ', microtime());
659$time_diff = $time_end[0] + $time_end[1] - $time_start[0] - $time_start[1];
660
661echo sprintf("%.2f", $time_diff);
662?> seconds.</p>
663
664<div id="footer">GeSHi &copy; 2004-2007 Nigel McNie, 2007-2008 Benny Baumann, released under the GNU GPL</div>
665</body>
666</html>
667