X-Git-Url: http://git.cyclocoop.org/?p=velocampus%2Fweb%2Fwww.git;a=blobdiff_plain;f=www%2Fecrire%2Fxml%2Fanalyser_dtd.php;fp=www%2Fecrire%2Fxml%2Fanalyser_dtd.php;h=25c691c26b0fcbec1ea7e46174a532fdaffb6e1e;hp=0000000000000000000000000000000000000000;hb=80b4d3e85f78d402ed2e73f8f5d1bf4c19962eed;hpb=aaf970bf4cdaf76689ecc10609048e18d073820c diff --git a/www/ecrire/xml/analyser_dtd.php b/www/ecrire/xml/analyser_dtd.php new file mode 100644 index 0000000..25c691c --- /dev/null +++ b/www/ecrire/xml/analyser_dtd.php @@ -0,0 +1,347 @@ +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) {$t=array_keys($dtc->attributs[$l]);echo "$l '$v' ", count($t), " attributs: ", join (', ',$t);$t=$dtc->peres[$l];echo "
",count($t), " peres: ", @join (', ',$t), "
\n";}exit; + ecrire_fichier($file, serialize($dtc), true); + } + + } + $dtd[$grammaire] = $dtc; + 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('()','', + preg_replace('/\s*,\s*/','', + preg_replace('/(\w+)\s*/','(\1 )', + preg_replace('/\s*\)/',')', + 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) +{ + // creer le repertoire de cache si ce n'est fait + // (utile aussi pour le resultat de la compil) + $file = sous_repertoire(_DIR_CACHE_XML); + // si DTD locale, ignorer ce repertoire pour le moment + if ($avail == 'SYSTEM') + $file = find_in_path($loc); + else { + $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); + } + } + + $dtd = ltrim($dtd); + if (!$dtd) { + spip_log("DTD '$loc' ($file) 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 + // mais gaffe aux DTD dont l'URL est relative a l'engloblante + if (($n[0] == 'PUBLIC') + AND !preg_match("%^http://%", $n[1])) { + $n[1] = substr($grammaire,0, strrpos($grammaire,'/')+1) . $n[1]; + } + analyser_dtd($n[1], $n[0], $dtc); + } + + return ltrim(substr($dtd,strlen($m[0]))); +} + +// il faudrait gerer plus proprement les niveaux d'inclusion: +// ca ne depasse pas 3 ici. + +// http://doc.spip.org/@analyser_dtd_data +function analyser_dtd_data($dtd, &$dtc, $grammaire){ + + if (!preg_match(_REGEXP_INCLUDE_USE,$dtd,$m)) + return -11; + if (!preg_match('/^((\s*]*>)*[^]<]*\]\]>)|([^]>]*>))*[^]<]*)\]\]>\s*/s',$m[2], $r)) + return -12; + + if ($dtc->macros[$m[1]] == 'INCLUDE') + $retour = $r[1] . substr($m[2], strlen($r[0])); + else $retour = substr($m[2], strlen($r[0])); + + 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, $k1,$k2,$k3,$k4,$k5,$k6, $c, $q, $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 ($k6) return $k6 . $dtd; // cas du synonyme complet + $val = expanserEntite(($k2 ? $k3 : ($k4 ? $k5 : $k6)), $dtc->macros); + + // cas particulier double evaluation: 'PUBLIC "..." "...."' + if (preg_match('/(PUBLIC|SYSTEM)\s+"([^"]*)"\s*("([^"]*)")?\s*$/s',$val,$r)) { + list($t, $type, $val, $q, $alt) = $r; + } + + if (!$term) + $dtc->entites[$nom] = $val; + elseif (!$type) + $dtc->macros[$nom] = $val; + else { + if (($type == 'SYSTEM') AND !$alt) $alt = $val; + if (!$alt) + $dtc->macros[$nom] = $val; + else { + if (($type == 'PUBLIC') + AND (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*(.*)$/s', $dtd, $m)) + return -3; + + list(,$nom, $contenu, $dtd) = $m; + $nom = expanserEntite($nom, $dtc->macros); + + if (isset($dtc->elements[$nom])) { + spip_log("redefinition de l'element $nom dans la DTD"); + return -4; + } + $filles = array(); + $contenu = expanserEntite($contenu, $dtc->macros); + $val = $contenu ? compilerRegle($contenu) : '(EMPTY )'; + 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->pcdata[$nom]= (strpos($contenu, '#PCDATA')===false); + $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; +} + + +// Remplace dans la chaine $val les sous-chaines de forme "%NOM;" +// par leur definition dans le tableau $macros +// Si le premier argument n'est pas une chaine, +// retourne les statistiques (pour debug de DTD, inutilise en mode normal) + +// http://doc.spip.org/@expanserEntite +function expanserEntite($val, $macros=array()) +{ + static $vu = array(); + if (!is_string($val)) return $vu; + + 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])) + spip_log("Entite $ent inconnu"); + else { + @$vu[$ent]++; + $val = str_replace($m[0], $macros[$ent], $val); + } + } + } + + return trim(preg_replace('/\s+/', ' ', $val)); +} +?>