diff --git a/ecrire/inc_filtres.php b/ecrire/inc_filtres.php
index e4cd2c5c05f177af2edba8edd5970eeca514cbe7..8b9ad524e4dfe17733d98eb9105a52a206e7b52c 100644
--- a/ecrire/inc_filtres.php
+++ b/ecrire/inc_filtres.php
@@ -276,25 +276,6 @@ function calculer_url($url) {
 	return $l;
 }
 
-//
-// Prend une URL (destinee a l'affichage) et lui ajoute/retire
-// un parametre.
-// Exemples : [(#SELF|parametre_url{suite,18})] (ajout)
-//            [(#SELF|parametre_url{suite,''})] (supprime)
-//            [(#SELF|parametre_url{suite})]    (prend $suite dans la _request)
-// http://www.spip.net/@parametre_url
-//
-function parametre_url($url, $parametre, $valeur = NULL) {
-	$link = new Link(str_replace('&', '&', $url));
-	if ($valeur === NULL)
-		$valeur = _request($parametre);
-	if (empty($valeur))
-		$link->delVar($parametre);
-	else
-		$link->addVar($parametre, $valeur);
-	return quote_amp($link->getUrl());
-}
-
 //
 // Ajouter le &var_recherche=toto dans les boucles de recherche
 //
diff --git a/ecrire/inc_utils.php b/ecrire/inc_utils.php
index 2a26d54a7881868dc1c09f88569bd8d3d6a4cbce..9cc2a98813fb53ae6f40fdfb3748f94a53c50c7f 100644
--- a/ecrire/inc_utils.php
+++ b/ecrire/inc_utils.php
@@ -203,10 +203,104 @@ function spip_query($query) {
 }
 
 
+// Renvoie le _GET ou le _POST emis par l'utilisateur
+function _request($var) {
+	global $_GET, $_POST;
+	if (isset($_GET[$var])) return $_GET[$var];
+	if (isset($_POST[$var])) return $_POST[$var];
+	return NULL;
+}
+
+
+//
+// Prend une URL et lui ajoute/retire un parametre.
+// Exemples : [(#SELF|parametre_url{suite,18})] (ajout)
+//            [(#SELF|parametre_url{suite,''})] (supprime)
+//            [(#SELF|parametre_url{suite})]    (prend $suite dans la _request)
+// http://www.spip.net/@parametre_url
+//
+function parametre_url($url, $c, $v=NULL, $sep='&') {
+
+	// lever l'#ancre
+	if (preg_match(',^([^#]*)(#.*)$,', $url, $r)) {
+		$url = $r[1];
+		$ancre = $r[2];
+	} else
+		$ancre = '';
+
+	// eclater
+	$url = preg_split(',[?]|&|&,', $url);
+
+	// recuperer la base
+	$a = array_shift($url);
+
+	// ajout de la globale ?
+	if ($v === NULL)
+		$v = _request($c);
+
+	// lire les variables et agir
+	foreach ($url as $n => $val) {
+		if (preg_match(',^'.$c.'(=.*)?$,', $val)) {
+			// suppression
+			if (!$v) {
+				unset($url[$n]);
+			} else {
+				$url[$n] = $c.'='.urlencode($v);
+				$v = '';
+			}
+		}
+	}
+
+	// ajouter notre parametre si on ne l'a pas encore trouve
+	if ($v)
+		$url[] = $c.'='.urlencode($v);
+
+	// eliminer les vides
+	$url = array_filter($url);
+
+	// recomposer l'adresse
+	if ($url)
+		$a .= '?' . join($sep, $url);
+
+	return $a . $ancre;
+}
+
+//
+// pour calcul du nom du fichier cache et autres
+//
+function nettoyer_uri() {
+	return preg_replace
+		(',[?&](PHPSESSID|(var_[^=&]*))=[^&]*,i',
+		'', 
+		$GLOBALS['REQUEST_URI']);
+}
+
+//
+// donner l'URL de base d'un lien vers "soi-meme", modulo
+// les trucs inutiles
+//
+function self($root = false) {
+	$url = nettoyer_uri();
+	if (!$root)
+		$url = preg_replace(',^[^?]*/,', '', $url);
+
+	// ajouter le cas echeant les variables _POST
+	foreach ($_POST as $v => $c)
+		if (substr($v,0,3) == 'id_')
+			$url = parametre_url($url, $v, $c, '&');
+
+	// supprimer les variables sans interet
+	if (!_DIR_RESTREINT)
+		preg_replace (',[?&]('
+		.'lang|set_options|set_couleur|set_disp|set_ecran|show_docs'
+		.')=[^&]*,i', '', $url);
+
+	return $url;
+}
+
+
 class Link {
-	var $file;
-	var $vars;
-	var $arrays;
+	var $uri;
 
 	//
 	// Contructeur : a appeler soit avec l'URL du lien a creer,
@@ -214,132 +308,31 @@ class Link {
 	//
 	// parametre $root = demander un lien a partir de la racine du serveur /
 	function Link($url = '', $root = false) {
-		global $_POST;
-		static $link = '';
-
-		$this->vars = array();
-		$this->arrays = array();
-
-		// Normal case
-		if ($link) {
-			if ($url) {
-				$v = split('[\?\&]', $url);
-				list(, $this->file) = each($v);
-				while (list(, $var) = each($v)) {
-					list($name, $value) = split('=', $var, 2);
-					$name = urldecode($name);
-					$value = urldecode($value);
-					if (preg_match(',^(.*)\[\]$,', $name, $regs)) {
-						$this->arrays[$regs[1]][] = $value;
-					}
-					else {
-						$this->vars[$name] = $value;
-					}
-				}
-			}
-			else {
-				$this->file = $link->file;
-				$this->vars = $link->vars;
-				$this->arrays = $link->arrays;
-			}
-			return;
-		}
-
-		// Si aucun URL n'est specifie, creer le lien "propre"
-		// ou l'on supprime de l'URL courant les bidules inutiles
-		if (!$url) {
-			// GET variables are read from the original URL
-			// (_GET may contain additional variables
-			// introduced by rewrite-rules)
-			$url = $GLOBALS['REQUEST_URI'];
-			// Warning !!!! 
-			// since non encoded arguments may be present
-			// (especially those coming from Rewrite Rule)
-			// find the begining of the query string
-			// to compute the script-name
-			if ($v = strpos($url,'?'))
-			  $v = strrpos(substr($url, 0, $v), '/');
-			else $v = strrpos($url, '/');
-			if (!$root) $url = substr($url, $v + 1);
-			if (!$url) $url = "./";
-			if (count($_POST)) {
-				$vars = array();
-				foreach ($_POST as $var => $val)
-					if (preg_match('/^id_/', $var))
-						$vars[$var] = $val;
-			}
-		}
-		$v = split('[\?\&]', $url);
-		list(, $this->file) = each($v);
-		if (!$vars) {
-			while (list(,$var) = each($v)) {
-				list($name, $value) = split('=', $var, 2);
-				$name = urldecode($name);
-				$value = urldecode($value);
-				if (preg_match(',^(.*)\[\]$,', $name, $regs))
-					$vars[$regs[1]][] = $value;
-				else
-					$vars[$name] = $value;
-			}
-		}
-
-		if (is_array($vars)) {
-			foreach ($vars as $name => $value) {
-				// items supprimes
-				if (!preg_match('/^('.
-				(!_DIR_RESTREINT ?
-					'|lang|set_options|set_couleur|set_disp|set_ecran':
-					'var_mode|show_docs')
-				. ')$/i', $name)) {
-					if (is_array($value))
-						$this->arrays[$name] = $value;
-					else
-						$this->vars[$name] = $value;
-				}
-			}
-		}
+		if (!$url)
+			$url = self($root);
+		$this->uri = $url;
 	}
 
 	//
 	// Effacer une variable
 	//
 	function delVar($name) {
-		if(isset($this->vars[$name])) unset($this->vars[$name]);
-		if($this->arrays[$name]) unset($this->arrays[$name]);
+		$this->uri = parametre_url($this->uri, $name, '', '&');
 	}
 
 	//
 	// Ajouter une variable
 	// (si aucune valeur n'est specifiee, prend la valeur globale actuelle)
 	//
-	function addVar($name, $value = '__global__') {
-		if ($value == '__global__') $value = $GLOBALS[$name];
-		if (is_array($value))
-			$this->arrays[$name] = $value;
-		else
-			$this->vars[$name] = $value;
+	function addVar($name, $value = NULL) {
+		$this->uri = parametre_url($this->uri, $name, $value, '&');
 	}
 
 	//
 	// Recuperer l'URL correspondant au lien
 	//
 	function getUrl($anchor = '') {
-		$url = $this->file;
-		if (!$url) $url = './';
-		$query = '';
-		foreach($this->vars as $name => $value) {
-			$query .= '&'.$name;
-			if (strlen($value))
-				$query .= '='.urlencode($value);
-		}
-
-		foreach ($this->arrays as $name => $table)
-		foreach ($table as $value)
-			$query .= '&'.$name.'[]='.urlencode($value);
-
-		if ($query) $query = '?'. substr($query, 1);
-		if ($anchor) $anchor = '#'.$anchor;
-		return "$url$query$anchor";
+		return $this->uri . ($anchor ? '#'.$anchor : '');
 	}
 
 	//
@@ -348,28 +341,18 @@ class Link {
 	//
 
 	function getForm($method = 'get', $query = '', $enctype = '') {
+		include_ecrire('inc_filtres');
+
 		if (preg_match(',^[a-z],i', $query))
 			$action = $query;
 		else
-			$action = $this->file.$query;
+			$action = preg_replace(',[?].*,', '', $this->uri).$query;
 
 		$form = "<form method='$method' action='"
 		.quote_amp($action)."'";
 		if ($enctype) $form .= " enctype='$enctype'";
 		$form .= " style='border: 0px; margin: 0px;'>\n";
-		foreach ($this->vars as $name => $value) {
-			$value = preg_replace(',&amp;(#[0-9]+;),', '&\1',
-				htmlspecialchars($value));
-			$form .= "<input type=\"hidden\" name=\"$name\" "
-				. "value=\"$value\" />\n";
-		}
-		foreach ($this->arrays as $name => $table)
-		foreach ($table as $value) {
-			$value = preg_replace(',&amp;(#[0-9]+;),', '&\1',
-				htmlspecialchars($value));
-			$form .= "<input type=\"hidden\" name=\"".$name."[]\" "
-				. "value=\"".$value."\" />\n";
-		}
+		$form .= form_hidden($this->uri);
 		return $form;
 	}
 }
diff --git a/ecrire/public-balises.php b/ecrire/public-balises.php
index 618566d5e11f1767375bd4d30238997dc299fa0c..1862bce2ea7117933492b64eea8187e30d95f2d0 100644
--- a/ecrire/public-balises.php
+++ b/ecrire/public-balises.php
@@ -720,7 +720,7 @@ function code_invalideur_forums($p, $code) {
 // mettre en cache #SELF car il peut correspondre a une autre page (attaque XSS)
 // http://www.spip.net/@self
 function balise_SELF_dist($p) {
-	$p->code = 'quote_amp($GLOBALS["clean_link"]->getUrl())';
+	$p->code = 'quote_amp(self())';
 	$p->interdire_scripts = false;
 	return $p;
 }
diff --git a/ecrire/public-global.php b/ecrire/public-global.php
index 061341ba0e351c2d730f69ea78c0178eeff986fb..790d07b44db7f97ab6c0ca5610cc4bf51a73ea11 100644
--- a/ecrire/public-global.php
+++ b/ecrire/public-global.php
@@ -320,22 +320,4 @@ function message_erreur_404 ($erreur= "") {
 		     'process_ins' => 'php');
 }
 
-//
-// pour calcul du nom du fichier cache et autres
-//
-
-function nettoyer_uri() {
-	return eregi_replace
-		('[?&](PHPSESSID|(var_[^=&]*))=[^&]*',
-		'', 
-		 $GLOBALS['REQUEST_URI']);
-}
-
-// Renvoie le _GET ou le _POST emis par l'utilisateur
-function _request($var) {
-	global $_GET, $_POST;
-	if (isset($_GET[$var])) return $_GET[$var];
-	if (isset($_POST[$var])) return $_POST[$var];
-	return NULL;
-}
 ?>