[SPIP][PLUGINS] v3.0-->v3.2
[lhc/web/www.git] / www / ecrire / xml / analyser_dtd.php
index f0b432e..7968a98 100644 (file)
@@ -3,33 +3,40 @@
 /***************************************************************************\
  *  SPIP, Systeme de publication pour l'internet                           *
  *                                                                         *
- *  Copyright (c) 2001-2016                                                *
+ *  Copyright (c) 2001-2017                                                *
  *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
  *                                                                         *
  *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
  *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
 \***************************************************************************/
 
-if (!defined('_ECRIRE_INC_VERSION')) return;
+if (!defined('_ECRIRE_INC_VERSION')) {
+       return;
+}
 
 include_spip('xml/interfaces');
 
-// http://doc.spip.org/@charger_dtd
-function charger_dtd($grammaire, $avail, $rotlvl)
-{
+// http://code.spip.net/@charger_dtd
+function charger_dtd($grammaire, $avail, $rotlvl) {
        static $dtd = array(); # cache bien utile pour le validateur en boucle
 
-       if (isset($dtd[$grammaire]))
+       if (isset($dtd[$grammaire])) {
                return $dtd[$grammaire];
+       }
 
-       if ($avail == 'SYSTEM') $grammaire = find_in_path($grammaire);
+       if ($avail == 'SYSTEM') {
+               $grammaire = find_in_path($grammaire);
+       }
 
-       $file = _DIR_CACHE_XML . preg_replace('/[^\w.]/','_', $rotlvl) . '.gz';
+       $file = _DIR_CACHE_XML . preg_replace('/[^\w.]/', '_', $rotlvl) . '.gz';
 
        if (lire_fichier($file, $r)) {
-               if (!$grammaire) return array();
-               if (($avail == 'SYSTEM') AND filemtime($file) < filemtime($grammaire))
-                               $r = false;
+               if (!$grammaire) {
+                       return array();
+               }
+               if (($avail == 'SYSTEM') and filemtime($file) < filemtime($grammaire)) {
+                       $r = false;
+               }
        }
 
        if ($r) {
@@ -39,22 +46,23 @@ function charger_dtd($grammaire, $avail, $rotlvl)
                $dtc = new DTC;
                // L'analyseur retourne un booleen de reussite et modifie $dtc.
                // Retourner vide en cas d'echec
-               if (!analyser_dtd($grammaire, $avail, $dtc)) 
+               if (!analyser_dtd($grammaire, $avail, $dtc)) {
                        $dtc = array();
-               else {
-               // tri final pour presenter les suggestions de corrections
+               else {
+                       // tri final pour presenter les suggestions de corrections
                        foreach ($dtc->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");
+                       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 "<b>$l</b> '$v' ", count($t), " attributs: ", join (', ',$t);$t=$dtc->peres[$l];echo "<br />",count($t), " peres: ", @join (', ',$t), "<br />\n";}exit;
                        ecrire_fichier($file, serialize($dtc), true);
                }
-               
+
        }
        $dtd[$grammaire] = $dtc;
+
        return $dtc;
 }
 
@@ -66,34 +74,33 @@ function charger_dtd($grammaire, $avail, $rotlvl)
 // 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))))));
+// http://code.spip.net/@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)
-{
+// http://code.spip.net/@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'){
+       if ($avail == 'SYSTEM') {
                $file = $loc;
-               if (_DIR_RACINE AND strncmp($file,_DIR_RACINE,strlen(_DIR_RACINE))==0)
-                       $file = substr($file,strlen(_DIR_RACINE));
-         $file = find_in_path($file);
-       }
-       else {
-         $file .= preg_replace('/[^\w.]/','_', $loc);
+               if (_DIR_RACINE and strncmp($file, _DIR_RACINE, strlen(_DIR_RACINE)) == 0) {
+                       $file = substr($file, strlen(_DIR_RACINE));
+               }
+               $file = find_in_path($file);
+       else {
+               $file .= preg_replace('/[^\w.]/', '_', $loc);
        }
 
        $dtd = '';
@@ -102,145 +109,186 @@ function analyser_dtd($loc, $avail, &$dtc)
        } else {
                if ($avail == 'PUBLIC') {
                        include_spip('inc/distant');
-                       if ($dtd = trim(recuperer_page($loc)))
-                               ecrire_fichier($file, $dtd, true); 
+                       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 ");
+       } else {
+               spip_log("analyse de la DTD $loc ");
+       }
 
        while ($dtd) {
-               if ($dtd[0] != '<')
+               if ($dtd[0] != '<') {
                        $r = analyser_dtd_lexeme($dtd, $dtc, $loc);
-               elseif ($dtd[1] != '!')
+               } elseif ($dtd[1] != '!') {
                        $r = analyser_dtd_pi($dtd, $dtc, $loc);
-               elseif ($dtd[2] == '[')
+               } elseif ($dtd[2] == '[') {
                        $r = analyser_dtd_data($dtd, $dtc, $loc);
-               else {
+               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;
+                               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) . ".....");
+                       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){
+// http://code.spip.net/@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))
+       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))
+// http://code.spip.net/@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){
+// http://code.spip.net/@analyser_dtd_lexeme
+function analyser_dtd_lexeme($dtd, &$dtc, $grammaire) {
 
-       if (!preg_match(_REGEXP_ENTITY_DEF,$dtd, $m))
+       if (!preg_match(_REGEXP_ENTITY_DEF, $dtd, $m)) {
                return -9;
+       }
 
-       list(,$s) = $m;
+       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
+               // 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];
+                       and !tester_url_absolue($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])));
+
+       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){
+// http://code.spip.net/@analyser_dtd_data
+function analyser_dtd_data($dtd, &$dtc, $grammaire) {
 
-       if (!preg_match(_REGEXP_INCLUDE_USE,$dtd,$m))
+       if (!preg_match(_REGEXP_INCLUDE_USE, $dtd, $m)) {
                return -11;
-       if (!preg_match('/^((\s*<!(\[\s*%\s*[^;]*;\s*\[([^]<]*<[^>]*>)*[^]<]*\]\]>)|([^]>]*>))*[^]<]*)\]\]>\s*/s',$m[2], $r))
-         return -12;
+       }
+       if (!preg_match('/^((\s*<!(\[\s*%\s*[^;]*;\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]));
+       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('/^<!NOTATION.*?>\s*(.*)$/s',$dtd, $m))
+// http://code.spip.net/@analyser_dtd_notation
+function analyser_dtd_notation($dtd, &$dtc, $grammaire) {
+       if (!preg_match('/^<!NOTATION.*?>\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))
+// http://code.spip.net/@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;
+       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])
+       if (isset($dtc->macros[$nom]) and $dtc->macros[$nom]) {
                return $dtd;
-       if (isset($dtc->entites[$nom]))
+       }
+       if (isset($dtc->entites[$nom])) {
                spip_log("redefinition de l'entite $nom");
-       if ($k6) return $k6 . $dtd; // cas du synonyme complet
+       }
+       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)) {
+       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)
+       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 {
+               else {
                        if (($type == 'PUBLIC')
-                       AND (strpos($alt, '/') === false))
+                               and (strpos($alt, '/') === false)
+                       ) {
                                $alt = preg_replace(',/[^/]+$,', '/', $grammaire)
-                               . $alt ;
+                                       . $alt;
+                       }
                        $dtc->macros[$nom] = array($type, $alt);
                }
-       } 
+       }
 
        return $dtd;
 }
@@ -248,70 +296,77 @@ function analyser_dtd_entity($dtd, &$dtc, $grammaire)
 // 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 +)
+// 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('/^<!ELEMENT\s+([^>\s]+)([^>]*)>\s*(.*)$/s', $dtd, $m))
+// http://code.spip.net/@analyser_dtd_element
+function analyser_dtd_element($dtd, &$dtc, $grammaire) {
+       if (!preg_match('/^<!ELEMENT\s+([^>\s]+)([^>]*)>\s*(.*)$/s', $dtd, $m)) {
                return -3;
+       }
 
-       list(,$nom, $contenu, $dtd) = $m;
+       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 )')
+       if ($val == '(?:EMPTY )') {
                $dtc->regles[$nom] = 'EMPTY';
-       elseif  ($val == '(?:ANY )')
+       } elseif ($val == '(?:ANY )') {
                $dtc->regles[$nom] = 'ANY';
-       else {
-               $last = substr($val,-1);
+       else {
+               $last = substr($val, -1);
                if (preg_match('/ \w/', $val)
-               OR strpos('*+', $last) === false)
+                       or (!empty($last) and strpos('*+?', $last) === false)
+               ) {
                        $dtc->regles[$nom] = "/^$val$/";
-               else
+               } else {
                        $dtc->regles[$nom] = $last;
-                       $filles = array_values(preg_split('/\W+/', $val,-1, PREG_SPLIT_NO_EMPTY));
+               }
+               $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;
+               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;
+       $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('/^<!ATTLIST\s+(\S+)\s+([^>]*)>\s*(.*)/s', $dtd, $m))
+// http://code.spip.net/@analyser_dtd_attlist
+function analyser_dtd_attlist($dtd, &$dtc, $grammaire) {
+       if (!preg_match('/^<!ATTLIST\s+(\S+)\s+([^>]*)>\s*(.*)/s', $dtd, $m)) {
                return -5;
+       }
 
-       list(,$nom, $val, $dtd) = $m;
+       list(, $nom, $val, $dtd) = $m;
        $nom = expanserEntite($nom, $dtc->macros);
        $val = expanserEntite($val, $dtc->macros);
-       if (!isset($dtc->attributs[$nom]))
+       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) {
+               foreach ($r2 as $m2) {
                        $v = preg_match('/^\w+$/', $m2[2]) ? $m2[2]
-                         : ('/^' . preg_replace('/\s+/', '', $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);
@@ -322,30 +377,38 @@ function analyser_dtd_attlist($dtd, &$dtc, $grammaire)
 }
 
 
-// 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())
-{
+/**
+ * Remplace dans la chaîne `$val` les sous-chaines de forme `%NOM;`
+ * par leur definition dans le tableau `$macros`
+ *
+ * Si le premier argument n'est pas une chaîne,
+ * retourne les statistiques (pour debug de DTD, inutilise en mode normal)
+ *
+ * @param string $val
+ * @param array $macros
+ * @return string|array
+ **/
+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);
+       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 {
+                               if (!isset($vu[$ent])) {
+                                       $vu[$ent] = 0;
+                               }
+                               ++$vu[$ent];
+                               $val = str_replace($m[0], $macros[$ent], $val);
+                       }
                }
-         }
        }
 
        return trim(preg_replace('/\s+/', ' ', $val));
 }
-?>