diff --git a/ecrire/exec/messagerie.php b/ecrire/exec/messagerie.php
index 1d0d2533a2b19dc9a585f75a1aef253f757cda29..4971da81dfd1f3d53633e32f00ff17628e02d203 100644
--- a/ecrire/exec/messagerie.php
+++ b/ecrire/exec/messagerie.php
@@ -138,7 +138,9 @@ if (spip_num_rows($result) > 0) {
 		$id_auteur = $row['id_auteur'];
 		$nom = typo($row["nom"]);
 		$total = $row["total"];
-		echo "<div class='tr_liste' onmouseover=\"changeclass(this,'tr_liste_over');\" onmouseout=\"changeclass(this,'tr_liste');\" style='padding: 2px; padding-left: 10px; border-bottom: 1px solid #cccccc;'><div class='verdana1'><img src='" . _DIR_IMG_PACK . "redac-12.gif' style='border: 0px'> <a href='" . generer_url_ecrire("auteurs_edit","id_auteur=$id_auteur") . "'>$nom</a> ($total)</div></div>";
+		echo "<div class='tr_liste'\nonmouseover=\"changeclass(this,'tr_liste_over');\"\nonmouseout=\"changeclass(this,'tr_liste');\"\nstyle='padding: 2px; padding-left: 10px; border-bottom: 1px solid #cccccc;'><div class='verdana1'><img src='" . _DIR_IMG_PACK . "redac-12.gif'\nstyle='border: 0px' alt=' ' /> <a href='" . generer_url_ecrire("auteurs_edit","id_auteur=$id_auteur"), "'>",
+		  $nom,
+		  "</a> ($total)</div></div>";
 		if ($count == ceil(spip_num_rows($result)/2)) echo "</td><td valign='top' width='50%' style='background-color: #eeeeee;'>";
 	}
 	echo "</td></tr></table>";
diff --git a/ecrire/inc/valider_xml.php b/ecrire/inc/valider_xml.php
index 5cec999ea72ccaaac42fe5011ff9bac9683cfc59..56d0e6b7c644e27ede430a31503bc6316398ad54 100644
--- a/ecrire/inc/valider_xml.php
+++ b/ecrire/inc/valider_xml.php
@@ -120,28 +120,29 @@ function analyser_dtd($grammaire, $avail, &$dtc)
 		}
 	}
 
-	// reperer pour chaque noeud ses fils potentiels.
-	// mais tant pis pour leur eventuel ordre de succession (, * +):
-	// les cas sont rares et si aberrants que interet/temps-de-calcul -> 0
+	// memoriser la regle de production de l'element
+	// et dresser le tableau de ses fils potentiels
+	// pour traquer tres vite les balises filles illegitimes
+
 	if (preg_match_all('/<!ELEMENT\s+(\S+)\s+([^>]*)>/', $dtd, $r, PREG_SET_ORDER)) {
-	  foreach($r as $m) {
-	    list(,$nom, $val) = $m;
-	    $nom = expanserEntite($nom, $dtc->macros);
-	    $val = str_replace("\n",' ', trim(expanserEntite($val, $dtc->macros)));
-	    $dtc->regles[$nom]= $val;
-	    $val = array_values(preg_split('/\W+/', $val,-1, PREG_SPLIT_NO_EMPTY));
-	    $dtc->elements[$nom]= $val;
-
-	    foreach ($val as $k) {
-		if (!isset($dtc->peres[$k])
-		OR !in_array($nom, $dtc->peres[$k]))
-			$dtc->peres[$k][]= $nom;
-	    }
-	  }
-	  foreach ($dtc->peres as $k => $v) {
-	    asort($v);
-	    $dtc->peres[$k] = $v;
-	  } 
+		foreach($r as $m) {
+			list(,$nom, $val) = $m;
+			$nom = expanserEntite($nom, $dtc->macros);
+			$val = expanserEntite($val, $dtc->macros);
+			$dtc->regles[$nom]= $val;
+			$val = array_values(preg_split('/\W+/', $val,-1, PREG_SPLIT_NO_EMPTY));
+			$dtc->elements[$nom]= $val;
+
+			foreach ($val as $k) {
+				if (($k != 'EMPTY') AND ($k != 'ANY') AND ($k[0] != '#') AND ((!isset($dtc->peres[$k])) OR !in_array($nom, $dtc->peres[$k])))
+				$dtc->peres[$k][]= $nom;
+			}
+		}
+		// tri pour presenter les suggestions de corrections
+		foreach ($dtc->peres as $k => $v) {
+			asort($v);
+			$dtc->peres[$k] = $v;
+		} 
 	}
 
 	if (preg_match_all('/<!ATTLIST\s+(\S+)\s+([^>]*)>/', $dtd, $r, PREG_SET_ORDER)) {
@@ -177,7 +178,7 @@ function expanserEntite($val, $macros)
 			if (isset($macros[$m[1]]))
 				$val = str_replace($m[0], $macros[$m[1]], $val);
 	}
-	return $val;
+	return trim(preg_replace('/\s+/', ' ', $val));
 }
 
 // http://doc.spip.org/@validerElement
@@ -192,6 +193,12 @@ function validerElement($phraseur, $name, $attrs)
 		$phraseur_xml->err[]= " <b>$name</b>"
 		. _L(' balise inconnue ')
 		.  coordonnees_erreur($phraseur);
+	// controler les filles illegitimes, mais pas le droit d'ainesse
+	// (i.e. l'ordre de succession indique par une virgule dans la regle:
+	// le cas est rare, du coup interet/temps-de-calcul tend vers 0)
+	// Pour XHTML 1.0 il n'y a que <html>, <head> et <table>.
+	// Mais a-t-on jamais mis  head apres body et caption apres tr ?
+	// Quant a Head, c'est juste pour avoir 1 seul title et 1 seul base.
 	else {
 	  $depth = $phraseur_xml->depth;
 	  $ouvrant = $phraseur_xml->ouvrant;
@@ -372,15 +379,24 @@ function finElement($phraseur, $name)
 
 	$regle = $phraseur_xml->dtc->regles[$name];
 	$vide = ($regle  == 'EMPTY');
+	// controler que les balises devant etre vides le sont (ok),
+	// idem pour les nons vides (approximatif, car on ignore "|")
+	// Pour Xhtml 1.0, cette approximation suffit
 	if ($vide) {
 		if ($n <> $k)
 			$phraseur_xml->err[]= " <p><b>$name</b>"
 			.  _L(' balise non vide')
 			.  coordonnees_erreur($phraseur);
 	} elseif ($n == $k) {
-		if (preg_match('/^\(.*\)\+$/', $regle)
-		OR ((strpos($regle,',') !== false)
-			AND strpos($regle,'|') === false)) {
+		if (strpos($regle, '+')) $ok = false;
+		else {
+		  if (!preg_match('/^\(.*(.)$/', $regle,$r))
+			$ok = true; // DATA: ok.
+		  elseif ($r[1] != ')') $ok = true; // i.e. ? ou *: ok
+		  elseif (preg_match('/[^?*)][)]*,/', $regle)) $ok= false;
+		  else $ok= true;
+		}
+		if( !$ok) {
 			$phraseur_xml->err[]= " <p>\n<b>$name</b>"
 			.  _L(' balise vide')
 			.  coordonnees_erreur($phraseur);