[SPIP] +2.1.12
[velocampus/web/www.git] / www / ecrire / xml / sax.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2011 *
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('inc/charsets');
16 include_spip('xml/interfaces');
17
18 // http://doc.spip.org/@xml_debutElement
19 function xml_debutElement($phraseur, $name, $attrs)
20 {
21 $depth = $phraseur->depth;
22
23 $t = isset($phraseur->ouvrant[$depth]) ? $phraseur->ouvrant[$depth] : ' ';
24 // espace initial signifie: deja integree au resultat
25 if ($t[0] != ' ')
26 {
27 $phraseur->res .= '<' . $t . '>';
28 $phraseur->ouvrant[$depth] = ' ' . $t;
29 }
30 $t = $phraseur->contenu[$depth];
31 // n'indenter que s'il y a un separateur avant
32 $phraseur->res .= preg_replace("/[\n\t ]+$/", "\n$depth", $t);
33 $phraseur->contenu[$depth] = "";
34 $att = '';
35 $sep = ' ';
36 foreach ($attrs as $k => $v) {
37 $delim = strpos($v, "'") === false ? "'" : '"';
38 $val = htmlspecialchars($v,ENT_QUOTES);
39 $att .= $sep . $k . "=" . $delim
40 . ($delim !== '"' ? str_replace('&quot;', '"', $val) : $val)
41 . $delim;
42 $sep = "\n $depth";
43 }
44 $phraseur->depth .= ' ';
45 $phraseur->contenu[$phraseur->depth] = "";
46 $phraseur->ouvrant[$phraseur->depth] = $name . $att;
47 $phraseur->reperes[$phraseur->depth] = xml_get_current_line_number($phraseur->sax);
48 }
49
50 // http://doc.spip.org/@xml_finElement
51 function xml_finElement($phraseur, $name, $fusion_bal=false)
52 {
53 $ouv = $phraseur->ouvrant[$phraseur->depth];
54
55 if ($ouv[0] != ' ')
56 $phraseur->ouvrant[$phraseur->depth] = ' ' . $ouv;
57 else $ouv= "";
58 $t = $phraseur->contenu[$phraseur->depth];
59 $phraseur->depth = substr($phraseur->depth, 2);
60 $t = preg_replace("/[\n\t ]+$/", "\n" . $phraseur->depth, $t);
61
62 // fusion <balise></balise> en <balise />.
63 // ATTENTION, certains clients http croient que fusion ==> pas d'atttributs
64 // en particulier pour les balises Script et A.
65 // en presence d'attributs ne le faire que si la DTD est dispo et d'accord
66 // (param fusion_bal)
67
68 if ($t || (($ouv != $name) AND !$fusion_bal))
69 $phraseur->res .= ($ouv ? ('<' . $ouv . '>') : '') . $t . "</" . $name . ">";
70 else
71 $phraseur->res .= ($ouv ? ('<' . $ouv . ' />') : ("</" . $name . ">"));
72 }
73
74 // http://doc.spip.org/@xml_textElement
75 function xml_textElement($phraseur, $data)
76 {
77 $depth = $phraseur->depth;
78 $phraseur->contenu[$depth] .= preg_match('/^script/',$phraseur->ouvrant[$depth])
79 ? $data
80 : htmlspecialchars($data,ENT_QUOTES);
81 }
82
83 function xml_piElement($phraseur, $target, $data)
84 {
85 $depth = $phraseur->depth;
86
87 if (strtolower($target) != "php")
88 $phraseur->contenu[$depth] .= $data;
89 else {
90 ob_start();
91 eval($data);
92 $data = ob_get_contents();
93 ob_end_clean();
94 $phraseur->contenu[$depth] .= $data;
95 }
96 }
97
98
99 // http://doc.spip.org/@xml_defautElement
100 function xml_defaultElement($phraseur, $data)
101 {
102 $depth = $phraseur->depth;
103
104 if (!isset($phraseur->contenu[$depth])) $phraseur->contenu[$depth]='';
105 $phraseur->contenu[$depth] .= $data;
106 }
107
108 // http://doc.spip.org/@xml_parsestring
109 function xml_parsestring($phraseur, $data)
110 {
111 $phraseur->contenu[$phraseur->depth] ='';
112
113 if (!xml_parse($phraseur->sax, $data, true)) {
114 coordonnees_erreur($phraseur,
115 xml_error_string(xml_get_error_code($phraseur->sax))
116 . "<br />\n" .
117 (!$phraseur->depth ? '' :
118 ('(' .
119 _T('erreur_balise_non_fermee') .
120 " <tt>" .
121 $phraseur->ouvrant[$phraseur->depth] .
122 "</tt> " .
123 _T('ligne') .
124 " " .
125 $phraseur->reperes[$phraseur->depth] .
126 ") <br />\n" )));
127 }
128 }
129
130 // http://doc.spip.org/@coordonnees_erreur
131 function coordonnees_erreur($phraseur, $msg)
132 {
133 $entete_length = substr_count($phraseur->entete,"\n");
134 $phraseur->err[] = array($msg,
135 xml_get_current_line_number($phraseur->sax) + $entete_length,
136 xml_get_current_column_number($phraseur->sax));
137 }
138
139 // http://doc.spip.org/@xml_sax_dist
140 function xml_sax_dist($page, $apply=false, $phraseur=NULL)
141 {
142 // init par defaut si pas fait (compatibilite Tidy espace public)
143 if (!$phraseur) {
144 $indenter_xml = charger_fonction('indenter', 'xml');
145 return $indenter_xml($page, $apply);
146 }
147
148 $xml_parser = xml_parser_create($GLOBALS['meta']['charset']);
149
150 xml_set_element_handler($xml_parser,
151 array($phraseur, "debutElement"),
152 array($phraseur, "finElement"));
153
154 xml_set_character_data_handler($xml_parser,
155 array($phraseur, "textElement"));
156
157 xml_set_processing_instruction_handler($xml_parser,
158 array($phraseur, 'piElement'));
159
160 xml_set_default_handler($xml_parser,
161 array($phraseur, "defaultElement"));
162
163 xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, false);
164
165 if ($apply) {
166 ob_start();
167 if (is_array($apply))
168 $r = call_user_func_array($page, $apply);
169 else $r = $page();
170 $page = ob_get_contents();
171 ob_end_clean();
172 // fonction sans aucun "echo", ca doit etre le resultat
173 if (!$page) $page = $r;
174 }
175
176 // charger la DTD et transcoder les entites,
177 // et escamoter le doctype que sax mange en php5 mais pas en php4
178 list($entete,$page, $dtc) = sax_bug($page);
179
180 $phraseur->sax = $xml_parser;
181 $phraseur->entete = $entete;
182 $phraseur->page = $page;
183 $phraseur->dtc = $dtc;
184 $phraseur->phraserTout($xml_parser, $page);
185 xml_parser_free($xml_parser);
186 }
187
188 // SAX ne dit pas si une Entite est dans un attribut ou non.
189 // Les eliminer toutes sinon celles des attributs apparaissent en zone texte!
190 // Celles fondamentales pour la lecture (lt gt quot amp) sont conservees
191 // (d'ailleurs SAX ne les considere pas comme des entites dans un attribut)
192 // Si la DTD est dispo, on va chercher les entites dedans
193 // sinon on se rabat sur ce qu'en connait SPIP en standard.
194
195 // http://doc.spip.org/@sax_bug
196 function sax_bug($data)
197 {
198 $r = analyser_doctype($data);
199
200 if (!$r) {
201 $data = _MESSAGE_DOCTYPE . _DOCTYPE_ECRIRE
202 . preg_replace(_REGEXP_DOCTYPE, '', $data);
203 $r = analyser_doctype($data);
204 }
205
206 list($doctype, $topelement, $avail, $grammaire, $rotlvl, $len) = $r;
207
208 include_spip('xml/analyser_dtd');
209 $dtc = charger_dtd($grammaire, $avail, $rotlvl);
210
211 // l'entete contient eventuellement < ? xml... ? >, le Doctype,
212 // et des commentaires autour d'eux
213 $entete = ltrim(substr($data,0,$len));
214
215 if ($dtc) {
216 $trans = array();
217
218 foreach($dtc->entites as $k => $v) {
219 if (!strpos(" amp lt gt quot ", $k))
220 $trans["&$k;"] = $v;
221 }
222 $data = strtr(substr($data,$len), $trans);
223 } else {
224 $data = html2unicode(substr($data,$len), true);
225 }
226 return array($entete,unicode2charset($data), $dtc);
227 }
228
229 // Reperer le Doctype et le decomposer selon:
230 // http://www.freebsd.org/doc/fr_FR.ISO8859-1/books/fdp-primer/sgml-primer-doctype-declaration.html
231 // Si pas de Doctype et premiere balise = RSS prendre la doctype RSS 0.91:
232 // les autres formats RSS n'ont pas de DTD,
233 // mais un XML Schema que SPIP ne fait pas encore lire.
234 // http://doc.spip.org/@analyser_doctype
235 function analyser_doctype($data)
236 {
237 if (!preg_match(_REGEXP_DOCTYPE, $data, $page)) {
238 if (preg_match(_REGEXP_XML, $data, $page)) {
239 list(,$pico, $topelement) = $page;
240 $pico = strlen($pico);
241 if ($topelement == 'rss')
242 return array('',
243 'rss',
244 'PUBLIC',
245 _DOCTYPE_RSS,
246 'rss-0.91.dtd',
247 $pico);
248 else {
249 $dtd = $topelement . '.dtd';
250 $f = find_in_path($dtd);
251 if (file_exists($f))
252 return array('', $topelement, 'SYSTEM', $f, $dtd, $pico);
253 }
254 }
255 spip_log("Dtd pas vu pour " . substr($data, 0, 100));
256 return array();
257 }
258 list($doctype,$pico, $topelement, $avail,$suite) = $page;
259
260 if (!preg_match('/^"([^"]*)"\s*(.*)$/', $suite, $r))
261 if (!preg_match("/^'([^']*)'\s*(.*)$/", $suite, $r))
262 return array();
263 list(,$rotlvl, $suite) = $r;
264
265 if (!$suite) {
266 if ($avail != 'SYSTEM') return array();
267 $grammaire = $rotlvl;
268 $rotlvl = '';
269 } else {
270 if (!preg_match('/^"([^"]*)"\s*$/', $suite, $r))
271 if (!preg_match("/^'([^']*)'\s*$/", $suite, $r))
272 return array();
273
274 $grammaire = $r[1];
275 }
276
277 return array(substr($doctype,strlen($pico)), $topelement, $avail, $grammaire, $rotlvl, strlen($doctype));
278 }
279 ?>