[ PHPXref.com ] [ Generated: Sun Jul 20 20:25:42 2008 ] [ SPIP 1.8.3 ]
[ Index ]     [ Variables ]     [ Functions ]     [ Classes ]     [ Constants ]     [ Statistics ]

title

Body

[close]

/ecrire/ -> inc_texte.php3 (source)

   1  <?php
   2  
   3  /***************************************************************************\
   4   *  SPIP, Systeme de publication pour l'internet                           *
   5   *                                                                         *
   6   *  Copyright (c) 2001-2005                                                *
   7   *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
   8   *                                                                         *
   9   *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
  10   *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
  11  \***************************************************************************/
  12  
  13  
  14  //
  15  // Ce fichier ne sera execute qu'une fois
  16  if (defined("_ECRIRE_INC_TEXTE")) return;
  17  define("_ECRIRE_INC_TEXTE", "1");
  18  
  19  include_ecrire("inc_filtres.php3");
  20  
  21  
  22  //
  23  // Gerer les variables de personnalisation, qui peuvent provenir
  24  // des fichiers d'appel, en verifiant qu'elles n'ont pas ete passees
  25  // par le visiteur (sinon, pas de cache)
  26  //
  27  function tester_variable($var, $val){
  28      if (!isset($GLOBALS[$var]))
  29          $GLOBALS[$var] = $val;
  30  
  31      if (
  32          $_REQUEST[$var] !== NULL
  33          AND $GLOBALS[$var] == $_REQUEST[$var]
  34      )
  35          die ("tester_variable: $var interdite");
  36  }
  37  
  38  tester_variable('debut_intertitre', "\n<h3 class=\"spip\">");
  39  tester_variable('fin_intertitre', "</h3>\n");
  40  tester_variable('ligne_horizontale', "\n<hr class=\"spip\" />\n");
  41  tester_variable('ouvre_ref', '&nbsp;[');
  42  tester_variable('ferme_ref', ']');
  43  tester_variable('ouvre_note', '[');
  44  tester_variable('ferme_note', '] ');
  45  tester_variable('les_notes', '');
  46  tester_variable('compt_note', 0);
  47  tester_variable('nombre_surligne', 4);
  48  tester_variable('url_glossaire_externe', "http://@lang@.wikipedia.org/wiki/");
  49  
  50  
  51  // On ne prend la $puce_rtl par defaut que si $puce n'a pas ete redefinie
  52  
  53  //if (!tester_variable('puce', "<li class='spip_puce' style='list-style-image: url(puce.gif)'>")) {
  54  if (!tester_variable('puce', "<img class='spip_puce' src='puce.gif' alt='-' />&nbsp;")) {
  55      tester_variable('puce_rtl', "<img class='spip_puce' src='puce_rtl.gif' alt='-' />&nbsp;");
  56  }
  57  
  58  
  59  //
  60  // Diverses fonctions essentielles
  61  //
  62  
  63  // Ne pas afficher le chapo si article virtuel
  64  function nettoyer_chapo($chapo){
  65      if (substr($chapo,0,1) == "="){
  66          $chapo = "";
  67      }
  68      return $chapo;
  69  }
  70  
  71  // points d'entree de pre- et post-traitement pour propre() et typo()
  72  function spip_avant_propre ($letexte) {
  73      $letexte = avant_propre_ancres($letexte);
  74      $letexte = extraire_multi($letexte);
  75  
  76      if (function_exists('avant_propre'))
  77          $letexte = avant_propre($letexte);
  78  
  79      return $letexte;
  80  }
  81  
  82  function spip_apres_propre ($letexte) {
  83      if (function_exists('apres_propre'))
  84          $letexte = apres_propre($letexte);
  85  
  86      return $letexte;
  87  }
  88  
  89  function spip_avant_typo ($letexte) {
  90      $letexte = extraire_multi($letexte);
  91      $letexte = avant_typo_smallcaps($letexte);
  92  
  93      if (function_exists('avant_typo'))
  94          $letexte = avant_typo($letexte);
  95  
  96      return $letexte;
  97  }
  98  
  99  function spip_apres_typo ($letexte) {
 100  
 101      // relecture des &nbsp;
 102      if (!_DIR_RESTREINT AND $GLOBALS['revision_nbsp'])
 103          $letexte = str_replace('&nbsp;',
 104              '<span class="spip-nbsp">&nbsp;</span>', $letexte);
 105  
 106      if (function_exists('apres_typo'))
 107          $letexte = apres_typo($letexte);
 108  
 109      return $letexte;
 110  }
 111  
 112  
 113  //
 114  // Mise de cote des echappements
 115  //
 116  
 117  // Definition de la regexp de echappe_html
 118  define ('__regexp_echappe',
 119          "/(" . "<html>((.*?))<\/html>" . ")|("    #html
 120          . "<code>((.*?))<\/code>" . ")|("    #code
 121          . "<(cadre|frame)>((.*?))<\/(cadre|frame)>" #cadre
 122          . ")|("
 123          . "<(poesie|poetry)>((.*?))<\/(poesie|poetry)>" #poesie
 124          . ")/si");
 125  define('__preg_img', ',<(img|doc|emb)([0-9]+)(\|([^>]*))?'.'>,i');
 126  
 127  function echappe_html($letexte, $source='SOURCEPROPRE', $no_transform=false) {
 128      if (preg_match_all(__regexp_echappe, $letexte, $matches, PREG_SET_ORDER))
 129      foreach ($matches as $regs) {
 130          $num_echap++;
 131          $marqueur_echap = "@@SPIP_$source$num_echap@@";
 132  
 133          if ($no_transform) {    // echappements bruts
 134              $les_echap[$num_echap] = $regs[0];
 135          }
 136          else
 137          if ($regs[1]) {
 138              // Echapper les <html>...</ html>
 139              $les_echap[$num_echap] = $regs[2];
 140          }
 141          else
 142          if ($regs[4]) {
 143              // Echapper les <code>...</ code>
 144              $lecode = entites_html($regs[5]);
 145  
 146              // supprimer les sauts de ligne debut/fin (mais pas les espaces => ascii art).
 147              $lecode = ereg_replace("^\n+|\n+$", "", $lecode);
 148  
 149              // ne pas mettre le <div...> s'il n'y a qu'une ligne
 150              if (is_int(strpos($lecode,"\n"))) {
 151                  $lecode = nl2br("<div style='text-align: left;' class='spip_code' dir='ltr'><tt>".$lecode."</tt></div>");
 152                  $marqueur_echap = "</no p>$marqueur_echap<no p>";
 153              } else
 154                  $lecode = "<span class='spip_code' dir='ltr'><tt>".$lecode."</tt></span>";
 155  
 156              $lecode = str_replace("\t", "&nbsp; &nbsp; &nbsp; &nbsp; ", $lecode);
 157              $lecode = str_replace("  ", " &nbsp;", $lecode);
 158              $les_echap[$num_echap] = $lecode;
 159          }
 160          else
 161          if ($regs[7]) {
 162              // Echapper les <cadre>...</cadre>
 163              $lecode = trim(entites_html($regs[9]));
 164              $total_lignes = substr_count($lecode, "\n");
 165  
 166              $les_echap[$num_echap] = "<form action=\"/\" method=\"get\"><div><textarea readonly='readonly' cols='40' rows='$total_lignes' class='spip_cadre' dir='ltr'>".$lecode."</textarea></div></form>";
 167              // Les marques ci-dessous indiquent qu'on ne veut pas paragrapher
 168              $marqueur_echap = "\n\n</no p>$marqueur_echap<no p>\n\n";
 169          }
 170          else
 171          if ($regs[12]) {
 172              $lecode = $regs[14];
 173              $lecode = ereg_replace("\n[[:space:]]*\n", "\n&nbsp;\n",$lecode);
 174              $lecode = str_replace("\r", "\n", $lecode); # gestion des \r a revoir !
 175              $lecode = "<div class=\"spip_poesie\"><div>".ereg_replace("\n+", "</div>\n<div>", $lecode)."</div></div>";
 176              $marqueur_echap = "\n\n</no p>$marqueur_echap<no p>\n\n";
 177              $les_echap[$num_echap] = propre($lecode);
 178          } 
 179  
 180          $letexte = str_replace($regs[0], $marqueur_echap, $letexte);
 181      }
 182  
 183      // Gestion du TeX
 184      if (!(strpos($letexte, "<math>") === false)) {
 185          include_ecrire("inc_math.php3");
 186          $letexte = traiter_math($letexte, $les_echap, $num_echap, $source);
 187      }
 188  
 189      // Traitement des images et documents <IMGxx|right>
 190      // (insertion dans echappe_retour pour faciliter les doublons)
 191      if (preg_match_all(__preg_img, $letexte, $matches, PREG_SET_ORDER)) {
 192          foreach ($matches as $match) {
 193              $num_echap++;
 194  
 195              // Si le resultat contient des <div>, forcer la sortie du paragraphe
 196              if ( $match[1] == "doc" OR $match[1] == "emb" OR eregi("^\|center", $match[3]) )  {
 197                  $letexte = str_replace($match[0],
 198                      "\n\n</no p>@@SPIP_$source$num_echap@@<no p>\n\n", $letexte);
 199              } else {
 200              $letexte = str_replace($match[0],
 201                  "@@SPIP_$source$num_echap@@", $letexte);
 202              }
 203              $les_echap[$num_echap] = $match;
 204          }
 205      }
 206  
 207      return array($letexte, $les_echap);
 208  }
 209  
 210  // Traitement final des echappements
 211  function echappe_retour($letexte, $les_echap, $source='') {
 212      $expr = ",@@SPIP_$source([0-9]+)@@,";
 213      if (preg_match_all($expr, $letexte, $regs, PREG_SET_ORDER)) {
 214          foreach ($regs as $reg) {
 215              $rempl = $les_echap[$reg[1]];
 216  # si $rempl est un tableau, c'est le resultat (cf echappe_html) de eregi sur :
 217  # <(IMG|DOC|EMB)([0-9]+)(\|([^\>]*))?
 218              if (is_array($rempl)) {
 219                  include_ecrire("inc_documents.php3");
 220                  $type = strtoupper($rempl[1]);
 221                  if ($type == 'EMB')
 222                      $rempl = embed_document($rempl[2], $rempl[4]);
 223                  else
 224                      $rempl = integre_image($rempl[2], $rempl[4], $type);
 225                      
 226              }
 227              $letexte = str_replace($reg[0], $rempl, $letexte);
 228          }
 229      }
 230      return $letexte;
 231  }
 232  
 233  
 234  // fonction en cas de texte extrait d'un serveur distant:
 235  // on ne sait pas (encore) rapatrier les documents joints
 236  
 237  function supprime_img($letexte) {
 238      $message = _T('img_indisponible');
 239      preg_replace(__preg_img, "($message)", $letexte);
 240      return $letexte;
 241  }
 242  
 243  # il y a 3 couples de fonctions homonymes au prefixe _doublons pres
 244  # pour eviter a tous les appels de se trimbaler ce qui concerne les squelettes
 245  
 246  function echappe_retour_doublon($letexte, $les_echap, $source, &$doublons)
 247  {
 248    if (!$les_echap)
 249      return $letexte;
 250    foreach($les_echap as $rempl) {
 251      if (is_array($rempl)) # cf commentaires dans la fonction echappe_retour
 252        $doublons['documents'] .= "," . $rempl[2];
 253    }
 254    return echappe_retour($letexte, $les_echap, $source);
 255  }
 256  
 257  //
 258  // Gerer les outils mb_string
 259  //
 260  function spip_substr($c, $start=0, $end='') {
 261      include_ecrire('inc_charsets.php3');
 262      if (init_mb_string()) {
 263          if ($end)
 264              return mb_substr($c, $start, $end);
 265          else
 266              return mb_substr($c, $start);
 267      }
 268  
 269      // methode substr normale
 270      else {
 271          if ($end)
 272              return substr($c, $start, $end);
 273          else
 274              return substr($c, $start);
 275      }
 276  }
 277  
 278  function spip_strlen($c) {
 279      include_ecrire('inc_charsets.php3');
 280      if (init_mb_string())
 281          return mb_strlen($c);
 282      else
 283          return strlen($c);
 284  }
 285  // fin mb_string
 286  
 287  
 288  function couper($texte, $taille=50) {
 289      $texte = substr($texte, 0, 400 + 2*$taille); /* eviter de travailler sur 10ko pour extraire 150 caracteres */
 290  
 291      // on utilise les \r pour passer entre les gouttes
 292      $texte = str_replace("\r\n", "\n", $texte);
 293      $texte = str_replace("\r", "\n", $texte);
 294  
 295      // sauts de ligne et paragraphes
 296      $texte = ereg_replace("\n\n+", "\r", $texte);
 297      $texte = ereg_replace("<(p|br)( [^>]*)?".">", "\r", $texte);
 298  
 299      // supprimer les traits, lignes etc
 300      $texte = ereg_replace("(^|\r|\n)(-[-#\*]*|_ )", "\r", $texte);
 301  
 302      // supprimer les tags
 303      $texte = supprimer_tags($texte);
 304      $texte = trim(str_replace("\n"," ", $texte));
 305      $texte .= "\n";    // marquer la fin
 306  
 307      // travailler en accents charset
 308      $texte = filtrer_entites($texte);
 309  
 310      // remplacer les liens
 311      if (preg_match_all(',[[]([^][]*)->(>?)([^][]*)[]],', $texte, $regs, PREG_SET_ORDER))
 312          foreach ($regs as $reg) {
 313              if (strlen($reg[1]))
 314                  $titre = $reg[1];
 315              else
 316                  list(,,$titre) = extraire_lien($reg);
 317              $texte = str_replace($reg[0], $titre, $texte);
 318          }
 319  
 320      // supprimer les notes
 321      $texte = ereg_replace("\[\[([^]]|\][^]])*\]\]", "", $texte);
 322  
 323      // supprimer les codes typos
 324      $texte = ereg_replace("[}{]", "", $texte);
 325  
 326      // supprimer les tableaux
 327      $texte = ereg_replace("(^|\r)\|.*\|\r", "\r", $texte);
 328  
 329      // couper au mot precedent
 330      $long = spip_substr($texte, 0, max($taille-4,1));
 331      $court = ereg_replace("([^[:space:]][[:space:]]+)[^[:space:]]*\n?$", "\\1", $long);
 332      $points = '&nbsp;(...)';
 333  
 334      // trop court ? ne pas faire de (...)
 335      if (spip_strlen($court) < max(0.75 * $taille,2)) {
 336          $points = '';
 337          $long = spip_substr($texte, 0, $taille);
 338          $texte = ereg_replace("([^[:space:]][[:space:]]+)[^[:space:]]*$", "\\1", $long);
 339          // encore trop court ? couper au caractere
 340          if (spip_strlen($texte) < 0.75 * $taille)
 341              $texte = $long;
 342      } else
 343          $texte = $court;
 344  
 345      if (strpos($texte, "\n"))    // la fin est encore la : c'est qu'on n'a pas de texte de suite
 346          $points = '';
 347  
 348      // remettre les paragraphes
 349      $texte = ereg_replace("\r+", "\n\n", $texte);
 350  
 351      // supprimer l'eventuelle entite finale mal coupee
 352      $texte = preg_replace('/&#?[a-z0-9]*$/', '', $texte);
 353  
 354      return trim($texte).$points;
 355  }
 356  
 357  // prendre <intro>...</intro> sinon couper a la longueur demandee
 358  function couper_intro($texte, $long) {
 359      $texte = extraire_multi(eregi_replace("(</?)intro>", "\\1intro>", $texte)); // minuscules
 360      while ($fin = strpos($texte, "</intro>")) {
 361          $zone = substr($texte, 0, $fin);
 362          $texte = substr($texte, $fin + strlen("</intro>"));
 363          if ($deb = strpos($zone, "<intro>") OR substr($zone, 0, 7) == "<intro>")
 364              $zone = substr($zone, $deb + 7);
 365          $intro .= $zone;
 366      }
 367  
 368      if ($intro)
 369          $intro = $intro.'&nbsp;(...)';
 370      else
 371          $intro = couper($texte, $long);
 372  
 373      // supprimer un eventuel chapo redirecteur =http:/.....
 374      $intro = ereg_replace("^=[^[:space:]]+","",$intro);
 375  
 376      return $intro;
 377  }
 378  
 379  
 380  //
 381  // Les elements de propre()
 382  //
 383  
 384  // Securite : empecher l'execution de code PHP
 385  function interdire_scripts($source) {
 386      $source = preg_replace(",<(\%|\?|[[:space:]]*(script|base)),ims", "&lt;\\1", $source);
 387      return $source;
 388  }
 389  
 390  // Securite : utiliser SafeHTML s'il est present dans ecrire/safehtml/
 391  function safehtml($t) {
 392      static $process, $test;
 393  
 394      # attention safehtml nettoie deux ou trois caracteres de plus. A voir
 395      if (strpos($t,'<')===false)
 396          return str_replace("\x00", '', $t);
 397  
 398      if (!$test) {
 399          define('XML_HTMLSAX3', _DIR_INCLUDE."safehtml/classes/");
 400          if (@file_exists(XML_HTMLSAX3.'safehtml.php')) {
 401              include_local(XML_HTMLSAX3.'safehtml.php');
 402              $process = new safehtml();
 403          }
 404          if ($process)
 405              $test = 1; # ok
 406          else
 407              $test = -1; # se rabattre sur interdire_scripts
 408      }
 409  
 410      if ($test > 0) {
 411          # reset ($process->clear() ne vide que _xhtml...),
 412          # on doit pouvoir programmer ca plus propremement
 413          $process->_counter = array();
 414          $process->_stack = array();
 415          $process->_dcCounter = array();
 416          $process->_dcStack = array();
 417          $process->_listScope = 0;
 418          $process->_liStack = array();
 419  #        $process->parse(''); # cas particulier ?
 420          $process->clear();
 421          $t = $process->parse($t);
 422      }
 423  
 424      return interdire_scripts($t); # gere le < ?php > en plus
 425  }
 426  
 427  // Passer safehtml sur les rows de l'espace privé
 428  function safehtml_sur_row($row) {
 429      $champs_surs = array(
 430      'date', 'date_heure', 'statut', 'ip', 'url_article', 'maj', 'idx',
 431      'parametres_forum');
 432      foreach($row as $champ => $valeur)
 433          if (!in_array(strtolower($champ), $champs_surs)    AND !preg_match(',^id_,', $champ))
 434              $row[$champ] = safehtml($valeur);
 435      return $row;
 436  }
 437  
 438  // Correction typographique francaise
 439  function typo_fr($letexte) {
 440      static $trans;
 441  
 442      // Nettoyer 160 = nbsp ; 187 = raquo ; 171 = laquo ; 176 = deg ; 147 = ldquo; 148 = rdquo
 443      if (!$trans) {
 444          $trans = array(
 445              "&nbsp;" => "~",
 446              "&raquo;" => "&#187;",
 447              "&laquo;" => "&#171;",
 448              "&rdquo;" => "&#148;",
 449              "&ldquo;" => "&#147;",
 450              "&deg;" => "&#176;"
 451          );
 452          $chars = array(160 => '~', 187 => '&#187;', 171 => '&#171;', 148 => '&#148;', 147 => '&#147;', 176 => '&#176;');
 453  
 454          include_ecrire('inc_charsets.php3');
 455          while (list($c, $r) = each($chars)) {
 456              $c = unicode2charset(charset2unicode(chr($c), 'iso-8859-1', 'forcer'));
 457              $trans[$c] = $r;
 458          }
 459      }
 460  
 461      $letexte = strtr($letexte, $trans);
 462  
 463      $cherche1 = array(
 464          /* 1        '/{([^}]+)}/',  */
 465          /* 2 */     '/((^|[^\#0-9a-zA-Z\&])[\#0-9a-zA-Z]*)\;/',
 466          /* 3 */        '/&#187;| --?,|:([^0-9]|$)/',
 467          /* 4 */        '/([^<!?])([!?])/',
 468          /* 5 */        '/&#171;|(M(M?\.|mes?|r\.?)|[MnN]&#176;) /'
 469      );
 470      $remplace1 = array(
 471          /* 1        '<i class="spip">\1</i>', */
 472          /* 2 */        '\1~;',
 473          /* 3 */        '~\0',
 474          /* 4 */        '\1~\2',
 475          /* 5 */        '\0~'
 476      );
 477      $letexte = preg_replace($cherche1, $remplace1, $letexte);
 478      $letexte = ereg_replace(" *~+ *", "~", $letexte);
 479  
 480      $cherche2 = array(
 481          '/([^-\n]|^)--([^-]|$)/',
 482          '/(http|https|ftp|mailto)~:/',
 483          '/~/'
 484      );
 485      $remplace2 = array(
 486          '\1&mdash;\2',
 487          '\1:',
 488          '&nbsp;'
 489      );
 490      $letexte = preg_replace($cherche2, $remplace2, $letexte);
 491  
 492      return $letexte;
 493  }
 494  
 495  // rien sauf les "~" et "-,"
 496  function typo_en($letexte) {
 497  
 498      $cherche1 = array(
 499          '/ --?,/'
 500      );
 501      $remplace1 = array(
 502          '~\0'
 503      );
 504      $letexte = preg_replace($cherche1, $remplace1, $letexte);
 505  
 506      $letexte = str_replace("&nbsp;", "~", $letexte);
 507      $letexte = ereg_replace(" *~+ *", "~", $letexte);
 508  
 509      $cherche2 = array(
 510          '/([^-\n]|^)--([^-]|$)/',
 511          '/~/'
 512      );
 513      $remplace2 = array(
 514          '\1&mdash;\2',
 515          '&nbsp;'
 516      );
 517  
 518      $letexte = preg_replace($cherche2, $remplace2, $letexte);
 519  
 520      return $letexte;
 521  }
 522  
 523  //
 524  // Typographie generale
 525  //
 526  function typo_generale($letexte) {
 527      global $spip_lang;
 528  
 529      // Appeler la fonction de pre-traitement
 530      $letexte = spip_avant_typo ($letexte);
 531  
 532      // Caracteres de controle "illegaux"
 533      $letexte = corriger_caracteres($letexte);
 534  
 535      // Proteger les caracteres typographiques a l'interieur des tags html
 536      $protege = "!':;?";
 537      $illegal = "\x1\x2\x3\x4\x5";
 538      if (preg_match_all("/<[a-z!][^<>!':;\?]*[!':;\?][^<>]*>/ims",
 539      $letexte, $regs, PREG_SET_ORDER)) {
 540          foreach ($regs as $reg) {
 541              $insert = $reg[0];
 542              // hack: on transforme les caracteres a proteger en les remplacant
 543              // par des caracteres "illegaux". (cf corriger_caracteres())
 544              $insert = strtr($insert, $protege, $illegal);
 545              $letexte = str_replace($reg[0], $insert, $letexte);
 546          }
 547      }
 548  
 549      // zouli apostrophe
 550      $letexte = str_replace("'", "&#8217;", $letexte);
 551  
 552      // typo francaise ou anglaise ?
 553      // $lang_typo est fixee dans l'interface privee pour editer
 554      // un texte anglais en interface francaise (ou l'inverse) ;
 555      // sinon determiner la typo en fonction de la langue
 556      if (!$lang = $GLOBALS['lang_typo']) {
 557          include_ecrire('inc_lang.php3');
 558          $lang = lang_typo($spip_lang);
 559      }
 560      if ($lang == 'fr')
 561          $letexte = typo_fr($letexte);
 562      else
 563          $letexte = typo_en($letexte);
 564  
 565      // Retablir les caracteres proteges
 566      $letexte = strtr($letexte, $illegal, $protege);
 567  
 568      // Appeler la fonction de post-traitement
 569      $letexte = spip_apres_typo ($letexte);
 570  
 571      # un message pour abs_url - on est passe en mode texte
 572      $GLOBALS['mode_abs_url'] = 'texte';
 573  
 574      // et retour
 575      return $letexte;
 576  }
 577  
 578  function typo($letexte) {
 579      // echapper les codes <html>...</html> etc.
 580      list($letexte, $les_echap) = echappe_html($letexte, "SOURCETYPO");
 581      
 582      $letexte = typo_generale($letexte);
 583      // reintegrer les echappements
 584      return echappe_retour($letexte, $les_echap, "SOURCETYPO");
 585  }
 586  
 587  function typo_doublon(&$doublons, $letexte)
 588  {
 589      // echapper les codes <html>...</html> etc.
 590      list($letexte, $les_echap) = echappe_html($letexte, "SOURCETYPO");
 591      
 592      $letexte = typo_generale($letexte);
 593      // reintegrer les echappements
 594      return echappe_retour_doublon($letexte, $les_echap, "SOURCETYPO", $doublons);
 595  }
 596  
 597  function charger_generer_url()
 598  {
 599          // Traitement des liens internes
 600      if (!_DIR_RESTREINT)
 601          include_ecrire('inc_urls.php3');
 602      else if (@file_exists("inc-urls.php3"))
 603          include_local("inc-urls.php3");
 604      else    include_local("inc-urls-".$GLOBALS['type_urls'].".php3");
 605  }
 606  
 607  
 608  
 609  // cette fonction est tordue : on lui passe un tableau correspondant au match
 610  // de la regexp ci-dessous, et elle retourne le texte a inserer a la place
 611  // et le lien "brut" a usage eventuel de redirection...
 612  function extraire_lien ($regs) {
 613      $lien_texte = $regs[1];
 614  
 615      $lien_url = entites_html(trim($regs[3]));
 616      $compt_liens++;
 617      $lien_interne = false;
 618      if (ereg('^[[:space:]]*(art(icle)?|rub(rique)?|br(.ve)?|aut(eur)?|mot|site|doc(ument)?|im(age|g))?[[:space:]]*([[:digit:]]+)(#.*)?[[:space:]]*$', $lien_url, $match)) {
 619          $id_lien = $match[8];
 620          $ancre = $match[9];
 621          $type_lien = substr($match[1], 0, 2);
 622          $lien_interne=true;
 623          $class_lien = "in";
 624          charger_generer_url();
 625          switch ($type_lien) {
 626              case 'ru':
 627                  $lien_url = generer_url_rubrique($id_lien);
 628                  if (!$lien_texte) {
 629                      $req = "select titre from spip_rubriques where id_rubrique=$id_lien";
 630                      $row = @spip_fetch_array(@spip_query($req));
 631                      $lien_texte = $row['titre'];
 632                  }
 633                  break;
 634              case 'br':
 635                  $lien_url = generer_url_breve($id_lien);
 636                  if (!$lien_texte) {
 637                      $req = "select titre from spip_breves where id_breve=$id_lien";
 638                      $row = @spip_fetch_array(@spip_query($req));
 639                      $lien_texte = $row['titre'];
 640                  }
 641                  break;
 642              case 'au':
 643                  $lien_url = generer_url_auteur($id_lien);
 644                  if (!$lien_texte) {
 645                      $req = "select nom from spip_auteurs where id_auteur = $id_lien";
 646                      $row = @spip_fetch_array(@spip_query($req));
 647                      $lien_texte = $row['nom'];
 648                  }
 649                  break;
 650              case 'mo':
 651                  $lien_url = generer_url_mot($id_lien);
 652                  if (!$lien_texte) {
 653                      $req = "select titre from spip_mots where id_mot=$id_lien";
 654                      $row = @spip_fetch_array(@spip_query($req));
 655                      $lien_texte = $row['titre'];
 656                  }
 657                  break;
 658              case 'im':
 659              case 'do':
 660                  $lien_url = generer_url_document($id_lien);
 661                  if (!$lien_texte) {
 662                      $req = "select titre,fichier from spip_documents
 663                      WHERE id_document=$id_lien";
 664                      $row = @spip_fetch_array(@spip_query($req));
 665                      $lien_texte = $row['titre'];
 666                      if (!$lien_texte)
 667                          $lien_texte = ereg_replace("^.*/","",$row['fichier']);
 668                  }
 669                  break;
 670              case 'si':
 671                  # attention dans le cas des sites le lien pointe non pas sur
 672                  # la page locale du site, mais directement sur le site lui-meme
 673                  $row = @spip_fetch_array(@spip_query("SELECT nom_site,url_site
 674                  FROM spip_syndic WHERE id_syndic=$id_lien"));
 675                  if ($row) {
 676                      $lien_url = $row['url_site'];
 677                      if (!$lien_texte)
 678                          $lien_texte = typo($row['nom_site']);
 679                  }
 680                  break;
 681              default:
 682                  $lien_url = generer_url_article($id_lien);
 683                  if (!$lien_texte) {
 684                      $req = "select titre from spip_articles where id_article=$id_lien";
 685                      $row = @spip_fetch_array(@spip_query($req));
 686                      $lien_texte = $row['titre'];
 687  
 688                  }
 689                  break;
 690          }
 691  
 692          $lien_url .= $ancre;
 693  
 694          // supprimer les numeros des titres
 695          include_ecrire("inc_filtres.php3");
 696          $lien_texte = supprimer_numero($lien_texte);
 697      }
 698      else if (preg_match(',^\?(.*)$,s', $lien_url, $regs)) {
 699          // Liens glossaire
 700          $lien_url = substr($lien_url, 1);
 701          $class_lien = "glossaire";
 702      }
 703      else {
 704          // Liens non automatiques
 705          $class_lien = "out";
 706          // texte vide ?
 707          if ((!$lien_texte) and (!$lien_interne)) {
 708              $lien_texte = str_replace('"', '', $lien_url);
 709              if (strlen($lien_texte)>40)
 710                  $lien_texte = substr($lien_texte,0,35).'...';
 711              $class_lien = "url";
 712              $lien_texte = "<html>$lien_texte</html>";
 713          }
 714          // petites corrections d'URL
 715          if (preg_match(",^www\.[^@]+$,",$lien_url))
 716              $lien_url = "http://".$lien_url;
 717          else if (strpos($lien_url, "@") && email_valide($lien_url))
 718              $lien_url = "mailto:".$lien_url;
 719      }
 720  
 721      $insert = "<a href=\"$lien_url\" class=\"spip_$class_lien\""
 722          .">".typo($lien_texte)."</a>";
 723  
 724      return array($insert, $lien_url, $lien_texte);
 725  }
 726  
 727  //
 728  // Tableaux
 729  //
 730  function traiter_tableau($bloc) {
 731  
 732      // Decouper le tableau en lignes
 733      preg_match_all(',([|].*)[|]\n,Ums', $bloc, $regs, PREG_PATTERN_ORDER);
 734  
 735      // Traiter chaque ligne
 736      foreach ($regs[1] as $ligne) {
 737          $l ++;
 738  
 739          // Gestion de la premiere ligne :
 740          if ($l == 1) {
 741          // - <caption> et summary dans la premiere ligne :
 742          //   || caption | summary || (|summary est optionnel)
 743              if (preg_match(',^\|\|([^|]*)(\|(.*))?\|$,ms', $ligne, $cap)) {
 744                  $l = 0;
 745                  if ($caption = trim($cap[1]))
 746                      $debut_table .= "<caption>".$caption."</caption>\n";
 747                  $summary = ' summary="'.entites_html(trim($cap[3])).'"';
 748              }
 749          // - <thead> sous la forme |{{titre}}|{{titre}}|
 750          //   Attention thead oblige a avoir tbody
 751              else if (preg_match(',^(\|[[:space:]]*{{[^}]+}}[[:space:]]*)+$,ms',
 752                  $ligne, $thead)) {
 753                  $debut_table .= "<thead>
 754                      <tr class='row_first'>".
 755                      preg_replace(",[|]([^|]*),",
 756                          "<th scope='col'>\\1</th>", $ligne)
 757                      ."</tr></thead>\n";
 758                  $l = 0;
 759              }
 760          }
 761  
 762          // Sinon ligne normale
 763          if ($l) {
 764              // definition du tr
 765              if ($l == 1
 766              AND ereg("^(\|[[:space:]]*[{][{][^}]+[}][}][[:space:]]*)+$", $ligne)) {
 767                  $class = 'row_first';
 768              } else {
 769                  $class = 'row_'.alterner($l, 'even', 'odd');
 770              }
 771  
 772              // Gerer les listes a puce dans les cellules
 773              if (ereg("\n-[*#]", $ligne))
 774                  $ligne = traiter_listes($ligne);
 775  
 776              // Pas de paragraphes dans les cellules
 777              $ligne = preg_replace(",\n\n+,", "<br />\n", $ligne);
 778  
 779              // definition du <td>
 780              $ligne = preg_replace(",[|]([^|]*),",
 781                  "<td>\\1</td>", $ligne);
 782  
 783              // ligne complete
 784              $html[$l] = "<tr class=\"$class\">" . $ligne . "</tr>\n";
 785          }
 786      }
 787  
 788      return "\n\n</no p><table class=\"spip\"$summary>\n"
 789          . $debut_table
 790          . "<tbody>\n"
 791          . join ('', $html)
 792          . "</tbody>\n"
 793          . "</table><no p>\n\n";
 794  }
 795  
 796  
 797  //
 798  // Traitement des listes (merci a Michael Parienti)
 799  //
 800  function traiter_listes ($texte) {
 801      $parags = preg_split(",\n[[:space:]]*\n,", $texte);
 802      unset($texte);
 803  
 804      // chaque paragraphe est traite a part
 805      while (list(,$para) = each($parags)) {
 806          $niveau = 0;
 807          $lignes = explode("\n-", "\n" . $para);
 808  
 809          // ne pas toucher a la premiere ligne
 810          list(,$debut) = each($lignes);
 811          $texte .= $debut;
 812  
 813          // chaque item a sa profondeur = nb d'etoiles
 814          unset ($type);
 815          while (list(,$item) = each($lignes)) {
 816              preg_match(",^([*]*|[#]*)([^*#].*)$,s", $item, $regs);
 817              $profond = strlen($regs[1]);
 818  
 819              if ($profond > 0) {
 820                  unset ($ajout);
 821  
 822                  // changement de type de liste au meme niveau : il faut
 823                  // descendre un niveau plus bas, fermer ce niveau, et
 824                  // remonter
 825                  $nouv_type = (substr($item,0,1) == '*') ? 'ul' : 'ol';
 826                  $change_type = ($type AND ($type <> $nouv_type) AND ($profond == $niveau)) ? 1 : 0;
 827                  $type = $nouv_type;
 828  
 829                  // d'abord traiter les descentes
 830                  while ($niveau > $profond - $change_type) {
 831                      $ajout .= $pile_li[$niveau];
 832                      $ajout .= $pile_type[$niveau];
 833                      if (!$change_type)
 834                          unset ($pile_li[$niveau]);
 835                      $niveau --;
 836                  }
 837  
 838                  // puis les identites (y compris en fin de descente)
 839                  if ($niveau == $profond && !$change_type) {
 840                      $ajout .= $pile_li[$niveau];
 841                  }
 842  
 843                  // puis les montees (y compris apres une descente un cran trop bas)
 844                  while ($niveau < $profond) {
 845                      if ($niveau == 0) $ajout .= "\n\n";
 846                      $niveau ++;
 847                      $ajout .= "</no p>"."<$type class=\"spip\">";
 848                      $pile_type[$niveau] = "</$type>"."<no p>";
 849                  }
 850  
 851                  $ajout .= "<li class=\"spip\">";
 852                  $pile_li[$profond] = "</li>";
 853              }
 854              else {
 855                  $ajout = "\n-";    // puce normale ou <hr>
 856              }
 857  
 858              $texte .= $ajout . $regs[2];
 859          }
 860  
 861          // retour sur terre
 862          unset ($ajout);
 863          while ($niveau > 0) {
 864              $ajout .= $pile_li[$niveau];
 865              $ajout .= $pile_type[$niveau];
 866              $niveau --;
 867          }
 868          $texte .= $ajout;
 869  
 870          // paragraphe
 871          $texte .= "\n\n";
 872      }
 873  
 874      // sucrer les deux derniers \n
 875      return substr($texte, 0, -2);
 876  }
 877  
 878  // Nettoie un texte, traite les raccourcis spip, la typo, etc.
 879  function traiter_raccourcis_generale($letexte) {
 880      global $debut_intertitre, $fin_intertitre, $ligne_horizontale, $url_glossaire_externe;
 881      global $compt_note;
 882      global $marqueur_notes;
 883      global $ouvre_ref;
 884      global $ferme_ref;
 885      global $ouvre_note;
 886      global $ferme_note;
 887      global $lang_dir;
 888  
 889      // Appeler la fonction de pre_traitement
 890      $letexte = spip_avant_propre ($letexte);
 891  
 892      // Puce
 893      if (!$lang_dir) {
 894          include_ecrire('inc_lang.php3');
 895          $lang_dir = lang_dir($GLOBALS['spip_lang']);
 896      }
 897      if ($lang_dir == 'rtl' AND $GLOBALS['puce_rtl'])
 898          $puce = $GLOBALS['puce_rtl'];
 899      else
 900          $puce = $GLOBALS['puce'];
 901  
 902      // Harmoniser les retours chariot
 903      $letexte = preg_replace(",\r\n?,", "\n", $letexte);
 904  
 905      // Corriger HTML
 906      $letexte = preg_replace(",</?p>,i", "\n\n\n", $letexte);
 907  
 908      //
 909      // Notes de bas de page
 910      //
 911      $regexp = ',\[\[(.*?)\]\],ms';
 912      if (preg_match_all($regexp, $letexte, $matches, PREG_SET_ORDER))
 913      foreach ($matches as $regs) {
 914          $note_source = $regs[0];
 915          $note_texte = $regs[1];
 916          $num_note = false;
 917  
 918          // note auto ou pas ?
 919          if (preg_match(",^ *<([^>]*)>,", $note_texte, $regs)){
 920              $num_note = $regs[1];
 921              $note_texte = str_replace($regs[0], "", $note_texte);
 922          } else {
 923              $compt_note++;
 924              $num_note = $compt_note;
 925          }
 926  
 927          // preparer la note
 928          if ($num_note) {
 929              if ($marqueur_notes) // quand il y a plusieurs series
 930                                   // de notes sur une meme page
 931                  $mn = $marqueur_notes.'-';
 932              $ancre = $mn.urlencode($num_note);
 933  
 934              // creer le popup 'title' sur l'appel de note
 935              if ($title = supprimer_tags(propre($note_texte))) {
 936                  $title = $ouvre_note.$ancre.$ferme_note.$title;
 937                  $title = ' title="<html>'
 938                  . texte_backend(couper($title,80)).'</html>"';
 939              }
 940  
 941              $insert = "$ouvre_ref<a href=\"#nb$ancre\" name=\"nh$ancre\" class=\"spip_note\"$title>$num_note</a>$ferme_ref";
 942              $appel = "<html>$ouvre_note<a href=\"#nh$ancre\" name=\"nb$ancre\" class=\"spip_note\">$num_note</a>$ferme_note</html>";
 943          } else {
 944              $insert = '';
 945              $appel = '';
 946          }
 947  
 948          // l'ajouter "brut" dans les notes
 949          if ($note_texte) {
 950              if ($mes_notes)
 951                  $mes_notes .= "\n\n";
 952              $mes_notes .= $appel . $note_texte;
 953          }
 954  
 955          // dans le texte, mettre l'appel de note a la place de la note
 956          $pos = strpos($letexte, $note_source);
 957          $letexte = substr($letexte, 0, $pos) . $insert
 958              . substr($letexte, $pos + strlen($note_source));
 959      }
 960  
 961      //
 962      // Raccourcis automatiques [?SPIP] vers un glossaire
 963      // (on traite ce raccourci en deux temps afin de ne pas appliquer
 964      //  la typo sur les URLs, voir raccourcis liens ci-dessous)
 965      //
 966      if ($url_glossaire_externe) {
 967          $regexp = "|\[\?+([^][<>]+)\]|";
 968          if (preg_match_all($regexp, $letexte, $matches, PREG_SET_ORDER))
 969          foreach ($matches as $regs) {
 970              $terme = trim($regs[1]);
 971              $terme_underscore = urlencode(preg_replace(',\s+,', '_', $terme));
 972              if (strstr($url_glossaire_externe,"%s"))
 973                  $url = str_replace("%s", $terme_underscore, $url_glossaire_externe);
 974              else
 975                  $url = $url_glossaire_externe.$terme_underscore;
 976              $url = str_replace("@lang@", $GLOBALS['spip_lang'], $url);
 977              $code = '['.$terme.'->?'.$url.']';
 978              $letexte = str_replace($regs[0], $code, $letexte);
 979          }
 980      }
 981  
 982  
 983      //
 984      // Raccourcis liens [xxx->url] (cf. fonction extraire_lien ci-dessus)
 985      // Note : complique car c'est ici qu'on applique la typo() !
 986      //
 987      $regexp = "|\[([^][]*)->(>?)([^]]*)\]|ms";
 988      $inserts = array();
 989      if (preg_match_all($regexp, $letexte, $matches, PREG_SET_ORDER)) {
 990          $i = 0;
 991          foreach ($matches as $regs) {
 992              list($insert) = extraire_lien($regs);
 993              $inserts[++$i] = $insert;
 994              $letexte = str_replace($regs[0], "@@SPIP_ECHAPPE$i@@", $letexte);
 995          }
 996      }
 997      $letexte = typo($letexte);
 998      foreach ($inserts as $i => $insert) {
 999          $letexte = str_replace("@@SPIP_ECHAPPE$i@@", $insert, $letexte);
1000      }
1001  
1002      //
1003      // Tableaux
1004      //
1005  
1006      // traiter le cas particulier des echappements (<doc...> par exemple)
1007      // qui auraient provoque des \n\n</no p> devant les | des tableaux
1008      $letexte = preg_replace(',[|]\n\n</no p>@@,','|@@', $letexte);
1009      $letexte = preg_replace(',@@<no p>\n\n[|],','@@|', $letexte);
1010  
1011      // ne pas oublier les tableaux au debut ou a la fin du texte
1012      $letexte = preg_replace(",^\n?[|],", "\n\n|", $letexte);
1013      $letexte = preg_replace(",\n\n+[|],", "\n\n\n\n|", $letexte);
1014      $letexte = preg_replace(",[|](\n\n+|\n?$),", "|\n\n\n\n", $letexte);
1015  
1016      // traiter chaque tableau
1017      if (preg_match_all(',[^|](\n[|].*[|]\n)[^|],Ums', $letexte,
1018      $regs, PREG_SET_ORDER))
1019      foreach ($regs as $tab) {
1020          $letexte = str_replace($tab[1], traiter_tableau($tab[1]), $letexte);
1021      }
1022  
1023      //
1024      // Ensemble de remplacements implementant le systeme de mise
1025      // en forme (paragraphes, raccourcis...)
1026      //
1027  
1028      $letexte = "\n".trim($letexte);
1029  
1030      // les listes
1031      if (ereg("\n-[*#]", $letexte))
1032          $letexte = traiter_listes($letexte);
1033  
1034      // autres raccourcis
1035      $cherche1 = array(
1036          /* 0 */     "/\n(----+|____+)/",
1037          /* 1 */     "/\n-- */",
1038          /* 2 */     "/\n- */",
1039          /* 3 */     "/\n_ +/",
1040          /* 4 */     "/[{][{][{]/",
1041          /* 5 */     "/[}][}][}]/",
1042          /* 6 */     "/(( *)\n){2,}(<br[[:space:]]*\/?".">)?/",
1043          /* 7 */     "/[{][{]/",
1044          /* 8 */     "/[}][}]/",
1045          /* 9 */     "/[{]/",
1046          /* 10 */    "/[}]/",
1047          /* 11 */    "/(<br[[:space:]]*\/?".">){2,}/",
1048          /* 12 */    "/<p>([\n]*)(<br[[:space:]]*\/?".">)+/",
1049          /* 13 */    "/<p>/",
1050          /* 14         "/\n/", */
1051          /* 15 */    "/<quote>/",
1052          /* 16 */    "/<\/quote>/"
1053      );
1054      $remplace1 = array(
1055          /* 0 */     "\n\n@@SPIP_ligne_horizontale@@\n\n",
1056          /* 1 */     "\n<br />&mdash;&nbsp;",
1057          /* 2 */     "\n<br />$puce&nbsp;",
1058          /* 3 */     "\n<br />",
1059          /* 4 */     "\n\n@@SPIP_debut_intertitre@@",
1060          /* 5 */     "@@SPIP_fin_intertitre@@\n\n",
1061          /* 6 */     "<p>",
1062          /* 7 */     "<strong class=\"spip\">",
1063          /* 8 */     "</strong>",
1064          /* 9 */     "<i class=\"spip\">",
1065          /* 10 */    "</i>",
1066          /* 11 */    "<p class=\"spip\">",
1067          /* 12 */    "<p class=\"spip\">",
1068          /* 13 */    "<p class=\"spip\">",
1069          /* 14         " ", */
1070          /* 15 */    "\n\n<blockquote class=\"spip\"><p class=\"spip\">",
1071          /* 16 */    "</p></blockquote>\n\n"
1072      );
1073      $letexte = preg_replace($cherche1, $remplace1, $letexte);
1074      $letexte = preg_replace("@^ <br />@", "", $letexte);
1075  
1076      // paragrapher
1077      if (strpos(' '.$letexte, '<p class="spip">')) # ce test est destine a disparaitre, avec un impact sur les textes a un seul paragraphe
1078      {
1079          $letexte = '<p class="spip">'.str_replace('<p class="spip">', "</p>\n".'<p class="spip">', $letexte).'</p>';
1080      }
1081      $letexte = preg_replace(',(<p class="spip">)?\s*(<center>)?\s*</no p>,ims', '\2', $letexte);
1082      $letexte = preg_replace(',<no p>\s*(</center>)?\s*(</p>)?,ims', '\1', $letexte);
1083  
1084      // intertitres / hr / blockquote / table / ul compliants
1085      $letexte = preg_replace(',(<p class="spip">)?[[:space:]]*@@SPIP_debut_intertitre@@,ms', $debut_intertitre, $letexte);
1086      $letexte = preg_replace(',@@SPIP_fin_intertitre@@[[:space:]]*(</p>)?,ms', $fin_intertitre, $letexte);
1087      $letexte = preg_replace(',(<p class="spip">)?[[:space:]]*@@SPIP_ligne_horizontale@@[[:space:]]*(</p>)?,ms', $ligne_horizontale, $letexte);
1088      $letexte = preg_replace(',(<p class="spip">)?[[:space:]]*<blockquote class=\"spip\"></p>,ms', "\n<blockquote class=\"spip\">", $letexte);
1089      $letexte = preg_replace(',</blockquote>[[:space:]]*(</p>)?,ms', "</blockquote>\n", $letexte);
1090      $letexte = preg_replace(',(<p class="spip">)?[[:space:]]*<table([>[:space:]]),ms', "\n<table\\2", $letexte);
1091      $letexte = preg_replace(',</table>[[:space:]]*(</p>)?,ms', "</table>\n", $letexte);
1092      $letexte = preg_replace(',(<p class="spip">)?[[:space:]]*<ul([>[:space:]]),ms', "<ul\\2", $letexte);
1093      $letexte = preg_replace(',</ul>[[:space:]]*(</p>)?,ms', '</ul>', $letexte);
1094      $letexte = preg_replace(',<p class="spip">[[:space:]]*</p>,ms', "\n", $letexte);
1095  
1096      // Appeler la fonction de post-traitement
1097      $letexte = spip_apres_propre ($letexte);
1098  
1099      return array($letexte,$mes_notes);
1100  }
1101  
1102  function traiter_les_notes($mes_notes, $les_echap) {
1103      list($mes_notes,) = traiter_raccourcis_generale($mes_notes);
1104      if (ereg('<p class="spip">',$mes_notes))
1105          $mes_notes = str_replace('<p class="spip">', '<p class="spip_note">', $mes_notes);
1106      else
1107          $mes_notes = '<p class="spip_note">'.$mes_notes."</p>\n";
1108      $mes_notes = echappe_retour($mes_notes, $les_echap, "SOURCEPROPRE");
1109      $GLOBALS['les_notes'] .= interdire_scripts($mes_notes);
1110  }
1111  
1112  function traiter_raccourcis($letexte, $les_echap=false) {
1113      // echapper les <a href>, <html>...< /html>, <code>...< /code>
1114  
1115      if (!$les_echap)
1116          list($letexte, $les_echap) = echappe_html($letexte, "SOURCEPROPRE");
1117      list($letexte, $mes_notes) = traiter_raccourcis_generale($letexte);
1118      if ($mes_notes) traiter_les_notes($mes_notes, $les_echap);
1119      // Reinserer les echappements
1120      return trim(echappe_retour($letexte, $les_echap, "SOURCEPROPRE"));
1121  }
1122  
1123  function traiter_raccourcis_doublon(&$doublons, $letexte) {
1124      // echapper les <a href>, <html>...< /html>, <code>...< /code>
1125      list($letexte, $les_echap) = echappe_html($letexte, "SOURCEPROPRE");
1126      list($letexte, $mes_notes) = traiter_raccourcis_generale($letexte);
1127      if ($mes_notes) traiter_les_notes($mes_notes, $les_echap);
1128      // Reinserer les echappements
1129      return trim(echappe_retour_doublon($letexte, $les_echap, "SOURCEPROPRE", $doublons));
1130  }
1131  
1132  
1133  // Filtre a appliquer aux champs du type #TEXTE*
1134  function propre($letexte, $echap=false) {
1135      return interdire_scripts(traiter_raccourcis(trim($letexte), $echap));
1136  }
1137  
1138  ?>


[ Powered by PHPXref - Served by Debian GNU/Linux ]