accueil: correction d'un lien (pour Gustavo)
[aidenligne_francais_universite.git] / ecrire / xml / analyser_dtd.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2007 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
8 * *
9 * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
10 * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
11 \***************************************************************************/
12
13 if (!defined("_ECRIRE_INC_VERSION")) return;
14
15 include_spip('xml/interfaces');
16
17 function charger_dtd($grammaire, $avail)
18 {
19 spip_timer('dtd');
20 $dtc = new DTC;
21 // L'analyseur retourne un booleen de reussite et modifie $dtc.
22 // Retourner vide en cas d'echec
23 if (!analyser_dtd($grammaire, $avail, $dtc)) return array();
24
25 // tri final pour presenter les suggestions de corrections
26 foreach ($dtc->peres as $k => $v) {
27 asort($v);
28 $dtc->peres[$k] = $v;
29 }
30
31 spip_log("Analyser DTD $avail $grammaire (" . spip_timer('dtd') . ") " . count($dtc->macros) . ' macros, ' . count($dtc->elements) . ' elements, ' . count($dtc->attributs) . " listes d'attributs, " . count($dtc->entites) . " entites");
32 # $r = $dtc->regles; ksort($r);foreach($r as $l => $v) echo "<b>$l</b> '$v' ", join (', ',array_keys($dtc->attributs[$l])), "<br />\n";exit;
33 return $dtc;
34 }
35
36 // Compiler une regle de production en une Regexp qu'on appliquera sur la
37 // suite des noms de balises separes par des espaces. Du coup:
38 // supprimer #PCDATA etc, ca ne sert pas pour le controle des balises;
39 // supprimer les virgules (les sequences sont implicites dans une Regexp)
40 // conserver | + * ? ( ) qui ont la meme signification en DTD et en Regexp;
41 // faire suivre chaque nom d'un espace (et supprimer les autres) ...
42 // et parentheser le tout pour que | + * ? s'applique dessus.
43
44 // http://doc.spip.org/@compilerRegle
45 function compilerRegle($val)
46 {
47 $x = str_replace('\s*()\s*','',
48 preg_replace('/\s*,\s*/','',
49 preg_replace('/(\w+)\s*/','(\1 )',
50 preg_replace('/\s*([(+*|])\s*/','\1',
51 preg_replace('/\s*#\w+\s*[,|]?\s*/','', $val)))));
52 return $x;
53 }
54
55
56 // http://doc.spip.org/@analyser_dtd
57 function analyser_dtd($loc, $avail, &$dtc)
58 {
59 if ($avail == 'SYSTEM')
60 $file = $loc;
61 else {
62 $file = sous_repertoire(_DIR_CACHE_XML);
63 $file .= preg_replace('/[^\w.]/','_', $loc);
64 }
65
66 $dtd = '';
67 if (@is_readable($file)) {
68 lire_fichier($file, $dtd);
69 } else {
70 if ($avail == 'PUBLIC') {
71 include_spip('inc/distant');
72 if ($dtd = trim(recuperer_page($loc)))
73 ecrire_fichier($file, $dtd, true);
74 }
75 }
76
77 if (!$dtd) {
78 spip_log("DTD '$loc' inaccessible");
79 return false;
80 } else spip_log("analyse de la DTD $loc ");
81
82 while ($dtd) {
83 if ($dtd[0] != '<')
84 $r = analyser_dtd_lexeme($dtd, $dtc, $loc);
85 elseif ($dtd[1] != '!')
86 $r = analyser_dtd_pi($dtd, $dtc, $loc);
87 elseif ($dtd[2] == '[')
88 $r = analyser_dtd_data($dtd, $dtc, $loc);
89 else switch ($dtd[3]) {
90 case '%' : $r = analyser_dtd_data($dtd, $dtc, $loc); break;
91 case 'T' : $r = analyser_dtd_attlist($dtd, $dtc, $loc);break;
92 case 'L' : $r = analyser_dtd_element($dtd, $dtc, $loc);break;
93 case 'N' : $r = analyser_dtd_entity($dtd, $dtc, $loc);break;
94 case 'O' : $r = analyser_dtd_notation($dtd, $dtc, $loc);break;
95 case '-' : $r = analyser_dtd_comment($dtd, $dtc, $loc); break;
96 default: $r = -1;
97 }
98 if (!is_string($r)) {
99 spip_log("erreur $r dans la DTD " . substr($dtd,0,80) . ".....");
100 return false;
101 }
102 $dtd = $r;
103 }
104 return true;
105 }
106
107 // http://doc.spip.org/@analyser_dtd_comment
108 function analyser_dtd_comment($dtd, &$dtc, $grammaire){
109 // ejecter les commentaires, surtout quand ils contiennent du code.
110 // Option /s car sur plusieurs lignes parfois
111
112 if (!preg_match('/^<!--.*?-->\s*(.*)$/s',$dtd, $m))
113 return -6;
114 return $m[1];
115 }
116
117 // http://doc.spip.org/@analyser_dtd_pi
118 function analyser_dtd_pi($dtd, &$dtc, $grammaire){
119 if (!preg_match('/^<\?.*?>\s*(.*)$/s', $dtd, $m))
120 return -10;
121 return $m[1];
122 }
123
124 // http://doc.spip.org/@analyser_dtd_lexeme
125 function analyser_dtd_lexeme($dtd, &$dtc, $grammaire){
126 if (!preg_match(_REGEXP_ENTITY_DEF,$dtd, $m))
127 return -9;
128
129 list(,$s) = $m;
130 $n = $dtc->macros[$s];
131 if (is_array($n)) {
132 // en cas d'inclusion, l'espace de nom est le meme
133 analyser_dtd($n[1], $n[0], $dtc);
134 }
135
136 return ltrim(substr($dtd,strlen($m[0])));
137 }
138
139 // il faudrait prevoir plusieurs niveaux d'inclusion.
140 // (Ruby en utilise mais l'erreur est transparente. Scandaleux coup de pot)
141
142 // http://doc.spip.org/@analyser_dtd_data
143 function analyser_dtd_data($dtd, &$dtc, $grammaire){
144 if (!preg_match('/^<!\[\s*%\s*([^;]*);\s*\[\s*(.*?)\]\]>\s*(.*)$/s',$dtd, $m))
145 return -11;
146 if ($dtc->macros[$m[1]] == 'INCLUDE')
147 $retour = $m[2] . $m[3];
148 else $retour = $m[3];
149 return $retour;
150 }
151
152 // http://doc.spip.org/@analyser_dtd_notation
153 function analyser_dtd_notation($dtd, &$dtc, $grammaire){
154 if (!preg_match('/^<!NOTATION.*?>\s*(.*)$/s',$dtd, $m))
155 return -8;
156 spip_log("analyser_dtd_notation a ecrire");
157 return $m[1];
158 }
159
160 // http://doc.spip.org/@analyser_dtd_entity
161 function analyser_dtd_entity($dtd, &$dtc, $grammaire)
162 {
163 if (!preg_match(_REGEXP_ENTITY_DECL, $dtd, $m))
164 return -2;
165
166 list($t, $term, $nom, $type, $val, $q, $c, $alt, $dtd) = $m;
167
168 if (isset($dtc->macros[$nom]) AND $dtc->macros[$nom])
169 return $dtd;
170 if (isset($dtc->entites[$nom]))
171 spip_log("redefinition de l'entite $nom");
172 if (!$term)
173 $dtc->entites[$nom] = expanserEntite($val, $dtc->macros);
174 elseif (!$type)
175 $dtc->macros[$nom] = expanserEntite($val, $dtc->macros);
176 elseif (!$alt)
177 $dtc->macros[$nom] = expanserEntite($val, $dtc->macros);
178 else {
179 if (strpos($alt, '/') === false)
180 $alt = preg_replace(',/[^/]+$,', '/', $grammaire)
181 . $alt ;
182 $dtc->macros[$nom] = array($type, $alt);
183 }
184 return $dtd;
185 }
186
187 // Dresser le tableau des filles potentielles de l'element
188 // pour traquer tres vite les illegitimes.
189 // Si la regle a au moins une sequence (i.e. une virgule)
190 // ou n'est pas une itération (i.e. se termine par * ou +)
191 // en faire une RegExp qu'on appliquera aux balises rencontrees.
192 // Sinon, conserver seulement le type de l'iteration car la traque
193 // aura fait l'essentiel du controle sans memorisation des balises.
194 // Fin du controle en finElement
195
196 // http://doc.spip.org/@analyser_dtd_element
197 function analyser_dtd_element($dtd, &$dtc, $grammaire)
198 {
199 if (!preg_match('/^<!ELEMENT\s+(\S+)\s+([^>]*)>\s*(.*)$/s', $dtd, $m))
200 return -3;
201
202 list(,$nom, $val, $dtd) = $m;
203 $nom = expanserEntite($nom, $dtc->macros);
204 $val = compilerRegle(expanserEntite($val, $dtc->macros));
205 if (isset($dtc->elements[$nom])) {
206 spip_log("redefinition de l'element $nom dans la DTD");
207 return -4;
208 }
209 $filles = array();
210 if ($val == '(EMPTY )')
211 $dtc->regles[$nom] = 'EMPTY';
212 elseif ($val == '(ANY )')
213 $dtc->regles[$nom] = 'ANY';
214 else {
215 $last = substr($val,-1);
216 if (preg_match('/ \w/', $val)
217 OR strpos('*+', $last) === false)
218 $dtc->regles[$nom] = "/^$val$/";
219 else
220 $dtc->regles[$nom] = $last;
221 $filles = array_values(preg_split('/\W+/', $val,-1, PREG_SPLIT_NO_EMPTY));
222
223 foreach ($filles as $k) {
224 if (!isset($dtc->peres[$k]))
225 $dtc->peres[$k] = array();
226 if (!in_array($nom, $dtc->peres[$k]))
227 $dtc->peres[$k][]= $nom;
228 }
229 }
230 $dtc->elements[$nom]= $filles;
231 return $dtd;
232 }
233
234
235 // http://doc.spip.org/@analyser_dtd_attlist
236 function analyser_dtd_attlist($dtd, &$dtc, $grammaire)
237 {
238 if (!preg_match('/^<!ATTLIST\s+(\S+)\s+([^>]*)>\s*(.*)/s', $dtd, $m))
239 return -5;
240
241 list(,$nom, $val, $dtd) = $m;
242 $nom = expanserEntite($nom, $dtc->macros);
243 $val = expanserEntite($val, $dtc->macros);
244 if (!isset($dtc->attributs[$nom]))
245 $dtc->attributs[$nom] = array();
246
247 if (preg_match_all("/\s*(\S+)\s+(([(][^)]*[)])|(\S+))\s+([^\s']*)(\s*'[^']*')?/", $val, $r2, PREG_SET_ORDER)) {
248 foreach($r2 as $m2) {
249 $v = preg_match('/^\w+$/', $m2[2]) ? $m2[2]
250 : ('/^' . preg_replace('/\s+/', '', $m2[2]) . '$/');
251 $m21 = expanserEntite($m2[1], $dtc->macros);
252 $m25 = expanserEntite($m2[5], $dtc->macros);
253 $dtc->attributs[$nom][$m21] = array($v, $m25);
254 }
255 }
256
257 return $dtd;
258 }
259
260
261 // http://doc.spip.org/@expanserEntite
262 function expanserEntite($val, $macros)
263 {
264 if (preg_match_all(_REGEXP_ENTITY_USE, $val, $r, PREG_SET_ORDER)){
265 foreach($r as $m) {
266 $ent = $m[1];
267 // il peut valoir ""
268 if (isset($macros[$ent]))
269 $val = str_replace($m[0], $macros[$ent], $val);
270 }
271 }
272 return trim(preg_replace('/\s+/', ' ', $val));
273 }
274
275
276
277 ?>