peres as $k => $v) {
asort($v);
$dtc->peres[$k] = $v;
}
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");
# $r = $dtc->regles; ksort($r);foreach($r as $l => $v) echo "$l '$v' ", join (', ',array_keys($dtc->attributs[$l])), "
\n";exit;
return $dtc;
}
// Compiler une regle de production en une Regexp qu'on appliquera sur la
// suite des noms de balises separes par des espaces. Du coup:
// supprimer #PCDATA etc, ca ne sert pas pour le controle des balises;
// supprimer les virgules (les sequences sont implicites dans une Regexp)
// conserver | + * ? ( ) qui ont la meme signification en DTD et en Regexp;
// faire suivre chaque nom d'un espace (et supprimer les autres) ...
// et parentheser le tout pour que | + * ? s'applique dessus.
// http://doc.spip.org/@compilerRegle
function compilerRegle($val)
{
$x = str_replace('\s*()\s*','',
preg_replace('/\s*,\s*/','',
preg_replace('/(\w+)\s*/','(\1 )',
preg_replace('/\s*([(+*|])\s*/','\1',
preg_replace('/\s*#\w+\s*[,|]?\s*/','', $val)))));
return $x;
}
// http://doc.spip.org/@analyser_dtd
function analyser_dtd($loc, $avail, &$dtc)
{
if ($avail == 'SYSTEM')
$file = $loc;
else {
$file = sous_repertoire(_DIR_CACHE_XML);
$file .= preg_replace('/[^\w.]/','_', $loc);
}
$dtd = '';
if (@is_readable($file)) {
lire_fichier($file, $dtd);
} else {
if ($avail == 'PUBLIC') {
include_spip('inc/distant');
if ($dtd = trim(recuperer_page($loc)))
ecrire_fichier($file, $dtd, true);
}
}
if (!$dtd) {
spip_log("DTD '$loc' inaccessible");
return false;
} else spip_log("analyse de la DTD $loc ");
while ($dtd) {
if ($dtd[0] != '<')
$r = analyser_dtd_lexeme($dtd, $dtc, $loc);
elseif ($dtd[1] != '!')
$r = analyser_dtd_pi($dtd, $dtc, $loc);
elseif ($dtd[2] == '[')
$r = analyser_dtd_data($dtd, $dtc, $loc);
else switch ($dtd[3]) {
case '%' : $r = analyser_dtd_data($dtd, $dtc, $loc); break;
case 'T' : $r = analyser_dtd_attlist($dtd, $dtc, $loc);break;
case 'L' : $r = analyser_dtd_element($dtd, $dtc, $loc);break;
case 'N' : $r = analyser_dtd_entity($dtd, $dtc, $loc);break;
case 'O' : $r = analyser_dtd_notation($dtd, $dtc, $loc);break;
case '-' : $r = analyser_dtd_comment($dtd, $dtc, $loc); break;
default: $r = -1;
}
if (!is_string($r)) {
spip_log("erreur $r dans la DTD " . substr($dtd,0,80) . ".....");
return false;
}
$dtd = $r;
}
return true;
}
// http://doc.spip.org/@analyser_dtd_comment
function analyser_dtd_comment($dtd, &$dtc, $grammaire){
// ejecter les commentaires, surtout quand ils contiennent du code.
// Option /s car sur plusieurs lignes parfois
if (!preg_match('/^\s*(.*)$/s',$dtd, $m))
return -6;
return $m[1];
}
// http://doc.spip.org/@analyser_dtd_pi
function analyser_dtd_pi($dtd, &$dtc, $grammaire){
if (!preg_match('/^<\?.*?>\s*(.*)$/s', $dtd, $m))
return -10;
return $m[1];
}
// http://doc.spip.org/@analyser_dtd_lexeme
function analyser_dtd_lexeme($dtd, &$dtc, $grammaire){
if (!preg_match(_REGEXP_ENTITY_DEF,$dtd, $m))
return -9;
list(,$s) = $m;
$n = $dtc->macros[$s];
if (is_array($n)) {
// en cas d'inclusion, l'espace de nom est le meme
analyser_dtd($n[1], $n[0], $dtc);
}
return ltrim(substr($dtd,strlen($m[0])));
}
// il faudrait prevoir plusieurs niveaux d'inclusion.
// (Ruby en utilise mais l'erreur est transparente. Scandaleux coup de pot)
// http://doc.spip.org/@analyser_dtd_data
function analyser_dtd_data($dtd, &$dtc, $grammaire){
if (!preg_match('/^\s*(.*)$/s',$dtd, $m))
return -11;
if ($dtc->macros[$m[1]] == 'INCLUDE')
$retour = $m[2] . $m[3];
else $retour = $m[3];
return $retour;
}
// http://doc.spip.org/@analyser_dtd_notation
function analyser_dtd_notation($dtd, &$dtc, $grammaire){
if (!preg_match('/^\s*(.*)$/s',$dtd, $m))
return -8;
spip_log("analyser_dtd_notation a ecrire");
return $m[1];
}
// http://doc.spip.org/@analyser_dtd_entity
function analyser_dtd_entity($dtd, &$dtc, $grammaire)
{
if (!preg_match(_REGEXP_ENTITY_DECL, $dtd, $m))
return -2;
list($t, $term, $nom, $type, $val, $q, $c, $alt, $dtd) = $m;
if (isset($dtc->macros[$nom]) AND $dtc->macros[$nom])
return $dtd;
if (isset($dtc->entites[$nom]))
spip_log("redefinition de l'entite $nom");
if (!$term)
$dtc->entites[$nom] = expanserEntite($val, $dtc->macros);
elseif (!$type)
$dtc->macros[$nom] = expanserEntite($val, $dtc->macros);
elseif (!$alt)
$dtc->macros[$nom] = expanserEntite($val, $dtc->macros);
else {
if (strpos($alt, '/') === false)
$alt = preg_replace(',/[^/]+$,', '/', $grammaire)
. $alt ;
$dtc->macros[$nom] = array($type, $alt);
}
return $dtd;
}
// Dresser le tableau des filles potentielles de l'element
// pour traquer tres vite les illegitimes.
// Si la regle a au moins une sequence (i.e. une virgule)
// ou n'est pas une itération (i.e. se termine par * ou +)
// en faire une RegExp qu'on appliquera aux balises rencontrees.
// Sinon, conserver seulement le type de l'iteration car la traque
// aura fait l'essentiel du controle sans memorisation des balises.
// Fin du controle en finElement
// http://doc.spip.org/@analyser_dtd_element
function analyser_dtd_element($dtd, &$dtc, $grammaire)
{
if (!preg_match('/^]*)>\s*(.*)$/s', $dtd, $m))
return -3;
list(,$nom, $val, $dtd) = $m;
$nom = expanserEntite($nom, $dtc->macros);
$val = compilerRegle(expanserEntite($val, $dtc->macros));
if (isset($dtc->elements[$nom])) {
spip_log("redefinition de l'element $nom dans la DTD");
return -4;
}
$filles = array();
if ($val == '(EMPTY )')
$dtc->regles[$nom] = 'EMPTY';
elseif ($val == '(ANY )')
$dtc->regles[$nom] = 'ANY';
else {
$last = substr($val,-1);
if (preg_match('/ \w/', $val)
OR strpos('*+', $last) === false)
$dtc->regles[$nom] = "/^$val$/";
else
$dtc->regles[$nom] = $last;
$filles = array_values(preg_split('/\W+/', $val,-1, PREG_SPLIT_NO_EMPTY));
foreach ($filles as $k) {
if (!isset($dtc->peres[$k]))
$dtc->peres[$k] = array();
if (!in_array($nom, $dtc->peres[$k]))
$dtc->peres[$k][]= $nom;
}
}
$dtc->elements[$nom]= $filles;
return $dtd;
}
// http://doc.spip.org/@analyser_dtd_attlist
function analyser_dtd_attlist($dtd, &$dtc, $grammaire)
{
if (!preg_match('/^]*)>\s*(.*)/s', $dtd, $m))
return -5;
list(,$nom, $val, $dtd) = $m;
$nom = expanserEntite($nom, $dtc->macros);
$val = expanserEntite($val, $dtc->macros);
if (!isset($dtc->attributs[$nom]))
$dtc->attributs[$nom] = array();
if (preg_match_all("/\s*(\S+)\s+(([(][^)]*[)])|(\S+))\s+([^\s']*)(\s*'[^']*')?/", $val, $r2, PREG_SET_ORDER)) {
foreach($r2 as $m2) {
$v = preg_match('/^\w+$/', $m2[2]) ? $m2[2]
: ('/^' . preg_replace('/\s+/', '', $m2[2]) . '$/');
$m21 = expanserEntite($m2[1], $dtc->macros);
$m25 = expanserEntite($m2[5], $dtc->macros);
$dtc->attributs[$nom][$m21] = array($v, $m25);
}
}
return $dtd;
}
// http://doc.spip.org/@expanserEntite
function expanserEntite($val, $macros)
{
if (preg_match_all(_REGEXP_ENTITY_USE, $val, $r, PREG_SET_ORDER)){
foreach($r as $m) {
$ent = $m[1];
// il peut valoir ""
if (isset($macros[$ent]))
$val = str_replace($m[0], $macros[$ent], $val);
}
}
return trim(preg_replace('/\s+/', ' ', $val));
}
?>