PNG  IHDRQgAMA a cHRMz&u0`:pQ<bKGDgmIDATxwUﹻ& ^CX(J I@ "% (** BX +*i"]j(IH{~R)[~>h{}gy)I$Ij .I$I$ʊy@}x.: $I$Ii}VZPC)I$IF ^0ʐJ$I$Q^}{"r=OzI$gRZeC.IOvH eKX $IMpxsk.쒷/&r[޳<v| .I~)@$updYRa$I |M.e JaֶpSYR6j>h%IRز if&uJ)M$I vLi=H;7UJ,],X$I1AҒJ$ XY XzI@GNҥRT)E@;]K*Mw;#5_wOn~\ DC&$(A5 RRFkvIR}l!RytRl;~^ǷJj اy뷦BZJr&ӥ8Pjw~vnv X^(I;4R=P[3]J,]ȏ~:3?[ a&e)`e*P[4]T=Cq6R[ ~ޤrXR Հg(t_HZ-Hg M$ãmL5R uk*`%C-E6/%[t X.{8P9Z.vkXŐKjgKZHg(aK9ڦmKjѺm_ \#$5,)-  61eJ,5m| r'= &ڡd%-]J on Xm|{ RҞe $eڧY XYrԮ-a7RK6h>n$5AVڴi*ֆK)mѦtmr1p| q:흺,)Oi*ֺK)ܬ֦K-5r3>0ԔHjJئEZj,%re~/z%jVMڸmrt)3]J,T K֦OvԒgii*bKiNO~%PW0=dii2tJ9Jݕ{7"I P9JKTbu,%r"6RKU}Ij2HKZXJ,妝 XYrP ެ24c%i^IK|.H,%rb:XRl1X4Pe/`x&P8Pj28Mzsx2r\zRPz4J}yP[g=L) .Q[6RjWgp FIH*-`IMRaK9TXcq*I y[jE>cw%gLRԕiFCj-ďa`#e~I j,%r,)?[gp FI˨mnWX#>mʔ XA DZf9,nKҲzIZXJ,L#kiPz4JZF,I,`61%2s $,VOϚ2/UFJfy7K> X+6 STXIeJILzMfKm LRaK9%|4p9LwJI!`NsiazĔ)%- XMq>pk$-$Q2x#N ؎-QR}ᶦHZډ)J,l#i@yn3LN`;nڔ XuX5pF)m|^0(>BHF9(cզEerJI rg7 4I@z0\JIi䵙RR0s;$s6eJ,`n 䂦0a)S)A 1eJ,堌#635RIgpNHuTH_SԕqVe ` &S)>p;S$魁eKIuX`I4춒o}`m$1":PI<[v9^\pTJjriRŭ P{#{R2,`)e-`mgj~1ϣLKam7&U\j/3mJ,`F;M'䱀 .KR#)yhTq;pcK9(q!w?uRR,n.yw*UXj#\]ɱ(qv2=RqfB#iJmmL<]Y͙#$5 uTU7ӦXR+q,`I}qL'`6Kͷ6r,]0S$- [RKR3oiRE|nӦXR.(i:LDLTJjY%o:)6rxzҒqTJjh㞦I.$YR.ʼnGZ\ֿf:%55 I˼!6dKxm4E"mG_ s? .e*?LRfK9%q#uh$)i3ULRfK9yxm܌bj84$i1U^@Wbm4uJ,ҪA>_Ij?1v32[gLRD96oTaR׿N7%L2 NT,`)7&ƝL*꽙yp_$M2#AS,`)7$rkTA29_Iye"|/0t)$n XT2`YJ;6Jx".e<`$) PI$5V4]29SRI>~=@j]lp2`K9Jaai^" Ԋ29ORI%:XV5]JmN9]H;1UC39NI%Xe78t)a;Oi Ҙ>Xt"~G>_mn:%|~ޅ_+]$o)@ǀ{hgN;IK6G&rp)T2i୦KJuv*T=TOSV>(~D>dm,I*Ɛ:R#ۙNI%D>G.n$o;+#RR!.eU˽TRI28t)1LWϚ>IJa3oFbu&:tJ*(F7y0ZR ^p'Ii L24x| XRI%ۄ>S1]Jy[zL$adB7.eh4%%누>WETf+3IR:I3Xה)3אOۦSRO'ٺ)S}"qOr[B7ϙ.edG)^ETR"RtRݜh0}LFVӦDB^k_JDj\=LS(Iv─aTeZ%eUAM-0;~˃@i|l @S4y72>sX-vA}ϛBI!ݎߨWl*)3{'Y|iSlEڻ(5KtSI$Uv02,~ԩ~x;P4ցCrO%tyn425:KMlD ^4JRxSهF_}شJTS6uj+ﷸk$eZO%G*^V2u3EMj3k%)okI]dT)URKDS 7~m@TJR~荪fT"֛L \sM -0T KfJz+nإKr L&j()[E&I ߴ>e FW_kJR|!O:5/2跌3T-'|zX ryp0JS ~^F>-2< `*%ZFP)bSn"L :)+pʷf(pO3TMW$~>@~ū:TAIsV1}S2<%ޟM?@iT ,Eūoz%i~g|`wS(]oȤ8)$ ntu`өe`6yPl IzMI{ʣzʨ )IZ2= ld:5+請M$-ї;U>_gsY$ÁN5WzWfIZ)-yuXIfp~S*IZdt;t>KūKR|$#LcԀ+2\;kJ`]YǔM1B)UbG"IRߊ<xܾӔJ0Z='Y嵤 Leveg)$znV-º^3Ւof#0Tfk^Zs[*I꯳3{)ˬW4Ւ4 OdpbZRS|*I 55#"&-IvT&/윚Ye:i$ 9{LkuRe[I~_\ؠ%>GL$iY8 9ܕ"S`kS.IlC;Ҏ4x&>u_0JLr<J2(^$5L s=MgV ~,Iju> 7r2)^=G$1:3G< `J3~&IR% 6Tx/rIj3O< ʔ&#f_yXJiގNSz; Tx(i8%#4 ~AS+IjerIUrIj362v885+IjAhK__5X%nV%Iͳ-y|7XV2v4fzo_68"S/I-qbf; LkF)KSM$ Ms>K WNV}^`-큧32ŒVؙGdu,^^m%6~Nn&͓3ŒVZMsRpfEW%IwdǀLm[7W&bIRL@Q|)* i ImsIMmKmyV`i$G+R 0tV'!V)֏28vU7͒vHꦼtxꗞT ;S}7Mf+fIRHNZUkUx5SAJㄌ9MqμAIRi|j5)o*^'<$TwI1hEU^c_j?Е$%d`z cyf,XO IJnTgA UXRD }{H}^S,P5V2\Xx`pZ|Yk:$e ~ @nWL.j+ϝYb퇪bZ BVu)u/IJ_ 1[p.p60bC >|X91P:N\!5qUB}5a5ja `ubcVxYt1N0Zzl4]7­gKj]?4ϻ *[bg$)+À*x쳀ogO$~,5 زUS9 lq3+5mgw@np1sso Ӻ=|N6 /g(Wv7U;zωM=wk,0uTg_`_P`uz?2yI!b`kĸSo+Qx%!\οe|އԁKS-s6pu_(ֿ$i++T8=eY; צP+phxWQv*|p1. ά. XRkIQYP,drZ | B%wP|S5`~́@i޾ E;Չaw{o'Q?%iL{u D?N1BD!owPHReFZ* k_-~{E9b-~P`fE{AܶBJAFO wx6Rox5 K5=WwehS8 (JClJ~ p+Fi;ŗo+:bD#g(C"wA^ r.F8L;dzdIHUX݆ϞXg )IFqem%I4dj&ppT{'{HOx( Rk6^C٫O.)3:s(۳(Z?~ٻ89zmT"PLtw䥈5&b<8GZ-Y&K?e8,`I6e(֍xb83 `rzXj)F=l($Ij 2*(F?h(/9ik:I`m#p3MgLaKjc/U#n5S# m(^)=y=đx8ŬI[U]~SцA4p$-F i(R,7Cx;X=cI>{Km\ o(Tv2vx2qiiDJN,Ҏ!1f 5quBj1!8 rDFd(!WQl,gSkL1Bxg''՞^ǘ;pQ P(c_ IRujg(Wz bs#P­rz> k c&nB=q+ؔXn#r5)co*Ũ+G?7< |PQӣ'G`uOd>%Mctz# Ԫڞ&7CaQ~N'-P.W`Oedp03C!IZcIAMPUۀ5J<\u~+{9(FbbyAeBhOSܳ1 bÈT#ŠyDžs,`5}DC-`̞%r&ڙa87QWWp6e7 Rϫ/oY ꇅ Nܶըtc!LA T7V4Jsū I-0Pxz7QNF_iZgúWkG83 0eWr9 X]㾮݁#Jˢ C}0=3ݱtBi]_ &{{[/o[~ \q鯜00٩|cD3=4B_b RYb$óBRsf&lLX#M*C_L܄:gx)WΘsGSbuL rF$9';\4Ɍq'n[%p.Q`u hNb`eCQyQ|l_C>Lb꟟3hSb #xNxSs^ 88|Mz)}:](vbۢamŖ࿥ 0)Q7@0=?^k(*J}3ibkFn HjB׻NO z x}7p 0tfDX.lwgȔhԾŲ }6g E |LkLZteu+=q\Iv0쮑)QٵpH8/2?Σo>Jvppho~f>%bMM}\//":PTc(v9v!gոQ )UfVG+! 35{=x\2+ki,y$~A1iC6#)vC5^>+gǵ@1Hy٪7u;p psϰu/S <aʸGu'tD1ԝI<pg|6j'p:tպhX{o(7v],*}6a_ wXRk,O]Lܳ~Vo45rp"N5k;m{rZbΦ${#)`(Ŵg,;j%6j.pyYT?}-kBDc3qA`NWQū20/^AZW%NQ MI.X#P#,^Ebc&?XR tAV|Y.1!؅⨉ccww>ivl(JT~ u`ٵDm q)+Ri x/x8cyFO!/*!/&,7<.N,YDŽ&ܑQF1Bz)FPʛ?5d 6`kQձ λc؎%582Y&nD_$Je4>a?! ͨ|ȎWZSsv8 j(I&yj Jb5m?HWp=g}G3#|I,5v珿] H~R3@B[☉9Ox~oMy=J;xUVoj bUsl_35t-(ՃɼRB7U!qc+x4H_Qo֮$[GO<4`&č\GOc[.[*Af%mG/ ňM/r W/Nw~B1U3J?P&Y )`ѓZ1p]^l“W#)lWZilUQu`-m|xĐ,_ƪ|9i:_{*(3Gѧ}UoD+>m_?VPۅ15&}2|/pIOʵ> GZ9cmíتmnz)yߐbD >e}:) r|@R5qVSA10C%E_'^8cR7O;6[eKePGϦX7jb}OTGO^jn*媓7nGMC t,k31Rb (vyܴʭ!iTh8~ZYZp(qsRL ?b}cŨʊGO^!rPJO15MJ[c&~Z`"ѓޔH1C&^|Ш|rʼ,AwĴ?b5)tLU)F| &g٣O]oqSUjy(x<Ϳ3 .FSkoYg2 \_#wj{u'rQ>o;%n|F*O_L"e9umDds?.fuuQbIWz |4\0 sb;OvxOSs; G%T4gFRurj(֍ڑb uԖKDu1MK{1^ q; C=6\8FR艇!%\YÔU| 88m)֓NcLve C6z;o&X x59:q61Z(T7>C?gcļxѐ Z oo-08jہ x,`' ҔOcRlf~`jj".Nv+sM_]Zk g( UOPyεx%pUh2(@il0ݽQXxppx-NS( WO+轾 nFߢ3M<;z)FBZjciu/QoF 7R¥ ZFLF~#ȣߨ^<쩡ݛкvџ))ME>ώx4m#!-m!L;vv#~Y[đKmx9.[,UFS CVkZ +ߟrY٧IZd/ioi$%͝ب_ֶX3ܫhNU ZZgk=]=bbJS[wjU()*I =ώ:}-蹞lUj:1}MWm=̛ _ ¾,8{__m{_PVK^n3esw5ӫh#$-q=A̟> ,^I}P^J$qY~Q[ Xq9{#&T.^GVj__RKpn,b=`żY@^՝;z{paVKkQXj/)y TIc&F;FBG7wg ZZDG!x r_tƢ!}i/V=M/#nB8 XxЫ ^@CR<{䤭YCN)eKOSƟa $&g[i3.C6xrOc8TI;o hH6P&L{@q6[ Gzp^71j(l`J}]e6X☉#͕ ׈$AB1Vjh㭦IRsqFBjwQ_7Xk>y"N=MB0 ,C #o6MRc0|$)ف"1!ixY<B9mx `,tA>)5ػQ?jQ?cn>YZe Tisvh# GMމȇp:ԴVuږ8ɼH]C.5C!UV;F`mbBk LTMvPʍϤj?ԯ/Qr1NB`9s"s TYsz &9S%U԰> {<ؿSMxB|H\3@!U| k']$U+> |HHMLޢ?V9iD!-@x TIî%6Z*9X@HMW#?nN ,oe6?tQwڱ.]-y':mW0#!J82qFjH -`ѓ&M0u Uγmxϵ^-_\])@0Rt.8/?ٰCY]x}=sD3ojަЫNuS%U}ԤwHH>ڗjܷ_3gN q7[q2la*ArǓԖ+p8/RGM ]jacd(JhWko6ڎbj]i5Bj3+3!\j1UZLsLTv8HHmup<>gKMJj0@H%,W΃7R) ">c, xixј^ aܖ>H[i.UIHc U1=yW\=S*GR~)AF=`&2h`DzT󑓶J+?W+}C%P:|0H܆}-<;OC[~o.$~i}~HQ TvXΈr=b}$vizL4:ȰT|4~*!oXQR6Lk+#t/g lԁߖ[Jڶ_N$k*". xsxX7jRVbAAʯKҎU3)zSNN _'s?f)6X!%ssAkʱ>qƷb hg %n ~p1REGMHH=BJiy[<5 ǁJҖgKR*倳e~HUy)Ag,K)`Vw6bRR:qL#\rclK/$sh*$ 6덤 KԖc 3Z9=Ɣ=o>X Ώ"1 )a`SJJ6k(<c e{%kϊP+SL'TcMJWRm ŏ"w)qc ef꒵i?b7b('"2r%~HUS1\<(`1Wx9=8HY9m:X18bgD1u ~|H;K-Uep,, C1 RV.MR5άh,tWO8WC$ XRVsQS]3GJ|12 [vM :k#~tH30Rf-HYݺ-`I9%lIDTm\ S{]9gOڒMNCV\G*2JRŨ;Rҏ^ڽ̱mq1Eu?To3I)y^#jJw^Ńj^vvlB_⋌P4x>0$c>K†Aļ9s_VjTt0l#m>E-,,x,-W)سo&96RE XR.6bXw+)GAEvL)͞K4$p=Ũi_ѱOjb HY/+@θH9޼]Nԥ%n{ &zjT? Ty) s^ULlb,PiTf^<À] 62R^V7)S!nllS6~͝V}-=%* ʻ>G DnK<y&>LPy7'r=Hj 9V`[c"*^8HpcO8bnU`4JȪAƋ#1_\ XϘHPRgik(~G~0DAA_2p|J묭a2\NCr]M_0 ^T%e#vD^%xy-n}-E\3aS%yN!r_{ )sAw ڼp1pEAk~v<:`'ӭ^5 ArXOI驻T (dk)_\ PuA*BY]yB"l\ey hH*tbK)3 IKZ򹞋XjN n *n>k]X_d!ryBH ]*R 0(#'7 %es9??ښFC,ՁQPjARJ\Ρw K#jahgw;2$l*) %Xq5!U᢯6Re] |0[__64ch&_}iL8KEgҎ7 M/\`|.p,~`a=BR?xܐrQ8K XR2M8f ?`sgWS%" Ԉ 7R%$ N}?QL1|-эټwIZ%pvL3Hk>,ImgW7{E xPHx73RA @RS CC !\ȟ5IXR^ZxHл$Q[ŝ40 (>+ _C >BRt<,TrT {O/H+˟Pl6 I B)/VC<6a2~(XwV4gnXR ϱ5ǀHٻ?tw똤Eyxp{#WK qG%5],(0ӈH HZ])ג=K1j&G(FbM@)%I` XRg ʔ KZG(vP,<`[ Kn^ SJRsAʠ5xՅF`0&RbV tx:EaUE/{fi2;.IAwW8/tTxAGOoN?G}l L(n`Zv?pB8K_gI+ܗ #i?ޙ.) p$utc ~DžfՈEo3l/)I-U?aԅ^jxArA ΧX}DmZ@QLےbTXGd.^|xKHR{|ΕW_h] IJ`[G9{).y) 0X YA1]qp?p_k+J*Y@HI>^?gt.06Rn ,` ?);p pSF9ZXLBJPWjgQ|&)7! HjQt<| ؅W5 x W HIzYoVMGP Hjn`+\(dNW)F+IrS[|/a`K|ͻ0Hj{R,Q=\ (F}\WR)AgSG`IsnAR=|8$}G(vC$)s FBJ?]_u XRvύ6z ŨG[36-T9HzpW̞ú Xg큽=7CufzI$)ki^qk-) 0H*N` QZkk]/tnnsI^Gu't=7$ Z;{8^jB% IItRQS7[ϭ3 $_OQJ`7!]W"W,)Iy W AJA;KWG`IY{8k$I$^%9.^(`N|LJ%@$I}ֽp=FB*xN=gI?Q{٥4B)mw $Igc~dZ@G9K X?7)aK%݅K$IZ-`IpC U6$I\0>!9k} Xa IIS0H$I H ?1R.Чj:4~Rw@p$IrA*u}WjWFPJ$I➓/6#! LӾ+ X36x8J |+L;v$Io4301R20M I$-E}@,pS^ޟR[/s¹'0H$IKyfŸfVOπFT*a$I>He~VY/3R/)>d$I>28`Cjw,n@FU*9ttf$I~<;=/4RD~@ X-ѕzἱI$: ԍR a@b X{+Qxuq$IЛzo /~3\8ڒ4BN7$IҀj V]n18H$IYFBj3̵̚ja pp $Is/3R Ӻ-Yj+L;.0ŔI$Av? #!5"aʄj}UKmɽH$IjCYs?h$IDl843.v}m7UiI=&=0Lg0$I4: embe` eQbm0u? $IT!Sƍ'-sv)s#C0:XB2a w I$zbww{."pPzO =Ɔ\[ o($Iaw]`E).Kvi:L*#gР7[$IyGPI=@R 4yR~̮´cg I$I/<tPͽ hDgo 94Z^k盇΄8I56^W$I^0̜N?4*H`237}g+hxoq)SJ@p|` $I%>-hO0eO>\ԣNߌZD6R=K ~n($I$y3D>o4b#px2$yڪtzW~a $I~?x'BwwpH$IZݑnC㧄Pc_9sO gwJ=l1:mKB>Ab<4Lp$Ib o1ZQ@85b̍ S'F,Fe,^I$IjEdù{l4 8Ys_s Z8.x m"+{~?q,Z D!I$ϻ'|XhB)=…']M>5 rgotԎ 獽PH$IjIPhh)n#cÔqA'ug5qwU&rF|1E%I$%]!'3AFD/;Ck_`9 v!ٴtPV;x`'*bQa w I$Ix5 FC3D_~A_#O݆DvV?<qw+I$I{=Z8".#RIYyjǪ=fDl9%M,a8$I$Ywi[7ݍFe$s1ՋBVA?`]#!oz4zjLJo8$I$%@3jAa4(o ;p,,dya=F9ً[LSPH$IJYЉ+3> 5"39aZ<ñh!{TpBGkj}Sp $IlvF.F$I z< '\K*qq.f<2Y!S"-\I$IYwčjF$ w9 \ߪB.1v!Ʊ?+r:^!I$BϹB H"B;L'G[ 4U#5>੐)|#o0aڱ$I>}k&1`U#V?YsV x>{t1[I~D&(I$I/{H0fw"q"y%4 IXyE~M3 8XψL}qE$I[> nD?~sf ]o΁ cT6"?'_Ἣ $I>~.f|'!N?⟩0G KkXZE]ޡ;/&?k OۘH$IRۀwXӨ<7@PnS04aӶp.:@\IWQJ6sS%I$e5ڑv`3:x';wq_vpgHyXZ 3gЂ7{{EuԹn±}$I$8t;b|591nءQ"P6O5i }iR̈́%Q̄p!I䮢]O{H$IRϻ9s֧ a=`- aB\X0"+5"C1Hb?߮3x3&gşggl_hZ^,`5?ߎvĸ%̀M!OZC2#0x LJ0 Gw$I$I}<{Eb+y;iI,`ܚF:5ܛA8-O-|8K7s|#Z8a&><a&/VtbtLʌI$I$I$I$I$I$IRjDD%tEXtdate:create2022-05-31T04:40:26+00:00!Î%tEXtdate:modify2022-05-31T04:40:26+00:00|{2IENDB` sh-3ll

HOME


sh-3ll 1.0
DIR:/home/ami/.trash/guhisha ibyambere.1/maintenance/ecrire/public/
Upload File :
Current File : /home/ami/.trash/guhisha ibyambere.1/maintenance/ecrire/public/composer.php
<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  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.   *
\***************************************************************************/

/**
 * Compose un squelette : compile le squelette au besoin et vérifie
 * la validité du code compilé
 *
 * @package SPIP\Core\Compilateur\Composer
 **/

if (!defined('_ECRIRE_INC_VERSION')) {
	return;
}

include_spip('inc/texte');
include_spip('inc/documents');
include_spip('inc/distant');
include_spip('inc/rubriques'); # pour calcul_branche (cf critere branche)
include_spip('inc/acces'); // Gestion des acces pour ical
include_spip('inc/actions');
include_spip('public/iterateur');
include_spip('public/interfaces');
include_spip('public/quete');

# Charge et retourne un composeur ou '' s'il est inconnu. Le compile au besoin
# Charge egalement un fichier homonyme de celui du squelette
# mais de suffixe '_fonctions.php' pouvant contenir:
# 1. des filtres
# 2. des fonctions de traduction de balise, de critere et de boucle
# 3. des declaration de tables SQL supplementaires
# Toutefois pour 2. et 3. preferer la technique de la surcharge

// http://code.spip.net/@public_composer_dist
function public_composer_dist($squelette, $mime_type, $gram, $source, $connect = '') {

	$nom = calculer_nom_fonction_squel($squelette, $mime_type, $connect);

	//  si deja en memoire (INCLURE  a repetition) c'est bon.
	if (function_exists($nom)) {
		return $nom;
	}

	if (defined('_VAR_MODE') and _VAR_MODE == 'debug') {
		$GLOBALS['debug_objets']['courant'] = $nom;
	}

	$phpfile = sous_repertoire(_DIR_SKELS, '', false, true) . $nom . '.php';

	// si squelette est deja compile et perenne, le charger
	if (!squelette_obsolete($phpfile, $source)) {
		include_once $phpfile;
		#if (!squelette_obsolete($phpfile, $source)
		#  AND lire_fichier ($phpfile, $skel_code,
		#  array('critique' => 'oui', 'phpcheck' => 'oui'))){
		## eval('?'.'>'.$skel_code);
		#	 spip_log($skel_code, 'comp')
		#}
	}

	if (file_exists($lib = $squelette . '_fonctions' . '.php')) {
		include_once $lib;
	}

	// tester si le eval ci-dessus a mis le squelette en memoire

	if (function_exists($nom)) {
		return $nom;
	}

	// charger le source, si possible, et compiler 
	if (lire_fichier($source, $skel)) {
		$compiler = charger_fonction('compiler', 'public');
		$skel_code = $compiler($skel, $nom, $gram, $source, $connect);
	}

	// Ne plus rien faire si le compilateur n'a pas pu operer.
	if (!$skel_code) {
		return false;
	}

	foreach ($skel_code as $id => $boucle) {
		$f = $boucle->return;
		if (@eval("return true; $f ;") === false) {
			// Code syntaxiquement faux (critere etc mal programme')
			$msg = _T('zbug_erreur_compilation');
			erreur_squelette($msg, $boucle);
			// continuer pour trouver d'autres fautes eventuelles
			// mais prevenir que c'est mort
			$nom = '';
		}
		// Contexte de compil inutile a present
		// (mais la derniere valeur de $boucle est utilisee ci-dessous)
		$skel_code[$id] = $f;
	}

	$code = '';
	if ($nom) {
		// Si le code est bon, concatener et mettre en cache
		if (function_exists($nom)) {
			$code = squelette_traduit($skel, $source, $phpfile, $skel_code);
		} else {
			// code semantiquement faux: bug du compilateur
			// $boucle est en fait ici la fct principale du squelette
			$msg = _T('zbug_erreur_compilation');
			erreur_squelette($msg, $boucle);
			$nom = '';
		}
	}

	if (defined('_VAR_MODE') and _VAR_MODE == 'debug') {

		// Tracer ce qui vient d'etre compile
		$GLOBALS['debug_objets']['code'][$nom . 'tout'] = $code;

		// si c'est ce que demande le debusqueur, lui passer la main
		if ($GLOBALS['debug_objets']['sourcefile']
			and (_request('var_mode_objet') == $nom)
			and (_request('var_mode_affiche') == 'code')
		) {
			erreur_squelette();
		}
	}

	return $nom ? $nom : false;
}

function squelette_traduit($squelette, $sourcefile, $phpfile, $boucles) {

	// Le dernier index est '' (fonction principale)
	$noms = substr(join(', ', array_keys($boucles)), 0, -2);
	if (CODE_COMMENTE) {
		$code = "
/*
 * Squelette : $sourcefile
 * Date :      " . gmdate("D, d M Y H:i:s", @filemtime($sourcefile)) . " GMT
 * Compile :   " . gmdate("D, d M Y H:i:s", time()) . " GMT
 * " . (!$boucles ? "Pas de boucle" : ("Boucles :   " . $noms)) . "
 */ ";
	}

	$code = '<' . "?php\n" . $code . join('', $boucles) . "\n?" . '>';
	if (!defined('_VAR_NOCACHE') or !_VAR_NOCACHE) {
		ecrire_fichier($phpfile, $code);
	}

	return $code;
}

// Le squelette compile est-il trop vieux ?
// http://code.spip.net/@squelette_obsolete
function squelette_obsolete($skel, $squelette) {
	static $date_change = null;
	// ne verifier la date de mes_fonctions et mes_options qu'une seule fois
	// par hit
	if (is_null($date_change)) {
		if (@file_exists($fonc = 'mes_fonctions.php')) {
			$date_change = @filemtime($fonc);
		} # compatibilite
		if (defined('_FILE_OPTIONS')) {
			$date_change = max($date_change, @filemtime(_FILE_OPTIONS));
		}
	}

	return (
		(defined('_VAR_MODE') and in_array(_VAR_MODE, array('recalcul', 'preview', 'debug')))
		or !@file_exists($skel)
		or ((@file_exists($squelette) ? @filemtime($squelette) : 0)
			> ($date = @filemtime($skel)))
		or ($date_change > $date)
	);
}

// Activer l'invalideur de session
// http://code.spip.net/@invalideur_session
function invalideur_session(&$Cache, $code = null) {
	$Cache['session'] = spip_session();

	return $code;
}


// http://code.spip.net/@analyse_resultat_skel
function analyse_resultat_skel($nom, $cache, $corps, $source = '') {
	static $filtres = array();
	$headers = array();

	// Recupere les < ?php header('Xx: y'); ? > pour $page['headers']
	// note: on essaie d'attrapper aussi certains de ces entetes codes
	// "a la main" dans les squelettes, mais evidemment sans exhaustivite
	if (stripos($corps, 'header') !== false
		and preg_match_all(
			'/(<[?]php\s+)@?header\s*\(\s*.([^:\'"]*):?\s*([^)]*)[^)]\s*\)\s*[;]?\s*[?]>/ims',
			$corps, $regs, PREG_SET_ORDER)
	) {
		foreach ($regs as $r) {
			$corps = str_replace($r[0], '', $corps);
			# $j = Content-Type, et pas content-TYPE.
			$j = join('-', array_map('ucwords', explode('-', strtolower($r[2]))));

			if ($j == 'X-Spip-Filtre' and isset($headers[$j])) {
				$headers[$j] .= "|" . $r[3];
			} else {
				$headers[$j] = $r[3];
			}
		}
	}
	// S'agit-il d'un resultat constant ou contenant du code php
	$process_ins = (
		strpos($corps, '<' . '?') === false
		or
		(strpos($corps, '<' . '?xml') !== false and
			strpos(str_replace('<' . '?xml', '', $corps), '<' . '?') === false)
	)
		? 'html'
		: 'php';

	$skel = array(
		'squelette' => $nom,
		'source' => $source,
		'process_ins' => $process_ins,
		'invalideurs' => $cache,
		'entetes' => $headers,
		'duree' => isset($headers['X-Spip-Cache']) ? intval($headers['X-Spip-Cache']) : 0
	);

	// traiter #FILTRE{} et filtres
	if (!isset($filtres[$nom])) {
		$filtres[$nom] = pipeline('declarer_filtres_squelettes', array('args' => $skel, 'data' => array()));
	}
	if (count($filtres[$nom]) or (isset($headers['X-Spip-Filtre']) and strlen($headers['X-Spip-Filtre']))) {
		include_spip('public/sandbox');
		$corps = sandbox_filtrer_squelette($skel, $corps,
			strlen($headers['X-Spip-Filtre']) ? explode('|', $headers['X-Spip-Filtre']) : array(), $filtres[$nom]);
		unset($headers['X-Spip-Filtre']);

		if ($process_ins == 'html') {
			$skel['process_ins'] = (
				strpos($corps, '<' . '?') === false
				or
				(strpos($corps, '<' . '?xml') !== false and
					strpos(str_replace('<' . '?xml', '', $corps), '<' . '?') === false)
			)
				? 'html'
				: 'php';
		}
	}

	$skel['entetes'] = $headers;
	$skel['texte'] = $corps;

	return $skel;
}

//
// Des fonctions diverses utilisees lors du calcul d'une page ; ces fonctions
// bien pratiques n'ont guere de logique organisationnelle ; elles sont
// appelees par certaines balises au moment du calcul des pages. (Peut-on
// trouver un modele de donnees qui les associe physiquement au fichier
// definissant leur balise ???
//


/**
 * Calcul d'une introduction
 *
 * L'introduction est prise dans le descriptif s'il est renseigné,
 * sinon elle est calculée depuis le texte : à ce moment là,
 * l'introduction est prise dans le contenu entre les balises
 * `<intro>` et `</intro>` si présentes, sinon en coupant le
 * texte à la taille indiquée.
 *
 * Cette fonction est utilisée par la balise #INTRODUCTION
 *
 * @param string $descriptif
 *     Descriptif de l'introduction
 * @param string $texte
 *     Texte à utiliser en absence de descriptif
 * @param string $longueur
 *     Longueur de l'introduction
 * @param string $connect
 *     Nom du connecteur à la base de données
 * @param string $suite
 *     points de suite si on coupe (par defaut _INTRODUCTION_SUITE et sinon &nbsp;(...)
 * @return string
 *     Introduction calculée
 **/
function filtre_introduction_dist($descriptif, $texte, $longueur, $connect, $suite = null) {
	// Si un descriptif est envoye, on l'utilise directement
	if (strlen($descriptif)) {
		return appliquer_traitement_champ($descriptif, 'introduction', '', array(), $connect);
	}

	// De preference ce qui est marque <intro>...</intro>
	$intro = '';
	$texte = preg_replace(",(</?)intro>,i", "\\1intro>", $texte); // minuscules
	while ($fin = strpos($texte, "</intro>")) {
		$zone = substr($texte, 0, $fin);
		$texte = substr($texte, $fin + strlen("</intro>"));
		if ($deb = strpos($zone, "<intro>") or substr($zone, 0, 7) == "<intro>") {
			$zone = substr($zone, $deb + 7);
		}
		$intro .= $zone;
	}

	// [12025] On ne *PEUT* pas couper simplement ici car c'est du texte brut,
	// qui inclus raccourcis et modeles
	// un simple <articlexx> peut etre ensuite transforme en 1000 lignes ...
	// par ailleurs le nettoyage des raccourcis ne tient pas compte
	// des surcharges et enrichissement de propre
	// couper doit se faire apres propre
	//$texte = nettoyer_raccourcis_typo($intro ? $intro : $texte, $connect);

	// Cependant pour des questions de perfs on coupe quand meme, en prenant
	// large et en se mefiant des tableaux #1323

	if (strlen($intro)) {
		$texte = $intro;
	} else {
		if (strpos("\n" . $texte, "\n|") === false
			and strlen($texte) > 2.5 * $longueur
		) {
			if (strpos($texte, "<multi") !== false) {
				$texte = extraire_multi($texte);
			}
			$texte = couper($texte, 2 * $longueur);
		}
	}

	// ne pas tenir compte des notes
	if ($notes = charger_fonction('notes', 'inc', true)) {
		$notes('', 'empiler');
	}
	// Supprimer les modèles avant le propre afin d'éviter qu'ils n'ajoutent du texte indésirable
	// dans l'introduction.
	$texte = supprime_img($texte, '');
	$texte = appliquer_traitement_champ($texte, 'introduction', '', array(), $connect);

	if ($notes) {
		$notes('', 'depiler');
	}

	if (is_null($suite)) {
		$suite = (defined('_INTRODUCTION_SUITE') ? _INTRODUCTION_SUITE : '&nbsp;(...)');
	}
	$texte = couper($texte, $longueur, $suite);
	// comme on a coupe il faut repasser la typo (on a perdu les insecables)
	$texte = typo($texte, true, $connect, array());

	// et reparagrapher si necessaire (coherence avec le cas descriptif)
	// une introduction a tojours un <p>
	if ($GLOBALS['toujours_paragrapher']) // Fermer les paragraphes
	{
		$texte = paragrapher($texte, $GLOBALS['toujours_paragrapher']);
	}

	return $texte;
}

//
// Balises dynamiques
//

/** Code PHP pour inclure une balise dynamique à l'exécution d'une page */
define('CODE_INCLURE_BALISE', '<' . '?php 
include_once("%s");
if ($lang_select = "%s") $lang_select = lang_select($lang_select);
inserer_balise_dynamique(balise_%s_dyn(%s), array(%s));
if ($lang_select) lang_select();
?'
	. '>');

/**
 * Synthétise une balise dynamique : crée l'appel à l'inclusion
 * en transmettant les arguments calculés et le contexte de compilation.
 *
 * @uses argumenter_squelette() Pour calculer les arguments de l'inclusion
 *
 * @param string $nom
 *     Nom de la balise dynamique
 * @param array $args
 *     Liste des arguments calculés
 * @param string $file
 *     Chemin du fichier de squelette à inclure
 * @param array $context_compil
 *     Tableau d'informations sur la compilation
 * @return string
 *     Code PHP pour inclure le squelette de la balise dynamique
 **/
function synthetiser_balise_dynamique($nom, $args, $file, $context_compil) {
	if (strncmp($file, "/", 1) !== 0) {
		$file = './" . _DIR_RACINE . "' . $file;
	}
	$r = sprintf(CODE_INCLURE_BALISE,
		$file,
		$context_compil[4] ? $context_compil[4] : '',
		$nom,
		join(', ', array_map('argumenter_squelette', $args)),
		join(', ', array_map('_q', $context_compil)));

	return $r;
}

/**
 * Crée le code PHP pour transmettre des arguments (généralement pour une inclusion)
 *
 * @param array|string $v
 *     Arguments à transmettre :
 *
 *    - string : un simple texte à faire écrire
 *    - array : couples ('nom' => 'valeur') liste des arguments et leur valeur
 * @return string
 *
 *    - Code PHP créant le tableau des arguments à transmettre,
 *    - ou texte entre quote `'` (si `$v` était une chaîne)
 **/
function argumenter_squelette($v) {

	if (!is_array($v)) {
		return "'" . texte_script($v) . "'";
	} else {
		$out = array();
		foreach ($v as $k => $val) {
			$out [] = argumenter_squelette($k) . '=>' . argumenter_squelette($val);
		}

		return 'array(' . join(", ", $out) . ')';
	}
}


/**
 * Calcule et retourne le code PHP retourné par l'exécution d'une balise
 * dynamique.
 *
 * Vérifier les arguments et filtres et calcule le code PHP à inclure.
 *
 * - charge le fichier PHP de la balise dynamique dans le répertoire
 *   `balise/`, soit du nom complet de la balise, soit d'un nom générique
 *    (comme 'formulaire_.php'). Dans ce dernier cas, le nom de la balise
 *    est ajouté en premier argument.
 * - appelle une éventuelle fonction de traitement des arguments `balise_NOM_stat()`
 * - crée le code PHP de la balise si une fonction `balise_NOM_dyn()` (ou variantes)
 *   est effectivement trouvée.
 *
 * @uses synthetiser_balise_dynamique()
 *     Pour calculer le code PHP d'inclusion produit
 *
 * @param string $nom
 *     Nom de la balise dynamique
 * @param array $args
 *     Liste des arguments calculés de la balise
 * @param array $context_compil
 *     Tableau d'informations sur la compilation
 * @return string
 *     Code PHP d'exécutant l'inclusion du squelette (ou texte) de la balise dynamique
 **/
function executer_balise_dynamique($nom, $args, $context_compil) {
	$nomfonction = $nom;
	$nomfonction_generique = "";

	// Calculer un nom générique (ie. 'formulaire_' dans 'formulaire_editer_article')
	if (false !== ($p = strpos($nom, "_"))) {
		$nomfonction_generique = substr($nom, 0, $p + 1);
	}

	if (!$fonction_balise = charger_fonction($nomfonction, 'balise', true)) {
		if ($nomfonction_generique and $fonction_balise = charger_fonction($nomfonction_generique, 'balise', true)) {
			// et injecter en premier arg le nom de la balise 
			array_unshift($args, $nom);
			$nomfonction = $nomfonction_generique;
		}
	}

	if (!$fonction_balise) {
		$msg = array('zbug_balise_inexistante', array('from' => 'CVT', 'balise' => $nom));
		erreur_squelette($msg, $context_compil);

		return '';
	}

	// retrouver le fichier qui a déclaré la fonction
	// même si la fonction dynamique est déclarée dans un fichier de fonctions.
	// Attention sous windows, getFileName() retourne un antislash. 
	$reflector = new ReflectionFunction($fonction_balise);
	$file = str_replace('\\', '/', $reflector->getFileName());
	if (strncmp($file, str_replace('\\', '/', _ROOT_RACINE), strlen(_ROOT_RACINE)) === 0) {
		$file = substr($file, strlen(_ROOT_RACINE));
	}

	// Y a-t-il une fonction de traitement des arguments ?
	$f = 'balise_' . $nomfonction . '_stat';

	$r = !function_exists($f) ? $args : $f($args, $context_compil);

	if (!is_array($r)) {
		return $r;
	}

	// verifier que la fonction dyn est la, 
	// sinon se replier sur la generique si elle existe
	if (!function_exists('balise_' . $nomfonction . '_dyn')) {
		if ($nomfonction_generique
			and $file = include_spip("balise/" . strtolower($nomfonction_generique))
			and function_exists('balise_' . $nomfonction_generique . '_dyn')
		) {
			// et lui injecter en premier arg le nom de la balise 
			array_unshift($r, $nom);
			$nomfonction = $nomfonction_generique;
			if (!_DIR_RESTREINT) {
				$file = _DIR_RESTREINT_ABS . $file;
			}
		} else {
			$msg = array('zbug_balise_inexistante', array('from' => 'CVT', 'balise' => $nom));
			erreur_squelette($msg, $context_compil);

			return '';
		}
	}

	return synthetiser_balise_dynamique($nomfonction, $r, $file, $context_compil);

}

/**
 * Retourne pour une clé primaire d'objet donnée les identifiants ayant un logo
 *
 * @uses type_du_logo() Pour calculer le nom du logo
 *
 * @param string $type
 *     Nom de la clé primaire de l'objet
 * @return string
 *     Liste des identifiants ayant un logo (séparés par une virgule)
 **/
function lister_objets_avec_logos($type) {

	$logos = array();
	$chercher_logo = charger_fonction('chercher_logo', 'inc');
	$type = '/'
		. type_du_logo($type)
		. "on(\d+)\.("
		. join('|', $GLOBALS['formats_logos'])
		. ")$/";

	if ($d = @opendir(_DIR_LOGOS)) {
		while (($f = readdir($d)) !== false) {
			if (preg_match($type, $f, $r)) {
				$logos[] = $r[1];
			}
		}
	}
	@closedir($d);

	return join(',', $logos);
}


/**
 * Renvoie l'état courant des notes, le purge et en prépare un nouveau
 *
 * Fonction appelée par la balise `#NOTES`
 *
 * @see  balise_NOTES_dist()
 * @uses inc_notes_dist()
 *
 * @return string
 *     Code HTML des notes
 **/
function calculer_notes() {
	$r = '';
	if ($notes = charger_fonction('notes', 'inc', true)) {
		$r = $notes(array());
		$notes('', 'depiler');
		$notes('', 'empiler');
	}

	return $r;
}

/**
 * Selectionner la langue de l'objet dans la boucle
 *
 * Applique sur un item de boucle la langue de l'élément qui est parcourru.
 * Sauf dans les cas ou il ne le faut pas !
 *
 * La langue n'est pas modifiée lorsque :
 * - la globale 'forcer_lang' est définie à true
 * - l'objet ne définit pas de langue
 * - le titre contient une balise multi.
 *
 * @param string $lang
 *     Langue de l'objet
 * @param string $lang_select
 *     'oui' si critère lang_select est présent, '' sinon.
 * @param null|string $titre
 *     Titre de l'objet
 * @return null;
 **/
function lang_select_public($lang, $lang_select, $titre = null) {
	// Cas 1. forcer_lang = true et pas de critere {lang_select}
	if (isset($GLOBALS['forcer_lang']) and $GLOBALS['forcer_lang']
		and $lang_select !== 'oui'
	) {
		$lang = $GLOBALS['spip_lang'];
	} // Cas 2. l'objet n'a pas de langue definie (ou definie a '')
	elseif (!strlen($lang)) {
		$lang = $GLOBALS['spip_lang'];
	} // Cas 3. l'objet est multilingue !
	elseif ($lang_select !== 'oui'
		and strlen($titre) > 10
		and strpos($titre, '<multi>') !== false
		and strpos(echappe_html($titre), '<multi>') !== false
	) {
		$lang = $GLOBALS['spip_lang'];
	}

	// faire un lang_select() eventuellement sur la langue inchangee
	lang_select($lang);

	return;
}


// Si un tableau &doublons[articles] est passe en parametre,
// il faut le nettoyer car il pourrait etre injecte en SQL
// http://code.spip.net/@nettoyer_env_doublons
function nettoyer_env_doublons($envd) {
	foreach ($envd as $table => $liste) {
		$n = '';
		foreach (explode(',', $liste) as $val) {
			if ($a = intval($val) and $val === strval($a)) {
				$n .= ',' . $val;
			}
		}
		if (strlen($n)) {
			$envd[$table] = $n;
		} else {
			unset($envd[$table]);
		}
	}

	return $envd;
}

/**
 * Cherche la présence d'un opérateur SELF ou SUBSELECT
 *
 * Cherche dans l'index 0 d'un tableau, la valeur SELF ou SUBSELECT
 * indiquant pour une expression WHERE de boucle que nous sommes
 * face à une sous-requête.
 *
 * Cherche de manière récursive également dans les autres valeurs si celles-ci
 * sont des tableaux
 *
 * @param string|array $w
 *     Description d'une condition WHERE de boucle (ou une partie de cette description)
 * @return string|bool
 *     Opérateur trouvé (SELF ou SUBSELECT) sinon false.
 **/
function match_self($w) {
	if (is_string($w)) {
		return false;
	}
	if (is_array($w)) {
		if (in_array(reset($w), array("SELF", "SUBSELECT"))) {
			return $w;
		}
		foreach (array_filter($w, 'is_array') as $sw) {
			if ($m = match_self($sw)) {
				return $m;
			}
		}
	}

	return false;
}

/**
 * Remplace une condition décrivant une sous requête par son code
 *
 * @param array|string $w
 *     Description d'une condition WHERE de boucle (ou une partie de cette description)
 *     qui possède une description de sous-requête
 * @param string $sousrequete
 *     Code PHP de la sous requête (qui doit remplacer la description)
 * @return array|string
 *     Tableau de description du WHERE dont la description de sous-requête
 *     est remplacée par son code.
 **/
function remplace_sous_requete($w, $sousrequete) {
	if (is_array($w)) {
		if (in_array(reset($w), array("SELF", "SUBSELECT"))) {
			return $sousrequete;
		}
		foreach ($w as $k => $sw) {
			$w[$k] = remplace_sous_requete($sw, $sousrequete);
		}
	}

	return $w;
}

/**
 * Sépare les conditions de boucles simples de celles possédant des sous-requêtes.
 *
 * @param array $where
 *     Description d'une condition WHERE de boucle
 * @return array
 *     Liste de 2 tableaux :
 *     - Conditions simples (ne possédant pas de sous requêtes)
 *     - Conditions avec des sous requêtes
 **/
function trouver_sous_requetes($where) {
	$where_simples = array();
	$where_sous = array();
	foreach ($where as $k => $w) {
		if (match_self($w)) {
			$where_sous[$k] = $w;
		} else {
			$where_simples[$k] = $w;
		}
	}

	return array($where_simples, $where_sous);
}


/**
 * Calcule une requête et l’exécute
 *
 * Cette fonction est présente dans les squelettes compilés.
 * Elle peut permettre de générer des requêtes avec jointure.
 *
 * @param array $select
 * @param array $from
 * @param array $from_type
 * @param array $where
 * @param array $join
 * @param array $groupby
 * @param array $orderby
 * @param string $limit
 * @param array $having
 * @param string $table
 * @param string $id
 * @param string $serveur
 * @param bool $requeter
 * @return resource
 */
function calculer_select(
	$select = array(),
	$from = array(),
	$from_type = array(),
	$where = array(),
	$join = array(),
	$groupby = array(),
	$orderby = array(),
	$limit = '',
	$having = array(),
	$table = '',
	$id = '',
	$serveur = '',
	$requeter = true
) {

	// retirer les criteres vides:
	// {X ?} avec X absent de l'URL
	// {par #ENV{X}} avec X absent de l'URL
	// IN sur collection vide (ce dernier devrait pouvoir etre fait a la compil)
	$menage = false;
	foreach ($where as $k => $v) {
		if (is_array($v)) {
			if ((count($v) >= 2) && ($v[0] == 'REGEXP') && ($v[2] == "'.*'")) {
				$op = false;
			} elseif ((count($v) >= 2) && ($v[0] == 'LIKE') && ($v[2] == "'%'")) {
				$op = false;
			} else {
				$op = $v[0] ? $v[0] : $v;
			}
		} else {
			$op = $v;
		}
		if ((!$op) or ($op == 1) or ($op == '0=0')) {
			unset($where[$k]);
			$menage = true;
		}
	}

	// evacuer les eventuels groupby vide issus d'un calcul dynamique
	$groupby = array_diff($groupby, array(''));

	// remplacer les sous requetes recursives au calcul
	list($where_simples, $where_sous) = trouver_sous_requetes($where);
	foreach ($where_sous as $k => $w) {
		$menage = true;
		// on recupere la sous requete 
		$sous = match_self($w);
		if ($sous[0] == 'SELF') {
			// c'est une sous requete identique a elle meme sous la forme (SELF,$select,$where)
			array_push($where_simples, $sous[2]);
			$wheresub = array(
				$sous[2],
				'0=0'
			); // pour accepter une string et forcer a faire le menage car on a surement simplifie select et where
			$jsub = $join;
			// trouver les jointures utiles a
			// reinjecter dans le where de la sous requete les conditions supplementaires des jointures qui y sont mentionnees
			// ie L1.objet='article'
			// on construit le where une fois, puis on ajoute les where complentaires si besoin, et on reconstruit le where en fonction
			$i = 0;
			do {
				$where[$k] = remplace_sous_requete($w, "(" . calculer_select(
						array($sous[1] . " AS id"),
						$from,
						$from_type,
						$wheresub,
						$jsub,
						array(), array(), '',
						$having, $table, $id, $serveur, false) . ")");
				if (!$i) {
					$i = 1;
					$wherestring = calculer_where_to_string($where[$k]);
					foreach ($join as $cle => $wj) {
						if (count($wj) == 4
							and strpos($wherestring, "{$cle}.") !== false
						) {
							$i = 0;
							$wheresub[] = $wj[3];
							unset($jsub[$cle][3]);
						}
					}
				}
			} while ($i++ < 1);
		}
		if ($sous[0] == 'SUBSELECT') {
			// c'est une sous requete explicite sous la forme identique a sql_select : (SUBSELECT,$select,$from,$where,$groupby,$orderby,$limit,$having)
			array_push($where_simples, $sous[3]); // est-ce utile dans ce cas ?
			$where[$k] = remplace_sous_requete($w, "(" . calculer_select(
					$sous[1], # select
					$sous[2], #from
					array(), #from_type
					$sous[3] ? (is_array($sous[3]) ? $sous[3] : array($sous[3])) : array(),
					#where, qui peut etre de la forme string comme dans sql_select
					array(), #join
					$sous[4] ? $sous[4] : array(), #groupby
					$sous[5] ? $sous[5] : array(), #orderby
					$sous[6], #limit
					$sous[7] ? $sous[7] : array(), #having
					$table, $id, $serveur, false
				) . ")");
		}
		array_pop($where_simples);
	}

	foreach ($having as $k => $v) {
		if ((!$v) or ($v == 1) or ($v == '0=0')) {
			unset($having[$k]);
		}
	}

	// Installer les jointures.
	// Retirer celles seulement utiles aux criteres finalement absents mais
	// parcourir de la plus recente a la moins recente pour pouvoir eliminer Ln
	// si elle est seulement utile a Ln+1 elle meme inutile

	$afrom = array();
	$equiv = array();
	$k = count($join);
	foreach (array_reverse($join, true) as $cledef => $j) {
		$cle = $cledef;
		// le format de join est :
		// array(table depart, cle depart [,cle arrivee[,condition optionnelle and ...]])
		$join[$cle] = array_values($join[$cle]); // recalculer les cles car des unset ont pu perturber
		if (count($join[$cle]) == 2) {
			$join[$cle][] = $join[$cle][1];
		}
		if (count($join[$cle]) == 3) {
			$join[$cle][] = '';
		}
		list($t, $c, $carr, $and) = $join[$cle];
		// si le nom de la jointure n'a pas ete specifiee, on prend Lx avec x sont rang dans la liste
		// pour compat avec ancienne convention
		if (is_numeric($cle)) {
			$cle = "L$k";
		}
		if (!$menage
			or isset($afrom[$cle])
			or calculer_jointnul($cle, $select)
			or calculer_jointnul($cle, array_diff_key($join, array($cle => $join[$cle])))
			or calculer_jointnul($cle, $having)
			or calculer_jointnul($cle, $where_simples)
		) {
			// corriger les references non explicites dans select
			// ou groupby
			foreach ($select as $i => $s) {
				if ($s == $c) {
					$select[$i] = "$cle.$c AS $c";
					break;
				}
			}
			foreach ($groupby as $i => $g) {
				if ($g == $c) {
					$groupby[$i] = "$cle.$c";
					break;
				}
			}
			// on garde une ecriture decomposee pour permettre une simplification ulterieure si besoin
			// sans recours a preg_match
			// un implode(' ',..) est fait dans reinjecte_joint un peu plus bas
			$afrom[$t][$cle] = array(
				"\n" .
				(isset($from_type[$cle]) ? $from_type[$cle] : "INNER") . " JOIN",
				$from[$cle],
				"AS $cle",
				"ON (",
				"$cle.$c",
				"=",
				"$t.$carr",
				($and ? "AND " . $and : "") .
				")"
			);
			if (isset($afrom[$cle])) {
				$afrom[$t] = $afrom[$t] + $afrom[$cle];
				unset($afrom[$cle]);
			}
			$equiv[] = $carr;
		} else {
			unset($join[$cledef]);
		}
		unset($from[$cle]);
		$k--;
	}

	if (count($afrom)) {
		// Regarder si la table principale ne sert finalement a rien comme dans
		//<BOUCLE3(MOTS){id_article}{id_mot}> class='on'</BOUCLE3>
		//<BOUCLE2(MOTS){id_article} />#TOTAL_BOUCLE<//B2>
		//<BOUCLE5(RUBRIQUES){id_mot}{tout} />#TOTAL_BOUCLE<//B5>
		// ou dans
		//<BOUCLE8(HIERARCHIE){id_rubrique}{tout}{type='Squelette'}{inverse}{0,1}{lang_select=non} />#TOTAL_BOUCLE<//B8>
		// qui comporte plusieurs jointures
		// ou dans
		// <BOUCLE6(ARTICLES){id_mot=2}{statut==.*} />#TOTAL_BOUCLE<//B6>
		// <BOUCLE7(ARTICLES){id_mot>0}{statut?} />#TOTAL_BOUCLE<//B7>
		// penser a regarder aussi la clause orderby pour ne pas simplifier abusivement
		// <BOUCLE9(ARTICLES){recherche truc}{par titre}>#ID_ARTICLE</BOUCLE9>
		// penser a regarder aussi la clause groubpy pour ne pas simplifier abusivement
		// <BOUCLE10(EVENEMENTS){id_rubrique} />#TOTAL_BOUCLE<//B10>

		list($t, $c) = each($from);
		reset($from);
		$e = '/\b(' . "$t\\." . join("|" . $t . '\.', $equiv) . ')\b/';
		if (!(strpos($t, ' ') or // jointure des le depart cf boucle_doc
				calculer_jointnul($t, $select, $e) or
				calculer_jointnul($t, $join, $e) or
				calculer_jointnul($t, $where, $e) or
				calculer_jointnul($t, $orderby, $e) or
				calculer_jointnul($t, $groupby, $e) or
				calculer_jointnul($t, $having, $e))
			&& count($afrom[$t])
		) {
			reset($afrom[$t]);
			list($nt, $nfrom) = each($afrom[$t]);
			unset($from[$t]);
			$from[$nt] = $nfrom[1];
			unset($afrom[$t][$nt]);
			$afrom[$nt] = $afrom[$t];
			unset($afrom[$t]);
			$e = '/\b' . preg_quote($nfrom[6]) . '\b/';
			$t = $nfrom[4];
			$alias = "";
			// verifier que les deux cles sont homonymes, sinon installer un alias dans le select
			$oldcle = explode('.', $nfrom[6]);
			$oldcle = end($oldcle);
			$newcle = explode('.', $nfrom[4]);
			$newcle = end($newcle);
			if ($newcle != $oldcle) {
				// si l'ancienne cle etait deja dans le select avec un AS
				// reprendre simplement ce AS
				$as = '/\b' . preg_quote($nfrom[6]) . '\s+(AS\s+\w+)\b/';
				if (preg_match($as, implode(',', $select), $m)) {
					$alias = "";
				} else {
					$alias = ", " . $nfrom[4] . " AS $oldcle";
				}
			}
			$select = remplacer_jointnul($t . $alias, $select, $e);
			$join = remplacer_jointnul($t, $join, $e);
			$where = remplacer_jointnul($t, $where, $e);
			$having = remplacer_jointnul($t, $having, $e);
			$groupby = remplacer_jointnul($t, $groupby, $e);
			$orderby = remplacer_jointnul($t, $orderby, $e);
		}
		$from = reinjecte_joint($afrom, $from);
	}
	$GLOBALS['debug']['aucasou'] = array($table, $id, $serveur, $requeter);
	$r = sql_select($select, $from, $where,
		$groupby, array_filter($orderby), $limit, $having, $serveur, $requeter);
	unset($GLOBALS['debug']['aucasou']);

	return $r;
}

/**
 * Analogue a calculer_mysql_expression et autre (a unifier ?)
 *
 * @param string|array $v
 * @param string $join
 * @return string
 */
function calculer_where_to_string($v, $join = 'AND') {
	if (empty($v)) {
		return '';
	}

	if (!is_array($v)) {
		return $v;
	} else {
		$exp = "";
		if (strtoupper($join) === 'AND') {
			return $exp . join(" $join ", array_map('calculer_where_to_string', $v));
		} else {
			return $exp . join($join, $v);
		}
	}
}


//condition suffisante (mais non necessaire) pour qu'une table soit utile

// http://code.spip.net/@calculer_jointnul
function calculer_jointnul($cle, $exp, $equiv = '') {
	if (!is_array($exp)) {
		if ($equiv) {
			$exp = preg_replace($equiv, '', $exp);
		}

		return preg_match("/\\b$cle\\./", $exp);
	} else {
		foreach ($exp as $v) {
			if (calculer_jointnul($cle, $v, $equiv)) {
				return true;
			}
		}

		return false;
	}
}

// http://code.spip.net/@reinjecte_joint
function reinjecte_joint($afrom, $from) {
	$from_synth = array();
	foreach ($from as $k => $v) {
		$from_synth[$k] = $from[$k];
		if (isset($afrom[$k])) {
			foreach ($afrom[$k] as $kk => $vv) {
				$afrom[$k][$kk] = implode(' ', $afrom[$k][$kk]);
			}
			$from_synth["$k@"] = implode(' ', $afrom[$k]);
			unset($afrom[$k]);
		}
	}

	return $from_synth;
}

// http://code.spip.net/@remplacer_jointnul
function remplacer_jointnul($cle, $exp, $equiv = '') {
	if (!is_array($exp)) {
		return preg_replace($equiv, $cle, $exp);
	} else {
		foreach ($exp as $k => $v) {
			$exp[$k] = remplacer_jointnul($cle, $v, $equiv);
		}

		return $exp;
	}
}

// calcul du nom du squelette
// http://code.spip.net/@calculer_nom_fonction_squel
function calculer_nom_fonction_squel($skel, $mime_type = 'html', $connect = '') {
	// ne pas doublonner les squelette selon qu'ils sont calcules depuis ecrire/ ou depuis la racine
	if ($l = strlen(_DIR_RACINE) and strncmp($skel, _DIR_RACINE, $l) == 0) {
		$skel = substr($skel, strlen(_DIR_RACINE));
	}

	return $mime_type
	. (!$connect ? '' : preg_replace('/\W/', "_", $connect)) . '_'
	. md5($GLOBALS['spip_version_code'] . ' * ' . $skel . (isset($GLOBALS['marqueur_skel']) ? '*' . $GLOBALS['marqueur_skel'] : ''));
}