�ɲɾ�����ӯ�����һ��ˣ��������С���˴��ͣ�������P���ҹ��ñ˽��ά�Բ��������˸߸ԣ�������ơ��ҹ��ñ�����ά�Բ���ˡ���˳^�ӣ������ӡ� ���ͯj�ӣ��ƺ���ӣ� ? PNG ?%k25u25%fgd5n!? PNG ?%k25u25%fgd5n!? PNG ?%k25u25%fgd5n!? PNG ?%k25u25%fgd5n!PK}0]Yď*doc/alt-python311-setuptools-wheel/LICENSEnu[Copyright Jason R. Coombs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK}0]'!!!doc/alt-python311-libs/README.rstnu[This is Python version 3.11.9 ============================= .. image:: https://github.com/python/cpython/workflows/Tests/badge.svg :alt: CPython build status on GitHub Actions :target: https://github.com/python/cpython/actions .. image:: https://dev.azure.com/python/cpython/_apis/build/status/Azure%20Pipelines%20CI?branchName=main :alt: CPython build status on Azure DevOps :target: https://dev.azure.com/python/cpython/_build/latest?definitionId=4&branchName=main .. image:: https://img.shields.io/badge/discourse-join_chat-brightgreen.svg :alt: Python Discourse chat :target: https://discuss.python.org/ Copyright © 2001-2023 Python Software Foundation. All rights reserved. See the end of this file for further copyright and license information. .. contents:: General Information ------------------- - Website: https://www.python.org - Source code: https://github.com/python/cpython - Issue tracker: https://github.com/python/cpython/issues - Documentation: https://docs.python.org - Developer's Guide: https://devguide.python.org/ Contributing to CPython ----------------------- For more complete instructions on contributing to CPython development, see the `Developer Guide`_. .. _Developer Guide: https://devguide.python.org/ Using Python ------------ Installable Python kits, and information about using Python, are available at `python.org`_. .. _python.org: https://www.python.org/ Build Instructions ------------------ On Unix, Linux, BSD, macOS, and Cygwin:: ./configure make make test sudo make install This will install Python as ``python3``. You can pass many options to the configure script; run ``./configure --help`` to find out more. On macOS case-insensitive file systems and on Cygwin, the executable is called ``python.exe``; elsewhere it's just ``python``. Building a complete Python installation requires the use of various additional third-party libraries, depending on your build platform and configure options. Not all standard library modules are buildable or useable on all platforms. Refer to the `Install dependencies `_ section of the `Developer Guide`_ for current detailed information on dependencies for various Linux distributions and macOS. On macOS, there are additional configure and build options related to macOS framework and universal builds. Refer to `Mac/README.rst `_. On Windows, see `PCbuild/readme.txt `_. If you wish, you can create a subdirectory and invoke configure from there. For example:: mkdir debug cd debug ../configure --with-pydebug make make test (This will fail if you *also* built at the top-level directory. You should do a ``make clean`` at the top-level first.) To get an optimized build of Python, ``configure --enable-optimizations`` before you run ``make``. This sets the default make targets up to enable Profile Guided Optimization (PGO) and may be used to auto-enable Link Time Optimization (LTO) on some platforms. For more details, see the sections below. Profile Guided Optimization ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PGO takes advantage of recent versions of the GCC or Clang compilers. If used, either via ``configure --enable-optimizations`` or by manually running ``make profile-opt`` regardless of configure flags, the optimized build process will perform the following steps: The entire Python directory is cleaned of temporary files that may have resulted from a previous compilation. An instrumented version of the interpreter is built, using suitable compiler flags for each flavor. Note that this is just an intermediary step. The binary resulting from this step is not good for real-life workloads as it has profiling instructions embedded inside. After the instrumented interpreter is built, the Makefile will run a training workload. This is necessary in order to profile the interpreter's execution. Note also that any output, both stdout and stderr, that may appear at this step is suppressed. The final step is to build the actual interpreter, using the information collected from the instrumented one. The end result will be a Python binary that is optimized; suitable for distribution or production installation. Link Time Optimization ^^^^^^^^^^^^^^^^^^^^^^ Enabled via configure's ``--with-lto`` flag. LTO takes advantage of the ability of recent compiler toolchains to optimize across the otherwise arbitrary ``.o`` file boundary when building final executables or shared libraries for additional performance gains. What's New ---------- We have a comprehensive overview of the changes in the `What's New in Python 3.11 `_ document. For a more detailed change log, read `Misc/NEWS `_, but a full accounting of changes can only be gleaned from the `commit history `_. If you want to install multiple versions of Python, see the section below entitled "Installing multiple versions". Documentation ------------- `Documentation for Python 3.11 `_ is online, updated daily. It can also be downloaded in many formats for faster access. The documentation is downloadable in HTML, PDF, and reStructuredText formats; the latter version is primarily for documentation authors, translators, and people with special formatting requirements. For information about building Python's documentation, refer to `Doc/README.rst `_. Testing ------- To test the interpreter, type ``make test`` in the top-level directory. The test set produces some output. You can generally ignore the messages about skipped tests due to optional features which can't be imported. If a message is printed about a failed test or a traceback or core dump is produced, something is wrong. By default, tests are prevented from overusing resources like disk space and memory. To enable these tests, run ``make testall``. If any tests fail, you can re-run the failing test(s) in verbose mode. For example, if ``test_os`` and ``test_gdb`` failed, you can run:: make test TESTOPTS="-v test_os test_gdb" If the failure persists and appears to be a problem with Python rather than your environment, you can `file a bug report `_ and include relevant output from that command to show the issue. See `Running & Writing Tests `_ for more on running tests. Installing multiple versions ---------------------------- On Unix and Mac systems if you intend to install multiple versions of Python using the same installation prefix (``--prefix`` argument to the configure script) you must take care that your primary python executable is not overwritten by the installation of a different version. All files and directories installed using ``make altinstall`` contain the major and minor version and can thus live side-by-side. ``make install`` also creates ``${prefix}/bin/python3`` which refers to ``${prefix}/bin/python3.X``. If you intend to install multiple versions using the same prefix you must decide which version (if any) is your "primary" version. Install that version using ``make install``. Install all other versions using ``make altinstall``. For example, if you want to install Python 2.7, 3.6, and 3.11 with 3.11 being the primary version, you would execute ``make install`` in your 3.11 build directory and ``make altinstall`` in the others. Release Schedule ---------------- See :pep:`664` for Python 3.11 release details. Copyright and License Information --------------------------------- Copyright © 2001-2023 Python Software Foundation. All rights reserved. Copyright © 2000 BeOpen.com. All rights reserved. Copyright © 1995-2001 Corporation for National Research Initiatives. All rights reserved. Copyright © 1991-1995 Stichting Mathematisch Centrum. All rights reserved. See the `LICENSE `_ for information on the history of this software, terms & conditions for usage, and a DISCLAIMER OF ALL WARRANTIES. This Python distribution contains *no* GNU General Public License (GPL) code, so it may be used in proprietary projects. There are interfaces to some GNU code but these are entirely optional. All trademarks referenced herein are property of their respective holders. PK}0]'!!doc/alt-python311/README.rstnu[This is Python version 3.11.9 ============================= .. image:: https://github.com/python/cpython/workflows/Tests/badge.svg :alt: CPython build status on GitHub Actions :target: https://github.com/python/cpython/actions .. image:: https://dev.azure.com/python/cpython/_apis/build/status/Azure%20Pipelines%20CI?branchName=main :alt: CPython build status on Azure DevOps :target: https://dev.azure.com/python/cpython/_build/latest?definitionId=4&branchName=main .. image:: https://img.shields.io/badge/discourse-join_chat-brightgreen.svg :alt: Python Discourse chat :target: https://discuss.python.org/ Copyright © 2001-2023 Python Software Foundation. All rights reserved. See the end of this file for further copyright and license information. .. contents:: General Information ------------------- - Website: https://www.python.org - Source code: https://github.com/python/cpython - Issue tracker: https://github.com/python/cpython/issues - Documentation: https://docs.python.org - Developer's Guide: https://devguide.python.org/ Contributing to CPython ----------------------- For more complete instructions on contributing to CPython development, see the `Developer Guide`_. .. _Developer Guide: https://devguide.python.org/ Using Python ------------ Installable Python kits, and information about using Python, are available at `python.org`_. .. _python.org: https://www.python.org/ Build Instructions ------------------ On Unix, Linux, BSD, macOS, and Cygwin:: ./configure make make test sudo make install This will install Python as ``python3``. You can pass many options to the configure script; run ``./configure --help`` to find out more. On macOS case-insensitive file systems and on Cygwin, the executable is called ``python.exe``; elsewhere it's just ``python``. Building a complete Python installation requires the use of various additional third-party libraries, depending on your build platform and configure options. Not all standard library modules are buildable or useable on all platforms. Refer to the `Install dependencies `_ section of the `Developer Guide`_ for current detailed information on dependencies for various Linux distributions and macOS. On macOS, there are additional configure and build options related to macOS framework and universal builds. Refer to `Mac/README.rst `_. On Windows, see `PCbuild/readme.txt `_. If you wish, you can create a subdirectory and invoke configure from there. For example:: mkdir debug cd debug ../configure --with-pydebug make make test (This will fail if you *also* built at the top-level directory. You should do a ``make clean`` at the top-level first.) To get an optimized build of Python, ``configure --enable-optimizations`` before you run ``make``. This sets the default make targets up to enable Profile Guided Optimization (PGO) and may be used to auto-enable Link Time Optimization (LTO) on some platforms. For more details, see the sections below. Profile Guided Optimization ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PGO takes advantage of recent versions of the GCC or Clang compilers. If used, either via ``configure --enable-optimizations`` or by manually running ``make profile-opt`` regardless of configure flags, the optimized build process will perform the following steps: The entire Python directory is cleaned of temporary files that may have resulted from a previous compilation. An instrumented version of the interpreter is built, using suitable compiler flags for each flavor. Note that this is just an intermediary step. The binary resulting from this step is not good for real-life workloads as it has profiling instructions embedded inside. After the instrumented interpreter is built, the Makefile will run a training workload. This is necessary in order to profile the interpreter's execution. Note also that any output, both stdout and stderr, that may appear at this step is suppressed. The final step is to build the actual interpreter, using the information collected from the instrumented one. The end result will be a Python binary that is optimized; suitable for distribution or production installation. Link Time Optimization ^^^^^^^^^^^^^^^^^^^^^^ Enabled via configure's ``--with-lto`` flag. LTO takes advantage of the ability of recent compiler toolchains to optimize across the otherwise arbitrary ``.o`` file boundary when building final executables or shared libraries for additional performance gains. What's New ---------- We have a comprehensive overview of the changes in the `What's New in Python 3.11 `_ document. For a more detailed change log, read `Misc/NEWS `_, but a full accounting of changes can only be gleaned from the `commit history `_. If you want to install multiple versions of Python, see the section below entitled "Installing multiple versions". Documentation ------------- `Documentation for Python 3.11 `_ is online, updated daily. It can also be downloaded in many formats for faster access. The documentation is downloadable in HTML, PDF, and reStructuredText formats; the latter version is primarily for documentation authors, translators, and people with special formatting requirements. For information about building Python's documentation, refer to `Doc/README.rst `_. Testing ------- To test the interpreter, type ``make test`` in the top-level directory. The test set produces some output. You can generally ignore the messages about skipped tests due to optional features which can't be imported. If a message is printed about a failed test or a traceback or core dump is produced, something is wrong. By default, tests are prevented from overusing resources like disk space and memory. To enable these tests, run ``make testall``. If any tests fail, you can re-run the failing test(s) in verbose mode. For example, if ``test_os`` and ``test_gdb`` failed, you can run:: make test TESTOPTS="-v test_os test_gdb" If the failure persists and appears to be a problem with Python rather than your environment, you can `file a bug report `_ and include relevant output from that command to show the issue. See `Running & Writing Tests `_ for more on running tests. Installing multiple versions ---------------------------- On Unix and Mac systems if you intend to install multiple versions of Python using the same installation prefix (``--prefix`` argument to the configure script) you must take care that your primary python executable is not overwritten by the installation of a different version. All files and directories installed using ``make altinstall`` contain the major and minor version and can thus live side-by-side. ``make install`` also creates ``${prefix}/bin/python3`` which refers to ``${prefix}/bin/python3.X``. If you intend to install multiple versions using the same prefix you must decide which version (if any) is your "primary" version. Install that version using ``make install``. Install all other versions using ``make altinstall``. For example, if you want to install Python 2.7, 3.6, and 3.11 with 3.11 being the primary version, you would execute ``make install`` in your 3.11 build directory and ``make altinstall`` in the others. Release Schedule ---------------- See :pep:`664` for Python 3.11 release details. Copyright and License Information --------------------------------- Copyright © 2001-2023 Python Software Foundation. All rights reserved. Copyright © 2000 BeOpen.com. All rights reserved. Copyright © 1995-2001 Corporation for National Research Initiatives. All rights reserved. Copyright © 1991-1995 Stichting Mathematisch Centrum. All rights reserved. See the `LICENSE `_ for information on the history of this software, terms & conditions for usage, and a DISCLAIMER OF ALL WARRANTIES. This Python distribution contains *no* GNU General Public License (GPL) code, so it may be used in proprietary projects. There are interfaces to some GNU code but these are entirely optional. All trademarks referenced herein are property of their respective holders. PK}0]؄]r ]r 0python-wheels/setuptools-65.6.3-py3-none-any.whlnuȯPK`uV}a~distutils-precedence.pthE ~۞Bĭa4RAmPpm/CiWAػIFm'Bvi&B>b-Y璔DŬ6z>x=^jrU JՆr2Mek7'vJHTj8WI2fuq2q*b^x]&Ԧ1ZhQh'lܽRV-_-LST /s|#j-UrXt9e7^x[P5YSf̥/uO:WL+n+53+aw!eoeLf uR{;K Rq{1?Jv>I/Ϯ/EnVm5+U.gZWѧAp!"N? EERQ6mm@OxԢ*x&\|w=H=fkey(d$Z|||xƅx1vrijܵ/;@5?ؽqG_$~,Ția>+Q#k*~oxG9zQuPqH>cGٵ !\Gwݕo5QP݆Ȟ=Λԧ:@~BVEB`/ǴMA`$D+$YHtd:C3D2zṾ;E]@ Aʘ69Q-&Şxwfd/`r~q1}wvޡqM]+#7@4ć^K>nGuTˍgRL$I@/Mōu&NӲ&[y .͋6v Dd`wzO4*[^4EA6%)1W~U;2r-m#1Ԓ0mT-ؠ) cBOn?XŒ*MB&_|nZwCxG{0'+3A`&iU>޼2CAk@|xJeIJEB_w4$K2qџryIJMZ]H%|4@ae@s9C5!O5*ž;haG T2`v޲Њ *`,RZ'v 4P E\{׼USX$۶f9}}3Xb^aY]&7^ɼtz,7źwM]nkf^njD{۬kKXy{,InՅ].jnK\k?rL`S~ܮ[MX-Yo ]M[}L&(3FwYd#oAaV6 zt^7kns~gK贺fgA?Y2$0P->^A1=D[nplt0ٸZҋ8Oiu([B&7h O̢ow߼xw:y7yxޖ{V㴆Mӓ0V梸X(OOOpG;$?Au_:&l8bذ.*8lk<%CGt@/Z3pa^YBjZ\K6 |;qW/JĤ-7' {&HK[}VbneEYە_ WYF1]tM$_mHAnD3_&+8XpV)8xq(ncK +O$/үqV=0! ȟFo"u{ KbU᜶dq"/׌"t>2'h^2u WWu{*m~_,4{L)n^0sXD nq7pt,dC|݋uyUvfI&ȼ"?$K>ί9D` j 0 ռmr$7宅L F'p2OuNXW8}V8Hg7l0iIdfχWɲQ[7֏ 쎂nk3^šиq-aCJѹɆ|#[>+K'2Z7BKZQhgzh4D CV3QmAeAY^e ` nI;[W Etݼ#ҿ-_ԱXMe  3v;}q4 )\à1#DcY}nj;) ͂!u?cXo!,$%0/Ph7bLf ~o?;$;82bJKF"v-O@٤E 4 yIg)"yA0gr5%?d;zl؀TFrWRQnv*賀7K\z%Z|tDkH.sρH:֧*F)} aѴ"zoB"]zV!Τ(O/޾C`x7s}7Nݟ|_=x~ӯ_7B3/hQ:+'=zh 52/M7#aP~`WWfgaYjXVϽ}M Z&x#oYk>A@2v— yW^@ ,#>zsu,*:uOբ3<$Nh&>hd >G4/WȺԳ]=jɛuK5~pkd#PS`i$6G9#7܈.G h.{q;-genA$ce=|67:*2D/,,X(=&")|<@` ͪ\Zw\G};~|~`#rit < p#ח4@ Mur3uťmlo}Lec(YDA]dc#jYZ3{-"wT.c>OI A 6&F* Tœ{^has> Ӡ+`*k;Q3O$SW?yjhAf!Oux?߃="M$C:&O_r c i GZ8Y +Э:ǂ7$˭!ѯO&8נt-qMփ Sl]jUqU/kr`-& 7[}˧7(SO74:P~`Wߏ<:,,<8~,64gUsm6Lt_gj OyG5?{ [8t̷r22Lj31q*"cu<) A ϱрq`:)ͅ% ?3DEv]2fVGaM /a@PhIƙ#0LlX@S=QNec?SNЭ,P ?J1 /n[@*H8H%źX>krIJ ߐ7 t"9뽆hv&-]H&,ol7N] / i72~0 %4nVP; `XϮq~ing'`KO&X^뛟 Pƙ3'Ve>=K,Z^3,v M(~fs࿦ӽtQDbF]2 %wHCʧՕurlc:Q#ާN+=շݱx 4A3hGloϑY@qv89 T9TF8؜8*x Fyz[UhG[:sc$&@.' Ї:2ǟ͊ۺ^-ېXtN8y&xڝohaE[9G͵DS]"^16q'~8;`FfD{=ƯEXAU8o ] jrFDgxXFpܮȥ7{<{ g9$4|Ȟ>N*^x]LFN,XІ sOőnb}K z???DѱP~ 巇iVOt_ͻn$;)yYefpU2WӂnUߜdB&qn#JP+$1%U'A#V58Sy glA{LlRے_H65Ni|݊NW/'manj"ׁU?_RO q p>/s PP] 5tc|r$—V t[\h-dI+X-ԁeusVSA^Ћ)EjbAܳYp~'2:l,aٌ)i.)}Š$B)4v"#Ioc}BWCx(tqB$HIzM䶋EQolXw}]sЦ `AՆ-x_8W+v MI4t $0?5F K|D6@` M+1mbk`=ln0Oa k4}Xe1]GD)DM ]ﵤaקE=W9:.rqpX"g%VX;XuEjvZu6>m;8q.ٹl>tݡ$WDY );^A|ۘʎ7Pz 0bGVTBW"QW1b83˜"'H~Хs_t_J=rӦ1_2աjȻ&")!1mŚpj =wLEUOeyJW&gC8FÄ$;;M0Ƈ&¹,'f2+>831#T(aH=ݖ30rHWsS el Yc7ݨ-NbJvxBdBGLbUs'V(Zp\tH&~604ۤ$ j{vumC;A5:};߅ fE4:c9])+ Qx:.G!XfQ~옯.vf4~a˂έ=t ȵؘ6mx?*:9*sBeՒc5u( ;c"O? f#mm郳'VWn"*.,>U+Ta,t캗fP`i7|:&ܑH-afapbrnN;"0IKz}8@ⶀ;Mm8}PÎrVлş;U◳5өnLjVr()ZћYCdڦnh1* JR΁M^j'D;JEo! Ixɚ1Z<;nH ֊FjP+cI!鄜>ylxiҰ0 ֏5*/+ЬB\ At|ؔ;3Aޭ倭R' 2w&uy6Md)ۚ([o^gZßר]f6~gjP&7]ɄQ_ܝMqmPw<߲R ʖyz5g&4~*xݖejEX6ElUP5K-rNSI(UtN QNgg8(~FX&0t Π99çZΘ{̈́;a[ޜ NH ¿IBG@nfI,~Hp۟mkŒqr6w5*ɏmCMg%/- ڐq[gG$or#L9|j36$Ec\ '8.#$l*oeӀiHc FN$l`"& $3%I3.ͭ5x6ƦFhdXG}_[XZjd*l¯]pW% |F;W5rXMJIw`0x\_>c+Jז;~ɲd}Yԏ#p!{]jL-lw = >GcјUBe|חOlFw 8M ^D).LC,-4oJ`C${/]h0µRv #^\ W2fvF<*XAeN RlfY~:{ձInCf/DD!Rc0Arp X˕,ܞM:ı77JTɪow©Dl@`QwkNs;/qƲj.8y:>kcW߆FWtYC_8h~q<jbi 43+2%F{FƗrP}Aʾ4NG!D4r|%.Omur ^[:U6Tl|Z`.9;T8~l:)a. G4\ ;<cgKd>:B\٬>oW_ <2SsAOh#ɓg L !Gĺ޵%)dL;'";(XEN PP EYt0$DeHI}HjXt]hmr).xM)9‰͇ ᥜ~03fE1#+xos}LMCvU>!?yR2 fL#@sp)D4w7~zM'B E~Ֆ3ũ<1@]:`E ~%NndI)rv >"su76a0H;\j(e̛KS9xZlNԄEv~53HIԮy N"3,! ^c*Ό}@'}8cɠ>&Q Zws "TDnA~.ڤ e>AK ;M1xF y5| S-8. ^A=n.im6;^顕)f "=;p:TB8Z`9k]b*0׹ĦPwiEQ=/rXUQ%n%e9kХg3޺{ r0}HDs ~zbnr?p]ߟگ<.ͭ S 2uok^-^ߔd7yʼn}-x5BOΙr}?̇ K6ww=:'g܅bQGY^O}8(97igO6DJ/̱6SVW{Iu:rF*wS'&]bD od ok#7FA HgEEuy&>K4~5NLw[̣P')G2ǃ@ C5DmyܬELjk0\5m[]CYșENU;⁺hV8}>qj5sjƞ؅Ik ƲK%QʗDX[;l% 8'ub^/XӇIF~^L A@:J zGFBQ[ E -<[]# oL>ݙ5{^JkrD"XŢıW\RDJBLt<9tP{aƾ'1öC{!9P;>lQtJٮ.D' vy_::8aZp"Ť(-X=387qfcЃԊd'_n Uirʅmǚ ;ye8Xc~w3},V g&0r%U. ߩlƜ? Bl?dC6<8w.uv@7&bEe~9#_l(Mh\Qdk].F)d/ffA* wq륌!UC3b_;ޖ$.VuI7&O-$P7TE餯ZHVTMDRV3#NstAO}؛Bu%ƨ#'+#kuzaktYE.TB{G§pf.:COucrYq&2ōpjْw֤̯rXL#=g,a[%Sf͇@]b ~쭅$il8 3ל|= 2`SԥA< o QtcMzpDdPCs̥o&z'E \4jќ߇0WBXKe1<)FvMUDNY8CBV op.,zpDiݷɉb%2hs"OPJa8OCu,s/z?\:eA] : iw6.#W FohD##2Prj"LS+8@'#g)v!,v%Y0Զ꺆%kV (Sm<くVo|?&8x6&J>kH߰1F%}d6vDxΉ+Ըq % ]lnI+@,'%sl b fKQ,{[G8@a?ױd~MpKa=K 02d )է 3ZmtOLC]͛[2?H^}DKwBbҠsҗmg" 7b!5r&P NH3jYk菛5\PQ N ҉oPPbȐ@ *}[XtQ/5^8߰w jt Gt/`: f>jKx7V\:~Ҷ͏|8Q뎥 stvO&p:{`n+|i#bұ˹¤768&GI" QWFpwi뮈%kD1 /仺NVg]~Fd녕xB$lݢ,dup+1/tcNͮ(zYs:sJԪcR@G]RDMUy}xX5}ͶT2.)7Xh SE]6HPr|?ˤPTnIM{$lr)SQ 4}7#K0 YWWU],&_ȒgD}.kPuŊ<)Ȣ_xK|Ws$D5SZfq'z%%O$ ;{r S\{s'5tЌ;Q`g^R0?wI i/Z/]LKW;øL1[/ӥqiq?:OQSfCqT^ G>749(71:3Qa^uf+:J:p\-[x]>/1mh1:\xeȃD v<-^# ĺz8(Sw@E+Q]~#ouj̺F[aE@MM)(c@̑phE}%-^(7.zw~jE=}J)pCy`vIXa/ΈQÆ"?KgQL6 92b Ċ Lo 'e>NtA.x©Pv!ӉeR/3H)tiTdOǩ YH9cLC8 o(kE9 Yzb =7|T+0îa 3U+4c[i2\3\7< VX>6;@y| We- G#GBUn-6AAl5ݥ A oBORi68:0gh{jL.v7w.ԋVC\l$c.=Ҷ[8aiYh][mk-`7Qo5Y -+ "REX) w b&GbjcҾ*=Zbc:􈺌9Y]7ja|&;ywhMHxDrf;KJ w@s .p5u);Q%>o6Q*^X&HhqhlL@Y${7/G n,h vexH~+w 0KxuxjcKyrT1UcD-Bq|ü/㒬tBwkZ,r@&l-xIAf4ÀP&ЪkQ]\a6mwڵ1F)iV&P^Cdtje9f]wC0bֽ ;ЧȃAaU;]zeT0/۞,T1}Yha!| JtU]e=jG?O>]Cá|uOӅ[@9ntaXf_/. 7*I<~xvtcCG-Mڑ'`w; ,]4&eiN$E_DsGofFt_roZh!7%inWցf=cm2xMy-#b"9kR w 9q)YGeBN=񥸰6" 9yrhFbn]Ayr[*:JiB ӳD*j|\ V2Y?nV .I{<ѢԄV]n|тjcH*IfPaL׭G)Zr79]dL"mQz M{)!kąsN]qNKnY||zup:A^V$A<ā'j9BhTf`::8X4E{C4QxA6-gDRΥևפsM'8<8$i]pgxt/1~h~2觎|t,aX7V s1DO% n5pU\cpq3.Jx8pAgxҙdA 2'g&V(kKLN"tфphҋ_=F@;6!9}}wd_j6s\_.g&#g.OV+sM\_ 9^`5N[^LEv4xGj7I"u7Khw;{_^ٶ3X0I?@uطI҃-!x=4OA {)Q^A6'o9l>O'QC-T|;( ʫIpLҒg~_{ M`%732KC(y,Y>nvc'Ņ lr.ݪ mOxdď?&'` VE+DqEM t:0Ictf|u}Ul<:wk=Ѱ'5LgѷgahXGuR+,7gwj'|٧$ )5EI-÷ 7럨aBMDŽ1̑3&>! $%b__#[}eD-Q/yV)ϥ>ahHCN;$͗UʌNO-yTw-4&$5#^䖳!0k(!bđNJiS*1%.Y4ᗅdqhy fH{ݙ`t2sYʞu,||_5*Jl$ r|ڰ))b^1FwӥF%c^t)^| 6/x2;Ã+= u7O3.v%w\̌VzYO>lapM,`jUWò~[rqfW_ eA<\rզBk J1j~^oφ˦yxQ͇MU@CjrPG'ݵw]taXˏC{xhӖdoѷpk1a^wk Rul(}:'2- LP j.5Y\%"ꁦCW` }x(*3΁,8kVkP {^e(2:.JXߎ x.A"5if2ԃd7׈8צj잨 ʍm8,߁eGp1 Pڄw4ң+FH#a!9f~sBorr!X21 ԌWW:a0S7)}}lq1M'vlWhk];i1.NZ ^Dі@=0\Dc$ 9Iݔt,,V^]ƫ̬EO+ԉqkŜXf㮐b !->k [FKDש?D/;LD (58ӟ_Au8T{< vP5!kyoPMd++e~Af /3zW^7a a?Rty8[%B$vkz*@~ΛQx6\8^y3j>e֢, ̱U- SY!R>f =: _C}ixPw޸嚱+_SAELJ'ޏ/Gx˘LNTمcHrd~)Z,!U 5.B˕K-Ҿѣ[jY-*O{Q:I5)R=DE0k0HT2:Fq)aHxg)(v+tU~=-PKaxXeo(MN*5C稇@dB2#Vaa7k%' O £H֗kqe iP }1ƻF|([  ;KLMe/ }oI20beGdr@}+qm'ٗUp%s+&e/kE?!Ze 'MOGT篫% 3)7"53=1LmGi8m80c<a>z kuyXB25~VКל}CVm\$?q{l<-7g e2]cy`y&OZҥ퓖 r`o5e=l( " xZv_6+@O`N`RTgXsc,uoƿgys[#V'=e!;u%pSDѪu1x WP9ĵU9,^,墙D2ct}[r|@B|6 saQyFl]AX dR@v*הz? Qp8HF'J{ wTtnI M DN2}C^ln9^ wu>CqpO#r8~lK UNincD&/o🡃Y6Řŋs__>.›E!>lײёœٿ)ƐxՖj _#`6~g]8#ߊC7ɒہczPd?EB_3lJ]tѼ/*QʹV.XiX.Ѽ\`*> ֻW%=ʸ?{&f o$O9|z -'qPuUs`Äh/aY@aH'*LeFfd:rLPP_3 Inl%Џ &Nj74(пNv >H_K%5ݽ:lD%2 C9 …by #>KɂjN8;kx&|.Z]BoܰoviJ| ;ONy)x B23?9ĄFkl*?Qȑ󰰦.s9I.g+e=kDBě'b'>L1QK;LJ \@AMPFfsSߋJLmr&?`,96&/$?-Oa&-.SxPy.=)߃$=E U{ss&I5GdH~ueS96+6SҸlU O+zM&(Q3U fz]CÎO~t<V'uiK랸u3\'ao`jgtˢG4M䯑_t.$`/*&V7xܾ^t~ ?:TGYPS|[A<'Zw!r q%"#ph6eLj&2jt-D$HPPȆD'<6fY>5MCP[uc R$~Kɥ"QߗZc$ۆvh@Yc^/SHĝӗo(o9%8DKVj+ $`oZџ梴EIFmoI; qS~ M,n}Ġ^85I|H=q;ޘ]õ?)D1|{WDH#17%@Guĩ zܔHNmMf`O)N 8.3-L[LPdoMS)f'PItm$=k*RA60XI˓}0 (ym8aGEH8đ,XIQDZ2/ٚ*4(ikKz& :arN,Hj˂kә5oT&bOt46Y|鍕<׫OiQg)%E焱u(ZI.cVh3{ TTL<9s-]%|vP"O }IЩRrAU#9,KL@+6!۳k2h0Uu9vTô5j4qBGE(3q|je,LwiӋI qɌ 6O+gqTU&,|+v,+ՓuU-ޕW77W= ϧϞuz}]*LGxSs.JʄW:p=P@clPK#4>])p{Y{S9EE!{(aP"PM{椸SJ[+0-V6'Or7/޾;;U&E1qƘźԐ6aqM7!68S҆f?y\[w]r;g)ojj VC+D8OeJNXЗtK \tiL]=lj/CdM52:c0w,ݣTc հ{q jb[RBhr"T PK<#Kq: n.[8i2MuaJAh3ĠBcԢUk .JȘ"aT5y=.+\d LJϸ36URq`F*̟W H!Rr˭XMp;[ݞ,*Ci3Ex@!bAa )/0|ջ6E9\wxę:mTN`<zğSe1Kĩ0 J$:r{Zq-S1!#*™9S U2$ņ듊6hD8k@WMmZWkQ/^CYB,scR[8͝&iwX =GDAxGv 4yS0.lF̹J~U/øIċBhY]Ir&z#ydiKc (N41IPdEN f[vs,֒ZQة-eQ4ԘKԻgO)@Ƣ0‘ЀS\|qbg`A'/fBouc`6( J8#-^#-_\ 1qVMF,}%$rTga5VWΩ2 #8$&͔ϱZP$ŧE@ih~yD o[2tRg h%IS>~ylȂEmW굋ŅSƲ(o=fR]m{f38hDX\AG&8A/Ĕq@12(G#`,"[q_]`}Jk=jK]%ǚSx:Q]O+;ٶݱyB{BCJx#T&6P~% TGstgs5 й6p|jxek!)ՔTxVvzN|zGgh^/|i)A],~j}|Q&HBtT^ PS!QKֺj^8mLy8?mEș,{PպZO`}$X=kj. R؛o_5ѣߡ@ QA5gGkE&[{Wymu. G-Ux`E֋jYq <{4OrCz~KZպ(#nT:@tO..%^0E`.0ޮjҳM:p_? !h>(N~}J$ךo?~GpsbXɫ/Ƽ);߿W}|l1}CoW;|O!O#D?0MoiNj?z鳧oO:C:7.J;b^+TM7eE0Zբqۓh̩WZABy,޹qWRJa0(\46)2}_qkFm$zu AaM(1˄HyD<AnLspQBPrP2p?Y*qru8ȋOޘf+ #Yel^| i9GR?Ju2x䊐!GOF"%9mlwxj*rC cW}Wr|(]:&b]'Wh$JInY*CE95n+\{`:o,rKaym{%a}!+6Z,jMP0GGM_ZDF<;`=,b>s?j&hh`hcO?ޝ$qxj`xr_}?Z@t'Y;oFӮgi q:Z$?TY7)ɞ^((? nB8{ZeO?;sęPB*̉Ҝjtti,dlNSw, 1!F'fTKjxz>![n[Tـ20ӨCd0A◴B$ʜ\#ĩpn1qـ`5d5187"7>N;ʭŽ3b P'冷`^&`?dO8dm/@oVB򃐬N0{d1;Eu(ӗ nTD*z}oaZ,şz AuQʝ6\|lXaa\eSY1U!olWЭ@Lޠndqր=ċv 4:FhlP>,QdґtU0.5) _8 q`tjz[c :ʋ\VeA}!2nѥ= n*<:;ot8U96\* [i:I6c>&j`C'jݘq4TҺtOSg >}ejFr'BAR{B>%犽dT_HLMZ!~''pdpsg?5}:[t./W>hgUϗ]r7qhNbMK]x =Y_@*?I?W{ }ܠ3^PVZh- ɤ^9LyOh.)R}ցpwI v;)mz=gƨw[[`v5 g+; ʳlXe>n-+v4d$@9=hQaKp?|`T2wp%>AOg!5KA={R{6loˠqOI@YU18/Rk/?CF1~{Q5Wmܔ뺚*F߇WC0GysÅ%nPMwdfxDX PJU{^رJ\; AuP>F!O_l[v* m,ΤX§YUM)IDViYV*UqJRw8oAxoV?)(QEbl^$dw2U=|Vl;8 ˏblW_p@D`aQ(K(g[XeYϹ겺Z4|L  8RiؗF5a9<̊֓0 { wOcq1F,ziҴL#?V%XIrUtK"-QČ.Z%.)P!ǟOA׻` &3 V;m=ꄚ .^O{<:+Y:;fXGݷΐ<4O8QE%>w/ưLM(BuA:˞\(nEO/BE%0H:`%^?}nj ,7G/#zqߒiW$o((0H;? %?Qs/RJYTv/&]GZY+3+Qa40;W!TgWTm=6R2^R~;|iS eڡ4h==F:3å1Fنf.I+5 ݝ23ݖ\*]=dgk!XO"R .xg5VEKӎ)a >1!U&RM&!3~QT.C80OZJR7Y] , Jyt~t}-Wq|B"POf }ܕ]w.Tu,%'MeMmiv_UV^Iҹ: L"FHJ&G6 Oq yڀA[]H06̟9UC0N#8+R 9?{jJVd#s[zkZjoqQH)NeKvMGPa$0՞5vVZňPVUD6 26(jk.+2N"Mwҕ=i={hTWPH&Zy<ʗ 6l/sp|I!^Ӣ C魋S5}\}i(b7$qEpN{;:D;UGwiB]L!T[-cXct ǧŇ h-IZ7vdnɨ\ 7 \y\\i4 Ac:r|ő:`(U#qbz}>a} /,ώ3UBk[+"N3f1d~e0b+ zrB]gBwB^npOJ!m])s8F$:OT$bP:-< ڮ힕=PmR7[GKt0 劲 Z?BH %FٕA Yr#haY;x%d:D|4J1sqcd4@Z-̩l%£# f>e4 Nqө& ]JiF} GrdHoh&K7 hcM#Yy,*pQ!s:&@P-Nn)54С qbҁv)7v{n͖:(ZQ_fmscRҺA(jE08s6Rғ@Kzr7'Su~F/b@`)XāHKfܭгyɚבBujl%\F.SՋ|] ^cN.$mFK40I3j35w,AXR!uKiaòe(8}=TDiQ>/??(y=ף\rɅ?M˛j-ϛ[X, PK\+<`v'_vS &$YM:Dn(`(:qG{Y7}x$}q \:Rs;|PX";/O=}S<Ige<@)`gG޳85a:e<# #c{FU\- `SU 8luTp X4s'ͧܶeLAEJJ䩳! {_NdU7A.F&KO xehƞv@[%LH[ܑiJWA.I]J?D@[ `٢\Jjլ"b?0E9qao>n:W@Oy|ߋu1krW"{.5`k&E[e_]t nZ W0:q 5nLy.JޫU.9Guw+N`vW΢P>&Ys#' r{pހB`?+M pϱI1aMcҏ(YVqXa^GI[*lf=LFאJsMdoŔ?/3} ^t !KѤDa֡81 ;PPJ=BNu~f*s;%R?PK1xU!pkg_resources/_vendor/__init__.pyPK1xUaQ}` pkg_resources/_vendor/appdirs.py\v8GLt:83n_:>OP$1H-AZRy}}*$AQ;nwwPP(Cߚ]/hg0 *lksa ,[?g7Ƭӭmv oNY+',p1;{כKGOU*e=ߋ=.X?X<y0\'`K=HؕJsbfsäkᨙc ñKM| s1zu|€ML!\-sb=_veL.8b{Q(@HďmG;['Z^< w[r>zG6 iB7 &!5aEC( \>oxy f"棦#&pC|t&'~y8a4hvAǖ"^tnx$NN=izW%D5Xqp^T ЗT.p=gebIasGԄ7dÚXLj/΍Saj6ݏ8#$!wq0 uW{PD80`>\Z3cp#6*5p` 6@@Rd ࢅ` ?t6X1k<z] `t3Vap\[{{d,}ObW&({@([ZW<% 85u0tk(0 N4RQ_Q@`:0['QﳱQB)0$u.%t#K_iD$@3Dž9sS C;A)^g.VHb>D. e;cF8==W~\qY~J-*H3IdI 龕}P&ҧ(iM8Xrqn.hYƈe7`-Q?`%+3mK<ØvBn ӀKZgǯ; Wz}qt5D(7r)C<E3qz&i2&ȏpqIh'Gn%SƫziIh~{zBR4_$u YZuy̩< V5 (`[l8 #J2QohZ E-!-Jh7hS_؉`T +LIz%MF;uypX `:^{r G01gj46Pm?pҨv| rdhU*&jd)'6([ ѯyǥQk~IB!: n ~:ک߾X6ņ^, \8]7~a_ V3@7d2gvI_]Cc!NBog!D K$"FKB`_S gk ]ysqbm^mzۀcf#eL8* Dh)| 'Y~ʙ >xjG35h uZ7UZA:9/΂` ?*yVS,312jgdSV 7x?bo?,*g!veeAqUkf, ]YB;7sLp TX,ɬOs)F2Y[j\_vDX {LÏiX KaYιb kkx6A K_&%ůdL}yk579>zr%"q|F^P-(ͣ߂z5嗱 Ukf\:*˫1GtUr\Rf__OQ2c=,Y@FJ n_)#R6`V(愎:C}cZ>egOkog-ctno尕-k[ R5+\/Djtch>Gt)TVbEG39)H.-X!ަwz+3ꥃ65:K5l֪$$IP0R~fL {褯*>m qkA"qbfm&OU ۶o++/>f,md{+*bRCnd)ƚ[dԞ[W8̊TP^i{Y sU)"xE)"ER5>;b9ZN7rhNr` =Xb+P@,pU=ǰR><Y9i43B43ߥpdkFG(fֆ9sGa:NfeHKSՃ;B'ǤnEv)Ft7J@%A4t-;%I  L93'0ܘjXj;AySG8 G6͡L?\tYzr.?-\nĝ<7FD Ch댽v){l[(bT>nå6?ΈIXA:-N{T.)B, GYw_ ?,~ݤ,Hl _Y:X][ݝ'Rƪɭv&2E6Wu^cRo.E{J/ˈFn{M2@k:1%1&Ks\VL)I/-PYSZܗA\&7š`դcm)/B9CA? tF( ~֛y,}7#yhI½K[Fq*lBQhAc7fk4h9z]yY C:n÷ھlkb>YܻF iU ֭4FtxI)ڱ( (rzEC>plְC_p M!~&̦L`ayf0_ؗPK1xUBq8u pkg_resources/_vendor/zipp.pyY۸NXN-0IPŢ 4Xh(sDzfb>D~I(YvA;h76yn<в^3ٮԵJ>u\O+e%R^mmxҕ̆u$vVAJM*e`ѷ1zWm aU%r Ŝ۾(\DVَZ)4‡{h^B,x/"黺2E>qc*{DML q-  wN݋F>ZTdzѓa{ґcmfcv?3zn H@Je@K*bvIxϒ(BfLDJ"Jy t^컶!O$ro>qkERkZM_GrF6VemrêxU"%t)!'44;Kyŕboںoe"?dwggz:oMڋFzM+)d32(t#?;#^ׂlx1n6D(asD7$Z+|b@C!zq}=Vy|/1'v$-F|B?)&=a|^pͩi?PnKiSLFĀ^q%=rڜ#ζy~؊1e cZ[Hp={3!3\*|FwGẠۘiDJw QC-mG#y 0MZ j]EgʚMA 2B%O= 0% CjJ:X%|gjqJ(**0?c[D3_6'Y$7(*ySZuΚA YU ^dNQKFZ7i@ i% }=ʯPXUU`aj; M:QvwGowwvs󠹼ؠV]+06^ˆ|U_-W wwr xK:[j -H.&Mi@2_ݬsBٝm?̮s/#JSwzlܸ]]"2 |l 8af=}j/Loi_.h3C6oIrT8 }ynF痧9Dxl3:fJɉ1@ő==z"}|3Ld"n  "WR4 5oFmR$1mPxxa>=}1DPu驜\!D4n SgΞLouFiF SKj)7)ԢQԫB{6v& y;۰Gѥx8OGGxp =nYL-ǘj3=ozqy+|EKsg{7nɸv9'̯gv_lĉ u(KXS5v"BP :-_ jzN@_]Թu s;Y8'l5L Ðz ӫEs*sv/[eU3pAAg73XSïW۫͆0y0\ FG /a+1Pb.$>`h EYQg_jp8?rt$q0|-rsLNx\cB,}Û6 ww~у޻G\D' ߧg籿 ./8*w,pTT zY.&Qsn;{y)G}FP[.g(fVș'+?m iV`~4Op6RlZOg|ꁡg:՞{x|7%KtPK1xUoq5pkg_resources/_vendor/importlib_resources/__init__.py]A0E=Ť41;4CZ֨RAV_/, kk h,23Fl,nn3G8> 1=fkqԢdbr#YU܁Fwܜ Ce0bT|]+iꅼB){^,yZ`ÒòbٝѦJpwhigj!0RWe}ژOӍ ##C]Q }D/ w.eV@M96EF߷9%xh:uۋ Zy!xP]bJ`+y0Ƣ& @T`eyxjm8"2\cw|@%C+Lb|qSRm7%T@a;]G`ǹzN6>cSyO%Hק9:1I$o(WyO{z9x/Jr0C`\skk3Nb!uE-vi 'Zg?73 {F'΍ŷ'x2Vw~nU>Waڗ>."հO;dVCW(!b!z_7[v(Ѹ ,~x&x&0Y<5P7<#7bv Ò71H8^6j=7n4ʕ_8aܸK7_wK0^2=af%c!O뤅D=zCkN"|nH/qMt{T\PK1xU0Q} 4pkg_resources/_vendor/importlib_resources/_common.pyV͎6 )al`Emr6XlCMϨcK$軗ԏf eȏIaƁ Qc/p;c&8Y[\FËO:mCI- RIhd|FѢËhɣ(]5z و#6YI4'q@x |񎕿v{|Ͳ8HAx=|,! ,сX΃wT@42&Dy*|҂Q/'lq98厘|Y5 @:W)7gFb 4GlNpDPc#H8  tMU% /^Uy>Y(gih GDvdVtaOlES+hAiv6잓:sX_Ѧx9-@~1aR#eYZUGίp|V M-J5V5#J w+|XJe \R+RU5xê.k{#. h"`R"BK곍F| \8W< dyǘ+E~-|my6a-b$ VA:d}ԟ.6'߃ލ{4VP9}Гj}Ul-4_P 5GOa+O >z߫Qh|FcҡpE!‰(Au7ڲsCyP1>aSPs^s)8L5aszµ*pǽM[4N`Iu:HZw_"d 5Z&d+WѰp¤n<0?&q=5?NBsTwѭWJ%xiuq-!}؞'s)<]Ϗߘ^ACьi'%GЌQ mDN{S6|f<^QOJ o@? -ͼ&(CNQy[ġqIZ;o +3> ?|V"YX=q{ x ϡ@xd%sL{PK1xUht7pkg_resources/_vendor/importlib_resources/_itertools.pyuRMk0 W [K)maؙvOvYW_bIOɩAz@֍*(U 吱*StB)S:D.zm4Z.zG]Vm\TI@2E8 +3XI-c|=AU1֔bz9:DݩgŠ|WMqӁ|Ґep8 py瑀 kXthOaƖhFPC 4b( ]\Oftb߄a˛M԰-Se^"&,;i6qQ1I:- zeS/4s7 ,/̔V*!A)axɁ 'sMl[# PK1xU.1 4pkg_resources/_vendor/importlib_resources/_legacy.pyVK6WL,=\$mzC ݴE`hͮD $^7 IC-qy|pdiUnl"Â:ᶍ n( zIRݲ ?)U4ïZ9|vhrE*apGFbв*uj$DH`{Gw3huoJGITXCR8R4[$@1bgDg̋uhal77;~/%'_X[Z)j=rO 4CAXa\gy131L[:XRi7dкyobU+p9cg >r6%Zd$?Q6F@; ʭ,5ǃ$ (O \hG?OiӊF+fc?1 Q %1WP !|.B#.$ {Arڡm 5ҥѰ_ȁ)3F~?AfO`+8aPP6Lm_  E30bc0<5\ V̈1^q1`Nc[(B E2SePeKfC_Fv_fOu&aP&8nF͟Q`'g?MifGWHH eg/{ǂi>^2%k}&?ufBB1vsBO6r7ȷApSs+,B9YRgګT+4;mp;Il8gC}C5]ԧh3y (gHhޅuTh׎@4͸/n]?:,e-Kڳϡ*] qi++4aҮ4[SG@:-| piZ"> (T0NL}c/$͑1wG{{gKE*ޞB 7gcY'`'PC;po,3#@\t Ul0u_pfo޾xO8P oBPm>~3#g"ە^r{aҔP(\4^=K9x[oOxvOcnyk wj9 BQc6ȍ︌xyԩ'[2NQ䠴3R3y!"{^~%PK1xU}>m.0pkg_resources/_vendor/importlib_resources/abc.pyW͎6),E6A$Xp(qbV"Uڬ޾3(Ӷ?mS}ei7#v8Ƌ2n: IͰf ]]ێ(nzd >H6R7Y ݂ս)lq)77VWMacB k4i 03d;b#Lޒ >"Z _@tj!j=!^yWͯC-(ƕGflM# }\^M"*[0nz2x82@B@f`kaOP:Eɕ=@BfG>пߌAZpiAM/HE4pR֡yLWyٴ]dV#OJ!ą%FS9ޠџ taI:+jG0()2|>vD$ B>.v@WP(xtb,e%1$,Jx\wM'n?5(5=$) ؚ=!T-5 D} i0 #|\0(guޢD4=9`hb_;C/▽=,8O{b.8‚#Fh3bc}1h'8)(ɫ4BEo$OoI{=d(csao!Gd뗧{YJ2OAs{,$܈GL2t4xG7 !S/3FվV&d=j{=rR^62=\iNЖ8g9Ŷ2)|PK1xUD 4pkg_resources/_vendor/importlib_resources/readers.pyVKo6WAR\ @ lSEbCBKTIʉvRh.}jE{qRBVv#QA}|=lZ;VxT i-m|9(Xjݡ(B_ͦf襨_n~ (F$GӀ敨]INa% odQo NL RUB WUԤ8T$vkURy 6fj&OLdzv 93`vɜf(~YB^2_wB1P+8+պ˳ާ[g9C/)H녓hbn vd: 2-_?;愷V8Apɳ2+V}4ЊGϥ^ݻlDzY^/7Aa12,RU,;3.i(Ofxw{1F-LraOkn/ }g®^L~ }~=Dp;eB(tfDtVã@t@?vàam}Ċ>5؂Ntcpӡ&> Eiw)raN*+TO}N#2ݲD{$8#QY8h6 "IA(xm4¥6JvThK(= =CQXGQA84 j9qi8|ׄyҌ`yʝ3KwEHqShc^A٪]Ғ@+p>iB&bX o9,-TmqnׯVi6Vr$iY3I#ntT~dn,IWȅdx6\"Rq^ǫBɝaehtM:j ݅ʷG|B: fk] Q_-Z* 6,#mAr 2/Ccf\ztwy)E 2`T$ DA5B ehBؖ#6:"neͮSF'P [bUӤ WtnvAsY>?ŽĔMyc%l&T5;ax>(%UEay^]V1NG^AD$Ih@}~:[ݜ8yFp=E)Ѳ_ZGogCa~J ^C9c!&m U5{:OxhkMVbֺ]PdͪPf C-7$ K9=[ ZZZ(/[NA;@cylj-mKP!~6u6LqD}u|~᪍H[6Qn1wl F/g@n)jhyxw<%=V.Ţu[q6w$x&%yb/eg+0! Zg=1Tsq{3I7TjM \ijjsJ¢e;,a޶"K$E-~.BD+jͳk#MC2Cu4wc%J&n%輴 SGq5#%ǐ:N,JF`-7HvdejA_*oM4+z[*Rõm$=+Q5x_ya\Ͱe9>9҉b^B㑷PK7hC)+\f=Axsd򅌷Sd#zك˷]T)d.Av4N5dtes kJ=d );*W Cx#Ɔ zv]WW1,yD/{v6c!.C.h:"@Ylꮪh6P~ֺe ­.(^ w7^Rnc86u03w5FĮX.ѫ] b+ތŽ#ɉ/XJsLxp:v.4LyJ6#SluoTeO cǑu̗l`TNa~1wΫ^)%Ճb]PRg_#>جZF4)Aw>y8J\89 PQDɃIBP0gFEۘ~{h*>#KK-=\:a5ЦGRfM:XV_WA>T,(ۙNoR$žwuhySץ9(CS{.*Vƽ버ע:ZQq/L+SzDmjv"*{a}e)gJ5,۵xYV}L&gŷ vp&:~?VTj2pRT!H{~r rT Xj\UMlɜdY^ϳ,Itdg hܞ`Q-h ̜Jj^z h+|T9Fl얛` ==ߟ4xdDIL.F8 #h-NFN!o;?a°.zoX~,!;R܉ V Ƹ ll.ō]kK0rU"vR_|~rgnOM4|kY(ukKR!Jh4S$CF iה5`σl$Db6d}0ac3=DhZj y%|rH,,<^պ鐱nAdW1jf4n@5KIK5|6?0֋ V=54ˀ̟00bW';YiN' `=JF=+^`OTYmUi*{9CknPU|j_#'Y k6se\:|F9m><d4v\$d6R7$0+AK$P'锅ipw}>xwA(ׅhv4l$69\ǴMVٳY  &n~Y%i\_!qNfT.ruB {]"wօ;2pZC:zT=׾3qUs ] _3ͪp n/v, Ђ 5uZ8{^֡ZEl,tI H:>z/]K=ۨ`+@]cCb]-iI$};("4AYGȍ6AVwSC Uɋq=p]륮TzpV&y\ %\YxY<2p2rOe-8ZDI?>W1 ݞe%1A^.&hI}F  k2+l'9g/&$&{k}i!:ӷ~m^O2< r pA:{?1w;VPN0T0!֔Jq$y=!1|:>CR6Bf$bb@tc|š*Oy+1pHM@{BBȈ붱63ɤp ֤ыa| 9rUa$dn+Ԫ]O օ2Ss8;(*; *]A6kt]$"3)yei O=: nYх`A)^{l`bio@B4HSZJuS*ljAE8q;!Ew#T%̗xL?NBG:s*[Jcӎ4G*Lwp[dpg 6WwWی\seFMW:O61 㩦yB FU۷6h-T|Bͽ낽w܄ɠ ̌=&Ћz=U q7;"rD*HWrahWyu@΀֍CSޭ8{+8CU@w16crǘ:qsk`n`}8¿61ʋD }'*.ALvF5?j\ `ÔrAnOxnd-ZáਆnN0c uw4k gtkZJZ:ղ0;浢3 HP[[mǃ\ ^k`LDLw{n7&~۸q9B j\K9<2|7\u{pKsԂ^=^mΧuwicQbckz%"1ď.ƙm.!o|)> z~KjoHw~1&s?=S W>Y|U}5tkzmsѝΏU)nS2 Dt(Z,[n f"Fb-j#D'ɢHI31d:uSCB RceIb6\> S$7RCKil?,@Ԑ TJy#Dl-ҼP`UB5^=bbM&C$2xT҅@ֈ?C3TBb 4j$3cBʨhW;SJ^_Z]<[$87+U1uUnŵldNi'Er'*#ܒ*ZMY@S|Ǔ?SצUHW*$k~&7_zۀIzJg? ܬ$}FY\M8`kCMVzC d^,F6E mqaz ګ^b-*duXekؘ-4~A T֪N kҢymW+l'DXF0@bJx<#8%m{oY5'/T Nq\\fALF|6U58btZd/$̝;;䜵)*{n0l2<&BhU@U6&Fz"?GiYߨf2ܪ=8HCR~$*Q>J &'1VJh$|qi77H;EI[C}F|9 J60$(Df/&'mb84! ³,^ I[n]z%9bDL,G.E>#]9M|ZF҂3b/r >* " 1Z~{Ρ<.[=טemPOnr-@lJ7H)Tre*Od3J? qPV@N!& %JTXigϓb=]/FWIH+lM@ p]>a`M؞)#=2_)2Dȩad ]!{Č9HL"N7(ܓh)^dfݬڛ o!JCkPz2=.-H`ypY]"uڨ6˥ecx2X'*o!zS"d)MNƅd^2Q eu aO[$]j$\.u{n#)ۀ{v§(יޘvW4"x.)VHa^0@r,FC!2ݚu][*)H $:D#kJ YjT H!wYQӗk LBU H%oUk/̶D:-64WhV(18Ҧ%-RTݴѭ+/MPƠ6Ӕ܊l7k\(x1|q-%jQbA"hԻJrIJsrJ %ʴ mZPDl&oˍ$>@:װeTC+uIHb{uv7^׫>/F5Z -K`2oM*^zt PZv > jFk^05EsD H^Z*NjR:1͡-p 6^+ ښ2:nd\!7 %J͊ꕮAV0.j[ : ҙKϫ 纑z9 \{(Y/TCש/L$ͦo ANʴ"Q}Q0 f`|"*glk@9[9aμhaeA\b,.嬍IAqsԔ`ٯ>rUm{-$D$^J:; 9@VW[jyH^W _Р4izJĦ^J9cA8:K|ĹopT hWIKIyuHCXVhhjR.jf>!`LhRW ; N=;j /tsOwK#N;kv=niKR{7,OQ,x9%iD)uW cZ}[}sq+NܖNee˝!H&m KfI/G[ 3.D Zm6Tlah8wЪ])WzP#a ?gPj)Xu@Rr,BR(/F $gn}(h2UO|⍲ =:>:J/4ĐRziC%W|JQyOʘÞФ ҪcHK#CUPSg꓄GH4G; t;yvb0l<˛ڮVu[T\!ׯ:<@1xFP Jvm+;7!.v&ߓ tqtAzei!& K\۳o^ uYo  ph*JnX-w(|qFR޿ylNvRuw[qʽ~tnvQB>pv'L#j_? 7JY'j+nN;yf/!;|[A;rD.k .8QFb+$FT_^Yzow>4=dDt;N ;Ŀ$]u8%7Bv6_)X.Pp$6 e "]6u87$jFބ}:5|/+uVM354Yn=R;c|')%<tϡ47zc$!G7+ s&r|bN3,gr8Ў[Ԍ}6\/vfIdFhWW&)_^VEUWp53+v<)y6,3wPrn‡O㥩9 `iE" H :e4/#+%;DAǗ]by]"7cFw+?"-@ *0~?<B0O.Nx=3O÷!SQi{RHt.<S֥Xw B#fC|R\$O97Yp2Efžη6Vd@^]"ҦWX:Ҵa^⥇PR.5w'w9vqNŁ-p*:GFU$Oe!sk7;!)W|{sYՕc ss̼l>~@t_} /r㝜Rpre[;Mtx5:zGyo΅U42GHrL_7d{ }H.aˌYQn#̖5h@VsVï!H~EnN3~n~{3> БJ\,#_ѕ4njz| ҵ/N^u]O5ڨ~ol9'EvV ocPK1xUaʥuBS0pkg_resources/_vendor/more_itertools/__init__.pyK+U/JU-/*QRPPV/LJ&gcJsǗg+*[sPK1xU:$ν,pkg_resources/_vendor/more_itertools/more.py}kwFw }%;hGc;&sf JH@KJNWwW7$puY]]]]|-&M"/GVUIz-,$]d(Yftn?Qcxh*ŊEƅVbєڶM&Oףʖw[ۚ _gSpUW{ReurrGQ4/F@ݺ6`z~*׹yU:_ CJIMMzLo벸j5!oڌ6ێU `0uYp2Sf0>UZ,_!+UYmtͪz2vz7%Wk:{U5Rh=JhەDS"waYtBgd,FJ^M/:(YyʑfQyQ)Uc֍Frڞc ft $`A6% nװijZ<=qK֮WW={U8!2'2}GtorWȋ-.oM-ޮ }bG܆kFR6$d9yU˅B |H~PV5]UpΕw}8˖ꛣhA;k5p($CSrR%kuRƶdX Q/Ds1%'t<%Gɧc0.Lx~a\r/@3i>ְW6_k *FJ*NٴWA :Oki|v+I-]%i(/BuOke43dftd,ai̍%&C^y,Dfo]6 8bvf3=qN2`_ LpޜOvʲo%@LztkoP±Gjܿ%gxٜrR}J>q:_/<. ]VnzO; @NkW*"Y>+{m.˛j b$g3l~X3/=c14lIM&?8y{ -Ḧq L``=Eҩz=l@oi0\Hp섛mpwnoYg׻5/nh]09,%\nuUMJyAez.JQs nT(49]Eۛ.Hl9^)Q$Ǹ.2M5Kx9=CvܑR76&Kwu/Lf:z5;[1C)V"Csߔ5npoEA@'޸ȁ ?1( ԀH̋fg1 D4F,O/CUT yIŹ'n#hP|rdzKM Ass@B,ǥܝ#LܞA0Ni{T tsZ3cĬgë/0En$ӄyQm?`X=tk4j?jԚhH-x+ UO+{/~/~;~|?P}c%7YEokB'7MX6٦?ywxDZl^FTh}@+nM0C@ 5;~IUި 4M7mUQf CS@BPD#(kJw1rCfe>CC~=@BlP">a{Mp&=:Џ oYT<?,x)v M2hv}AԴ 9ULfK4 αs8r, Hwv٦50iGyV 'B:*f7tfv*e8Y^@#/HAtD@,)uքd<& {_V/ Ud/ͦyD$#Η0 boWT)N,b{>Q/1 ]SnE IGNcYZ+{ پNe-%9)UW٤9xB%\͇6ȯY7IV5o[nƽx Qҧ b&suҷ r2hsWeK%?B2 I_, p8LEl%x[V ČHȃtT )6$/!?˯3!j(ſ+,V-C#-"<{t'{-zp1P(2 T haA2`Onw:93 #%$j[:śk$UС'\&XxZ&s !HcYFUV&x4p\7 pS3qfSn!72owY-o' QX<99t\j)o?[Y4? a)c F|j-lF|n6#$rzJq/?2XaRJaP9ܝ%'C`Qi:9b"18$?S3X28"Di5e5-5d@)J=mpz) BW`T.Qq?:YU:m=2:Gy'˘)MŽJ|;>'2{0h{2_Ii*Ãhfᛰrqx,TYh;`ޔ,Q<۠Qڌ'K0 o`'240N%ZxE Pa =B ywFE"¨U|;ACQndWIdJBz&sSً R"h}8oc` MLDE!n#xuR];!,7o_O_׫x曯_~W8\Z973NX@ط |E!YF{*z5vYہ|ouZ/BUCCŐڇAoe~ڠx~YPib\y% ?Aq )A}>[`0ۡ;7.zuوi& tƠI^sB`xG6c]/萺{'M{$Fu5Cö>\TkC4nl{sm>bl¨sc|m3}kHGo`VZ|n(Cn+W<|ܮ&D'ԈLzA~CH>}^k(KZ7-vO) jZ[JAucK2׹rP0Nh[m\/0v 5{epzYf5K DL{ʥ)H"P9jQ~w0kw}I̺+wgma|% l#yIIGd m?G}љ9ؼCv?i=:FdnAwX~ wb&qA yZj!P{p1Po0:bb :Q݀ [>8񑠼d4g/|_y"ق&M?:K P]1detZ mz?&9QX+[͢KtܡыdU34I_.}S kpt`&v,5KC4M'HAb?? f(gx 4e]VTWL4hNJp +ScJdV\ F|,p[0|C?i!8nÛfs:n92}"OG\yX_p!3y;o'YF?z^L+-&' @r/B@ ~WcEÚjH?m{q_&*]z [GeIgV`{0"%Ǚap Q@hH6T6]?!]r3^3wmPk w{tJiG'.%愱h!Y b֋lև-n5ܽ4w2due@698;CGڔK2r6e^~ohݠ+%hT6-I5Ugnd9r&xehq<&[P=/ۯMp- *KG<# ^d`Mzovt!d>b6_F\ϑ DFJ8&4m0#.܂P mTy4̑BԩQa_aL^lsmX7ɨy%ޞa@hs4G֏\|?0jꜛo`[92[;a}|xڝ!S mOd(L+GuB=&Wng},ڶxE?1wK Tt3;tվAa 4e@Tɟi7n+4"tv:ǐt+_XNCh`a\~*G F7ŷ*s I0I7%೘ Ë}ϳ6F. ~1hQȿ3?art,,BS$.`o#i15|RU$y~\{c*Ck#\Zw -@bIOqNBbm&wC\#_gM=hE{ W ,86Z4$+p:q"s^m n)ҜNB#Mue ܔچ~? V6!| ~A{@FcKU ˟$fNbeol[# 쏩#ܓ}QH >. p7Ho!$H4RA&G(1G@]Hzg)Εh_s3u|gZV7 UF۴;]F8PV?7T:܎UC@<:; !#2,$Dpv/]SSh=%m-ԅPwI*yR#+3*zgYwX`@8ATb7L0[&*VH }4Q`~pV|:#pm ~Eb@ˏ;ÊAXn=XٹD2#*rd룤Gk*UHtEP蓤la1a\O_>0K.s 8ϲ/H;LsÎ 4ԘGH1η֐v6$)&ho=WTo[[$@ .RAk0'=y 11a[C}ڌF=lSv+:~ m'mJuN]+2z&]lUqFɳV%dB1~*Uv / g*w5uID_L~E&?#(`Bnrb߅K*3U4 a 5K9N Nj >3>40)EV,\./%C pV|;2qg[ɿIPhS:Ud e+CN"1 %O< ?߅@s`gIXYaܭ%cԳKA/] K9i!S;B"Q(1{V#sԜ/xn\$|Ok)sgCrXq6@,Q6Jrv:%f2fS3rasDOyЃ %w%,eb!G8CcB, )+rMkhTrWiRV*9̾db56A@B X uzpbj=uT=s I CO:eNĸrò]nMIGO,>kk+ReH;6c2'Cs݅QRc^#Z,qC̜ݺ+<5%L:\#>v+!U"*åp}МX{EeUְ7A5 pH+x(%=*D\7Tz>~M{&gnnP?M bm9oLHrpZ$TNY*8dw,2%Bۨg$m) ]s5kz,J.^2X'D6؆NU+<*Q&3q IG 1<@F#kZ 7Bx>0q}r#QuHNbSYe[(彸ҵ"ICW㑑׈4'=vq, Y[ 2ABښrwCY L +`Ok(oHlP'Ǔ\=ޖI̗X\84fނE/-W+g@kGZjwC 3fF@2X:=k:_0`"+K^;d#sםFv„ĸi8vM7\|,G 2ZFT&dv4oH~bAVSj(j}/pOmMeF"[ZNriX'yœP*bh4[E#]!왢a.?h4Mɒ!4D^HF6]G~\yfҭP0{=}; <ªG*CfVΨrȃm4wVRtn溢'^']qsane\8)MwG$^əZDK˫+#Fᷭʫ YSd_PrWS2; N* xj-8ڻ:٠U(&GK1ŎC~3}lO?{AlXZ1[l=uqф'͖9ab4y`ϋ9s az̉GZRsEhoj y|n?8P>)ۻjYIy/i'?`_Z6)oJzx'1z\;:w-[ntD#`tΈM mCT$^C)Kے M F+#J#FB]-7U7ȈvMܤT`y`'D toPtua/nME~H(T[k)GJ].)NEXsN=:-6j$F@UO#6CJG {vLXV|+18>y6ܘY[=AͶ2tyԎ?H 'eJN^tdMBŘ5,`%WP2ͷ %cCc qdSt[w*pf Ѡ*(!$j.Gly-ݴ@IĢB8kΛi͹+1'#; GnZg۔Ɖ<(#>?aLÏs`H?Ac#eQL(2! mQ!30X0vxI\cuX,zi]$CEq:A XEPء׆e"K eZ5xtRK"8z,tXC"Ѥ(mg+ ʋzl U{;H&=c=GFY\w<,#2pztv~|rdnк*דQeXWOvU28~^5Tӏ{dHު*]^=-<8{[Q'K6mtn(a7>U"Dxy$~3 `tc\CP@߀o7.w I#7EQ]cx8kdX'FɐZ ]Qo>NGFG9 ?/}4X_L[6,.qc+Ib̞G+W3w1%>prfz'"fCg'#UH)fӹvBvqբ8QUb V6U% `&YV, 8WXF??cAE?ۋ es|7AYg[y CoW'/ c$)f(uU2FZD-nSUţ *$*KaxTdQMmKwяtـCζ'B?Adisډy.uB.O0j*)YirlI#I/*-xy[?0%}$18{4QY)~\Gѯ=P*ȥ5TI I4e$j(I73"Nǧ#T:HCq>tRhJ]4[c`8? AJŵbJ91?' šXX+[QӞ#b^O+b4Zst]h=CO|Kg}jBR1{FC֓4H[GQl8 k}qYX*Ak$< 9{%J2:Ұg'@%L7qqB`ߐu dlM6@:;9uҾW » u@ 7k,,1J)LNT xaI+>>IG,?W09#ۮ ׂL;}ʗxY\r .`רjqk*$I >N.pxCJmp]Os%:iZ=]7zXJa{tD+D3A8uf`(NkE8l˜Ou9Ô۩'LvROXLqIhBXb %O$N~&6}oabm#ik,#pAӂ˴z /yT nwʶkTLj<[[! IBՅWDSPJJ)Cq"ؿ^䥓< a]h9* M;+_UWowLJc#.&? Z~cH)9GDD"8.3U752#y|((x:o TQ26Q{gsvM70-P:^(zG/c;ka0(g3ˊbăR|jA{GX|\U>+!ZkyȪHؓR>*Pe_(S X(3ʅQʽfڀCUAC*>D9s&]nx8nkkjۅn5)Ii$΁gvSTk>1]`meV@0@ G|[bE1v,4o.=( w6[r^_rU͢j{Cq O'64yޝZ"27=liDuB`D#f>{ㇲ<|,ϡ\`7o_O_׫x曯_~WFtU"v{R8DgIycےs_gw&&S;=~q|S#PUK0ܢ= ,eXP`&DNyi'Ԅv@5hqA _cAyϖ1g]GyB!;XŽrPONN) >)nq0}~¶ =[1m)p1W!4nmFpN-G %2ƈ3)ٝ+X5eSC(xw: 1zE vj{n 6So=:){;b( c@cto}2̔34C6klSXYNtT 8ם/lRg ӵҊުog"ZN-.mclj)&I cҠgXxֈ j1v(Uuy4k^7Y NjcF 4 ʈ=KW:"c~-uڡ( bH&&7x/i E*QVj,hWOF'9=%?H`i%DӸs=wALq ! KOT8>toV+X06՞kAg~d@=&#?Zc,2DVI2;)t;^_Q\nk}{z"1LG]`IxO\tsr䍛9`D'b0t3 Dj5As|̲^pN^5ܒh./[: 8plI'}\Rt,aDAk6ٟT{m&4N0A`#>P ^_|0ϾgCϑ W/^s d'j)^Ǒh}20D"i)Sm }KN:7ƶ1I#j)LfDsnk_DL=?Z.Yn$\h'X-WM QjJQK<-@NywK= cQ"98b CM)1%~!78G5mC\Meg aJ‚1D-#`ȅ]UC_*k̑3]ÀY۶K!ww,W]/&Gy@?&DnЀU"}t8gҷъ ɍׄ0=9= gY {/^kOݜ%7c iKYrϒc {˹1WgI7~W_NRi|A;MY䑏bۯR6WUU6F)}Mu{MڔRFdhÛh UQk@"fc,IZDqGZq0Fم0&ޫR.^kEcüZ"j!{DZKF.֩]zi1/Xj[PfCnkӍJj[ }M(?9|άO&pVȼ vez93CfXUd+qQھy{w81mXL_?/;Z!k(09Bt2SXo`zc_`*,zײ٤Sb=yQUF5xబ #NoeD'3[JM.8C_2:u Աzd.f:8W5cPSaxn[Pi2e+`^'gכ -m1Kt/Q- {4SQ&\= F⋃Fka#@MT4B<|pj]_= I7N qMx x)ǎH BK5BDWLr7Ѣ3 Df|班bzZ/;;Zcm)EZMv^ ہ(rGnN wM 1t [}'m 0bfkt? 1K(PJF k @(/LɃ+mD$v~pAlm4졹}Sѭ?c6j9`%Ű!`fX:/#k\N;"XbTpÍSt͛N z;9>FK3{}=!ޝVaZ9w\~Aw5`c">^ǥ;E<]u,ǃUii^I'9z/JҪ_o/1A DHsƾI 4cKGG&~~? = yo02Fܼ9FL2zJ6$mM],\e\gsN1/~{pػfkuY *keZ] _5\S x&MЏ,]fr. ?z-K("X)pl:ġKӹ& GR&Z]@#`yeyjp4;mĘ #g>|~Yo9M ̽'~͟䄐쫂dMe˅vFll_5 XuY.*?/5ɓRBRF@]M]s}o.PmgĝCVl KD^>)y Ҽ4 #{*Xc Rƫ`)Tfl;[m?IHK`XeNoq4sLKH9q&o\`[3aYH3O8Zbm\deMa3*|]*O*ces6 LO=B^: p}nvg8oFuhZKKR81GӖ+ZN*+g <ߕn?_.ʡ=~ xޡ() ׭a! vIdz7 5A~HC ALfMq~*[/wx*G%`Ow8엟{se&6k<-U%MM5f#qK8!^xBjf[˪ܒL51#xe߇ m)$p`UHРB%Ga 2O?쏏7C)=\qʾ_v-LF껀;=B"+EcҝА ٨ɅGi0 <3? j-s W NIm%L~Ѭ= 8&L| .&g'_$f/z1?g)}~SG1quhv%wdY^l2lf]퍄7k̕H[lBy6m|QpW-}͍`碽z6TdmdV}=ve[m6|F[ ETj=L 4*w^Njmft̠b͓Kg :rPstRBvIA6l nGbC.|lhryq|ViǏ1XJ=j鶬s$^ɜ]vI"AgvoDRnH^:WCyD`ul|&6Ҟ%m>apf##Beee >ꐤjXC[$& 02} ;Xb814`1Ua9ˠQf$Đ#i\a0oOjhGb6T˴-~!# 8mK`dVMp\h8ݦT{V6_9^ukT[7 =|`m[DMQ'Y.~f[Z-7MƘ`AK1.8ohi^֬j)HoGDWV@q0{ ӓ,܃bԲqZV'J OcIo EDMWfzڈ D6s +wr"Z})ِo7? .2 Zsga8@<6H3zc2 :}ɴ4&G訽ɨf71P>u94T Cl6k~Nh %-^"qdY^be kǀ<1*r:ζ4TWғdb(v`tA\ xNEƱ.p橑O0a{#$p I΀a!ğdRV]67U|YK^֞܂Ӭ0lfD3 f[q*)R;]/Rގ,VH/Hו+./wQA5)^K5D"3'],v; XO 3 /H@*KO&Һu+ɞ]2L|s$ªPO^bȑ"6D%3f5L>,m><F9Ę^@(y5Jў9:Qp0xb ߡKP yY"\( d>!"$ 4? ܇0hAӃwsdN{$| rT٥פ(i/(?8PR >lT֔e'#L.RQk:Ï?;g1vz$i)1vf!:RgPj r8G_"J{>Astrh.2JjILX͕ї}pZ.m~Ae.6ݦR~Xʔtjx҇tƪҶt_-JE2∹tqsVK{x*Nh# m\V+[=׍Q(Uv@cn^4Mn8A%怃,NSXK/2yz#H߀dE$Aܚ|f =Rv[bFlp-Vn/zZ{dK{K{y%w­|7O׬799}7쮯!&R.|<@ImmGTDڤlz$hX6Q,9|rxd\ ؚ5|M^jE5BP7KyPj>:T|D, b k3̇Zq_G]/iifמ38.lGGKk{HT-@x+<`IVm2YQ Sdy2[ݡ]1eُEӿ\e6αYݢ&=Pܛ6skXd9q^) ݯ(1x1ߒb7ߚ]_WۑڑҔّ=f'6BEťxtX׉)LTty@;xSN>G{JO0 tn`'VQjU#[wG$#R2 wޓݥ4-tHDZ}6-m֡ŢB,/u^ ُYt7 \"t: /u=%#'~?}YGp[ ># #MF!&UQtk)[6s%2+Hԝ=#(@3h^C(:_5OV[1R%* Zy 3j˄H"$WRxg]7!`! l@D_мW7( Qr85мl̈́LNr24wd~(3U4~i]Cc]Ky0.,*RZrZĽr qtaz] ݕU 'ƥ4$BĢ+e-I#L"܄( k"]@.-*̎wqG*rDpE īW ($^L, R^#ms}҇8]|:J4J>3v9oKNj'GQiUMJL hZ mX,- ,hYgᑔ_߁o@Mq<0E!j. 5AN-%uZH0 YaIDnJ"[5m׍AН8?  vKl'IM<(EE xTgOƮ_l`_<)S.x)}@{(zbHNs}Kvc8AL`nm<̇g3lfe+f~[PEY9UcޤfP5d |fX .#;6}S;8B Rޫx((o(B0%9cqe۾m'۴Y{G,'ԯ2 Þ᮹#[rGʠtS輚14تeGHyIoO{C6Z~W"*_tva]K0dv'm( c ?ihrq 77T& c/$gMf: f*#:^$<9(9H@m,vKaFRgAة[JM5<:1 Oe>(0G%Lͤ 2aplIZd=0!\48⦧%r)rGwƺLjY2B˯zJNsueg_+Hݭ,>ťNbz[ȡ(tO+lMfnN>QZ#1^L5}P-2Fꣳ8l3J:¼WFYbhy( #,MreAfH%/ 7C9`b2?Xy)gJaޛXfx⬠@(_i=۹' r|F&.?7n }*SZA!ڝlkHu|D-𵙷^+9j{l'ե?D^ԼI*> 093 o]1=/.dsV#-ZrTADʹ޷g%P3/_uvCZy8W?9O"[z2[#e•h@,0j}uZݬ70rXj qb VKt9 jm&GaHG22ϚfeϭGt)6yϠ 7 ##aݲ\QYɛ}ͷ}/_}+}py%QC1;QEF݄Βwl*b?(e7RtHLdF;:5;6x`u\$0``$m ATV=S-f5H6;&_b. uFn(nˊX(j^MJn) D<SCR#)KI3޻xVV0&h;Q@3.4,(6F;gZLOlW}sQ7h:>3vث_1t. [Eh}QS .S(Z DͲo^E*HZ#!t19Tfx9Q0?mEu4uhJDY1*%kK.ˑV(ɨS1L{oĥEЭ" jULn*%jhFQWX6}Ȼ4\$E#@?r Y!WRF35cE29N$Uy[$mz?& ĔjVYC--K9*_0GPڷH0+n1ۃzeՁ9nl9iY2`9ŒUmմ'ÄM*x@|y7JfjCVL*H$#< GV&zAТqb)X*&dPF.k WUZB+Q~+k-I;ӧvh /9#AC{_Q񲊒n-tMn6%x3F WH씥޺4^4H⪊.MwSCZ{usy{&mN凪m#\+Նw'pc@YdOy.v<ϯzfc=ԗ)A]+927f a~Z$ 1w<>氉gO~/?`}{;/ 8!XBd5ڲ{ Z}1 kZk7Wϟ5;؜_妥ߒmFWGa׸umSt{W%xMO>YWX5AK;7\} gY̼3Ǘ4pcn.7],n͞X0]RiGqnI%}%IP(.l 76[鸬⯧`-N}}?$OI3̝/eЃQ+ (Js;Dɚ*_;'&fh0 RHv7pEXK`Ir|obkL&HW#'UסVxW(pQQ 6Y?]mo_A8(x$hZ-vAM] G$E)U)߻{{F}}})oܱNAoc>.dӧK65_nR1> qK&ߓ5uyo Y2)2T(gnqU Q%&71xQd9--Xu|xИ(܉_-o%$O S5 <лzt *<7*:  ^И^u=rPWz.<)%DD!\xz½O^2ɕ8Q +Ou@dx/al`bBuX=\"+pqxb "dM܂9%-8XxF6y*=go) )Z.nAg"c.xqh J_WxF eǴoHwFꤧY1wcn ,KmE4eEĺHfɓCX Kp="WA w,j'o /3tNS4V*mf8z!'8YK۫b-JF%vT8,/7xY/)*@i+{)6c'VP4nMX.f91ȥFJ|zʌǿ[B_]\1@e܀AeĠ ]g7;(D(Lat=A2оKmi'j3kS.>Yϲ~u 3UGHQ {mWe?GJKا@tC5-`u7o'ˬ3I9ɢ܄7@C\\NM),2n z-͵&f2:=mf0y2&p׷#S= {'\`M/yŢUKO9r*}`%owPCQW!l5#i? ?lR\UĢ * 8ljN1.Б?hQxV@j3qkN𖷍nH_{S\Eς<ݧA0̳#mD29)uX1F2O3b>k3~Th;Y58W昳qG;_h7]FV!sUY{Z̖--l#X#-s IV8iaL:GD`c}t0宋T-7:+J`4P*tn:ӃqK8+_dXVmhⷡOOużH-u>k̦RLh| EX& =jc&~n%0m ̲E^aAVf2F<W8 UꍏtIhC>^M `d8]^ 8@M((n|ΰH9_cnF$ԥ.Vo:ܾO&j[_e<B ~wIߖڃVžv*\Xz2#'צ R΁ ߗsTyϳi- l=p zٹ&ƻo84k$AbJ03XG=c ֍UzQ_0fӾB?=;u6 s*I12F{Zm(`||1h L&_ӱxK{}S~[DeW'S?MطZb~+0_N޳j?\pʺiLQ|URg#rVz)ah49d;t p$Kȡw 72֊~z l/ 0Aro;♹P | Ҿx5FnXaF-ŋUvk{~LOYNhbי13D`k&bo$)7`i 8Dk’yrS6F12w!Ĉf)]l7x]]C5A&i NxX 3?2ְ{DW_~ iӘ$|7ד%X w5k|5zJwƕ h `ؾ2CV1|m^kDk@y%}p颯~: ٍkѠ>ʄd8,h==p{S6pӚx(p7*\HWdla׋Coؒɚ@TO_`7Y]ݶa;FDѣko  *+MEjxŘe=C<#4xn7?_ 'fԮE6ZMrN3Cm6:Mvb i 2G_=ސ[$Re^<7>E v w̻B$['э<#I%^Dg/U?b^8cjC7=l/ᬵm]<¡Okws'|`Zh<Q|pL˨/g;bR}d=DT ;e L"&aJbOؿ~%ojVq ~@/H˟3( HŽ y1Tyؙ 3?fO'8 v[7Xf*CaBJd }ā)þoO0$ e1N?㢚H#G6gnrE{# : 8V3Be&Nq(Vʴi =4<W5"?#\jp8+_ZU#4\I˨tߘdj(pv;.!f#\ LOUVU0C֨)!6fqP,~/JaڛkM2~/[+JI'j-s Ո`VORcҲ#`iȵ_R.Dzr\.o1Dl039`)ʾfڍ.0yx]%춿wRny;^o {p1E5]v]ڤ-"r;)Cκ3s>PdǴB/y<&ԅ&JNfW^8u/ gǓ847`8:xC4U-wr&qe{CB)ʐo*[~]ѧ'd6M&?NCB{VATWS+|^A߇wٴ ;xT 80< D .,@{BhBFA-~7̒db%$/}H8.QvX4D[j,p~̂# \wϿ̱&U|||$U%ˍ'a'܁]ŻsrqQFֽ6<gGaL0م}iDrۣ\ uijKo$7!PML(sD(4'~:cu@W ~@s 7t#6{ԯP91iNS^>^I8D$8ENI "&@- 8vj!S%ъ.BՏ{we/} O`|-զ7Duư/sGj ׽s'9r8RӢU9B/Y11xAiǯ_ptH(w)6p's"ZeU.@'u PFG3p{ː&u!7ؽad|rtkLpb`.F?l`uM=`KݠL||GZ#zg1 d"P/Yٻ)֗|i·D<3yo)S@N1kx%JEIKUbɶꐜf5["wϿlweULr+Z2¨eB+zkUeV㳳wy.VFQUrdu@nz?}-J,IVŲ`Eȅu}zGh0^[)h<IlzwB-++xSoo,ggg"BksF,Tk!FlMlDpFH0$/۷oU*Hd8&g|!W  eƚ`,A_18Dぶ'"IXs,ߔB};!`XZgkb|0>YhCKx8[Gj$̦dC Z = +ˢVkDVkr(c/Q0;+ ^J]IB.d, [V` F K2{Ų#'{+r]dR<߈g1(6gjؕuD.$IGb%OaWl陋0 ̉ >0*Kp`){W'v8]B#XI n4; m Tpq6:8PG;Ӱ7xuۈ jT[ ](50l{`nŃso,N_Ğ) |H#r֔@nLTʑl眍4=1H! Tݹ+~8G#-1u>]DC2{H܂ryEHГGy~`c|k ~da,?Q옌=,sFI!OfO|b9&MZ;sӔ⸲"K}ۊn@x)#4:ǵHLマfoYGTQuKG(tڵoX9*aL rY.|_5}I Iu@AuFi/ z0s1 5!%5NCGMz;7x){=h/вBjjwTUe[ 0sD/U#Rg3P^*qϬs3n^0Zr*sr/ f\>ľ,[[R4=ל;tׇ#~z` 9mt[hjJw1Ic P`kӨrv'+U— b%@=` !3+[;* y1ދ}o@tO^9/$EoKlY(u'<}Rp_tEѰ>b? |j\pn}_P`&.}"4s%ΜP M K0[}*o@F$ &SBdr,$8a8n~5Vu>Gl}vY,kUY\m1#3,;3ͳ?RZXˋEnd*N0 F~LɚC~T|w? ` m].t+PWD50h4x G8C=#p#>jQoQ ,7kJ; ځ = И:Zi#$π0]װr}i#h~# No\~SoT͆88路=.>WCf>!߹3v+ GgrǞS{Ԃp+%朘p6%|RŠ7\s3),ctARPqǫdh ݺc-<፩hmQm?Eƪc3Vjd`)>;;;}y{bI/}lmZ[-:O ɈcqCGp|WM+7. Ƚ=ZtfHnq ׃:mK{N*BZGTYLDg68Gt9J%6BÕ)MF )ǂBY`ȏpѝ^BBbCRpilJZLIP 7PIXeS![isV’HʧΉhpl}E=}k;y\ +5 Eh1p?KCfm[ !%b$ vV)2s2A8@e>ǵa 2Ayф4".?-yf$!ih\^("ILR8g])o<&PalLC`` oR(0Bg&v7xhUoI ðӊR6SQغș _^a]IlC؏(r]_Z摇mU=3=[SK0w KӕY*%֔B''ĹwrQ-[ˮ]*TAvαr!⦨; \Df|D v.hm(g* *61vdZ@dLBrv!չ챷1GܥKg7ǽl\;Uz'zΨ@r_5g]m 3A%ވ)DB<d4F-_A&B^do] 9oZ6[AQ<ޡxa[GتzG&,#G{8n-=i|8d hwCl âJw>zGatX ػu}Epzʦ/ k6oㆧ7c`!+.փ^~6miS'#>PbC{; "X'_(; %c7lCjX\5 7ϿY[2Xڳ4%nd=i8LM87*Dd *,.O `C8j!#Ұ;|Sfp yt֔hwKytlG6Xrn'jx3DD֮wvRGHS84RUsWth7[gumݿ }O'8gqN/}6cXL rJN*udUGHSm0tm(X⥝Xp G5׿L*" B.Kn90Y56|Dy"9.՝ ǹ87#e8TE((.Iۑ+skjp^~P v~Tu,=,jZrtG hi]exjXc]Ci %k'zr]ĆdE_Qsp#@P0 Aأ.\яIjqj4ѣFfj>w)}5}D@ WuUNw Ͷy Syk[m I%$&CD>3??ck)ZRkf|$8 _xJ3шxE=&Q׷0CC`ݔX %Bؒt*hL_D k# 0^"`OxJ%[)(gúhRI۹/rQ rC8`v;DV &s_X _W}O8&8 X | HFGL|hƌ_E<㌮&'F c V#naB<O$|s"hд0~m|M ~m9V7QR@>2+`;!xIVeZU'N ps"ch\X2oxӥdxe_M`쟥8cA'vs7s`׋N}|/{EwA+'7Ic(١uܸ \aܡK^LU1(ɶβO7_K3NxP.EX4Ǝu `0{h{xWĸ3"'=pE̸Fɋ-vx kXeuv0PdtZcuuLn}.Z OT|O ;y;86{/^۰@[]7_'i27nv;(l[<\n=h;уhKmA23H!kG%w8)Lāp`g;L< ӀbW1+8|3ĸ:n Y!rCQ•濲`+O6fڇ-GjiLW0%m5Hb;>[>_;[] X~/ڒghG4[l#^`M(PK1xUFcf,pkg_resources/_vendor/packaging/__about__.pyMj0 y [. = t2Fps`˃lݜC~I{GZl Fe4-6^Q0-%ljbqw)e=/UdFkr{m}Vu?HdYEU>3O^UL,XŒ)rd;EqMJ5SpNj=G]xַ8:Y*|j$1;ހI6H1,ĝ;~.0L dIWAY7^ p ٵ-mjkGuo!o|Ynpw3(KWTɘv_w09d PK1xU+pkg_resources/_vendor/packaging/__init__.pyMj0 E 1i!2.Ea&C77QY.:7WG6@oB]4mq A;dA}xLe9XNCfs fxy{97u.`(X!_O -ɡ t(ƺP+3y6Ek~"S^QbU4l)}*rM\@ +J.Y#|a|6= b(`QMPK1xU*:c,-pkg_resources/_vendor/packaging/_manylinux.pyZs` A^5qRYlMeɵU9G*%&Ҳr2ɳba"d#o4,nodB4ʪêZ$wHe(G"KXq̳K$cg(xy,iOZ*/K+9R0^KYu!X6W:%ᙸa]Y,Y"XKQzXE'Hd E'2qb#Bitۀ N,YX `2=v_>9?e? c^,SX#<'oe."XlI{ĀQnũKO3y,#k?g(b8zP X4oKi[+h7GΎo^c/ACq|~tugSn4~:zs~OQ ݛ`.w˫z>?z{Ӂ!9%ٛ)OO'3x|n_L7_^Igd.s2UR!bzDy3f[?,MhQyo|A^.Ze)aovV{!zr3jN}$|!CHlDu~8s:>rO/ݡW[D{d(x&tS%Dv|R9[r2%OeU;+)KW4CFI 6}GNiue)jfTbYVx7J|RylQ#u,b)' C(D dr CW!2 yXj9JsxP[ERxxUD-aG|}qݡ e덋|gk7,j:nc 2!1֗}g2b' M:(ոcs3vx[S,Dd7lY]߬ bů^94s ܬet~ϰI 豼p"RYSM ̶XNu D(P&fYfdQnaP\0v=S55W;ΜQA݋g@_:-N+L=8;c6;#\?\!T<,p~>FL#B*Q ,O|$\PrJX"tZ!;ʿKrCx. 8&)( i"]VR˅>וG*6h2wof?Kjn*c"H4kQnJGlGݑ} 'RBȅ.Q*U{ƪ1#(ng"&.{ʣQ?˪ɎQDٯ03|E5hN ZISU4)__H6/<,ZG XLy,P/.JU#uU`0jJC]1NUX"8]d&"j KADWz@ KzX?P͡R" lpj̲XJ\[x1qh~AeIg ܎঵>9EjATdBad/QV-WT8!P=N{G: DP} PygqA.zGelҜEm4}P#8=\(M{A =[b`ZCFbb!sFAEV 1DHE,CJH3<[Ÿi()1>רT -5n *\hCҴ0mg.էȀnsGW!3ڙLJ@?˭gl@=$Ąq [z/x}/=? it }Y fiG<0mH_96ȪCMoxջ:d"e鞍ihkw_Ym m_qQ!y{>ۍğhVjۓl/I;.k`QJ-F3AZlȺ>e)JRV2ʑuxtF!V }cطXL &taFS1UQqK#Hjys+(+jO!RMʲ #9u`^55)mz!Y@Y=-zۂfm TB}3`]͌w-Ȏ6[r8+߅HF_uov\B(;{ּग़]v` eR'6)TR2Ih]ueZ4KiZ`M5[oF ]JֲAh#k5IkjI=pFn1 5z^p1;{_  8G^}IngG Ͷ};bXCjo$-EFFk͎0$I/†2:8zZwP;uF otf⃄J?SYK3=[YI^!э;9|fy1ob]Uw=VCmz@mkuu</jc9PK1xUt10-pkg_resources/_vendor/packaging/_musllinux.pyW[s6~ׯ8<,pNYv5&Ƴ/HPšYLf{/mg9p߹ k86M-Ln@Um)@VM)* l " [Y5{ЭRRmzoi&T"R Uk)p×-Yh#ih$Ţ.ʊ-[)Zٺ.MP7Bs[aGXfծ]gN2 ,/>Laa=)oZt>4uh[L&( Ղiq1C BQ)=R*;j6|VAYH8a'ef䯂$7\yJnMɜTH '}dt>.$ q:=k ;WDZ.x1zPlmx#eieuu\8?9=o_go/3.(w.z[4Z螡iDfªur"m=<{ymϘlkqJܗ{zd88N!:!bZ׺BV­a8 2KqMF ,g^* 6U[2=5"JpOBb #϶R ~!BFNx"uX}W 5`l4B@lAl:+QCKz. #מSk%N=3>='3>WWWm|_gpPקGkiو?~StO>y8S[0VC[/ 4mҍ v5I+*\,N|^;r#+zߓ$UQ;[-:~JV#j'E DAsq'%"' @-dr89Ȼ!:4ccZU`{]rlIo.s=+xhsw0 α$ 5Mݾ^[A<<:d%7ҟQ}uZ0B6uPTcaHeUHүy4&NZuU*v; N ժ+ޟσRF e1: B|Ln⹞N0ŧzkyu^+u'TwG˧~Zabn ̍>BЏ pVVA r#'h(MRvߍe!q++^DJC&TkE>FeM=:+ Sʱ"Ԩ E1Mhm+a$b6UEzqfvN?CɉM2_pLr''KCcܠ:{JPw J4p Î}PDžç?W5yNwHe=]//*Iej݊dJO#c9o+됍dr)ɮ4|3҈G 3vA/l.bG% 7Y4hup!rK>qo}8&WgiN0@kOC[/^'~"Ma'M+T-M#gE349n]ȍ<ňҨ[e.pwF^hNRG>tň\gjj4̰o2 h5P<08ԑ);hzOQDW6A4$d*~PK1xU0`.pkg_resources/_vendor/packaging/_structures.py͓=k0wYpLh)BB϶"ҹ_YN?dt HQY̠V-ܿTUC=#bCL>Т <1\JIUSMRۛT7"cu¾wʤz(r@J7{*ֱzMOr59# -dL)])dGhdK)`]=/&ﵚ[kRR!IdIe&}wחv"&d4%3TBC[&R glv`jQ傗;VScvLNYG S(bwq c4sϜ@6 ZS2EBpM2A~%ĥ$;EJaW6CL}ToyS!T7x<89__IZ9v|:aTJ` PrPI֜!wS]3:|"`g!%a_uf&C()n :kHe.4YS<yhKCH킙8r`ߍ `%MMQ2%M Oᡚz:\vl` ǘ_W-!t0Ct[4~ɾő#LAi@[Ϊjkb%4w "5)lOå=iMu+s|;4lD@ZFwx! I%Q;z0p$$fCo~r<\\B=d5%+9jJ04OM0,].ӎr~= XAysaAwה1$]2Mn ?_/..Ns>`G}z>hѵd WpS4xe[Bӆk\O>arqX*apyLv&he ڪEr,˵TW@gpmGE2~KiXU|~8JA J\™)hAa pj1 +GD/y߾%#7ٞV/XЃZ gP%X0,ȆbzU,Vl_ݙoL%i1+(*hp.G#U_XA+܊ d k *S1(᪂Z"@I$+ 7թҥRuPA%PCٮIXM2 8"%13w 34XĠ̽edDV~g[6S 6L%\"CKœ@qrX%ۼ2T4:T=Q&au7wb#ln[\`^Fd6#Ɂ]GZ0z!ҲS%. 33B8aC:e툣a@OZce8=6spƭҎx%^XJM2*e-W7yB[`'`=z! Vvt 0 KKiQ8Ť&#,P[^a1c܂FSXja`7W.)i+LiMNjk.IsA挠}N~two|"{S_Bs7:3W/G1!< FEc.j.2Ha't:nPzmmizR6)~C)1MNJ#(~K"CO'-WǠ4>^fcmz b3{eVŌ[.W+Q=&\C:/_ZU\YIr5:luI|aC=aF`j{Xc#mP,,G5|}㪉NSskܥkb*}beϪ}Ckf˕hҢX]iln20m pHXcA/3)pk J?9Q|))H"Jm^bāĴ v  IvͣK%W̧mH=TFT&w y]lTYC.q}l-M${y ~ $r)w,>CoM!#ԝ-ΔO?tX.J:KwƴpZg]{4Iu!ZNZܔ;MBh7[Z#(1"`n̜c$j̴LA$Y_(伍0ךDA~i Pn#|ɒgdJy >C?~Ut䣌 zPE~NC{F*ʂ`ajD6A2-C>i>Gy|(P}^m Ӌ5^yv7l-h!I]LÅ2N(.Dc'5E]]_珯`*YRχ1Sa j[3%sD\A z ܪ L#ʹBLi7ǎpt׋[(\=]AIe Q+Ukg7 &uYbvEW@ g4!.ѹlLqC}ODAn!,H* +Ƴ;L2[F?zb{jZsТGsřd CAyt9lFV_সU{`vBPyF0g@ ]UHݯƊkM t2ɯ=>#Nu50e/vv(ܨL1MW / [8VǞ`ٲc.S<|NFELvhn~X$>_fmuֲPgRQEB +(nQ6O be@6Kx~ij~HJ٠A̿McGT *G/JDU{@rɡ8-m9t(-qBz6?r>uPK1xU5u-pkg_resources/_vendor/packaging/specifiers.py=sƱ@Θi<ۯ<=S͸v&vy#,(DhrwÁ"Ih&vVћˬ.\E*ɣ<]&)Gߩ0z?GI1a_ڌJϞ|\OXг, TjYYSVwQVQZ^/sըh$ ^U%z*Ҧ,<cAo*bQ\Tul! "yy2՘>} v @ O/ZzlIΟIWJyZ0o6K]WF.^5b\eI7>@(oc LURvkZz>v~@#'|$m`e9suMx:*ENpDUͪ*j%TZM|hˢ}rJ߂k)ȍ2D,vaMI})E%ޑ:ز}QԨVWk@2]-P/iulR'h߻VJfcSW ШU3x (e/a!_0>|c^  a/cc,-eY0K~ܷOZpdA 1X Pb;8k6+` 9Ja1 RP|BtÖ f=d뎭;s5. %>(a Ip j \~ML*:KDoe^Z`w ug7Iw>vzsZjAX GSzl|3BAeEX\ uҤ5)od2!فw(P4}$U׺^ N}z>r hxQp`:08[G! FlhZ6ޚ79N)ڊ^thpM&|Q}8ouҬPn ԩ@Ox锄`:ߊZM^ &8:nb?|`a G-lԺ41TgCP`!z(<:ɵl^ 8Fpt znf5ؙ&)R5Ѵ+Mqno{F_F./'e Weٜ\5TGoo a-tiM&V AjN6_B%Ǎ5!ǝ@bZreL &fچryQ bAie[q䟧pΩ0onj>IGCԼY9=‘>P*@z i/ܱzCZecԀ[_>zAddi^02\(jpXrg\K:ߨJD^aB8LA=!U/%%PTMj@ udxЊOa0O = 6+@µzHXJDkP󒔆o!yQU*k\d)n`:#WG9DZ? в\Ⱥ9k%i4müt`撂k&`ɳE?ܹAX|V =ikh'd*p?FPD5ҀpbPW*52։9лBB(jF{Gd% ΛD|>a^89NKU̻.S}s-|Z4*+}΃\[HxHZE!:rͱe$m΢ UFܙW5na z/n^&)*7AZeA_^'K4/k4pWEmU( 9BBCdI7N444lS+wvl77\d%TN%K? 8~?o?{un'L (1h)5L;_=rUSt"NhRh zY9 P͟d պZM@jc[Ë>2 z8&~^ɭ#PЁ XMlֵ%`;:Mˣ{'0u`󒆿OƸc26 @ԗt!萋ȪNeeE3!:!VF*AʇLSm^VpO ~a#Lj \juW57 :m[  |J ?qk.頌?E>6(kuַ;r$ǎs]L#Zן#3s*xr5  8h7^UP6t6ZP!Ɵw#Rxhh;tUMFeSƀh d U%γE֘0vj^U*Q! `i[HlP巡w [H;i -9l^(F3,9}~üR^ߘlj_Q] RǦ/h$TltR/حMI HB`aŐ7F^Ud6;=aީww/JޭQ0Ɩ`',kڸ!?ňᬖ`aauMlJP $9x?̲^9:a=-YA'IwHec uҬp/ⱙɳZ.! tϭl>F19|f{'x-u lʵ&%1je)8wrM!/cZ€zu^LAO}S8v|~y F{^o?{\Fs<5 +1иc MSf1*!)/|`<^VoLVH\,y8FfyXz{.QEov$+EN'Jǘ:n֘K>{P?>ු+klUH:8SRWwX%',ۃh*ˣQgXߨޥN \bahi bF+?Tƒ)Bf{ WHd%GyΎa;sߠYBU͡@룳 -޳o'?[ -Ƴ#k&? Ia6京sL;N\<5] !Jm FðIM @Dރ N=޴2tb\xcuKv)0^&Q kJց=Deߎw)&aNe aPŋ8z#]-[|C>nCj!/uRo]`NɬwE;zj eCtIۉNBPz]Gvvwu <yZp]F?@ՔĿ:hnvTUF}tZZzTaeۢmW r Y:zMK(tBg9?qŶv匆߃b/]_ %nS! ϩ.xP[ ýwltQׁ5jl-jjW bEAn]G<,b]hz@UG6t "YgνkZseJjTiF`/g:f-Etq1eƔSoIs?j`Py;6S3spĻ7vnIajėhg<пQ\4j妃eMK* =(-]t2M6,S'*P(Q)}{2CF<6#[-YCR*o|Barz1 LazO9؛Sܺ@lPfEz(ͻWhΤ76dq҂C )rK p\L &>گ{GҾQ9-jsrāIp 21|1x88@*QV 4Ƚ[Hzd4M  ;j::w)kwms`'z]u{wF jh掍dͤ 4OFb࿬)o/EȃI_i/6]jQx0"͌=C`ZdU!x1U$H;ꢒP8ƞg{ITT7h"gA( S | [F  wQ?Mr1:%cF;&Ҏ: \~&:ܘ#ŜNN?]>Kım1]=Z {#ysߎ AN;r\oW{14- rB6^U$hI5%Au"Bt踲n+oAc;0 uԾ߭#%sT`BDԤ2[&*SBE;KD U"Ek_00$:( 'awZ浶2E oy?jVk$T20EKݭ͚CKߩn2#Az_.1S;CE(,ZKXE.m']?+ l.3nc>M%3PK1xU msS='pkg_resources/_vendor/packaging/tags.py;oܸѿ_A( vۛR#N$N@.6N C%͜V҉={gDG{1=C;-E|'uT<crrdǣ3<Ǔ;ĚSY3g04Wތ Wq-@\x33qHDyB7@?L&b;|wpZ9 )~.fϟ6i$%;.vFϣ=˒KUJ͘B]Ո]_r3YEIɢgR=]ժPA 24`jQXGʜɺ@$hh0i^0z!@4|0:mnpNLOpLTaK.I#y}e߲y16@u5h>GޛY\ 7qX .Wh8 L)_ D)]4e*J88VWQ$T2QI5HW⠄ TAUF (/+U'c2 =MĊ#`?KR/?C%z "Biч֕ Ȋ2OjP).20;x˂.hN e_Bo=sSpc@ѯ F%|\k^3psH2N_98%!D7 p2R\R_j/Xh\Jq.(ؑo5QJzwA䕚*8:Xd}o!ӶBAd,=+FcCnhbDIs}LJl0N vT+E_**}tl\p#NFv6*z9u0]KbAflr 뙼0rgS1w:Ί,\)d7H,6U9_9Z8(ZiиzcfR޴}2O E n.}W*aNމF?WP OD5JT#x])+LX@E8kQz):N[rVowf:G}:|զT\oM<'tԙ\V*(hvKb|/H9`,t}#͛͏{ƞjn -Cy-R(fQuqG ;:X~0(,}y<6) p;=xs%ҋ[-;m!(ZmΚK{-xw  Q\ڽ2~!z\̈цAE ^N׍Vkuplg ,H#WB6~M(`V@QYtz)u潎v_BEH}KpLzr ,>rގCb~ CʸPpuIoH$ZJˁp 9fhvgH34: 03-)ME 0Dɔ3j"#荰֥9`qS]ewkTФ "["L{%62J.L(*;Ex1T6gN] 6@ $@w@Cy:U(4:&̈.r' U~}e ~:]EUpN"W4XLRHiC_*ֶ!V|?>ѣ=q) jl ЕQvѭ4Nn|v?p @ܱ.g3wAi]ɈeV\0N>Z{LJॽ兺*֯ 6opvخwSħ o,, &E 6dyWu6Cswb葵}hg>2vS%Z:l{&9@sk+u| Cg 8HNݬMa7ˤe`0 or |FE39)IKXɕkNO[9yfOU4~M:kzAj>)JtRJ4lgU޿Y=C?>J}ʀSm>YL57cԺvH%fÁm(WM˵pø߾`O^'熆h,X<[JŮ"/e|2\l N[vaz-&UTVU" =O쎍'_y6"uV7=3cqQz4~j`/ϻy*|sS/9c/7uQiP.O=jrYF`WmLW t 6T@8+6ebtjy^Wl=`GGHp|e^_\`CKy) 39}f:G{^0[/*WJZvFPgmo F2*23bRY$fUiCk| a+2ġ;ydF挅UIS*WMW<#8sc_`ՖksXHak~9|2 Wי?B b`& w9$ &G%:Wj[W uYQ/ &⒧g'PW2zJ2mSiKݚ:@nSŭ2P5EͬlvYOF1}N#A:@Q ASI4y*8U]+WP(v;XGu;Ej3\Vfp9n5,b2|;bKu&MxKo$thn]g3]n5<זcLzPʠȶd]QlZԄJ H9 َ qb 8`JYҟGg-ar=I5x J.M!Z05l6mȚȑ͡iР>f{WdEG-:2n)΅>Khm )P- tA2-wOw뀢m}ώYĺ.B (=!X:!7c ʩiVi"ϰWC :BAkJNv*atҿ3G(7YXFnXaG\9GZ;g16gg;Z. ZxL?PK1xUA3h(pkg_resources/_vendor/packaging/utils.pyWnF}WL"cp\@.`pIUɑ ew/wf)KZ\=rb1PY!Tk ra)j>B$Ef^odb@gN/Nž))Mm"PXJ-Tdo!r]h24uyb]JeH[(s[b wX\7%ExY>fiW&YF2YFP&J㜎u\#sZ\'\]JY̹BFf޹Tk2syFRqoB<Ѻe  >'y'JIN< E]+5,J.T2U .'𷸵E&4kDgߓ%זe`Y !ӤH-`sK!,3)Ptm\s;=;Lp~⃑Ryz?xýʷ&>_P?oR nB7<\haQ؝ne\*;sGvHFz69G}O=^c.Kvy^H8tT[ٿ﫴n\'7Z^jgмjYgGމ6l^x~5d:_&=E]{!QV] $ttst>ʂ~.FΌY~2+Q,Vi.njD&>oR|aN}o9Q6|;v;iZ=7Q5A#sHc.Gr: e )"gk>] xD_Sñ:gD:ET'ZkF_j؎*: ++60֌\ixwhQ(Ḳؙ"Mʋt ,ks-XMfS6 zc3׬7HBMKbSy{rb%Lr~(oL^ofmi ic}{hxm[:j.<Ts~" ="G>]`Pr!0eQ 57,ɥu WdJ1M4;*lT|^5sL۾ 5jX)pa UFXZ2z=^f^Kui~ij%)E$ewH1I|F)K_/N: 0NnϩMZC+V(]1^ XEqwadƫDdi֯y2QL"[$v~l 9Tg>u_ʌ.Rн)>>y[2 rs/ )e[,P0EBRos`Fm\=?{3f|okYMC4?7~me;[oC0z3a_Nn'O"I@_Ϧm<ք﷫yW}kD0ЇZd7ʠvtmSރPTlHsuE༣iO`"W'o|Y*Q^aG+%1ll4 WEEX*H!Vx YrE((Q}2J!#Li֚ǟϏ=Yʄ眎.Sd*U{Xf9]XyyHiM]T=Q`@#J 2IL:y\U};5z@&m0 4[%=vi1smL>+dGКBԊ(=tIulG~V ~YԾҙm'-Ih[4~Tw$wJ:`dy0a8muφV-3r]ۚ#'y2˺Vxt \d3#üݐ8`oiyGu 9MӞxۼ Ә۾yRJT7cOgWf4#yBg'՘u=vΦ#}{iLgJ4V 7n|6&6&HYF 8T{y5[lĎӒ!ԬԈu 8fCbd0whWupzvZO>Gl~y7 V+P}`5NV.m?4$VZŪ }Sl*J4橞9U橾PtMAWUIk,GGϘ]_+s3mmsj>G!=2$Yo6V5bId0SdȢ2F/؁ݚWlGbpeSVb 8XTAmD.Nia+BTLY.ɴFSA$RP[9 ˜qǭV]DjmětaetShNFJoF;-ǭYZ[|䤅2Ƅܭn.4JV,ѳÚn61oYPz;S7y }Xp6s-Dž΢?;wGpHNӜV9=^Z;y eg]B0kӹ]yE&{J<߽,1݉>2^?c b͑  TgԱ 25IU#;E\oe뙁.3@mB8zʹ Hֈ+õ2od7{B U@'i5N}@ BAԵ?Sr&kCLIņ[zaHP7A0TQZ "Kp!O2X`-jtNN G4è_0}ZuIG@ÿ~,7C$p{5NR.EYt@@c>R&NǓ[]o׎7^Xhph${gdll/Vұx1Yas b\ΙcIs𨼃EtSs錧ඤMWs-EҺ.;gЦv"1n@&M!XE?Ep`@!3$ OTroGZze {KEmF:CN=MYChDI6y',?paZMe @(x[g@@j,ڴx,| |rqTõWx/LBa'Ks!`LKn_#˺8 6TdS٢hCpEanpqߧ3 uaaZ3#UG%uLꏲ3׳ǣ7GH1#ׅh}$g֡":jۖ[+jG=<-DvlY&EC%+(0qeֽ.Ӭt+%zfa{WwGnoO%8]v3@w-c]ӛH".Ppgnb'٧#M:ّMxukByFڊ"J 2al,>_Ѧۨ8!19zoR<(Vt= DF-eU 3f8h\cr!Tf LO bbj-W{b<*=R1FТ(d8#ZRa<.Q?M[<+zR|) p=LC)C ~މ=Ld@08Z=I bIl"hG0dl4]L*§&|DQxK4%"gYk, ۔nRlz!(b dj}NVPN<a𥶻/5OS؁`bmo`mXU&faDŽ5: 2MfW90ĸALS?iAz)2Iӓ45ͬ҄E^%0-p Or;2u#jxw63ߤi 5-o: _`^X1|t;GW Q{9{mx YZC?UkVpoa5ly[XH,5-A$8_ΨuaPq}A| 翼Js|? u($&M B_ Va<"ʦolvjۥ1Z]P+{WvL-_] KiUWZUɴ+KHVQ<5'Eɥ p&hKJZӀ`EYKߢ~O aV[ct,Q*"ESE}9 BMW\y!f\:SaYkCbo]ӓq%m\?҃d5 Do7R'aC$ĥե.'omzGm#TJV5sM7#$ⶭV%v*ʒL-\zFtqը9o5 }IM)KNíZWhB`ЪEv[:=[ܢy]zm 5CȞ9wMc{tiVj(נxK ŋ.!oVSPcK / Ϻ>M4WEqE8';l.aw!Lí:8cc]t 殀 0Ĵ^\[1A1w{,m>8tl1OېRmaBw4>xX᪆?M$y[}cգ<%POPK1xU/HF*pkg_resources/_vendor/pyparsing/actions.pyX[o8~(T:jam3"E;-A 2es[I*J8mЇV@b<<<\!Ff4JMH&A4Nƪv!ZFQIkEHt$A*eDZQvpPn6B,SK'*xR"u]VeDʭeLk~gzI 3:n3ȈEDK"Sb' .|3̬KF3d0=t*l1֙<,l9 |4jS,)#5\0k?h(_rty^֎*ӶE=YVސR&Z:ÁP}׋ދܾIKcQ,] xxTFxeXb UTRktE [D:BE 9u`ZnX, f/aB~⧙(vב= *xҵT 8AVƤFULT|:|ӨIpZD$o3Nk*⌭ oDmUZgf %$/rޙΝ%mH0 [Ŝe*WvO"G^[ atVQ,CqhǬF3/6]Ho/.-$x4 Ο`iuTȢF!aok48(.#+^/_+~8G']o6okRT\*RNpv0p(ՒsiLd})Nqgņ"K"MMp$0.DYck>;GҖC3| TMJ Yg'_6THW_7؃sq0do Yf4/u`(TvzSe4;K+۬o+YL$sQAٵk#'vχ:R~$ e[m[3"Jȭ\숅iMw's M$Ef'd#&iOBROF {=њTgnݮmA&ĕ "VW5kx#XZ|>iD4t TxCR4_8@3jSt'l+oP_R `+seXc^msj W#{ { x"hԳTc:?ԍ*f0Lsn"嵷>CG׽gL#ឆi4 ;Fj0gJWː[4`Q.7v1\ԎNjB=î2G~`) ŀ7b-MV,D89w(b^]F|,zfem=ɭa[Ovu߬uoˌcix_PK1xUe~2)pkg_resources/_vendor/pyparsing/common.pyiw6d,R|$:v&y{Ưqo+)2DB7JB;^Iv=X\ K(ЊW;$ D #^G 'Os؜1K2 <܉良ChD1 '^gw o3.{A%i,R6[ KS/ Sb ݰthʈ4%!e00rF}s/%iL-W1Mp$}C4-K2Dl>[2+>tE` -L9M8r@.DqB>L.xEI} \y!gנUc(`zhH9:`f18)&l#' U,:Azn^.]2~% u]X~Ӏ:|ۻ/)1W>~x~ܳ˩h6\!twgW`nn\a]L3ݫs#(j 6 T'%PXc"='H}(K]M7~DWA>!|%Neg6&G[>"U1 }+Y,婾WNk'Q"d+:pI[@@1Xmx ᫘)h^O7:vlbfW Eٟ# wU9qj0--*ɛ7Rp6Ŝݮ*y=| b&&ǒ}Ɋl38jR0st4 FDø&0_CaO f\aWih[k)uw ~@P܎+/L[嵶Jl/dn cciB`HAC=KYTd\ۖZJ:=نC"IhƄGE$/$B r~˩f2GvIrsd+DdsےVTҭd9J\֤ kL dDJz"+':(XBS:tRxXɗtozK3δYe;>O{YFF/Lg#@nD󑻯U'$Pw9bDh=F Ȧoɖ'ZWl ZBZD3Y%ӕyOeʟOK Ƥߦ=~%RX*w]}!:i&"B6O,̦q Xh:l5EgjrP\r`p5rshw&)x'uodE0u(af3Y|ȻCz\ c>~%d{M/jG1B'u~p@h{a gn$0w?>d>gv܍5ɿTwŠ6K`Z*N*@f(xM*Vm<4F|>Սo7U2Q6׊|B@9Pd]{%(U 9@Չ\&#֋XCxN ģYcUxs4f |Q+LۥM {x݇1|bww>d|9;E˱Z ~K2r&K ^85g/7V˔&j1-|"7* %D|U0<%$4DFYJ'$C-ߠU#~zC % ,hd U6r/1ZTC k-Rt|݇̚3+1,97 rJˏК@\BD٨eE`᭔Do1u+'6Ha59Je! ;DT&/H=6G96j@8md|9l/s+@Ot|B4"sddQ+DA̅6 Ya척\8}qړO?Jo/,ev~o+ RoY[O",QP %f !~@ 0nJ4@ t.|\]UAzOdE'(+h_\*60)$tCE#:}zLP'''7l͐ ^CE:B!v($p~bd xdK#h:FƆ*-uX?`r%k!@=03b(rdFr~2Y 6|vGɬ_ʋ${D\,9"?]6_0bڝou[zuHCN?z+0Y?TO${+{ۚ_MF.yXzsUz%͟z#(`2t*ɭs7$c̣EAb.8Ean' (Q0og Xt{ܙ ˲_ @~꿬^oa}ѩF{Vψ(de7_h)#˽+L9'±x~=dՊE/壸*g"Ǣˑakih۩!g4`Q.c hSPDQ B) ʻ{9pv>DGhhgO,"x9d mykSAүﳧ]zF&8 1)wA[h>؋)^еW8K܅g/ q9a4lfZr p(/ZOFK*ym~i[a$y'DΚXG6ڹQ!ddZouSqOۉI#8xD,kC-vAupC#&CB#y3һ""Z o 7Lvoma‰s ΄{|2Gk?ee3k6xZE@7ȁ/2axJ;yǷm.f%g/Hyz2M"O Wi> I =:- mP=_ۘb7 2 V9.z}QVꦠ!hnP/^1AϢ` NX샎g1Te{d24'>fmؑu`2j6$VCjY)SP+GVmѾ^ ENu^۹;WGbI$_stg"Ljq pP j]2~ZG%ӎc$A1lipԑzo@9gXfa gR='\Xx>/cyS*:MVR:UBs*]'*Mf.BInRBvvv&ӅC),M8e(V.EoOks4*ZbS NoF<t&td5bgxPK1xU<ǀD>A'pkg_resources/_vendor/pyparsing/core.pykCG(_ё9l!db2I6B{] J0@:7%I2`e~kAgxBf" #AMdzTߏٰI \LVWE8:ȡz7Iz޼k?҉;y8ߍW2|p0̳qz9OR!?ɀ8Agzx 2yoëP/I\^ Espa2ͣ]-,~<ы&35&I9TAYQ&G ?L*,DU̠4C^ w~8q2whj{Q?xۋ.n,+a7zxƣGtv>Ny + JiZ0(-H.nK*q$Q>p:. DK_TqĴ-lu|\H_4(ΧIVňhN2TWЗu,uZ]jhİ3DUt Hp-p2q?TI:M@(켄q`_{4cfI ^4Jż%>&փ;ѫݟvW?zy]x_x~8A498v~1Qw_?D?x.ϯf-UI/ ?d G Жj0nzהA3RO3\D5/,IT`0 3vY<`<:]mX8cҕV P4,\G\+H'iuqy!T>LqC9wgĥlt~ x2D{z:)r-0Π1`vfE FJ;Q63<", Џ>Ozt~.<`'9DaQ8RQ+e9O{_?fTg!"@szN,z!MYyƣ!7ٹ.Y̯<^WV|WQ^?  3!F[rkuz3/Ptf U WSs"Yϻ}tٮWٲ7,b|Ա3Æj`w4Ky=Ez=QLz=Ur78'œ^eEGݏa3|1 "Qb "5+T"@f}S+\䣛*kp擴oc//|ZJcGC8C[<,)!C|@EfN/&"AYtȂT2^]! UZ%,&g@p t(q:ʶFKkxh&T{S)!( qp+*,!ɾK5mj^+Ŭ1_`AaUšFΔI<, P-ܩkBZQncԠs8w Nu6.DfCvjR#o#9un=:=83%*&j<:h|y燨$M;/N@{'?r/'e}h磝WzVg×s{`M"C~{!G]d^d6>~yDDk.kd<#9e*pUq>e*zJnmuk%ݍpޚGݢY"Xp J}5׋8˒HV \FD09i^Y\ "zil¡ 2K&OB..W]Z}b-p Ris49M̦3a8eV4$FEtIVn^5dOk=K xCM, i#R6}Nɸ 9[JSS UW@?J|߫k]M vq)2>4xe{\cHxLG/U<"&P$'B]ERx8U岳"xE6&*ٌ E^P?MQoh6[ڨAZFsR"eRt90MjdDA7_ jQNLqn`YO%i$7y12-c Djcо2G}5GK`Ǔp0v09"%KS\ɍ;ȡ!55֜U4-/aAגN \Dȣ.t06d֕ECqQO/.Pa39Ft~Oz#̭udL AR Puv,VQH$Rf{{ J>]Mz$uEX&qN!szҸH)t u}EWn*sL~OCX2Ywcz!Lݲ2ji>J5x 7oԳa䳐 ׉^"tyKCڛr˕9]6X7j^%z)33M}Y¹N[Ó&}aje-K\𡗁s+-M: %fذ9 |`-|Y1֋\\Z4#9'8|kZQ ;KG~8k5泽;? ~qdo٣c촢u("'MXVjO&ǴtvLLgZV?sPVWBu ۨ%HGdh30b..Flk5V%5EONvJT\<Ȇ300uO7{ό(oN SUfb06j6?@SV,ϯ5'D:|Y2܋k]E߈XUyFgJ< [XRTދ2֔`[l-뉌 8V,N=3JTg]ZŠ".iFal%r6[6"6O#$'2AMYpEE H̀?uU nRBIp.xMDmW?*bߴkAjon>zxZCgxaF<J`gUU F?ANܷvI - ߣˎW.|䢄vz3ٚ'"r <ҙjze G2pci f|,O-йZW蠟ӧ//XS΂bwǾSp ""oφV{bW*R#KOA7u  4I!["ə…a^W_=>/W_[W޽x:|g ,.MRƙQ( %j %(,"}4 ͣ{oGjE#{}* BZXEPI]ZYl3d i,A;tX~&[ul ?jr6ѲGJr'$N1{>0 p7se4ǤmOT".lR=:ao|^N2erF <1$˘R Q禃BAXgc `;f3Y̫ɰTdzD`QZNE\l!qB'=օ*(`gvOw*!A3t':`t=Z7Q҃Q{/FF(zKP^wb(Df*.6̪&b[okpǛ8SJ޿T1mGxuŵt}& pcӹ5r%# 7;F9c"bǏhѠ:N:,Bpm 6*Bj%CH;cleW+ y,1) CAY7Tr5(- ؐ)H hq]RNsd`^AUO34<󎟋d I*9b&%F<` ~i#cHS`K͜5scN.oS'rdXa\TX*C| =K`b OwZdÇϞ5EBUW L *PO-4VnzY8SGK=rtէo+ =Qcyՠ*g6ύ2Ҙ ƨ5T%_fٝU?5dU2wGoy1eTj+jw`&#P.!'f|Uv^턀õlʪPh&Y1#",#tꪛNͶ^IL1t5 ==ЏbDZ~{N%wU9C|Oի|P޶ճPŘ%n vF#G6rK}tJ#ߧ6>J۟ %a^m2> C٨:KM池M뼤's?Ime*cXDGd( pKl*(2@a ]*~#ZnR8~vK!qvH>_kQڠQ(+ZwzK|;v1 }v=?E|կ25IA"G*Mx\^ dYͤ(%lG,Cy]:xx7K;Ipy=H I◜-zb@UO. f,F-uءn} V1ǟe8W=V __19x2{:ӈ5tpZzAÓÝ\?f8G޶[P<&`Rϵc/?ú9LBZvGc?]OH#T#&"$x,nA5}2P$kM$52qo%5) T/` *X;ГUשNI!>vTC@lOM m0{%m6Llg`,O4F,ǔ('M6)*Zf策hax!BzD}{DΚBKCI!(  /f޸?_q{ʭGIk&Ļ fTum֡^׸t='noiZl46\sAvf3nԤHvBl+Duq2`]jfm%1'*Kbj~tXH= RE7v4Nj?6Y<o#Kziڒ_Z#%WQ)vԖb ņb#\'Gx+px"N<~GPKz!p[:} ӝ;ќOIoHkV31w/ܚ8} QKmӖTzA39L{ gi'g5=mݐ,ˮhPE銇j3ik qI6Ekdq5 Om1<ݰLT, +1gv'\%FZn[#oHYZA/POԖ gQzeDV'3hq@X$*ve[{X$GW4&ttX14FzYW\bҖ0(?(50rC&e!\QB!a"_^auBQQՀۑѡ}!9D{q D.G+eU %@ ')1`- umL]eR5&X8!Cu{#SE؅H96%a(G%"%^W,1f<.q6q4pi+(s ;~}1x! d.gmGlt $? >F[LMZ!4:j\$1[QPpDq,Ռme\΋7)KRrUMd6$}"pc>&#k񳣾i sv%OewPiCePk zv4nOF;M[ fcl{ĚZhE҉ZnS ŐQI~~99cvLw虓s+y\G_TqCfgtcjGsmf^m[Sγ*@l_dNt^<~Q{yd}@e֩o(oуN)CoJ ErԠ9b2%GR+^!*WHCޤ3X :RG/kf<9=sX9"%Ruw#Sݭ9`_WA.8ty Ԛ d*dwx (Qrvغ9Wk {!!Q0,eTL)fH@DJwOUpWaZʚ 4R--ܲ#o³ݧ'% P#$OSxP k%3huE i UB>LNCxh~"ГV7>Lɚ"HF<QWؙlEFtWiB1gw@ -MѨ=̑@@2-,b!t=P$T5A۹"Óc ܉#/$e䥖վ<)# w$4ysQY C+Pȫ#}UtJ #hB$@`jl3fp:hG"#:EX5%x,N=G&\ʉ<ƓNMn4V}r峊㽹p4՗b1H5c6^ enW'q(\jmEGbW/="!HN *&[v' z4b3y_qD{̡,cCw]pK}!݌;t-n95'LqMs R>`!K*2pNY 3gY۸cl]hSP bX'~  4$kM1q{iMiXX|ȚI<>깋ȩH{F%D6߭O>ExvDc\SXŖEbOOTMo\a|/6zEϓ:WdTXO((.! b`gB״)uh˝l6:$1&_fƖB{L{ Ke$!w S;}юL\=7p 3@Ơ Ͻ(KRrͱIj4o+FHµU:{!)`)W5ύ fj'Ja@rYfP5m.hևU=и w#um/!+~zGM9ё7l q6fhR]h88FJ\WDc[dxI97_~O:/NZG~SY> m2aN|޷7 6c+} j}AT^; [ N)]+>`aI!B? 1T4:A&)wSfVafcÖ ӎ<G rgĴ[㪬?n U}J$aA }N^A{N LnJw ڍ:=%g=1Spk2[{߯[V}o؈-"P[fJ@ɡu>,,l|AMY=IPq9]Z q&ᒤ/?"+5LTHsЕ7y`RXȆ];'XIP \#hrJJ8s#|NU=XCa:4츪FSO@ dF+Ur6}L2,..#.OY?ՅxaLiTD:C"Pvqeˆlg";QGwJ"/H ㄍ._,UZMO3RDi ED (.'yBIZ2&/Wȩ/I3rT{7vQAP'i) Hù@ݕ:ց3ˢS Grs> ؾCHP.qΧ.KnGexD@&$ZT@AsHKY6>Emla a[c5>sE KP9@Db(74E-X6#eّTQB՚1(9A XM*:VYOFj3]^UQ3>!설hG`8'ԄLc|" 9Hʱl1i;Txbo6I*ck6P1b[w[׿kS02X ]ړzܣܦ1zlbj%_DMiA/OO-P0=-TzE`8A6x+ʧ(ZmEEe;:r!;&'8Փh?,}=?mZxǝltTp]["$AW9Yiɮ[1.$;mbۈ%h /ڶkz7,:-J1KF[x&\i*ˮ'lvd|.,+jB%:,G*#Fs19v]> S@~ Zm0] r2Rbb.CXA ~eWԺK ?"YЯϠ[S`D sz%x"K3疯$[Z^t6@SXm*]0fes!YA0$hیYZ0ҥ(n^[t';n-pI Wk:O-@p^p꾸FCbQ}]ulSvGOQb\-@6e]*=9<khBm[gn=:[W5A^C1/`wpD5F&suu,g.f (6&RN nmsA#vO`n7%#畵`7ZCSf(@X,`pe< G,[ʬo^FOK"ly3]h  1;@ס퀏v,Juj'|̭4;qp6鲦0zo?PH$?x,d-?ճ:3ѕr,^9 GܱJ<<46j*zD1 Iԡ@x{.;3Ǹ+RZz t={kܢBݶPw˾P-`VojnQ A \8b-/fN ŧXGC縻?RxXEQ؟O!00HDԟR끑{M.-)/?Nbv~/9wO:шZ8RKj{>%)Y7w~W;Mx\ľ\0 HGhoL~c8xb I&*@imT "CQ(!Na#KC=&bg3Rwh"BT^,LsQ*iv@T(^Rg)~L[l,w-zSݲ}Z֩)ܨWΖ_SnNu[긝j6 WIܤrUPlm/N %{B*yz*[2f-<++u#5WGu9}[lRMB- I PfʿCč!td4dn33JbY:1R+XNHP2Tmr&ӐYk\n(:;{C?CRSlB&7bW=}a'K,wi>f& ]G +=_%IfVY *@dp1Kg[ nr@kt3"Q% AYbd`eSKNTs 9ԏn*Bv U@vB8gvygE%7PH 6Ʋp[eFq( z$1PTh 3^N'+=4<|, nD0*k-hG>.‚:_qOEα8P!w)&:^&%9J%_$wMD&TA"ib=;ta4):j=vO/+347 , YEm.-8i5cՔrYM%TOZg@*޴[| η- ɹO m*v[$JC[u[`N0MbJAc$:_&eh[<-+ jJ/q9Y)R,,fadD'L šMc[{'#A93uaQ9/n>QQ}2bƀl9q1_bac:?jv;˂| 2EЖؑ0yf=s%MMqM~a78ܪ򪹲?)Nu2 J&Œ`~%2pn/@O_J+u홀V;*$ Km 6*_w ScC{+52 M(λy9L2 yٮaq7n:rDL1W7:n$JtYeU@>_ M,$t6S[OϟUf~Hgds#l>^i4_a_Lӵ<4N;19Gڌ)h>d84GeZěx9!V*ѱEvޏ^rvX'R5JO#(b>*>ɞv njv[_%-TVAt>N'ǤUUߓƪϛݘ_%%Om̱1U1 =OlDIb,,46U7tWh'qr(LYTȅg/ bXpg^se^ t^^z34Yx8[T2ge>-w/;ʤ%q^Fti:2AʾC]P/b.9V̼ooNub9!4ܝH>I V1yTT ]%7e;Ph):3 VLƨX%)܊%V,2&3g9.[n+P3؂v)Q;dM-Jm[WJ. |`:ŎZ;STr0PYy+ÞƂD]eﱊ4sJƋu`8k%lJjQ<98y=7r( Z%+~ Jݦ_Hf+bhaWg%Y./5A G ɶq ?3S#gӀ ٹdQr~wQV§ ;GGu &Zw֬)xO,0Iф{T`jy>䴥/](ubaV~El+ӳ1܁/"ɫE'ɻj%\̆HIVBͫ7-S@2|߷ ˼F/i-[Y2qZyƘZ;Y" es*#^VRdehU2b5B]1r8K=SxjVFرrq"bMõ`)(= c:5:`%!CE b[}͸.uMS ^Z2 $`טmZ& ͅs4hP ٳ)[^^܆ګs+oQ964@R-Jߵs.Kb*)=t.X)AN j,ćawrt54BrlexPV dXagh+څAT1\!a]>Raj0L)Rv,fʯ$EHHHV !0W'aK\DB v[0&*nILoEJ]up+h4ԡ3D{(ȷݐZ3{mn;kl) Ij̚bBwO2m6ҁVN4Y| 4v:;Z31I2NBh)`)i_cX-FM&kTbXL!{%Ø;Imv#('1DC;e۱:iA+uoN1jԹ|Xj}QSd{JTqt2˓$8Q6R7eg0t$Ͷ_,L8̧#F`#SU!1bkk}Yby&t!ljYE'$h>ojZ<H&y׎{pPl@ ny:;#̮f5PMj7d,)JLP{*|xZbV5x0yVO~T-3Pm$$1]FeZ)R_oi*GUvveoCX빼wi;&"K֩?SxpCXdkt=7tOGkoKyP~S\hy|gR%وywQ=`_֒j>/8 %lB#>,VyiE;H !-na:l!UI!Q}#j0O#Z utĬBAMHVbĢU+W|J[ܘM,:vbuYѣUk<ڃ @~X$Cپq9F]QbI} ާ)9`ۼym?Ļ~vw;h>9bNؗC.E>=sVSSYk͙XOa~IʬUaU״mO掁v=ۡЛp̞I2}ZmqH'tqXLfQXyycOIez}L"땳gKprPWmp/Qz>Nck+I_m= 蹖I0U9UQ+@sl~ZөͿYM i6Oq7:P\ 㴠7`Tq `9zYh;y ?56~L,f5D\lh=QK1YQWRAZEsW SaYZ.U_ԅ㸊iut[%"}5nc +O,)<{k'0P`9 (~'jG% WJ1. Misͳ8KBX?/{N_؊ tJ@&s"X j- [ na4ƚP QlA-w-$y]uAU]O?~} C[4ǫnN;[ZX{9\ &#va;*o+yA zA$ziYiCim"Y6]ZtZ :@<{[(}ܰrO*ʖYRm.W73$BԞ?tuT )X0>"1Hs~>\cNg;l -UzY:x|u=mmcsu~ԂV_ۧs+m-v̓B=ʿPSr5ᖕWjA7U/HI#["шK`rBH-8s{(Ez %S2 ՏZtef M"Te(Y>3hLcc.˅; WY*ETߝ*0ii˷ӠTg($s (Dlzu^`7`-ðbͯYaX{ ,[*2γ]"Nu4E C .\Hv!%(Ћ9ف1j+u8MN`"W wDHJDL &iEœ!~̰o3"eos*ƋR#`%>"K0AA8ESOzim( ^4ʅb)ђ6{Ϋlu!401&lɍ/-7G$j{PGg;^Z2EQm:VUtG늼B *i\GJ J[C])s&xc2RN9n OPm7c{m:ެlH[pm/+qdĘk\JU)*QxJ7'5!\dS{ ڊkA[szM6և^q-cZù w:W /H@Q &EO$Hy`bd:S65PЭcD=pܳ~~n[0O;fWHel4ɐ1듊 6f:&;}|b fћEf I/9oCMp+*jfG6tӓl2㫾w8,AFVlmobGݶ 03.:x#l^ACOCT(lC4IGPi[lV~f[j{`za)aĵϮ YC1h! 0} p9C,NѫZ$$yy3m1gTohJw8g{w~:8}?QEgE=t DK IQϔDU%P^d pmu@,_:&ƃ"t>U!8̩bn yV9l>٭xLdCw5gx̆S{2vLN_g EN:yAiꝴrVrt !`Q.Ss.SԼT83 AC$ ה.=4QaIi.?X29^mފNO=m*|vm}X/~!+lԡ>\lZS$aunpsҀ&ܬ?D5է9?Rl?T@YE+;WДs3af]qm WnD Á|CAR`&Q?[*[`/]Om@AbVj[:^o$<Xw5iZ^g/GpTwq< j sO bS{-zIX4NǞg0E@B}L*R1;u#̽*/ЖŐj`GӐwn(]Oԏ b}A> נY9{ QZ|QFqnS]zy:ҡs'm5ő)O[ "T1Jue[qeK ^~RLE !,Jn9:Q3uMf|\-_U6uj wǿ좫j5+oi {ntw1Fj-EԜ1ǠĪ f'{H qNKt1֤ ZpFwl}qaL%ťݏ'Gl݊$3TvyPVڴ S\ a@ `0'}{qD BulOkwLR)12Ҽ"SAV?rLLq  ދ1&9g!JRDtl() A&ήsǞλټ$aFYeBH8H"WP8qM <˞xLO1Of9.Dy).^hM]W/ )񈔶vF#l%Q2Mh}py8E7Zz~':q=҉WA 2] Hco񌖌G 2"鰖v{e#6Gkk~t1%ִ5WO6zR_;]2J&/$ontD({v3mP迸J5/Hs_$ g*vwen>fW_ɦZ-a` w>szUkq6φ Ҧ%-SRH 7Ӌ|rL "a`QW _Q6tCS8!6*q&s*y۰) *B SQB{r2\hi=E$mv[l(m'Cyol>zW_o[2Ih[U0E;)r8ud-'R "-lӁ8#+$~^._7aU}c 6FǔBO62n=9psVi=֭4_No㗃g-Ͱ0 մD˞}"{f3G͘Ju  e2lZH=c5?LgVG$ρ3jdv~{yUmJw{SzywʚxXYwmw&#{`ļ"0fVgͬv/mCBʐboᕆU  9Me3P@`9=+tw^= Ssy׉p [6WHI כS?Pl*̉qSbvڝ' )a.K1P^_F?]Y0\CHI% DL*[b S* ӝng(ka;cvm3ScL7LCnŪͯ]~^$P6`b4բnJtz ]1 8V`^NqEL1[?ouC~lz.kgttUW2)VTgf~)?:п0zR]-H⍼ V="*kzA"VCi\^v#=X5`::޳i %Dn_x!2]ZKtL=Յӭ殯0*Ҩ*vXF Nwř~Z]I_Gp]O. 7ۼe΃шpVm,@FNϸIUp1ڀ]]a{6I ʘD !':nRSR}R𮕔hU#N"w,P^E>Wxz2fb7A3EII)_ёScW4JA8<ϮdJ?^XXAĶs=;g5P M9Gvt;|Ll"l, ذGAJMxE?z&kSc)0yKREqŔV ˌ]܈}h^Dn+(cI>}rUUrQ>,3*ϋˇB".n1}ˏUMSG8(M<['X)0@e%*Q^9 +!9}R)vR R|c=?Wiy,vhI=)Ep1vC 0L dQ;#Lrc,I F';RSH[xaqi*\Iv8Oaz,ZzuqoEj)\6)ƓwNˇo}x_u:-uӶNںuҞ2\ǁ(͢4uޗ:>_/r_}y]+8q(7Y&=0SWMO__(y.#D|(vmreCyk9N?l֗⿚Ӎo;XHh'1O1_QdVCd֟;Evo:3/q? Y-dΉ%h0Ek!2 G2\o}ja8gCQ8I,ߕĪJc؁$/02S9]#)a2B;q''fiZdqY@-lS:_1KF}~KpGz(@@0jDKa${Bh`ۍ0="o/ 9le3i7ȳi2C||.\ofy&YhzTxmNIa)C)F)FY9niQԄMXgn@y.Cn^r,7=4jumλy%HD8TX1Yv^M'5o"=\):20(VMwEFM/zlVɴyЅ7Țm#UY]6 9.f#_p<ˆ9-'S+}KNs Hyv&p\r؉uk2K(VYނ PHޡ \]Ho;t:nԡ׏50J,.(~\I=1=.s/(F$j[uFȯE 9zU hV:~+NBN{R:Gǝߊq:7I -D,n?|o@ݑÇz_&~jWo[u pUR[Ւ-fgM/8=mΚX;#Qp;uE1gF?3sL?gA? ҷLlf=ŦJ\=)$gH.;jYh$ ی[?n1A2|ma15b%T7ԙrW)gZoс ?1g,Hn,Hv*0f.:i6'bXm֣<ÄH4o4G?HYgu/Ck >l\ :`oSV}.0:h4ڐeLe)IAIv:Y4O}okϻumʞ뛑fYR Ҧr"q*=@:ݲŵRQjΏԟkbsEt55>53yĂW-hrV ڎ/P@E#GWWo{ߺݶOPMHʌ(OۖG'=-)%D$HJ^'9uWl,3ZH`/jW}ņ&TvXŇiU{4$KxK2g; 0 zPbϰlh%݂♉-3[*_B *^)H7:l*xE-[(ǫڜUT㫍 \j"Wv_or'bgFsg0l1`',Z96>Pfdd@ <6WIA0E6uX;vmX[@f2[(¶Ţ!uIqITPJfC۷oog-#iNQJ,#O8IN?<'o«mƟnzFc?}'G1=Kߢ7/.?~O9˭JwϞ^>^ 7yT~_ex͗G)9jH9Oӿ4 |ݛ4_Ro^eȺHY;mZp*bǓJD5ҞU늙\^V8QG'OOgϩ,bG.g>G:g Bz7p5 r"qapnBiS-uvFK"Mרfc/L `T=DIƓm&JY]g`gF6枬lTѬFʡZPB)w[kdJڹjwIuE|mJ&%m .]qU~T1dtobnƬȿىw*Wv%GVg%Ү0 !/T?.b&u5+5QՏ;r ѣb!vw-W>*Pu8 %|ZmŪB*AP~N'Fg'dėLkFCFF{#絃^v)xj2]NPקo YrK3n\ *ZoNOoteqs@aIݫYRe_P;<wt6sHIi_,Hb-ФURʄl-I;n@7h6RAϠ+gI㫴lz&E,6y6 f$m]?谪O񻤌[ժ!W>e,]٧4.~*M4v6H@y!HZ$4KcyjvV~V_֕lR@xϸFc5DZLTyJ53Z=]튉}2Cm *% x HߚF+k(_|IO&/EВM( K%нW2ޞ R}ǻ'x>G&"1AB Guw\֘FT+DG]lL h c R]EYIg2 fΜE[=rМU=vϺ.3PGet0 d ]ۺکdѧR,:0^\u5*up9%EsIJ,ܱ ZCbKǯxsrkMKCiYǀ_:.'|Cގi oxš9v6:^\=VE]޺v8rf 9)|vDV•=Ň/Eg I>'PoJ%ٕl9>%bN$9+|WLdP$#e`푨?*y׮v`0Js8|6X 31=g/)o8>@X↥LY! B@'J|O GjXMS*S}xԍJ訏@-B "%`68 lW%aQRd9dTc-rFwHm 7jmgAT'AJhx]2ٻ_VJG ;F IfuRVI#ꩥ=K˔A&'x)q${i+cMB:!@#5uIǥO&i^9kMmzX] d/=(ErN^u:LeH:QyMWfEx} FKV:Bd]H_NoK`$.R0rtps~ENp*gn-zqLȫE63cѱ58?kWW~$`ֿkmf3v+Рܗh;Gy5:"xdɞ^NRfҎ꠴gs! HjDfN>P#fE"Hn<^EtK}8%nuZ_A i #- ?C#䂲c fB\nku;/Dt%^ꢬt-$!I.0j)C=fT'*6v ݼN5Ŕg2|vg4V15m\@54 -]/y;Ƶt\5KN&TJN؝>!o =PР̤nhRU(g u [ΖaaR fU9[SgLy姬UAhiRꑓUhEꃀO=dBifn9:x&j؜ҹzvw%1;2da짣I!,!z%wP/~ű ʡ&3` cQ^uy.|{$9a Lj2Y)݋-Hs;]@qJʁ*W$ }\8%;mҋ # \!/Q rY~A[f>3AuM 6;X3%_AyQuu8 Fotm'ʬNB s`АT ?~ L2ܢ0~ӧ=nCdXb8B"Ҕ ~s}#p/IPZ7iFj:\W4҃d>2Uc`\lG{#(<-#GZI;@MH*`AyRMsQӹ`-5<kr5`Ϲ ="eKv9Ŋ"..zg۳$#c`Qa=\+l<+'2m(4aV+h:: R?\O|[RSܚ‘z3<|Jx/ ^S9hIw;OF(-$0A3KQ2VkT.?9frmG(-@b{Q뗖\ՊkH1 XN V[Wm =67(TT>$CLvkә\ 0%Fub&bT31lI&)Hgz:[Lȓ^#y sQCk{~t`/C>nC6qm!J&J-*f7Pr4zkZKs=MP& L~!D˱}Ā?9qYSVCjv7O˂-jbv)7^*c6 [b||d( ڷc"VBgLˈ=@03tعR!5 3,$zΏJnR7؞Uekq Z7Vzc +,뭾?[Q`Vr1 Hxz5` `jӔY5ަ n3$J!+jm:t'ՑC#ot[8)iǭ!vMe 7sSа *'WP/oŗf'A%ͮR_n_{VݯYQ=b]O[TX- >.]]~~W]] RËrHsMy\M0& A6_|wg?D?{s|s|G?Ä\Wf z d}ʇ/^Ee*A Τ_΢SyVtA9hctk{nڶ5bGCˣ_T }{dҟÏݖ]>i9I 0ĪɌ`n oHªJȚJHU>Хi+xԓ%/ R'C!`֨1fXD5l ECf9;5#,/E;[\h[&;,dp;[R^ug7hs>9ls7ʌ_(][[AQm Ikөz̵ZڌSG׻Ns7.)u}݉ye/xsCӡ/A`(@<,×a&ϥw<*Y /+/r^䮽c>U;>;C%NiH54DS3=moO%Z9 y :p)&b⭪tUgmv{(xstejԧes\:x V./-KM6w2bIk};Ж^t^$Yx&('&Ά'q Q' E ;u BęEN, wER#)Vt8Kd6SߓE]2۰z#D-y,g*xDΑթmLMHP)ʵP4Pt8tKei&?+_}hyCp2okaSYvf@ 0' :7l *BfjUJv]j&7*Vkmy8m#U* @M鑓 Rs~7QuRԙ4A{Q. y ԵeRM\vEP?ٹ#Kp2ȁ>xWG7F3fF#<:Ȝ_`x+tܫ)ssY`n2EKXzEM[Jt&|-驩'Gݜq3':s clFof0⮼Oם]12T]";Ρʻĭ\*XbT Mu +{ f_;ᡸ2Tj:aX#u9,Uu7C?)ֳbb$.ܺq,oOQSX^M@֩N=J IC-XYĭY6P`p-Z)?{'Z}|Bc R|nSf>lBh 6%ME #cKCRQnlh5\ѺI,Pؘrk$qΝ훆׺0>Z HZ^cBA_%BJ2ЊF7E2^N,oƐrGk$*e#0^\"[C4>%]OR8U)LSl4%*J7G]NO/-Pi䇎 k\QM ); H9nu)ƒbǗ,b{ 7Bpߨ`6jБ"[@KF$KvZζ DY]jr|f^.o{Umn[HMc~k}ma)a_=,C)'xe-lq^g*ܥs]׈Z&Yv$mPn;^ݾ!ޜ^/qg+qM\Ѯp˨iu[[6g;^_$Zx-Y\_x篧F?RaGg{ txGq`ضmefih ^LmZ}٪+0#8!bkR:Sk`v,}f"T)7}(p.~Xg%(p1pJ4?C!S6F8㋮箏]xwv_TSmzgYΚN۲'!7n$Z& z,.$4 @B[N(v!N E.J%ᐂJ2ׁ3Q9ϒ.ЅSG]1Q =+={-AY&-(^ݐ8=.ȸh6M:ewDiGm@u*bw7cM1]B9hZ k|WLkh恉`Q:&B&ы6`-fSSo;J̶S[ f"={ν t AR&QV#g u)@Q\<@VNՍ?޲ R]Lq1 P]q/E6quNUxɟZrBPa:  ڛlY߰ضYVJ#%"qElF؃{S }]K˩a h1!%ڢ%R t"ju.šV󮬜?'HO{M?Ƴd*ZG*TK~f21UsƯ*EŪd3{.2"uì9GH#]D=hVsZbq=IwqGp4H\ S3N[CXH~6țw~-;{0̦S[) q3@r?49j5Φ 9V:$%$_aSZ&mW2JEM,g*5b5Q< zy)2dhjZqGQld2*$Ls^>mC]y,f:cxd<$q}$Jd0Bel=r*µk Pe!*rQQu.zÜ+()da uQ;(Cŝ^hpzo8xe1ZG 4uLF5D.ob;mHy9;cα{RL^䋟uuB꽄ʑ*L)DR+Siy+zgF&L:Isuڀb%/Q)bV %ٲRQ[xLP~G#ƓeU}d8S(z6zA4wl;,nbnHiWeĶ\Pqx eKja+A<*G ~]gK+ ŇA+N+ԫJ%8S+`b4޹?,F?.q{ggT+OͷJݲ*u*-wmy U㻝!mMdd"1=ΣaS to'|-Zsӷ`BZ- V?VM/:<vu'pXO`DE-Hi0I_́tE|䛯2g\DAՖT/V`XOWl-ƒiF[c} (Qcjn-,'dH 8`o_yqY<ڏ4Gwl@]xDLdVߋ:peuv/@LAsyǓ,^8ZTV;)7`G*a#XVRI)>ϳy|SXU6ȸҍrmT]OKvCX!̹̀%lֿ\:tvܯֱQ]hFfγ š=ZYV6Δq-業ܣI/@C+/:a1PD*:]DR巊 ]GWzz'h%PZwDt7nG SA1P>&E6 Ԝk?Տur8d:nuw+O a~`[ZdptpŶWbS˽@vaLdB [WXq2qbF]U <1AuTGF *$4x'VD'5?7<ֹV@P#eZiG-l4$`e'fipk(luVinԵ F[ hUd ~il8ûmRhw~m :P_Z4mnn:˜ 'DY!^] l6P5?'> L q(d SN}r8?Oi~lOAZ jTd9b@HYNO%P:ʦJ!1 nOu)3Cf,v6[J"P`G!"NߴnK!Ւ +:gX"BtH_fx1-L]1cKO~:WcJ SgËi;j}diB3ON6g8+ rD)S)W"15iه*I(ލ70;b$hG蛤 @C}Zsgc?6Ӻ- |_H+Z8E2y\\Z>{!< t Fcx4p=G$;0kwn? +x< _q B6 qb6DV^ %0ڹml@m!72Cl/%p yl9H&i%TM ތ#X)#GPq27U^v><FdLfNc Ie-Hi?3-Y,YXVlO4zO:˞tm[:a|eu&U?@+BHƭ-X`6719<,7=]yU$ˀcјCj[dֲ%rItVwjs㼕Շ }C \+\a--T^9jjyeUwVW`S-AD:Օ@PXZB==A8vhHx{A|ꃺ ͵@-QVyct܎g0+he˅xrjzCOK9[S0a:/E6CİML-#"ڻ7d/UeJs4Jpҳx"(BM2 .U<%%S2qJ-8!p0hgfWа%<91dH<Z|.gٕ>1:G9 8=zd{wF3ϼ]JC("E0M/WHz;aʹ`7 FO몥Z`tKA37$k-:C2:N!]d\k OpFa{@@š'0hF.)QTX*>^(jnl1iN$$gT|Vu{/I6YYȤ-9 =JNjl=sgЯ$q ҩě`*2ੋNcDƩZs:֖n5LUH%UٌS!+.:ۭ+ g)}uaJobG9ȻB@&b;ݱ8X6;~[k ֔ܣL,fe0BTk+I@$>x a2 5+'k6F؞,Fup 4xPJ¡_RoSɟ"a$XNO~iAC1D3N3\jH'Ƃr& ڽa Jl(F^aO`ѸAD n3-_xSM+AZnR*i},Tr Z-,NIGRY${n$G=8IrHP;t u% T;%Bjk,S80B5A~op0NLp4=5ժ'[Zaw=L?بۡ3"S" x7Ѕ!,҈0a%sYrS_dtOZxǖh[Y@"h1Hw*hQ-7y4JY7vg䯼6mS&գ倪7ʹ,OX$I,U2w) /iJE(Dms&^-n%5W& W*ה  &뉊•N/Y}=i]# NC$WmMT/c+1#sNZ4%S .xNxx>-6Raˮ Ҹ*0jcIBIaF\,uD|1ƢLQm6/&1,Ӹz1*N~$8;5x*E&/GϱbY' Yyc, J j8LgDl_1rF}Pdpl^p,"p[]1![[INekn9~{V 1P\w5V`>.x I 񣨚" N},MH%ɐ:P7E:Fxu'D a:&NߤZ)րG#+z2b(%}_4@K%=рLЙǎEw*ޭ:K(#Z,=b(T_ r:S9bQ1-[AWH#mu'tFЕqI|dP -h{>A(JڌTc͙i<0ʼi1Ig%TfN"dSH#jQr)iduiV,E t-} Vz`w$ -MXydś-svkWIEq1!F@{6JӍ֢%۪^*Gd٬ʬT7z*;N,돣)0vb$O@k)Q3ZYdѱV+~Q , oǛwlg5kʖ['}4TM$v.q奖5}?"Svr8=}klT=c@.w jM0g!4RILϿDؔ\jHu%0־ ZGՃ}Οl 6ZF&Pat yB٘%2RD[P{pE+$5ITz>Q9- [rv2k<~& 3Ha K Dgl ͻ?ۿuZ*@N{OFڛɝ=>A]Gyu99*o~{rj %oPla٘rP7x{H\I?j؇^d3l݉mQ<;Gы/8jˢ&[o!w<&WЅ@s#1Yu,2&C"r,Yr_x״S{,tIo+]c lʈLr8KO&stMEJe֏y[oLIQ@"zyvGlgZ+dV'h9i-ZVop[OZy-c_~*72)!|~9ebO`|$NEO(hNвK u !׻`)E&[@EJծĺi(2 ]z1dX- ) 71: %yF Z*"y_]0,ۺ~g5 ! gxt)]J~cCTqP"FV.H `̆[n;4S=-5FjV: v(2o(RcM?g%]nty5I嵕+lʁ^wPn4BRd^eZB*SetTȱL\7 s8( «qr .a.4R>OIB}2&nLqlȬ4_cZo,B*') -( @rP=8N9 N/G%Ս@,@aS26 A ð^xDvrudRԛ!.-*t[٫T XB5 [5Au ];Y-:[*&LVDq}mG[h;vҘNSDg5NVuAFpsH,!FP?/9~: D?p_@O b~b!)9*^A忠v|twD(L*iM|&="y c-%0Qz#q9Z~籜+hEix.~ .F5KxDS1-Mq713$bey*grEAcW<-%.ABpLIGFUC)=W_F?:;-8d3(Vvz;{}{~Z]-y-Gwݻtan>ʖB(P7P֪Vn[*P[VS7}qŧ|x(Ɍ]dWcw,%0afe1%íM9%~1yg 34gcSQ5ȧv7Zl:NŁĠer&($g %KP(!.J03BS͋M; zLj 9> @;'\L Qϕ wx`FtlH_ ½-'o^J'Ri`k`:͊DKS"3w&p_o%IW4JNz|@GJk,7;tXPK1xU6 t ?#-pkg_resources/_vendor/pyparsing/exceptions.pyYm_iKiX 7H:F jE$%wf\.%|N zt33L|HEmJO&yYW0%'ݣ׹N&U,nL^v&)+r)쯬,Z$(ؖ*Q\n<2ё5Wv\n&IZp٫NП+dׄ \Χ=?_o^Eb#IxQlJ͖w!&v{#.HnaEGR@V8:/D{F?btS8ހ =RkXЛ٭΋f&/uvNF(X_r f$r`,-AMjgCJGlGk'Ù{rkƶ0KI&>X{bZb(6 MVen) > ׵nQѩ CF 6G~ b ?-OAL9DŽ$W' Z!I(Q;JB#zGRBFÐ$W80A␨ $%WHBR_dE|6t!&WزՁK\_M'dWeIyA  ѾQ-бFDiwZ^TŤ 4ߕu^W/y dh.qc2FH <žbtU$Z\vb;On *k lv+Ib2[h]R"hc 9 dTpS)Xzzʳ^L|PP tq/׽ǹݡ}c`5[X],^\+'>rB~c{ĿV a+֨?L8^!qFsֆ:=+]Ѧ!]CY Q_"WeP 1~Sf;~zHo%cQ rYݬPB*o PA)V;MaZ -*.dW-0KOH(*Cz;U/*gfV$%oP6bwj W-5CG=>n|l/ԖHQS52k۩}(N 6VcQA/I)4@B)2ÎX ziE;h1Sl]⭪:XU:x1Mi ]uk jl -^ZZWl+L3m jDǔ AJvȏoZWZ E%{ZUSb<%OþCL# Z0/W,}`Ыe0[ /lA!Vs` DK:y@( բ-N::B*2="˼ڱOv+7c7#;QQXb5#kK[ES\U,@mZ=ÁSFclAt=)[7;EW BXn+l߂Ϟ,NŊg3:Foa1A;)$j?4`OȮ6yiZFtƯHa?17[( \T?Pk="@$X)2*T'$h@Hw8\#H qvW;=\z5[=%=xx]{{ѵ'M5@qPK1xU*,٘*pkg_resources/_vendor/pyparsing/helpers.py}w8+0irU,ϳwwe\ٷ$fLN>IJzvޝ~%( B |"nde5\{bYPuZ҅2ͯ{yY,P8uiQJhG43'UT7ϳed5M2.I~-MRV^IΊIi|h&b&trgiU=eY/yZU];"˳L.`TD c ;-4#1) dG4) .kd)SVv_e]\IAX!wc g %@"bN$UTbO7)u*+tTII>*1Mrr|&ȡa,*Td*KbƺIHj^|efJbJM\ɫ+0Xr5r(^ *Qtu-y$ ຸ@KYh@3u?.o|U4[9w)L٦ ]T%͠ {T:qJngqnmG ٗdh? E9 lyTQ4\"gL0Hd2NH-V9VL2Y[l̍Tb~Ԫ--ү1Mk(4؊QP/ ɌEͤ܁,e2C 2ˠ'_ۦZ dL(_i(Ɗ@1fG@P:3ݤT*=t@nU6ZG}˲H 3- (΄#atkY҂|)!pg 7iOf4`ArSC؛ (&[(ڏϫL,YLfGZX_<(zf]6iwě$Ye+Ai2V*Bq"s K1( C- 6\,s `P י1Hg8#X1QB, ]/ ЬpTtF C.4 ܁#N' o/И?p`,K4K<ܙSpG88?tL=+##Ӿo@gj7Hl☵( δꉉC "v ȳb ´ϐȰLXdʼn͕uX`9C(aUvt+RGiVv>t?6&c(FE|ui΍f4'`VȺ`yDQufZ00<׶uPFH >Ū~a<>B=HΗrZgkUo&cԪVfGИ9 ά(nQ \G|(pi@AKSӲ=f1* ,:gEM]k`# \ `tnTtE&JآCT ,HBvr)ujZ]fz)FW "Y3/ v:oD-^Ty24rE&\7橕e1P[(oKyt J9Vk0/I|ht׊jY5bc۵R`5;.EE0/I7ҕ"8 1!-M%%D692Y%%Cx9D6G< x}E@; &I0,\Ac= a0ACkP#\΋ 0"3 n93@̓ TZ1x%O^?];#hy}JrZʄ^6,E|V9] \4ѡ@@n1 9VF9d^cKª3iR OA <fUŷr}"~cAotzk1%H %:|`{Tm@(w%]'_8ίeUܫ+S$:yN H =Li9)F\=Jk|#tE:x qq> pM(ar $v7GR4vIG^55 |:ktzUBВ*2 0G*`/_u8kCtDs%'@{C CPˍa2wƦO8WW wLSIM%E%]3DÄO-*kgW 47.2.p 1Ac8>ωP+7u Yv=xp^IVEm KE GMH=P3qz4>9ֿqIqq?EA0㉚):|ET鈊SX7tN;Àxj+YaUssg4F/ c3vPDBlc|`#TQk$I' (h+TtMv_* |bzSܶGeK2`൅e6>ORX(Oa$j Үq8]u[5ۘjC@+4GC.I NO <2?qk(0}(oGu35t֋I&lh{qiuYQov>+ %;mg]0) Lby~G9;_WIgujYX1h6|S"Ի'PN:"8C5XCl %tFHEY,nO JQ+9Xdl7@Ј'<|PaFԤ™E Y@@^ S+.nR# Pm~ ot"_=H}w iLS mhr8˨!^Q?VvA~BU'`ܶk* ygX^ujĬ۝1fS0,++rr7oߟ8pJTOu"tmg#yqk7##H}ňt247FX:ѱ>4bT@nxs7'L, >Nf`3#\ esT2{M8Ilj!>k"j[ kr*$TkTvg%xBS\Aޡ;J> &Xtȟuy#ɒՂ{ ¯aVܡѼ{Cn*V~:u/mڳ+%…,Qdb'0 bGdf`f`@*Pa^袴@\- [6Ƶb9 r=ܦAN9[\ڰ KASH(~s5bUך\ ɍ[ŧ $ "9R|PVYX]n_B?] +RCX-c] ,Kڜ 3DST`Q%hh{U>W+O :L D~CAZ~FD#'%k1w\158>wCn@ K?QԊ=qw ި"({$`e@*D ;t1%]/ m)E)"PMޡT1#`ϬɎ O,Ħ2*?PJį=n3)3Z8=Y!KnU 0gRؾzGi.r3ST/F=E^y9%#;ߟ/vч(52IȥCA6'O6m+ `JW6U RӁ\|ׂ !,X%wT@5Xsrϰh+d2Y.yMT 05Fb@e3C!g4P<%V3m2\4ʍٻr1p*?&ó=$Мi)!LpLH-B,ŇB#S},2047qgY1 YPOl 8M]1mW|0wͯ:Q,a9 D*9p݊PnF8]1lڶ6ѧ _:Y5{S,ňϲXv k5+6Ve@NW5rFԙm<'^7[Ya{C;d%)|}Ry=kCSȋ"G%.UF0RƊɵ=>/ޡ#%JN ˩D 5#H!1iNM<+IRH`Q%>S Ke0!z:ΉE9s>gTVюHXP}`kiےF ĭ5wT6>L( DF_]@۫U4sj5M729_aBΓIuQq6i:8+fByse'vaS&#[cOqo@a'g uqg~ ]óghC "e.j̓>{1}*@~Nk0 U;Wch59X6 % .=`CDP'mqOdSn£RX mU t=B~`iL::;:'Z$TP ;DT mˆ:BY"{3k[m'a4{p=ghO=G=O)r ޳(GGTnܐZ1ik;3HKOZݞ^9q{磂^*5aYәE]7 L|z&KUu8q& "od}'e.6(39inhY?/T҆f:M.VMRu%loT9r (; Dū,|o>:Њrow`!\>\ hŜ״P|hC2JFIr1Rʼn @yV̊ !r. nv)}Yw.lc&kvY5`Puz]}@_a?-xR05e1}v&1\Ɗܳi<3#\csx -pj*u0ﺢ4c#(C0yl6asԎoXS|0C^)I߸0+P|B:ms^ Po䗐vAPDz}?re K }B8xyw3nI7^/(pvP99p:pq?z[vpvrN{x@#1NUջ՝516'OR#Z]u*0Wh[=a k%>`b3Lumcqb6H,t#(4ƑԜ h98lޞ 5S= 3l*rcXJ N٢3:CU>*{VcPzUmP&$mdG(@S21&E1/~Q;(AƝqoT|pw5F6~UJ' ATPܐaxǍL>-aąwj7ĒE|ՉS+N~lhԄzi}nTjBjC`WL͊ }Yn CN0dJv mIWW4pcpGlG&| ֫\}D6G1*(7^Vke5\.Mch?9XkZCֽ1&`fZ4I3 dpI6ut]c 8R}6 H9iV7ӦbڼuƐL':NoS_C'fg7=(=hYPr uPųʵ~uxŪiԁV2s9 3ZwMY._a<$T߇;Q6n+^-cG5,WU ؈D;Qwwp P+zb> ? D_t^D¶xvi[ >M!V/J0!*UHi[rvÀY! ':ex)6OC qᎍr<C7|j-4ufzS'r0&?Ec8iAg=FNo=6h[v-mIB꫇ROPpQE}.7CWk9K6+V []^ZؒJX@6"!KTBWcjCy"d^.߹TOm%_+5hvvQ+Bʂq`K!}Fn?&)>kEhҥ_m4B2d\EѰH;]-?iðklبT2^d߼Zw3~JmM 9B#y^Z9HDx#"<ϼgS}[*N}^j$mdN>%\1hݻіѤ-5o ]Y~W #}S3^cPFFUE~aNgVn.8NZd;pP1Na[t } X/jr+#|'?F-7(٨Q0MifPݮ׿aHuY|ܲ2+oCVz.|z| 0O,*s2iD[܎Y&Mu.3(z7+_ ӱ=`cO~AK|=)pn ;x2FY^px12Oť=}$xLmpNd2Jo&.3&|qiўjWg/TCj{ޭj)q[Pjz,k %N:2YKijufyLUdMPX$yr-= nCtQx!@sX᧯IS9Nɮnp[Qc(4 O-ݸScK.RW%H9~wF!XŜ}tdy@ӥ)DUB:]=nZ_c{$o%RzsF=E)]Yiiɯa!شv@[" !SAq+eY5Z66K ƃfa5Hឡ95p?ZMKݧ*L܄^la#Ǖ?it*ڼyiz ۓ\;IUM5,sHyx -w6wsVF35wggݛX.v+\pIyBv;<"h2y̫bBKz6H" pש=bFMiݕtH=Q[C~&1ޜQC !pz| >YΐGI5R o#__q6DʒoAkKrew(zO`s֯'䢽5\Q7K6n ]K:9_2b\RHB$akܙr} }ou J3tbZ+"[ř6f}4\BPs# 'w00o7x7 IY&kgyߕsZ;9zzT:V]*r+o晞o".mݥ+.T'΅{=(h0'-EO0/l,=鴧v.TOy:Rz}nZI+7]uSWh\};R()?ື ۂs@YѨJ4uIPK1xU^b*pkg_resources/_vendor/pyparsing/results.py=ђ6XR"m<%L]nN*>hU%B3Q 5M߯j8Wu{TF4F`M^x[yuVb,˪fmd&}VF" >6XP*B﫬%l+@k"#B4 +b(3v#6LPձ"oy!QPAMYe 0X,)YY}f#x=`ıZ1f?Z\PA qVdu盈u(⃯'1Tf[^g5ߵ3i*ZluRstH؍"2n],_-Z$ FǺjO5/E.پ*Yd0alǓBf|. N}e딥I$Ɨ 3Q`y(r*)T@<Ҹ̧2b^OfI]W٪jɎ\BU9fK"A}w'@جT3dqoyT{YAD8!"bv0:Mn+7N(}@гC3R6{DMQƇ%*C?C] *_OA 7 P/y)&c zOш#Wd`dY mJv&Yi}頻Dvt,>;&.PKE*(b(;|s#)[yR5EzRq^]ͯ7E=b0_İ0$MVi€@SdC\of&Fu(j|-x.yL7T (*U:6fdw`D`\BaC{A$+xz*alӕL0g (Ņ ( ؃m9R?Us}UU5 rrYHH2')=[VĴ\ıѰ-s3JIn 2[;+j鹑|}p6@@Iedi0 UILI;Ҭ21CE)]v, A!g& @W!fE䇳Ӑ<5W٢Π=i>pAZU 3P_h>n9x~1< Mf=R h5W+m9L((mBC`YRlԵV9;#,O@"k]1nnuHS48uvG?܀@cV>HCB vGE/^_A3tPw'll` ] ,Սĺ{%0AqWhlx/tٍv&-^fU7~C-M*6 *jJ̊NxU57 &$ C=3`&/b6i!fNL:O ;-?P4ZkBbvƨ"g. NDtϛr)a,]K цx}WoM#> dqB\),p,GmqDLdLLA˽cL?c%+|m21 $ (rN:5{L.^}2@pKD~:[K*,,y~شp~cQb!0Ջ6ޫ_Q mFZ(@OR =bѱ Y(ZP `S\`W<udX3tђC?z1@9} Oq+Gad`L&J-SF;nNt'N7;s YdVuWlz{!#{W2ysZ4MVI̚gSӉq@L/:ahȓ5͒|*T/)noy* ޻wu j蚅VGX'4%a48|+N7t#&AʠBlYI6U{$ϧu?^| z\oָdČQimjzõ& AG,{e{ҶW~kn )3ύ'F ")goqXk V, i?T]d #26@-Ҹ … DZNqSZdm~8KkS`be8iWӌHqTLp̚%{oepc+Iq- iEXȾC퀫mvAe䯯f}6Y5q 2*5Nn.nN]\}c,v]'j= "ZO&U<.o`V/5 }FP*2_Tu+jy.5A;`VQd;~y3܀stʽh3p?h^i E6 uBK]4?8^ټ}A"多`i=31(E-4n^=dqHY9^/X~/?~W߉H4@N=/R-0} 79o"k.팳e+u7dsXlwFDOtA"I$q l@UL#j)]o_OsIoߞ#j KOg& 6f=$r4ʷ,:`I^y8al%dx&X#ʟEQ G)hpp7Oˬ]sux@[|+^m ~{L,_'̀FtFV<vaW7D_ۚ;_?l5M#F/Ħ9H{0nomCGrߠSUuV )WD )%GCJj_r{+T/6z| 3ǧuQ 13YV?˼M;~_LW rqe_t*~R ] xf튼U.n8&a,:?!YX\HI&0"[)5ld^zch<W9]7{Pؿ&-Sy;_6CkE)-IaDFuH_fjZgDŽyg׽`])1 Ĩ$W(VW9#WM _e#-,V4Xx|y`=MD;ӊ'56CM=)Vʅ8=XǚAs,'c=Źs1G֠l@EٳJr5INnE6ԆDE:s)MNgx \a* &Hfkr߿Oy_ө^KEZ_2UJJI}2=f;؜[Q2hW 0|JIG׾4NQ] ATҧS  ]-(ˎ(|{ڵXM`̬_Ȕ?emQ :$Af̂=o٥n%qdrxc]%6҂K˰POj T˷Z%6*w:-z|94l|a_W)2,~J8W'#?Ȥ+_0gª0gAi}α`}`rvf#*øm]oz/f_Jƫ&eu<[UIu|-kN]2[hw~ִOLY:5A!i:@Egza5FϰS3"x3C4J`SٯH}5l±*]çaGw {@X/hS$,VQ)VlUMF>C|{uƆ:;B3^j Pj32L g?ї5}۪lͲ.e 7VⰊ"1ջZ81{e[eBe,wϖqm90@4BHWe<{:`$TL' ~>imgsINl]@iV 2#smʆ}"G#];-l<*OtjoU;;hN:O} E\TF+˔e,˻5/r[-DDQ6ڻJZ}xrrTRK #})gf37%Qs7E~1A6MJxNn+*&_tDbQM[A14J m#Rd͐eBnwǒ; oNh ߒ4myꦂz1.<F+rϜmh [ hEUbxŘeLJHES" eM]b/gLowA Y-wcKk.^OMIpBY6ցM\J2Z hE3Z鞈,#q[H^<9{CтY 2"?1;*B͛ V l)a_opbxOS'Q^i;FN>r^q ^pW¡+XY( /Վ7)ų爞p6'WtNNǧB;A~QpZm=,F- pr( PX 1M[|5>6{$m}ywF?|/|4au~{{{3mHyx$=zfMs5W2T vu] ՕVÄuXN)Ke50rcG$1͈4HSגHҨ9STn\d85q0DecP٦ZfC CVGVk:cQRQ_$6F*BMx2P% B7 vV*ᦽy,2+ ;/^ TD1f~pa }}jq!-؀|{+?e`}|"`Q GƸ( :jpfhg#(ֱ`eV YCWCg]WǴ _9lCo!^ʞ@$w\wX$Q\l3VDNˋ*]26t d 6 ѣh /ـ_~s- Kr K YY&+@XTwmoh)Q@6d)Y^^b#V/I!OIזs͓tGk@N:Kd*z} "%BhS)/!ֶ;3}*ÿp) KZ)[LǠd$a :[T=v g ܿqI0( L'`1jެH]@3eGP~ݤȰMX*6-8R ǂ|,Q7e )k`8JF\L?qlHfRg"DqS82#mV˄4Blx>=Vo`&H4q̠ҝq8ҵ/4sv} EɕSV Z"'|A@>= A[K  u-zlwV' 0"d j+_qU~ͯ5 _a0TbA]20 S%^ObW4i/<̧Rɳc\Ba֍{#OƫBW-j&^$:2x#nm!:KOUnr< :{׃=^3h La;iϓ+T? #H$T=<ܪ6$tމ'@ph `4<EǪn2ݺ-Ee5v6p^ҝ"Tå:66.PCs1Q4(h8<|8 -Arw~Y~x%OZav/@|W${$s+w[,O5xMtk(Ghu#ϳ~"J aP%!9φfib)JHغnUΔ7 x2ViDE23d3wqwJ>uS"v2VrIqs 1`55%1EAB KE8$C! CGMxZveӗBBxUkܿ'ּBi͎$9Fv ob&%J^?~7Yh6P2{"(xwP_1A,V>tS-|aX4r['|/G32kr oH$4)~{NM#b6WfϿg׿Y<<C|ƇwRl#%Q:L\-,޵!exryƍ/HFSȳGnPČ f,TIlvƷ 5ƫEƻժl3 GxIq~4߲|N?X=_x>xL/}]s2 l GfLj*3^gaH+!NJ)aܽԱˬK432ﰞ ~{5W˴C زC. .HgCntETs /xs2̌kѻbn<ȷˍbK "Z\eǦ?s-S+,Ѭ |92罩SS~}`Yy¥6~]uf6 T5A uqe X+1 cXL>_/l T>jnlHM^`%S7 \sW%'9Pq׻O@傧[ 8zk Sͺ_L sX&cZ$L.=/%Fx )job fo`ai-wSw.Qp9rUۤH2"PK1xUzx #**pkg_resources/_vendor/pyparsing/unicode.pyZj[x-#Zx<ݙYMU  Mi 4-M[$@KRhC~_/|[UU?5ǻ6^ig{=9ܱw%ϱ-;[I$N$X m!NhaDRڎ Cm l1#\9BDa^v񜮗H$, pWWm@ʮOnFHN4aCGv`ZD3N?7Mgz~H?7F{>~B?MTd4Û Zω-&z`}yhsl]w[1 5HI*j._(ʕAY=1,K'S87 lq{+=X:ܒC[sje͵ܶ87;ձN뷾{|Mvv'}P;z]PӇnmFI8f:GrvfXX:lY ,[qdc`Ȱe 1#g #J7CSUϞUǀj߭F:ְkڳYmTJ?Yzzi')<ʵ`"W6J%IQyA-k(:~u)U jRYQGEF+R+uZ1vLRVe*F˒Zè7$o V0SaC,QB(KӔb^ƨ8Ds FQ&Q {+N[ll^S;h)Q59TeZ(v*IZU,X+i&[+ZE@@1 MHC jk&Xf$o IڰLT6OL lbK[{X3 l!M/6lvuz;sԸ{o6?mCQM Mo&Xgeg-M~)nɩ&*"L,<ձ "m ?zI{-K*RP`B.a".$U'_ԉ'`^0gql{QV+mγežDU%3c/䝛M9>ϑTmq"70uG$#/>׾8}tyQz|z#<õO=\{x>-\|HX~{7P~a~tuU!饫O/wP(؛aWuҦ yr'g>c_;(Mə7~i1#wۨ#lwn]XMcZ}w깍;6V?Xzk)TƠ~%PK1xU-Gl'pkg_resources/_vendor/pyparsing/util.pyXmo6_sH#lz_z&˵@qmޗlf+*I%]r.:e3gC~$j:qNR{۾cKRt5)i`gt!vuhsS'LukD6 H6 .Dz0> HŊ$IuSd˽?uK /FdS4S[+w̰k[(0P"<)evÖ]Ygшk߽S=%@J2)fپAg܀VĂ ܮEFkq+"?9 xdH'EvbSãe雜ʲNlA[0Fe=XH7G'zQrej'>{DQ\W0kE76ig>R3څ 0V2RY'&\Z:=a;NNJO06!7{ejBP |#qFdDya!z |WLa zkH]RS|޷"3H]N.$E$QHpV\JbiccF|uSvX.`yv89hs'Vaz@%WNn-4TYծ1Kf8i@j*S6'ް77?s(p1Oy٩@HW'-ݦK'}_^\>bg<5H ݋yVл~:wMkQx 8QxoW*5#X$!q$cQSP eh#?+j:V1)We·`RBj_kj#~a\7 ej> =}hГ&>`Is99Yl;MЕ _;KRbXRIW-*:FlZkCxpݗu-N)+릯KBD~qܔ`@wK1:ʓWk۰ك[ $LW8F6Dsl@knWM5%  % F 9]¸ûk{W5 DeqѢ<[Jߨ~jQ'223L??j.9c߈BP..Yc'V@$ߘd߽|=z-BV/ekM*+m^>t;;gdmZ=RfWFȔv]mƭ3!$ѐe>)F]~4ٻ8)Ua|knlO`Voq5%6P2tǢ)4Xvۭ%"ڹQyKq>rT >.5\[ _,5]6hI켧rG7G.n]y".s: ^_5G,u\F g Z*E+Lu`LSnnpR +?l޽u=cbIo.҃!InVvU!|k'XH= d<0N18Os$mkuB@KARx L/O̦)Sf}Y=*MJu-ϲmUZ{򗟪1(㷝lWj~Ѯ|)W:$}kkYt'''<ޙs6m!LF>y?]x^e_ȼ|I}QgO_)~^nWK$:zҽR)F~g"_ULT:/?GUer2FR|6%lFwF'0&jEYmTn.EQZFnڬX>1TU iWu5 x=f(.jUA L:BxbLktD!8I Hɉ40+YZ''䙨J a'eVmRH2rDl:OZ)f2a-U#~ӟV^ybP;ٝrU>ţU6jh"t׎'" bB^_YN 8kS5 3_yF G 7E 5arT.pq%m[1/XHyBtb(dQ,gYKg;v3DD@}yySAqUCx*.y`8ǥ@ӨB9+t~GDi0e ʪ`rt˼ bHXXms ݻk 1v|iY/&;ܮ6$OAΉ 3G)o e s7VvR @8"Y,l_mcu̫ޠoT4G4UiWS$Ӎ9aj<1nЩDG^/65'BmjyK`_7Zdrz;e)#u #H/WOO&ZA.[znnK|@ySgq3QNj yfs o߼Ss$3nQuzh׽c ,?W)*4o-7xq}AV P~F\NvߠDr-IK(EF&]]ӷ=Y"띻qFƬA'j6OT )An[0{oJ~(G&37n 1tQqR,@ RwyNsPz8 vu϶$M4f;_zxVિ1(À{4`+:DKLɁ12Ki9hFduTfP0sFS+~aùUr!)fARo~zs6lz݀ #^`Q$C] U$]ٍ%kV{̦" rD߳ݭfFRyL^R3_;LkOL:c" fϛ\Fk@V&4\*U톄t^Ƴ2S?"- W6eU,EYVc^iPC9b98Ƞ}|` f.U[qx1XvblfB33>LR5E)[ʈ,iFIJ]d0gAk(4BZlZPNF}$2b]@ g&фZ;gvl?b@Ų0 -_"Dž5"9"C?P7ðWJ?!kgGLY7(2Jt"=NǑrA0 p˝g5rk0lB{<&r'Fnb.ˉ"K7ѵB#Y`tfu)s}FzssXMrL)_Ր3lH VĮZݠy$Xl8HA8s\O3` y[J9r[-a"4d]B3@HPD$/4]I(q9ܝc|`{T,H!TבIP~: 9=WR]Qt!C W{ e9 TeDnt9n 5,2N˪.;9T.**l}9h-R``]p !>8Mj\xq<f#ygݶ*0$pѨVrxg y{>ea5޺y3c;wT9LDְDyDC<']xҼ۲\$13 % 򸁣|پ%z~~qDc %C(z^ CvN7pH3`B 73]b ֿpޖ9Y94:4`|$+7Vݗ ]؝X=]km/U>4;@a&&:BO+&&Bp [Urep"DGx발YH8&"I9mOw>0T=/d: ݡ鶨iZ`cbH(Q{"cRtN)\k꫋ʱi֦ piq`)}F||}.O[-!^| +-Dy!f=};iϼ=燁}&^&,q nEԶ)yZ'e\"7AR <7(T&Ʊ&m: $`e 9{9G.Fn,F0OXO}hpRe:U͠)?C%P?MU{HjS]ؘ.xD i kg)ZE"@١jwe)Fߍ 7 4FdXUl:LW_ F,>D<Y|0HvC a߬shwV&GJmTc8 6`b5ĵk6LX6uK)riNEY kjKeJP3VĶA%̶\*f)Xߵ/\b:5)GAF6Զt ȨaO.rq+,Yty-Ave#ߟHQm nUإ)QgJLK DΕV|LW%PE`nP12Nz4vmBD<ͫ1k}]^RBݒjddNm*Щcݱf.pmh&i~KүPt> |Ѵ4ᆑ' .nj-b ̦RlfAk_=Y1,767;jUcsh19qa/E XKMfK_2uGt:ƽ}֡r^LZQA~;E j'|2zt ʛ\v-f8Sa%0 x bo]?X jHBhؓX=;";rVTLc?{z-3-~(v}ǵ҃)bN 1f&Qs6U+#0yߛCGgY9ڡOi O pAi)LJ5f\A)l6v9,*غC6r15?`r?SA 6zӔmkQFm۴)(bܸa^TvYțK9 F,P>$ӽE qwWV.Qc?sr@'# K!9qňoQi+-iЯJII}la|O|hJ2TBx[~gmȍט.J8pպQRI8>}B{@e/x*+d}!Y!7 &&|Z2a0;=q/ 0W+/ &sN3?DPZI? }ó~qǘЅaJp1ۿud0Bxц" h DZm{ǿ2`q GSIvQ+@߀{՝7_q?rыV)˿~]\W?} __y[I x#;υI~ĎԏMo;?e((om6vX/#f$HO$giف;z% 7Q9^nLNѡ|)ݘth#:XM 7CA̞cՃ>5m:>,M;vMW :ަ2HٻzL>.#"hyW0}&nQ=ޓ YNې׺8kuAE5%ю Lr|K`:?} K1 q";sɻaNɟe)^27\n#Jթجaf78uh-Y%BԉݝݙVSd+W~^)`^ S~ˏW ӝ r۬hqʒ[AhPYk$cUMް5^;_@I\MAC)XM%tc4*w}惗:>߉`/PK1xU͈z pkg_resources/extern/__init__.pyVM6 ϯ b;E{k@{HAR֦gؒ"543ͩ lHZ ָ*zTAt ~áָx1ۮ[cJL˪(mWe~^<*kux&j~z?; nFcXja8!e_8?756oF{!32 XKkw0?R'Ѹ^{7]L%KF\NTԯNSȝzBٕxxN0qAe: fcfY&2Ľq&+06!}mJE|챈-W7eިI,V҇hl1!tZ231<3hC/]fڊ~KV2ȶnyǶ>"AsJG6FŶ<a,!bEYIzSQ)/Kxk]r>ybmPsD刀0[!',溆r$/*m:]u6-@0#/᪅:U秬bpK( 3pvy K_)VdFɴgGUB9(9 )KX lpDEU5CMO()ނ'+[M;jETջ "T= axF9)ikr^OB%EUl) q _kOM8Z;ȃ\JkJ4 iވJLz՗,TL_jD2aq/EL@R2"?תxSSΒRp"hrFZa9 Ny֯ *!k+$RHTV,X[e̓)Osl"gT:U{p09Wkkڡc)%oW3✹lȳ}HVġf{D$d,P(ɃBr;UZw{NTJR e+DiEY5_yi #¦K;):bBJϑ!tg)٫ʂ5`槳B {ڄh)+rr圴Q15G2_Pk$z@SQrHV=!v{ZcuNE g eljudn)"V" "J5`нܼ߾&{o][Dݣ=ؓ^I({}ᯪ=*h2V y 3Uq JNPWs wTH.tKEdm`h!'Q/ȟHGYvʥܖ6qr"R)>34㸜fzi05۸n5cwՕZb)D.8(@g<ܞEY?qD! IOh֬+خ>GO6 f%R]UpXCS1hBa'tsb25%+"GȐq`"Rc<|Aێ{@@JjQ-oLqh0r&`dђq+iR%ܔ>c&IrA (}FRvA‘kwZz`C %= zTs+TYY0Ox$."|Dwﱧzek7i7}1_ MG{N}եӆηpxǏ`| v~i\P=;S]DC; xP\zucyEt۪jLS5k2:ջRT0;~Ґ84v±kpP՜:~vplzg J'~ҎQ`䑚btvDR|] '[EȺvY?w\XpMG/Ă&.&pƦc3&]'KA#4^hVaDVey`0lB~t?"ZËgQ#s]zuю止n Nů [0?b._}ˈ bk:퉉5(҉:^K/ *7i *K0Rhwܶl(,p.ax6˰%KvxC .٣.Lr;hQ!oI'X2/p gtRz'n]Іq[|BkN7!~/ռȪCoު:=nTejn|iP wH,gPK]uVJ"setuptools/_deprecation_warning.pye10 н28# Mj)8Uq{P/߃ߗ]DURsI)L 'cni6ƘgTuH^#?mmb]i[$2/!l k޴ wXc9nO~PK]uV&lsetuptools/_entry_points.pyUM6WwH.zk-d&ɒtjPNRlސwGќMBӂuU~|2o{!{tQ[Bk=9^5tuiTT2w8ZU jG0<Y{*=dYVC3Usp!l6t% ۈxQ_u%'$ ^dTDbu_SzQ^^K._#;#0Bܘ]Њ'O4Wo\SkO_Q8 Jv'pAuU)FSjJw'k̾SBܓntH)aHsbz ]+ǖ e .[o.GrΰW0B 6!I-"GYn{g$ Tu"l^dMOXߣJBXptLŹɚߐ@.<3O~qS>YlO~9AsAYIr)ׄ\L]ZaVرl7#KRV16tꤴ-*y%ɠAI%kQb>'6 J+=)-r[z~~ƌ;q~$lev1koC/lxlfF(7mQ5xWx;ѩX+q9 7`SÏg,B8 ]7&zĩ؁M_ɰ>z P?WddB0n%K22GPPB $c;@DU9^;v3ᖽB|Lw~JWV6 ȪfkM y? 7aToJc77̘ oW,^e2-xnW,ȔՍ$WMÖc~k˭vOSʨJSN4ShyЄp|&cf@mJU )H˥E&e[P 5^8 h&V:9̙7tN iKqhV[b8u8s^!1{J4 }Fjnw3vOeð.<*C:sHuOgWgrVGC<;ۑX*Q ,TOdx|_+ %ŽFќdLd'I^#AYhÀK%,O3|i(; XJ^ ͗^#N7^jdV; ,M&f q]5ށW.\Cm8 KIe$ R[s&Ɇlg=kn}tl<L \z/iks)[ͰH>_$ۙ^}hLl/p,%{oWYױtT4A >ytߋjҬK@ԯ'sC-Rwvyw/,GƅZ#+PK]uVTXsetuptools/_itertools.pymQN0 +Lwh+IC|BM])4M$Eӵ+XKlkt謣wX#oo6I+lPZ&b4Fc+MkhU5¡CQ&x8 ,8T@YaMDTddv +8aΰɁ8Cx{Pwp$ɰw5haZO1 %VE˸\36^^ Xct7̨'OBb_ȧ`Wtx,&+kJ¬9oQgPK]uVTRsetuptools/_path.pyn0 y H4h=V*mzND5*, s&:(ovH9YFmAdv`?_92߄-vMV[l~񻉾 J|hޭz݁uGQDA`C* :d@ƻjU68TƲ٣dϽ 2KagF9VOz0Q,wUvНa@`i(P@^sj挈A"Gm <ٽAoVv񚔱}gV}`3@4Kk+3igБ?!15IVtQ.F9"ZwAw=܊j3U(My6V$B &-:I0A{` K4dU`Pe,gŸ`Q 58e%ޓMͽI=rKl9#-\u $+؜L##F|`"c&d@ u$L` cCffF*wfu{.ޞDh!dB#:ƄC&ke78 3%)2щHA™U YBMn*DTQM!iC%BOH*M<R}Hq|XPx8r&@fɭejwۜ1&wj[U PڌcBaDIk~6`L?02et%tj!TX>O/Ӷ*J &BVL|PRWlVEY¾ʧV%zÞ5{6vu<2u>iCt ^2Zl\Q7n>Qo."mI1.+S`*q'z\t@SU|jszoc sG #@xs n=@}M13()d#̷2|`=~Ϡ,}HhQ !N@N Po/S",sPS\ޯn)U5W=nAK|ovIs=ߔ3lWnJ/J՟@mDdPNmLߚ~i! =NA^WtCpY~pv&SG.o8tc(f9ci`q!૫4P &l}=&Jta{R?}V""+#奟{h0: ,hKc[IV6\S\-Z{,oH 7Pq`c]V~k,EFni\*69^܄0Q`;?YD;c!:nRe| [GiXY'7]2ϋ+**(a}Qܬ۴F#!+AT~qg0h+(ByA P<tMP7}F X16F1;ŕd#Eqc[ r2j",w4/'peGY-n!rQ,Ǥ47/k} 64BvK"áv_J=eܪNnf {HB-u ax^ ݕr.cvmLU/Z*ξ$27dht.rQx~ bIǭ!ۛ*$eϲkv:_Ҋ2?vHD( Zbg]x3(o3ӈ+b7x=KT8D\Ү_qH1G1~?,~/>D+)NW%S|)U#`(GGX/['W;}N+,=>[Jۋp޼y^/.vA k`BArf?uE%>*K1 ŖW |v}ϲr;PK]uV&MJLsetuptools/build_meta.py6(y+R&ecs'i)6"Uk 1쮨*b" X4O<^,aX 2h ;x <@2MQl#uT$*V#wiv7js , +E\Zxkl0`q.EKZu*q4>gvI @$dmQ"TM3;N-e"G4^8ÄP[06UV,xnyE %kr?9.ѬAj>/qPzX׼bk ȖN{ُŝ\< #րED/Ka$x2WJs|zq@LiV1-۔u-g~1)l FץzR|#"-2#J ǜBQ6Ra5RD |+~ֹ;j~ '4x#2Q q\ __%Jf͕ TEZo4:쏝}^D&J3+FPe‹͖Pef@@7l nmX/Ҹ -d5a8QM8_?A"zeYaĘ FL#4 KRD*Rd#=Ii z`TwK`-UY 7 2bzxHeg(I} <L+4'hOQwQjyg(Û//WO<~훳KXB0ߎp8.O#:{q~`%PDM BI( θg`~>Ă3N"Q4"K&LnE&(Q;^M HQ59?6:'f-W+y0"蔼#x`{b\0FA0Ng@][aRCM#CQ,΃ȓt..f_ =vyv3!crssCs M, Feftk ӘJ3;;EVT?h>}>Z.[rpIU2% [@`4n 0<~/ߍChxכ: 5 SBAk!DDh+$bB5'(..Ž("8قVf &tseGjw\brEƩXs Yf0XA$t4ɖ\@dq_GLXc*]x T,@nRdO^o L4}`VQ@^mX0%sH]8yݓu>w [vaf5Y-c6Q~LP fbe>aRZƫY~ޅCgj`:Ubr1$7C; [φ36Lb"Mag!.F0W`&Vh#!e % jƸgxA->ȍ'ߎHM,JG^?DQaeIs}kB'(  {QH1J ^ϞܐQwźO͉IW,+U)$fTr j5 8M˄3RڃفtI 2[u۔[DyK,q0Jv>пn`3aT,Uh: +&@l1n& O"@#>ܾ5zRrL^e>\^=z>nk:m! WS@=xay1EYA&]`S Vǀ>?E 0e i 7S?[a HDaI(kp0"Cf3^({AoS$h8g6FǺÀL7RPo._A( b3媨 $9LTmħ|(׿oAq!,JR'\U!o%m8l#~ҊHJ -BͮW*rb۲M^ɊJn,q>D>T8فDeҪhHPx/M{ cL-oxYt󳋫ׯgWO_62$kn2.CCdG y_"'׫X[/#=MI-7M]M:=ܑBųQ eigfo\KkHH4$6+^{.2%%DҥwN'/.>}6,[UB 0 eVt"=7e 86 gyԺ#dʯī[u:vԛצzfjR⤺wlk/־Wc:tTAȸ/4(DA>%pR'oNݺR.pS4d SLѩ;@QFe Q ~yQ"H.$֮l L*Fؤ<)mHGA=h< /hz 1r9+g  SzUYoK>t UvJu甜Y{ARwz=Tz7i_BK t]? CA&Mm>+uf^5N*M|J2xBhŗ : s ~z{yV.*r7D ] c9ҿN|^Z!Iev[;28BLU 7ŭ`RܱP:~nP.Xa[h-Ta(XT77l)2h>cw*{I4ÅձqCdغ(I7a߇s\s q a X}"lه/PR B}CuT *~to6Cx WCsm6pwqㆪP~o'P|*%guey @;z`k[TVj`BnÑ(EC@_shN^=V{>Uh.=/6}47u α:WkNխyx?nM/Hszr˥j= H#gS_wmq[(ٹJ5+PZe\  W/09;PzQq$78thtICp)gLý'<1ws4c IPK]uV+JΟsetuptools/dep_util.pyRn0 eyz =TU4klkl7& d+!a~lg1(۞1@rCg6BaR=ԳŤ/Ywanԅġo;ş(`46LĂ5d6DeM[48{#_1䑂o~U n{CNrgq)2jMZ2# #d/f0%2w X$[M!< y;P= yJJ2,G`3yh'j辮yə0͖ݡ՟cc{7;) NŨtЇ|&^lXW?qF0z;jn/(xAƬGYSOe#ѷSD"j,Mge5!`MxŒf@-{Qư[v0!T}?Pj;V";T_A/hބ qKHu=TT`l$2Bs gN8!Eȹ&-u0'!#ثί Q}HlZ@+!5lЗ{,Ϭlf>grJrjpT=9gZԆƪ}yēWQpR4}Zfހg(pPb,d.ؘ^e/ #j"}4 oY/]%po鶀̲I=t_?[*g6ܳ嬩vXx,jY8X0JyM am[&t3Q OI!#(l=z1*nhȩe{# k@h fܵ F6c =,t`G\O08K<عoSKO.j3%{By">͆PؾfgjFx{ٙaUe[ /ɷ+r[:~e:k܂t4w#[ǰ2͠܊Ϭ4sc֭Yƥl;^V-x$fw 8LX_{;/GJ`Zwv$:I zud\&l, HAP<'ǝ @߽<pk n c7Av}^&8.;<&l<-#wT9<NcWmgQͬs:"w!ͭ5W`AF@5jadȂ6Qx'j5{2[;!#1Ib=xy|d a-&}75BQ/lqhv~2N4wpCst4x p @ȇcy( =N-_%7@̹Ogǯ!ϯًY0C\z ay^~["3'(R#v1i7~;u?u#Yrv{~_1r: (cI V,PANTw7hȈM>@'W`USvAc^_:*, &l޴ ezAJȥ&Adv .IdwsL*j8 3 wm d|%v,PK]uVi7:?Qsetuptools/discovery.py\[sƱ~_1>\6E% %*˲ʢJ'bKX$7,VKN_ `)1+`0ۮڤ]efՍlvZ*ŦZn ي\:ͮ5<y66y)+9]/CU*_6L^Cg mSW-ѺUcM*6/ע6Jm#ڶr9LE6qmD 72*`u#[Yv?SghҼ`"pȥlb E Am oj @ ɒXʥ(,&MYM]LHOL/_{o+W _ov~ & %I\xq-2dCZK4 T:\XoMDv2] WV.T@uSٕVP=!>.Dž)<~$}-d۬N %Cd+H7$$Uɦ/vjX@dzVuQ-GiwU{xݮFKSCb_%g߽:ͻo,-tCO/==-X6CZ(ctu%DF BB&W1m/`Lg~6yz*E}3d@)p4ym~-/'뼀A - lu9 Xɇyߵd]%93k٣oH%?#Zmk¦d2YʕH}ˤL72M/ω]{ ެ DIoͤ% 5x&PU6NS~3Eq"CY a9'V xtPC/hPy.n-3֐D(+Ӓ o?LJՌU1Kvbۙq%F+<$EW;;4#[$YYMW/ Doa2Kpw;OqEM᪪b KQ ۅ];4B Ў5*ri&Ҭa+ZfhKʲؙ,ioHYs|X>GrVFDPa=(̇4- >P-Т`WF_id;f1;W>Női4{m?$:B*/C3hЧ.ҐS0#SC:CĐPke=0- zyD+]#udBk!֙ 偶 !-M%;*V1Gr! 9U>.OKYVLsjR2(&!F@,ے{#ZF)Ɨ;<{톤pyEroFe`lr?`/^a0E!X`|yz6ÊrQ#_7_p=|ē/a89 | HkySCvGGGG0\mZӯwHwALuC ~\LKuy9Bpq8Q,iψI%RdLuU)^VO˙\?!45PS'Q a%TQl<*ٳ5QjidNQ%:Xt9/i5. /To'6Hp"Զ@ Vj%D[q1m(̡2 =ΥJ\q~*H ri쌶_sNh\0,O>-@{hvCTN+_U9=$>aɜ(8P %}70RS/hݘ ؖj AIӢ p>ch,yڛkfi!^ѣjij|ۀ1z̀9m\=mF~s fC۳(TP[w r%Ci>:ౡR$*YȪ |~p nQ`eE !́~V`c9oO6m20o p.N:PT Z7RsCح`lv??(Ln6-q^FqP+^!\`(DHުQPTZfr%m--I"GAM6y%%"6 9Ld֎(# 1¡ F.xԉ&F%j߂>|slR,]b6q= Q4^$պr.x*0ڐSqB]Y>J> '$ؒ)x KTs̭wڏMK}!])M9'3pp}Lʶ(zXYg"GXv);Z}~7*:Ӛ}@X*U#iR.k_swLAcʾD dF ;07fzE lc,1@8'b1"u**↧sd2qp1O0$;!6Zd+7yj179U;<8zt⺪ĉE Eob"kV.Sĵ8D#gѰ|_s-=y/8>2N ml10^'}?jr~_M6m >aNǙ<%,GS>?FE^.ݒz{>|n0=9O(p--խČ g ApYk53PrOѦ+[ǰvضۍ |,w&"9T <1q JCw. 3PbԀxVQh(CȦG_v큀'M%G`Um*ѸOT[,j၄n*綏[f H G2ңa><\o+(מ.~+[b%MX<6t62444MTոc.1psO67_?7|LmD(#ZI(`TtlKϟ?7Eb)Կ8{\d.'Zxe_N== H*&+M/5r7젯Pv|lM&%$x[+i93]t|sӱXC^X0=mw"?SNMii +K"ղgJZUWk3곿ЗW_c}qD2~k4o?{$6F*,/mulq%4ŨqO`<@lBU@Oݫئ:4qg{:JB\_~5=[mw+;5t e+qw7|J]525:vi Pܬ*=|1ۨ?f0W鶦h9y_}M] W ˖_Hw96˼h`m󵽛nj|{ƭC6Һ)}=ܽf`W|߉36᪷ l.M e@ q'|alpZ'~~!#_$>X!, =Uv8(h nk`h*gXЇ]M՚?ԑy1:"tCq,4$*4'iY~ُmkż ^_qo]F~dzPK]uVHA*//İsetuptools/dist.py}mU*6 IK6SbK:IZoXr, +F_+ܕRw*iI`((4}Zu}[ӳjm>wk<)ۺ/z9W{bpXrl6eUV-J3,b'>7纁^67]{MS_. l[j`ťUe^,Vml7 ZFhUڅƆČ ~g}mՕ?UXυuQ![Yz۪EtzJqt31)!nʈj!lIO ծy=6d)iV}|NW(T}KgIOg˦V a8R>~?J CPmsv~1"5v5ˍ ط8F>M__F`2霼ydlvUĿ7 :4%"F tຂF3ۮf kUgh$,)YvyXlPnf X|fKsGgShk[ zb}]d8kK:ܜƒ3Rϓ~+\!.N͙&9I_^ ǸD*NoN7)>dAGLj+(LSAT5O'jDAB.)i(PpF yv.o)G}l$L6ݙQ"HқObXF* `HEYu"Adz|KC'Яd4ضtiR8p )%Y:Fg Un,JPt)5X=Xז K^?{|dSt4Q`4-FXdja4uPu.Vnλf[jRR];5#b(_(%0Rr%fV*إ]"aYjI;8)y"{ "G K{ɻWO_'opB޷L~j r!t\Ainr2ɁS`[Kunpٸen {< :9 qŕ|E4 :Koֺ)h =,ue{ aia{-susy#)QFjOKx@JXq.Slr;Nw?֓~x.;lHڙ0kDefF=&t 겦gZ c;Tp]J9]!ؐ3ˤny,YWe}}|: @1(U!G)Ɣ)DqK~gt,g5RfB'@N2XY1?Q2+~-6 $Yp&v ^l.E* AD?]\ŕsPtvƒU6zwV[ ?CxFp,h_9@(<.mͪI&cVW%{>j5 >I͵;9]"ϓɀX0684WY&lg08z ykN4O=uhg+_*cDÏ! ~x%8 tw8uLg  7$*1hn @ζ CiɅMb2ԅ F(Ȗ`ҼtP !|`cb Uxʙ ؏ 5x8- o !W7KyuDZ3K6喽S:zқqAP/+M;!IJRA}$lo.NMXjQ s۹S&:҉7S$%jXw}i2rG LM^ʝlmR&뤑W5_pLB ja&.ߧG]ȥ]2eɊƻW広?g8X3# Vdۻ'Pn]J9}b胰s6zXzv5&agF !KY_\G@MjFB!+~y|,H?[uA0޸B91Md](Q TQ%ȩl?ODg]@o& DX%EqJ"tX8ťџzɌNNn&ril0kKR7b>][:/VgƗ_7“.څSu nZrh^U8cBJ{!@,zB{Sd 궗z4Gj|Β܁4kUAҬM<1kN֑Rb9\gDDaؤ,`F=#$pr,VÃ}yDZnq1A~>Xm-KddJo~bJj Z.]mՠJV7Msce /;CgJPKS_Qax]ۓ`mŌtci-]RZĭ>a|FH@?r$ٿ W{bCz`/Vɾj}dϭC ܔ Bhb@?xQ@HonXן dtUR_#p膁¤ҕ9/vbu&|r, ,nN`C\j(`Dny562B-u5wCk ttNB *)ⴘuSƌM<=Y bJbJȰXz'°;~&fAS듇XM~`(+4G$,0Y E-ϫu) FڛŮ sV矧$&wM2{3`\NAtP+ÈLA&am &>pZ|e7^ zI:aݱ'zi"=ȥYf:DOU2H.? ;Qcf^E:Iwu#H@t"!c0Hj8 j+BI0XiVr{4wUY=V@=!Ԅ;!B쿐!jZ`iӶ%rnWu8L$ÊjVJj $UH2|Zb%Z4[Gs90OmW hdrV+{38%pY۬FV%ܻ9 gq;(?CN0R-PU:HFnnwi+a dI">}sHDTɦo,"޺]d=þڨ\ ;\]ef|dG(`eg4xvy@:A,]x Б+3-8D⒌Pa ,: @,{48ejRX)lݏx߽m˷߿~ͻgO{ǰ~A8YeyL.bV⦮" u Njvҩt79~D}.@:y>¸%ŦpiUgs6r, [-{Pvq%g~3h J>qsY-.Mlj^ю|W{\{0tЦV 3`QkFٞ']RZ[W8{Ct,TXik,tN#;܉ظ!T>KQaXˆYح"vY^+P|ϳv2p88J|; yqD&+܎@vQM6m&$tXS'k*P10of-gKD'8wAm# k٫ʱK04uv%A9~d+'v/\'׃Q!FAI"l%oI؂sQ #5 3?*'-δ;YHEhab1؃S NvGŗB )*&ze { ^տHmhbGKT8Fv.l]}d ^|wؾXVۇK'V3?QW5g`)T۸OS\k$HȖk sl4D5٨Ȣmz0,X-N"^\?GP<šCFtTknQD+ز"$W ]7 jfWs-t>/w\_ANJΥA|mQ F : "7"eF'?Gt~"Vdμ,d[M( ! \ 0sq,FD4Ә`p&Jl}vy)>C0.۴9ed˗N*֌+ljJs Fӳ(:~ăxjy/^~C_z/՛a60$^A*l"_h2e: #(7/rʄy;v~:@븊6^<,ډeY8ݹ+sIb 2.8L6F_)V\MQ.1>Ev>HW(8kҍqVp|?YgL?tfχ>⻔zQ.;9Pnyd Oz g]S,' kTÈӳ-:z*8ЯHŏ|25X/FOv[uu~ۊtKNo*^i=S.FWk{+K9YK?e~%FWUZ.҈e]7;<2c)SěENs{J F3^@'Q ڧˠsb~)uYCD#dH ts'9]k G| qBu&fUAIbܜR\ۊrvluqUAi + -u}Ǝ:sVN g N㹲1axzV,Xڀz<% @l+݉< ?} _Pw&(ԟ{wx+;p(Bk$f\wZ<d d>}#˗)5:|_8>.d%Lĝ$wpx'rd;"'Ux]0cxΝmtF(<Z;sXg#J,2 }:>.}uF{۝˳X8ÏOm ی\JxcIJH7 wb/IGKhB^ aGtFJRx a; J `.Ɲ"0RUԀ$¹/Xhg׷h#^N=܏y`fM6s/Y(dTMr`\6iHC[[аc| ۥiaD& ǀSSQlI j<%,(/PrTfbԐ5O5K t!It#Rl Uǰǖ拒-Eǻ9)H7uKpLN-|\m:'s@׮Pڧt.1@%fsVTuz ӳ<̳nVL e9m8Q:VD.}[`d7B=7` 1"NT{o~uif$dR-aWp7UPE»_tu<'#bv@T#aYDx[Yf.w"’M@V .ρШYdBy\x'5wKMU|}+\)bhbWH|=tH=^.MT9$gDXb|=}oUN cBN D[]Z|ޏ}lqsL*=I 1:q:I&܈KQ0m4 3oQge''_c '%A(=/j91/c0%$%qNZ`]FjBFMgU}o,"ϊdlk%Nр~]U뒵`U=<6oAw]yGN ;,K,-_@‘ͨmFxL`#~ :Ԁ^H㒳RAB]\t1x5͉3M%9;.E r2.IiO_V_D5n;2KoF0=BqΔ'R[=hr16Ift-t)mٞWҩbT^Y ke\Qࠓ 0НiZl-jəitW vCCL LvI?dba\P7,R1;߫|_2n?юXb;g|G?W o,͂23:ftD,v )BIgTk}⑌ qku!@}6 ڋ*m_%j9.l}CS6$n}Rd5 s#~nАҀKߐ}*gk2VEx-ut@>$IAtcmcʯ =$KZHu}! ON''gTa}ꞆE2#~jOH~SꛡqvOdrwLsE\$m9M i r^gSIre kxCybM6BPA-ۙ?H]'i!t-3?ZM:NMSrVHϙ%bo8XܖJl6ýj;9=8K7{ Cf͂3 c-( Jq6 : ⺹K`=M L0֫q M]DvhסGXTwM-0ڙTWhoK+MI'Hn]/szO6lN9hv͕Whtw>W]oCݬߺLOӳ!fFp3/-ᇁ:occ/ Eoc[׮L[Ϻ^AM5(D>ȗޔd@o"x|'1&I951nfrr$e1]5SVNH`Bm\#>VR4wc3}gp_ů=Y{RY=mHFbX!-"K됻'6ϓ1k /`/ґk~ %MHYr+$g:/U=_Oюileo#[]p(s?Cb)L &q?]}fJ!$wڠ{4:44I 5/gjKx^`%΋$,bo2ۣsFF UvUk :swSwTwt[n!{EM^j-^Q?%{Eݥbh+ ^QDr^S4b̷W])4v ]4i6+ ou:t ]]% P0lq&񀿀B2;$7Sb$G`SL0̃ȶu]aMc9 [¨s(#5'OJ9G96ɹV! xR%j=(Ԉ .ЂӢN,oaX:9i.K>~DY$@$>S̼ 2ߍqؿEB6t7.rU 9vp?8MI´v ;4?;q/̘D׃A* %wIH e~Qǔ.ҕLEi``?Ym$)-H| ˲WO9FA Q@kKF A[t|\ae;k-yW>ʍD OکAK3no'ϡ]HCY!xr 'n,.OtkPmb>c=ǃ#Hh>{zH;es,8O6 aM*Zbr/pO>F#eA5Wz`a`h䃥8ZB`?yK pGt^O9x'60£xkNGn~502:OTE'jD: Hݶ7.7(O=5 vH? `<OgFj8t{[d$b\l9=,B Q@NB3e)}aDm.=l9kDQ1$rjUG.73~R]W&)G)XB+%ɪqß9Rp8` wCDkaéRh@gT̸vy6xïU*F}s2iS߸i;NjP;8^<}0Oaf%qeA?:4zѮ*goO@7й$M-ҳUSg=elNOL[fGJ϶*(_f:lH"m;56S E,*{!Hl+g}іK z! Xz!'hYJ=bN:c6I ܙ"WzL23+r5l-Zy*Hw7X&o|Mmρu犯(hLDlY};dwhGuU胇&]8vl#h%|8-qdnH]F˰𿵌%$⸡dv]%YWE+\3 x˫ݪBAG!a])5 .sY9&SCgЊs(vim0Y<٠rM<߭L7:{̓L@mH*z?'ral N1Ėb"VY0ʼnF/syf Ƣ 4{mm"e'x\a2/IMnBiDEGۢ{o z:x؋*M2EFyW#l4APK]uVu=̕setuptools/extension.pyXmo_A H=/P8vHs9ݑzܒ\* }lV"/3<RՍ^XkSTη^U.+0lh%k}e4={N=lEVHGn/TN&"'ʋ=_\M>4|DZ|- 7a(J6NDC9:+q-fqJpӪ, w"8=Xty|?|wwzH`0;kX*^Z 1ǚҡ3B;Y#(^\h/.I`"}K #OyWle!c3#שAkşWac^(O50Gctx8]90 I0@/bԙ0Lyc˛4)aArH$h+iӬ(8+iMAhE6tl{Tj Z wžsSBEȬ rLՏ«^0ZJC& $(ʋȝd}~sj#m ΒV0R u|L3VYX.:)~ yR8 BQN O+xJʽ-l; t&Y I1]&!LUnNKTJ21 EV*;MǹΒ/ n*eN|qWAlPvW9c\8>N)dso0wL.}#pM=E>–;h?6@Ձ:,Ri'ٵ\xb3MPhVaڪ G 6{Ta_ e A̮c|a 'Y̍N~ l삏}U+ht]g 0c6{Io(XoP{b3; .T"/~ kS p  M :N6"t+@ d'\JLV(N.ϭ,oCjƿBd+U7* wN9N#.D:S狿nI I8I_NLre]IkQ _R(CN@b }M+|!#p8kTwS6D$dԩewKVC9:rAn"̡=vb%jsR(\6sXm%wf,W8]xׄ֫bc×MEV*IJlWsJ[!vϠg T׸E ?³JYsGv)M)Kqb;}뚨d58a`%$7>TV݅`?哒") kx̧xxY ۆ|0#P{s46& @<A.\FB?N%.27hxsx饤K\z#e hmÛU-)2giKԝ=+᥁2>N0[>'ְfPKu<00~Hoǯ3vGgX9 !ū/F4`^{2t-1 ┡Alas1<#&sYG,evx'1ƞO%+k PK]uV޷Y setuptools/glob.pyXMs6Wl%amgz8z$k$\)PH̯⋠$顺$vow ;^`+^,xMkz s#3W65K'TE^m I Ǫs`W0?U E&D?Zպj$X1I2` spPli$.m5ӥ7cO?3@ŕ6vG`-{"8ؖիB'_L:ij)9⌫Cm~ Qg\uCpJdJ!1ALuk_]/‚`+eLL]Vޘq1#=4o?0R=[$c*7ϥ#T UXQ,s_Y4B*$Ljk _Fr(ElM.(^=eTdV(D{*s+:c9rr4M25vrN4{F٧eM#AT?[Nr.*hj,SފՌL ahT@Y$=#o_>|HG/N]ײnZkN\} {)kys0vC4)avy@qxBqY\N[fC]Q[ඵ|6fdH=̲HipƬE<`5 :6{f.d:yq*7c t/PK]uV~(setuptools/installer.pyW]o6} Ƀ6ۺY@%" I%߹$%Qv{$4U9\rN`Zq ޾a6q$`bD d'9 ŃhF NcNI4(QGwVon |_LW6#gBreOQ^yi!T""`Fy,ѯ\4 mhMFwK;jDݬ,6QWO|gI7c-T`[TSK瑹nZ͆G<.R3R18uXlNPK]uV2S,setuptools/launch.pyURn `Gr-k$SUD`:_8 3;9⯚F ArQiR΀D ́ 0{JU;*)H8xOw3FfƢF \|*,f[1DE0V DT浗[i5ǬaLȊ <擄MkDjH-YpޒggwVOcUt?I`0Hц(֞F\.I,g'MI~u(F ~I0ҠzZ[r"݂}5Lqj?"6|޿c'ZGV[e%3s|͛;La\Ȱʗ3P`S *i(@3N4]lk L&} ]n:# a"uUO@ $ gwNx$g2 +jQ_&X@s_E Ms ,fe"h:=6Mv<%p{ey#E{*k?]e+]t,U~:fBNOq;Nj z"OYe :QT  <xdy@dMF-ͮALߎOn<7]V( 䓏 '˰)+~y(L'Q7J`|b;fxu"yeՔNWYG|\pOI\UQh&@@ ,2BWx xڲ̺sd>??}t?}擃}zϳ9>5Y,zvzE~k(ǩ*@H3ӸK `TIp9` Sr/kpE vfK#jcFH2UuY#ڝCqWv!`w78;oޑwwl)5"Y:֞?z`]γet/)]ݹoz0Y/VFA}Q'1XHPYQc]3.R3&o8(t˼lqK F{--(_N"'s,o)!~nHWx&NuVhk?SC 끗3N}` p}U A Ń܏O ڋhGX7kѴYp>q-g_AC*)S﷼bGʄV q,r-{#W3Bm'ȱݷ3/d s琖=l)Y/R؝@, =ϑ}a|1 |Ry)'C %/E(_ЅbRa-@~dQ4E,xXP"ZY`NJXƖH`S\̊>EB*AjS"v IYx$ϏyP`*Xey= rXKlRַ:_Fan5Hm8ȣ𭏋ӷaS:@<~|}2Me88zx:Mz'YwC{6þ~ߜ]owi 4=Kq#e{v7Yx0|뿻 bq>%^VAZ k^^}n4XDZm\'g@+rgKRWl\.IDm`}P+p)c 0?q\u^z;῎$(2zԆ$dt +Q WE͔бE.B%8{K4 r޸R| ,-F\wMn;Zܫ|AC .-EĽ*gugͱR49mwj GPrKudK9t\z{:NhM,j9oAԵU\\IĜJ5^hͻ= c)`=s[$3..;16i2Mu4XfJ}4ZrLײ&QjaH0 ?\,+aFi*d# ͣٔ`EC$l~Y@EBK(3Ìd'%]6*$ ]>g嶍D5ᗭ*>q߾:XlI(Q}{8.romzE\JY(`?;moXSW`3B7 c"Ja_-X/,tib\b&`TVv~6ᰎ?GuIܠM!aTA@a!E>(ޓ$I?blt BЩ״ܵ.:'ݣӂS:'  #<]PeP&3Z]Ҁ˱w%2_a<,mw!Z]uh, %Xm)y I䡯.*JT Sm4+bo9iE}Kx;ʘ5Uj[v|oj>.c]jflm= .fVvXX$ +65fa:dW)Ь!PNɚ[Us}Z UUMvu0HolΔAİ̸6|t Ӛ+; Rrr_ ^(`-!Vbq6|[j^J8^.oҌA`e2Phܹn[{&GdGm!e9ޮy*&ͮL`oQ {jMY#⯿B? }B0iΐ${C[W5H\[X>Ј4K<^g׊A4)7^$DhF"*1:ֹ[zs|=u슚 }E>t74 ܘOC]]Ι.v4 Xk=] &j%\F gRb.t;+l )}w5zPnciw;{*RKU0[Dt6,3x'7%b&MƲ wl·E+ 0#7'Nw,C)sҥ=1+xj]X0/Q}1? Oڈ=Elف/>8+SRe1IKը>",*ϰ׉IW&/i5m^FI\RBWmQJ5!Q5s*&C.zp-0".Pspm o :GtCAliƭAT\6ߕX%{.VsNi.MYtdt91UucbVnlsFfŞ/y_LTJ_.K l!/=x%Js D$Ղy18If?,'6 NOHF^YH =i )Я7] iE0e5 gSR*KJJ;Q-u G8m+|;@|rc0-״{jHU&' a> (׻ten["YUWA~M|fu^,[S"m;Zst l`MYrZ%VXC.oS!hj)4[6b<-"/鐽]W5b1 r 9v:š*^=d( 2o]BG:e@>p˖tghPNRsFxŠ6`4" <b0 aޏzuaI:,9rx^$)upң&mg6\ %?yc@q0q$qU:)YahtF)Y<_i .!i]e(0GS@ aV>'ý/=aŐF7fJP9M4Ek2Co-p4auL?b,ԡd5)BESL(21A&sɮD™7n/>u .V Oqn`F!;bП3*2Ƌ03/?&= Sjҫ$$v +GC%=!T*!"lm3 "W)"+aBpj65THdw_t+q%DȊF5 rjRE TD1d)9.!R*| 4Znr# v~IiX{-q\smv2R'$ +gl@v({2,,c(s[\Ba.*JeA}{>L䖣5a:AP#WlzL,65d;DX#4Y Oh7sY~XQ&EqثMGax#䶈2ڶ(/:yiGd]MȼB0,y7KSiv\7(u> ˙p0^=-s_}f0fQ:[P4lm@';{u5jCY'-?aQ~76#UEDP$c=kVIXpJa{͈T{h{aX>$Ñy5u1n%$'1gǜcQm1t7O'ӳo#vDWu﵂v1|UW|'l7sq2t^jt(w1RCMQZ*5<庐zBUu:#.M|TjZᏐhk+k3ҺZ `ҿͫY5N PFZ(OvDgr964_1oyaVHrFHn>X 04utkFB7D?ܶΕܿ?\m]r?~wam*GjrIY ba*hE65N"|U*Ji(eF 6KE6`f6Xu-C{VT<~ي(_BYWlr3J'eNu0!g_v!2*|3oP$jӉW~~<(X~] χ?,k@gL[mj$q]3.Sr-YK |`VwӪUlFXy,|P2Y~c+] sǡ3H p&jc_߽%-~i_kڳn]9v3׽z1ig-O5pf/Mm>] Oà!߹|up{}㱍Vrf\dHn(GAՕZmuUb}U򮏇e: Ch2&j#hXҤ!m/Y46SggXܦ4}*U(0ǣɎU|@z{O5PO;aLb\N暓GZ[Q4بd/;\K-ӒX2b3MnyGX#ܯPG}y c\] Qڿ>&QffjiP`^ N/T2w]%ߌx-@Q2 ă 8;;Smg#b_'m敷iEa8qRfuqt69qeˇT#j_Pn@jg(YR K-:2|NBJ6ӡ3Bi;Hy%JE܋ګ$I`t2䦰{ܻ4Y>!뜲`~"5c~9ٮg5el+ c^g\A<铬L5eN`ߵ~Zu-*(DPfvIOvR2 u jTk<1l#-ڋv],EVN=xEj, xd$5LZq5̙@*jgwqKs|c޿JʮLכa:=̅3o2ij˲(&WA hB ,49J6?-K{/EUG?Ko{a%^2bY!\=/Ί2ʼLK[^+唔B"yǮY)RTE'OSG3ϩ(7o]3cXM0N+ۄM'#J*ܵx'5{:PoPK]uVpm!N setuptools/namespaces.pyVK&W Wqnn5.V]Tx2=ṃf΃g}D6zoԩǨt@\h{M?U>Zn뵈Qt[O$aI OQK@fBZKyC1=|1OvGHp#8@=Ne8`tA@N/r+-l`N NC@/T[Qx"F7OK $11&#XU3ezK4ehUFtnqb9ݵ)f[ap?- EI3d[$7d} YT9bD@֙=κͿW̩fH5ZMr$BYhX;}1X|/"6FNȂour .UI>Þ@wТOt w+ʐ=F zhu7b_GM ! /Fo '#M|N ,IJ}{ : qRZQdFk_|fHA9:@0)q u˝hp7S7#K 5e@`-ͲeZ̯ͫWC6Ɋ{Wxi"-H/GDש#t$Pt>HȣB ^t횫^OND%PS⿧8[}"kmQJ]U ] 4iZF' &eri g}ft~ R]-eZeLzlw=}vήݼ6W2{ U)ڣB q^&0tl/^2].W* UiqLX-MjgdH]  YIsP Z! v5NUvWk6[.JBS,5 xqfuG+ڀei\N`*$I5E= O_ PQQ0_@mV^ 2b #欃a-Di}/̤4R'E!B?PK]uVN-setuptools/package_index.py}kwFw u=lz(ǖql35D4II@ɴo= 4(9=g$~TWW׻ - +1mu:}EŲL'`o/_˪ m[%Է &_{Ѩ.Z|u_H2C4jrGNObiUJѬ{N\zMlDn)fU fbJ *]!٬^j~?T.7T.^G?>ۋo&~3uS嗀_ZMZf~գSiqWe5&5zͿLLt%?DUr2˗4y0C?>/l/ Rɲ0uSk3ULISELO^$ !?N(HVe%&TM]L.Cboo~:}&'rTEU/1IGG$t~/0V{8PJwY886:W8x@x8O㽗|t/*& p | }xW{Aۓ&ob< aʁx}{~ݷ'e\/*juaehv`:xLSHW&:u -oI(ח^/Id1/?ӗ ˼⽽$].'tF+ _2zZdCȴ(ɬ&pLtr{(/֕htZ9ţy)cKYOMYL~ ^nEY|Zo'e`ol Ec&3< oj9?|-TGNy1+Ec=212zV^i|,*l.l;P3a*MttV5Dעu#?eFdCF@ p֓N50#qR+^[hJ,8g,벮˥m!-kQ 1L4,n V4#0(:oQ ,vC5dttx<=ԏRQákYF-Wxyj:?W Qʾ%ղ\[q. %I j+5BCNv, 8L&* p難j;W̐h=kQ/$txgX7aŬs IqR=Ó ~Ro7?z P#îMQ冴D y&,m1qe[<ZZP^:h@|mYR#x0΃]ey@E[;*lYMVƁ'Tff=P6^1J 0qd^utQQ#2QLѾqZǺ&k/9RHYPzHۀ2#%sTlnIr9'q %Ha0Ff}s Hme>ߔaJFO"vw{X.c6"!jUX~"ڜfB:֋4YW%Rv*lvg?xZL+t]e7ȝa>E G;%AMu[: λmj i yw\Őb*RT`{pΡ/4> ܜ:$ɺ&f IRE2F̧Gj3b7Rf"pTqa704P+*Av!1S ) 3/y«8:l9X'i*f{m?BTi#tNJt3K[(` 6F*ؠXf&j V:pqƒb ܥXp6@)AdPI-M?B{1M +Bň6lt wH]cd4+j?J0Շy_pD0WK)hT ĚKfC%d!TA 4RfzylU9lȀ%:uaWq*!`sY4P8S )(3KHP6%3br>=Kۈf 62/[])4abyxi캁D6-Z!f9hrH+*8/i:fDg5&9+fVi7xjbjb+"]W~# WOJȨ1E֚nIx2;0Mo _n@CNqFv-'F+8jw(a[*Vs[w﾿oqr0^-؀WϺ.o=8]pxtX}1NZ,_<.D,hy:W  ; du#5 p"]Nyf#| lh Jl@~RA@\`!onWj%  )A(p#kVph-Yu6-ԢV:3mIݔT1aǎՐFch~4W1f|FǖpaLLLDAQΈ{ EoŃQ7xx,A&njm`悀$m ?ё j|fb߇򇗽7]<* nb^T.CySnh3$ҩ _/<:u}Va FUH36XL"o&g=%hl&m"+u[nS5 :gW) {,?o;]3~} 304 ta0uM@}Z4Nl`%$μ`nF>Yw-^&[ q9_*bd8{N ՃNBۀ"g<cNz9F>khՑE5)+~Klg^08_-AhhDl!2lj§3ANt(VƯ^AmM  e23NSNP̰h_Gax#CiiH@.mNCXxmvI"?N:X?JPIsӟ:=qK?uM$F(Kr@,DGc5( &r0kƤE'&%= Ȗh.K R Rǐt{)`+Ri^xh;#٪;~;* ƚG4 A+ KiWA @.$%yqGe]YMpAIuX΢2qG'E$SX8bЁǿ ,/:Y'}D"lugiA^{vc ,I+8r(01S1٧ɏo޼\)k4Px ln%~DӦsc(Λ97jk0߲ޓE3fI)A7;H8"Euq$mjXQG۴060&{ s$mRpbFZS%m&s]3`)rA,P5z ";/e;&&ien/gG' ʏCʇ+QL^P X 5_:-~zj8F˱a<8C!Y`rqʐbt?]S43ZSG20+k&`S>$[L [6%[Ѭy̽(;e+y ZӠ:]r5a  ᱭ8 5tcY2Z@uᐋGh=!5=HYliI*@p[zqDH{Qá5ea^avc\D;G׊w ۶4wߪ/HihR\Z%ؓy,Nj'۫pLe' ֈľe%껌hn};O0KwP9NnWI m@ᣀbUwRqk1G?N^>9}kqcpZ)vf0ZBrygH@m%ʅvoV 4<ΏpϸtDyIY{NX9޾)[K؁(R0#DIa ֱr ozؿ-8K8t`B3l/l$Jr26:*,i:\#W;BߩTwl|~W=۝A Qd1T;@B(M%(!L"VI6ls/E2Maѫ\P@ot"U;DpC q=D$65`Ns w@Nܑtmhtt#ÏZ=P!6zt͸o⇶}U7: 1 V)zs JWYNsNʾ ܩ#.c{|1 㡻[肭YC;DnR1o{ z 2"`ٸeUYZ8׆STcrd!֡cAɧw[;t HG .LL`=tRy>kuTySKыM_Iߣ'zHg#ޒVOVDv ٝ@kOPL}lC={'JXCKgmAeAD9&]RZ@2DG&kh&90JYmoKM*0 s{TJ&j_rqa)dtpȬ28XqD*>b&"dW&#Ey"ē҅VA+W3y[F>S;+X/.FPG8 a`;+IN!X 9)~`E7 +tqAcomS6Ũ֦xש3ڭ`\bv,7t#?fzxnؕ!Gε$U;YM]̒X)Hr0`H7I ]n%`:H#sr@㦘.2Ttf :;b7AS\g s= yL:*rTjA9neݳkyUnf+< nDLܨ)Ld }B);$43wN =>SIՇf*EH7W.F.p碘Yg3y?Ҏ-5 4^ԍvpXߧa3];[m!Dr_ TlC%|Ynz'iݜQl١h~ 7Tjy xn,ד} qȬ4kW 4vK֛ۄ6K%n K~?+$)ݫ`P᳅@E3ErB\'lPMkP+2%kÄ*'Kc+gv̱[HEG^vR"D_(%]k\l^6 ꫨ#Bkrx%_M:֐ WqeT)נe3LM۩Z2~]bR{[,U=E2}DN4E*J T"ה&>ȀX$oe)夫KW9TG;3K ȑ7J1 a80oS!WXCNm66=J*|ΰd{-a24 'X;c⩎z]TF(z65D ]L8l(YUa y[o#,03j#'C{!؏[`JьIR|tlj_cmH3mW_?g_-R@{"$RA%,ERϰCwj_Yހ[-ÄwH)K(P8;e6cÀecDRrY0)߫JX#[,O / SjsZں/|&5Fh=07?>}7dkߊ HQSyR;þ@Y ˼J7,DI3Ye1b)cR)ǖ_um`aoxȡWc3n؞aG4XhgA8G C Yfk`džU1Uƹ{݋x9mYR|iZZ4٬m8U7.n8Ժ.svԫȃudV螫t?aއ|:`]2\Q3w>LmJIUۭchM$`}zSFGlӣWizF86.R{rB,[rR~5*oT4N5E1;Q/1VTqw<!>;<gyo9N;YUu7+nZyK[ѕ=.t\,ifȔ7ޏڲ^IDa*q"vg0PkL:Q fsk}lua'Tzk6? [CMBZzZF R֚Xr-'TϢ$q𗣿[9Z=jJvbn!V[@_U x6ʅ~t]d&ѕQ:zs1od?Jvߢc%Zp/ؔ/U\g_N&Z kF`CA-^k]-atF@] 7G ϭOי2i9ھ“V"J?PC'F=m%W(n)|·q0b /Y5u?lJ BKX%/1ރM]*6}m1Ջe 5bU} {Zy=y>()Ͱ@}V+UNR FaIQªz(Z4*n =qXzx*k-džd;\\'pq HEdR~LlKd x%OHW]QL 7g`Di5yUi + ]64$yeFH Բǒ/j/< h= g T] ~*GrgOm>^7i?GC{)XYPtS3 ,5)e$@ޅFџ*4ocF1kz#*p@⬐xz-*JsB;"1澧?9IB2xt-s_o9b~ۥP! ݚqǸyX_Y]ܪY%yYkʹ&I/XV oZƔ]g@f1\-qc)Ɣp2oS^AqR.o-};wOU}y[==CSTԋ-/o ]dJx´ApU]^=, sԛ5a1us-i~ U %S~!JJSl@0N[YKNX\weԉz+wiˠźR Xwe!ռgsŝpAl ʁ4,}ƂR9fgzUjTx4of'fؚYu SbCDQ8F#CXe+=>,x Ro/}c¢I&.`TOCMkrS9l>G>S%d ";4ijZ3M*q5]X=f1?twyO*Nܚ:'^yL`oK%&f:*N3j[)};\̮pYNn巡ĵ @}KL1 #0/ډ]P{E#6\k:[tXƉ΅`͂]!E-IUsl̀b~a1}G_6h: sJef]X>R,Q[MxG8w{5-@}>̭oVk?bFwC4oh 6k$@?F0\@]\YsY.[UgzSooRvo)ϲL~#.X69.=F)8WRmo<B|H[mL y1_dgzTh?gt4{8zr~/ur2o_ &0tSoW\IL\/v!ﴊ0 g+D\˪՘ut77IJ7$D4(اijV_@Ёdy;'*)lKzӡMSjakU 3m1@:t&= '1މpq}r'ҋ(w4oھ|,fއuUdV|p&f1mlւZsVnӡ>kM9mڙ\h*e19dh*O9zl9VNw#d=^>pɔ0ܿVKݞ.aa sK0[Z%;C=.Fu+N $}XZXt5\bIV.U]h6(fkk)45R/lH7N: ;l_;h pK%Jrr0|cORrD~:*RVv#B<-+)rWejpuWnjOvL^x77O:}M g3,wbýLeWT*&7~jܗ')4+Ʃ?)TגߖW>y׊R%_\k(frb{~IsP k"s-1wuOd/Yi;vČʈJcܴpf𶄕zIem=I,ίN+PͨAA@ꀚp Tu>B0|qfaCOD؃oP=H`cM)'_oy(actØM/p0Xa&zA0#{[jX4(Po*n^[&Uk8{=Ǵ֐K)^5IbGl}y١ c&PK]uVVDsetuptools/py34compat.pyeM ! 9E(tE=8c:J޾U) -x?ǜD _^egw"IF;Rഎ@vmɴm"CUתt{v7 ikBr?;vmiPK]uVQ짟 8setuptools/sandbox.py۶wLrh銢C+$-@岬-gղ/)Ka3p~P"5lYfk+vͺ(]7BKh [U+>e#Mm{ yclJm*; 5[zB][*RR13a=7Xn'j-TY#ISv]]Z.[u(My,jDQWv\v2k`Śߐ%-/Ւua|>7 ;QL,$7~T^o_@R,Aoyв\,Y*3~a#w !/YDG8~qň' `ߏE$WVH{ryhJ$7JciHIUjɰ#s:5x[h=~4h>.fq(\و<#狺ڊ#mLK^H|%$8p u>*&[kUùi3V?!sc2;iN'NmhRZa0;X ٿCAJ{[W$7 >$v2}R1a! Phe#$s+Y: Mi%L{D_~T踲wG(W{l 8[XT i;Y1Nh#=GϘW؉WT|J&JI5E-Hv A>` A P >Ry(HaBiOG74JI,zrހɁ.( mZcEY"ze[eoITq4B4k *dJkO 8Pf9+o-D Tix0%_H#I&IjM~R k4JubOC4zʞhĐ?s`G {ϑHk<y@̎7xZ\׸McI11+mc˗&n*&Pqƥ6ф\K^)Aճ!T=pĬI ko]a]٬ 9?sV7G1(ץC`vMTŞw^c*L*-mc)yn 7,M1;TvQ RFK"7B۷n5pZHL ,T:Hdobjap}Q!JHk]wsT/R bYS4C9cN\Frs^y_1JuV6.~8' /‰g1'kr{hF nV~[Opt9٩ /ar k|QbLj2zHј|sm_z7ϟ]\?BN"ðLS*-J7:`5JF t5A$R=19xڔmQ5?A%|E`qE=y~ne+k\8wܔEGId_/?I`%*b9PW:I=)ʞ]pӹsnqxY } <8ݻZCNV7E%zS,mH"'mU|-$&"4Ю<4e@WΡL# H%؊Ca1Aq!uN\{ha2,#UbڧPrCjz}&p8m' |u媣"Y\SGiaL:]/2`aS }a} "t0rR6~gbH+yiP=9e" f_6sNOU|ʵ\R)z/’X8 +Ա8.q^*(Mwc_}c7\\Q _B1 ڣ,+81T ;@]_;պζw Ao̎6_eB||POI8u*n#A+9޶R&_= i3z֦` 5"kne~ n,UXz>JoĎޚh`=F=pQYPbN=r:M1g!z b6P)|uX֡|5w2E1otA#ds(pĺ*\~#8r=D/6xxH@\N|ղł RV<_P-YǀbZ|]2oBcyj)-%6esˢ:2k=B9RxO3iaS R-a@+(@Et| NNL?-S܌94$s֚O-?cycrq ,!@*ޜ`$`M9(Ⱥ RHAY".u8RD!!\c<Ō|;eL6G_`_M _YPwmFK {$PPi!'tVIP5),r$/&ݞK= _(l jv! 60p掟7R!ۓFA_3LG G6JPĞZl!D#IL\cS5\7 RPo5m]db 8ЪsW,z`#,(mMghQ.UWYzzZ%f,tJQaP;{M#H<S5#(#m|CfDl܊$Ɔ s&,48JZht蹻 GIjid@ 붱1٬nq(}-a1m3+'z%N/<*Fj˸?^(*@#źA/>C4\U,q!ݫG@K~coM}ݴ;sexӋt`<6ĩ/d|#5]>ċdt'h4=--5]*gz2xqB|95?/MZSk)% SS6XΜ\+q7&H[彼)'JFƸ7#q& Yߙ;FW &cًo{)^#뜝»7r^b8rrK)zn.M욁@( ݿ$نcyb8H8]oeEВ'KqxM[~dhc@ L; /Z&{w}w?Q)DQz~}󓿿k5υ|sŠvnl#y;_eBOCI-SZ Ks-2z_ثٻ+xgkz)~^x/ssCûY{)oz2t(1n#%EGG=\A[ߜvEƾCg]#2O=O}"wM}" I k>XwiUޕBxaX\ru|^-K:q_cHrr؁5W_򈗿[m GĢu]Cy1/zj+zQKsE10 1@Ai)au) _^"#anP#'<frL3Yi2UUhPOu}?P?=z+uSFܛBg,4F́] Efڷ7שjF0Mm"z'WgJV(-uxuɮeyMvݢNXGdeUMV^7~9ש,ǼS`hPF*f'oEڷXڔ(JVO?Wˋ+ g > \YObcH:s}!ߜ'ͺEΓs:޼l-M:!b~+B,8[/f7^]J῟.^Wނ*cV4Qs)2e4"N9M%,[fkIs,D?խ`T;8rG𑙊}tH rQ# Ł Wwyl]d 9ﲢ ݬt$(oܒ8- 2"ՖEGHMާJm#PI6B`e6dPP:" (0ZfDM$ɗ˝eeeZqRiHG9dT \BYfb1juVMI/.pOU]mV>L,DV65\U$şSC {] E ![C!ژ c[}+03%gTJ-8nfQ]ӷ:9D sA-U%xsi lԡS E[}5CPo(KZ3Og3-RVJ,dZ=SM]g g)5Jʤ]V4bTHеrv2$=V{$c?i~`H$VUSrcC۷NhIg%M ϘpԵA9^'y} L.nzw iGb RNB: } 4u8ЮfwMr*}[_džKTUN662^0:q,(YYE$8X =^ׂP{dk+Yl \ol^H %\LKku}Ѡ %ǫa7eW`I}S{`"t v~ xɱ4 IPר݀K/+!mNvX"7˾PARdV=g5vIH,aiguxYrbNj Ooypnwc %|a1,=xCxK^-ᦽAuu}O/&]ͦ./{pUv\wIaT,|Rj'.\ZX4{kn s7fHSSg|N2e[rD{pbkRJimH4uM6'ra2.`񃻑ĒiOG@|h; V.*jAb{>by_dWl Z<_v&ә)Sj.,Չ GiO0g$УǿQEO0D;L>6e"h) >֍\#Ln?0Ň't@tl%lz1b+cL[Oj6LxI+s Evq9{/IOZ3ͳ_d]vy}&y ^8;jK/j/"#ŽqjD[}񢶪PVUހX X3yuԷ{Ce“2cޛ\|FhTU›A՘#O]W˓ ؔR׼Hĩix m +0M 1IX%ۜ(ٳI+ y2xĻ7 *5'u|c ̦olPnNn*P^00T&|(lKb8*_AW VGaqUJ'8?Efۚ9ΟD,!7c1oltÝ,t,Ivtvb{$JfA|0jPٔ(bh[A`2vldE/>(`#Iڦ=2y^fX+ " m\[pH;| ƭ‚C=#⃐*v8TF_Ӷn"ذք_8[%{Y~N\ A(|oacQ]1|/a)S{W{曋撟W||,`G98tr:%$7t} ĠFD7MIhسD:@9&tJFCcy 5A",L_} 9e=z# ԧEȯ*K&4sx[EΈMHLBv/b?U',ZJ CIE y-kk?8eiR4>`ȑq7 UPOZ+vçK PTtwކX1sw"hwgfn3wI)};#:_Wyg8#J:YovxdS@K! 0 qD,GG[]gB>Q~Y=p+y S! 쨫.O8Zrom'Ѩ*uGɕbVK&m~PK]uV=&-$+setuptools/_distutils/_log.py-/*QOOKla| 3HC PK]uV];F&setuptools/_distutils/_macos_compat.pyuA E9Ev0El!UeۏP&OT2WS+ \bE̖S:Zh%xn hkȜPkTv50e(kOd9rj^AQϷPK]uV78L&setuptools/_distutils/_msvccompiler.py5^q.AN* Oq2gqB6`p 0" *xȫŒZVl01d#"1^08Ɋ` 緜wͰ0Bl]uK4閻zD!D0kfCiڤ(b?60 8ɀ`^+s tG^MyDFv9Si$3[vҵ X,r|/Ԍ z0|Tn]2X+ CiMdWY9 b>g GpyO >7|5~ɽWz2-897o'#kX\v¾k]]ꪔ//?JʁZcFˇ/ >7G|]KbC0ЏZx2g6dyXХœ( {i :j%@<һ;c'5M7knjvA{dt"Uj9`/k'!#F&m4YK|My!6 YSmQ%$oмߊ%%sde(sxЍ;REjfx9P6ObG] $ݰhf \WYJdp$dL P MZ60ICp#-qS@i%<53%Nyb˳ۤ3lW ǗCgй?0LYd.-H?0-yU6W)z1O2WFsCt+Ӂ7u] Du-xAT{ *MO>f<|&w[6}J2(6=[ytI7e%wY#6q4*$ ?0HVI4nd޶6Z,'|2gŻ &7[p^٭r0|nY۰Yå@yau>mTWSszTî˾d`VM%3m|9y6Z =Ɵm ^̲$e Qqۡ'F[08d0m 9:\.'/NW?Ad姘Ҁ@\+Dr{%WG,0+&/~0|K.92|pٔ<hETO5_/]XrztnjlV|˔#m4݂w- "G]Qb~/M ZEIC$VUuBՠIEB-,45zJ'_FE< ZC*d Ql'm0C h=K𰈖5FYoA?6D<*s\_E~|~8}Ôb R$Oem9]^zL8yZ*F-aMeH&D|_t(M#B!Eb.ԵTD0 irJ|-fhgm[Gіpm;,$7 I* T }vO ~Prv}%ЧeQ"s2["3Hr<97÷whȽ<3"BԥY|wNmH:Me(Pi<-DɣL&) k:V! [f")8x4E@~{E5bE[F"$up,@ ցfHddqaTT=jQWx;* .co=caqru;׈Eڜ\h1HF1U}F;Hg`' W0&duggnQQ^.)~J$ ZmR[4b]$*l+v֯}H;0"+rI\ܔDs$d3pK.T} 04Uƨzi-zdYF1oL{0 W_}cS1qg8s.JRY*Qq4 u6β\[KÛ%VdJgIY #[$iw0 X*$5ʆق[e\}'b%Rk&_3z hMѺ2ؒa!$`teǥ"|C.b{pm JUw4ҎoEAX wGRB]<2z>x&7ibY5!53:C zy:{ @x :h]>C؎$KPB:wT*׮uW;Gt܁]4 O_0 (ؕ?bڽcvAM0:y#;n=!k=Ti-BcFvkZ6E;?UAAd_9mRyVyٚr?A vJV%i k'^XlG t۽H7Ild1{ u5 Oڕb\{g.J=[@Wv{9 peQm޺>D8Y& ,]MEL_OSAIgǮ3nC^#eOnƠCen)(-hȨi1&(HP}WGQ0C=j: EXAWVGfE?h`Pj|-^?#kKF0nc Fߕ9>Vwț/ ;3}}}Rw`s֛fǗ2jxh_Gm6x}fJط^]5PkQ_Xg+W ac|*]9{_.Tج(xaVqYॵdwC#٬gjJ;J!] FG_Iliv{}x[A Rj\B&!^22M<"aC Ә;kAL]w#}|*Jpu&GK?!47 ZScܟJV^=omTKiRiȨ ?szN'^{Fo1Ş!%Ӹ1JQL foEq={]}I9ڭXN# Jm{]1GZ!D5C+"MKfƚwEzh.DRj&Er`\j#b0MgatHxH}}CmvYk׍oOp0 궔|ƭ&]b/ow}:ܫIe_39Z 0V٦|Ws DM ^ /X +YV`Y? 3bU&9O?ɋK/c*\"xyhĶu$){]Jk;oFũ20@ xs1V:*z-[_T x@X4Ҩ~EFkRxsrZ4?cﴤŗ] n!n[;- NAx +  ?zeJ4/θ8xXd7q .7OMh@=}Չ;uC|G43`CF9]-䠱Kmǡ 4iiyF]|o:T-zWy2Sx_05y7$(>߽0Ե #$>cNHclk$vkYǹ'LnȖ~]ԦAп5^Hbd7ӑ{ S`[_˯ِl)k1wr%x]/LbGf5vxqf K$"nGCm[qPi.`zR7CGI;TF B4 S:h!6@`R+p49QOP"GoBTHΩq\U"niH*)fdB7Y7D?0DKk&TZn0@׬jj0.P,Yiڱ7Y%ù+6~A%nQ$FV f#\{~`Z &J( z^h΅=D39|pE8M] q^J lSCp(kSwAzov跓hYG B4Z8 %ߘ7/OtZpE/MNw>vn6ڧ1; Yoi(,]Rm/_$S;tk#=_<['Sz~;U )lGy$B¹xL"9]A5NPSl@ {j3b@DF][@0w\|Ui(Ů ?]]1uKtplx[L 4-LD@m @q/I5SSxCKsh]bZYZ ttic=t1@5[YD(J{,5 4&!Ŏ~{6X'%lYv8:Zu]Ǻ~[}<L;=)'Ǐڶ#ؔRm!z «zuDšyj6U\Liv*Q,M UKEoa]<4qaba yb"ଉrW*@=oټkvj6@s6 }s#{@79O*N$yn%v4E޿կ˷?ǫ?TX^4:x\lK)gܴEHDI?Α)y Lt$[nޖ pZ PAL`!"[==@Jlԛ FK]qDgjG%(Ftt[VRǁ鋉﹬ZLHt@)4x ݭ82hl._rho(}oi8r_[u#xN3[Y+wfjfi'7G_Kn. 3Y`.\Iԡn&zEm}y2 ѹċ6ek={cBܿ|Md e~5z9KL-zn<5|!X hb'n-YoNzv^;8Pwii?t[@6`~:PPK]uV*/-!9%setuptools/_distutils/bcppcompiler.py[mo_9! ή*Q ܻۈo:]>3˗%l7>Hw3;Z,MۨL6IU%e^LeĪ⛲""BU&s!* QnE"覎FtEZ_l˚XZ3Z],KnOxs?BE$6"Wj8~L:z2m1k*v<>ULfGQL=b:*T1zDxm|V%69Hɬi ;fR_.sYR}fЪiy(-d mlb'9xHИ^;~%wo_\\@^e݈RO._}q\d]5˕ކ?ߺ3$W4 k}+_* l]; =;YZVuHeb'n1fMZ5rMTV<:+w9>B cИ[Ҿ ɫddV{VyPF %?iQӱG$!@4'6O1K iZ=ҸEXHv$ȴ0VB@% -:hf{z#UWU5}<}zh׏\87ASĹ$[!k rztfk [04еg> Kb6.A{1Bc64_vz0G~y߼? VSiwԧm"fN w]1/>9\3 [C葂 &78WW05+Wk=/?olNu6j5BI"CU$YJCɻuLV\]CD@$tth0 _H0 XS6Z|Lkp[:rcDNNl7Վܲ4PҮ4(Dn8tWB\%AOLm|G|qN S&g3 kbLvs~ YtޏFɧDV&bDVHN@ᡶِxI)&xSf؉MW\"m`T,b\Wcb;~F~N HS)xR0(rkL0 2 20u,ʚMHbُz٨Mas /JcS5MH;3d Q!b BF9VY*!x~4$W:ZuR R>ʂ|&ѓ'r)DV"XB&~6wV6s XrʏKhd=g 6qzt\13^g70w6^}lN wIDZHŘ+盏SXԝvA`f>eo.BB4 O@$FK4![em Xdgjt9Q =p?$; 7o֔{Mpc:H 0p|LG?UGjC:|Ll+̎}ԀgnŻ 1TT(Z*;haO铲P*v2{ 9~8&-{.g' 1ߔ/|ȼ^< 4!&:\p 2{ qo\NnM"(J4{EƄ:A^845sCzc/q-4SXd,PKS PE4MJbٵ2p~Qt_M*mx:u`?Hn@%]p=J K.H] -dKPg $] $!,H#6,(%6J~O]Vب8jrOJer@)zoKDs@{t~@kj@մ)to%}<,dxˀ&i!caNĉȣbF*PZ:Q[ β@"D1tpf1LbY=;afeXhGBSrlon¬U^ `k[j#W9Ͳt}@g|D$2pQv=: fQʌ;qKn[ĮSb$*s>AnNTX:<;MZk=΄ l%i;j'Ͳex1.jLfʽ>Ì_m g*SYkSO/l2q}*pL[b?H䍏Kt>.hB =-:`.9Hru]jOP%^41v ;1\Kh϶azU/]#LS5.$ynKSd߾Q^%K7oa3%Qc}8S:C D) (Yl`qɖfgw98ײwBtCBۭ"X#ZH+YVwio Va`e;dq#IcYr0_N2fMM6۠8htWq4U$FuF¸qC :pa亐e]}c8rۗK!ڢpWA RJ7)ה9rrS8WiZA/٧QfHؼ珒gfL yS{Agq)ޔ﨔? ieҋ|eyp.4ݍ,uk:aŅ[}Y[BYk|GpA0Ȥ {kJ>: mx$<~R{;'lTju;&KK6y5dpQOpmz)qwV/IBoqp&ujd/ ,rq/) FferH+E[$ ;Nvq>w u# ;AkZwg$2aq.l Q?`̌g!F}wyc]m`=c9zs|f f'udmO?4}|s﷐%_bYO5wO,GˉD6"Rq jBs> GX.#;P|wmc_mm@(2ZYg=F}"x=ߛ!/³DC㲼/PK]uVUl=2ϸ"setuptools/_distutils/ccompiler.py}sƱ+U+-WSW/gKRYV_1-p1KkKj/sCN^ޅX$0L&yv(jUoE隣ueE&/^ȳyUIvvMuɪ6..ݺᒢ\Vh]7vhRQWɦ]L&GGf[7][5hԛdᚦnDG 8Wsz򺨮̟﫫-ƻ{]W3Dn*!/ӗ-4ȋ&~ͺK}ݸfyԻomYt_vuy>ծne}ף#^0'4KcZˢk˽ٵ!m V)+2,.˲ˬMzC퓍.MvkҺk׌9?p`*)r(.*OqB.[] .`6+ juԚ%W=-s[-wEE@%ۦ٭mt]S:o`K\vzQ61t*wKLlnUSUGM/!B PmЭ-kd)&p4A|v WwI[\:[[PK>M@{̮" 3PFCWU(r@m]1Cwi=LY租~BZ.ڤ;X*/u"mD$cac!7yA&/-Ks!$[\,ښ F2́w8u] uee"6@dY<؞IU8tN*>%ݻP1g@H:Pl\W @!, C-a@)U ,fuyaF9O `ULVd3 ~O/^kz0`^M7DJ mLF*DŁ enuplQ0@X A`we;P (;ϙQ "4~@Ep(Q@dk<޳j mlH9 3iR>9/H"U2_7eG )EDAsںl D:߫B _-T0]:4&庮ejHՎ5@y-q.pt-2T{EYMI8+Or8z,T8Z ˴P ~^ۆ8PiYZ20.\f 1Fc6¹Z%A2Y&z9Br< .W\"VO0"&]#~I{ >Pȿe8MCm#p$i%Jl- R&ayYJ&ɝuʳ&Y. %z` Aͮ_Vlvⱅ$ů+xIƯx)`:-;h$X0,2| Dy#,Z4(=łM<%b?anܖ(`!rf z 2y2c(qH`۲XUÐp ܳG,lcG\c"AKvc)*Nad ldxQ+l0)@!$mdz{84pȔO&x#?X'-ߐK2HzMDg-KZp xJu̮r=2xpnȑ8Gzjv24L|<0 583$)OBL(7Ub#="1ޑIt)Zp|< ^{8v 䈒hXJۺ*o01mjz]d0`PҌ+k+xɓasͲ_xZQ--i!zP0àM33k6 o +L`xsD'/߼{E7- `j<Q E1T1R& O9w e;1tA9j a1=(? ߕ%&w܀mܕeDZvPB#aPq\9VW-P%:U ȘFZ,7ZI}aNؠ2a$K8?\ ˄h`N1JV2?@i_pgHdg={ٔ_u:F>}G:mx 7dj 푟y?v5@; nSHH9 (X"~Q1O!(t~PЅm\PF<_0bTCxCş&+ ZaZx̮3~YbҒc%t\x5jԞ ̀@Tvrb`;d )N ?2n0d$U!r`L<"+̖S75&0yr<;  C=loV?{.*n?g'b`&5f#&nV-Ug7sFGضy 8~y7ﭮ8k C"kG6',4c-007֎h1%vmĖFf&naʼnM0KRĠY$qV(#(1~jQH ); cv"<A:+ 巰N>vao!V+EE8&б>:^شs>ǚrF%ӽ48 'lI(]hXV*ۢSK/Z=vvX{`C6 =o3Ѓ9tec@RQ%8k&ƪPe.c{Ym =+QҸIQe<)h7c$aÔwƁvRud,8?F_ dUYY_pKCoumpM!9/{8a?~b2D%-{ Fd(Jk#Aq620;$D6(!\rDA, ihv77O| ˄U=c '%?W9DȞ>O-NmqQ>5lVz6Ay\q1q;!, [Τ)щl'II 0:%v>SXx5l:U?;1zPb _I[nrNy6!g\*w-De/\ ådqDǷ0kgdՅKm f3+/a؆z/]2=~xٳ)G8`$YA90Pf̈́eO1"ʞ+* 0ՊI7*YK\*>'_b qlz~TMK!'~rƒnǓg{腈rĒ> ۢ}Sx i"ۄiI3Fe\"apYE,"`x+t:?Mgn5ԂbC.x"1@*|(Q ozv'w*O5FCdӐ Kt[![vsc2(75kfX9;*ң޲g;kwߎD8]~n$D;:nk(#2blv$Im60 mÉx@噙IBn{SbEET| 58?`vk2z\t}({M|A<'j\{}a~6Rm F, C"Č3՗yQ$lT6 jԳuOo^tXݎ=ܹc<32s9Z^m_?|1f #ޯ܉;q!Sa`r/߾"! >5q`ƶ*v$Q$%6znŶkz@y8i*d'7}R('sIu`#G<3 mH?k٦c#`< iG0Y*тc!m>z<~4a[1fĄ?\MVSkJ{`GLkD %A0{ cdg7aah̲\EhyC&+ L-V_/;G{X}. =/jV/::Q{v1F꣋r 08pߡ0ƻEjx9@:$7Vx7Z0uuHGDq2n/pYcVSa"<# URryCM5€L'TXtJMq#kl7FH54E9*GIwe$I0]ѐMt ,g>OhcBlP\ 4Pcku!͑ab4D6sBJ `vy&mL=͊qI\Uq+Zo#U':7.C a1ä\LdAU&cPu#VIIJzNg+1nRbO5)gg PY?-z 9\\8ME拘 ^$3tS\,}h|DCBz eag5Of&ᣞv6dR2Uq(U9E:$QF%uS\| `%P9yz5U 3cWKa=IR.RHcΞ Nׄ6'r7(5Z˚xn4iC$.rT dpdfL\/ןN) 2Aa3A4θ7 Yڒ7gu3<&'5 ҝ CYC5с1":69@\yec/NPAC)[{;N좖tYK\>.S5(v9KbU0"w3p 6#~iر6sNǢTi]cj$ZtfT6!p!%( ACcTHou/"U x/׫Up1Uevf~X4ʹ>Wʶ yaMz,FqN}1s?5g<իAy#F1}PdC-\wϊ .Ow1q3F?z0 [2z*]D͕V%\a$Ǭ1\.B~zdXꩈ#f`9 i$~wdBgEy6G80r"s>e{ҤOˊIQ:v2u5 |٘Og?Qと2-KPйj=c۱l66 p>ݡ}U["}PG)dsQ_. |ۂf{fmȁ۹QPsAUN~DQǾbܼcW*j| ]rNYVvlMb(;Yh s/9_ ΀ڬ>#<#7m^/OH[}/W)dKxر`.ݑ3ݣcA MĥMYm}52*:_qhz(t)y2A͊٤<Mn"_{jy.#cTL ni]/upQ\So?[h-3޵5 9_9#KlP.{nEvU+C51aO S}gYELj($#8 (vpX+@&wO"k2&vt9Bb# k7@R 0vVHɼAhs/*;=F5B ꭲ_ Ƣ2HBȑ@#evWm] cC5Z1`,*XiY*aRdd>E JwUbE豶dp=uGᥬi}p!Xv8 ڤto b-"p\ &KRs|ނV:91HD5Bh(9?i.o%<|<å&pytOe.S&_<ž:O|>EiW|]%auU{ ~]Cwhݯ<\Q{A[\뢩Mr9*y][t?W:jtҏ=7c(ȀL]Igr?)9^8ĸkXϚD^{1,/wf:^C>7?±p|ɚHb6c<$qp̑ >GaMn̑A΄^ AZE0%b@Oߥ@t4*<'ge|LBzdC?0 o̚cz&wT~/dϟzd)_>X^`*jٟbU֭M@wf_#RgFLӨl1)&+[wae]6EXGMQ7= ^OgNGؖ)>5g &?<f~oĨfvc:`xfyGmԏ$~?N c,$d_qfk:ԭ'oIls h ir0N+2*Y]){_=Po2]vUM{,Ǝ'4 Fs=NPH?^ewgp[E7e, n2 ?팧f..܍B2?xAy\уFpm ǜIw {8rz&+YO^ =-,~[ t7+•dfsS|۝ Dxuz\'x(-_g&Ys0BVfT)8mq^ܩL-pl÷X]% |v$t)],z{RTF).PXs,~Ft8c2Qf`ǁ)0&$ Fކ$`:TI(ASN\|)Gm;L-uzTV3wD}NM֛*+z}[3ibc1ʯ-|lOKT6(>)?jRWOYTU9M g Uy,iY_,(2BCl wSM|Wb/Q(cq7YSEہVk4MAʟ${!ҹclu91h'ЯzQ%({66̠!'_lm?O7[yA_]VtJc3`T~*KUO pш5A͏:AmM{gxH|$&rZWEQh0KPLWz9/,$wcrݍojy(jJe8Of/Z*FRP~X_v}A ]򫄬~>yΏ11 ˛q|GG :s#&ȯ^}kUݔ(O0$¬ǯ"A|L~vV(2s fKmҕc,`Re+t4fzЅHϴpXD<t@X* ˏHtP*%s]6Nk\4jϘ]gj%`Tj~ϾG,e |48͘3COd̜ӚM{9Ȝ"0.59䛤%2l~LmE ͍]X߂#<!-nS_og ߱|\7JK_ΓW3};Y,kjo񶍞uBlPk:7' ۹xKR{-Sx?SRŬ% -ɟ^ )sj=^ϗ,z& W*^ uѠb azFu|W\j-9]-8'@q틁PSQ3vŊb/ jo ^]_ nʘ)L]W3lДR(!ǖɌdR3IOHjM9 .nKu$!5.++,)8+1]\jB0O&SJkfHNO-_zx<ܢWqv Vv{RbiM7qP伖ꞠбNk]`2縮-Ϫ|:*D&|MBŒgC[ut$I=֞*tR/@5 FcY&=S#Ւzjd54Ahϒ Kąk{rI$@T89DYPIKQ]dfɳIJRAP=g!R˳ϣKj]|1+?{8תg0Fģ7cb ;rxA٥@2c'7k\r+`v] -xeik,UD5PoY{`CvT0^L+u|bH;c!q]1&bB(W:22?Mt3](4gG>5b@ e9比yá"s!w,1xo5NN 047%[-kEdZs︡,(hiE!Q٥veBBޗF/1>.Kȟ Kj1PㄏSSGIG};5`Uq-*Vмu҂%19.u  .ۺsrx8U B&d9.ƥ4Ug u]1-yHlNwqCS'ymԢwʉ$XFDpi277h((?(uƟIUgaN |$N1 E;|9zmdw2&b?PK]uVǢrEsetuptools/_distutils/cmd.py<ێF}h?`:g v@K-$Ȋ~7;ط`["N0Um6ũoR5zݞNyS΍.7}ӣ"M >?M$77թkA_[[UsImt߷Q[n;|)|.Z׼/ՃUꎾ-,<777LFx3CÔ1W >s19NkvO/E^X ST S`(sRQf-k`ع@G!|WCUR%-q${A~dK Llm d U^W譌\S8%D4[$UOp9-]Ȃ>W =(e&w5SޜUq$H/1g)Κb6[^ mLvȁB N8 pH2pN֥Zk@*;:ej٬3bP\,wQtSľa Ƚ{m8gqe$cʮo.:E ][^B'kjPe wߓԗ@E8P= e$0"备c[fLk^  - }yR@x+CLr"Zyݦ-PSU3vB&Pa1dTVyn'ۺ%{xy{dJY@F@`6S\ L> }8 AroX)Fȅ."n }Fc uR\/!pGQ{m)CcuP+U=-aaqW`z{6L@!|x ,TY+f?%c7i%ğeɲ„ьY4y_1Pa͛O`3UH0h#"q+EBTT'UѪW A~c턣Fݩ_~ f5,;¹ i!~[ݮY5{`&0gĴu1hf/T5+~.\Xy=!ĆX# l5Sߟ)Ĺ=1nСó6930"DxhFdF+E]OWK)4[Q\[MUllv "xG.:F6P#POK!*wĘx8gP yq`R6G j[ .n9;4DRB@V-w|[ @B7&ƋԌkhKBv+K\ +ЏZeA+_H =xysE.7FN5 JXP$TsLBД5Ak[4u{~gA\WB͍VBBk X@uh \@akٸ/ uWpQ7#bWc?C|awtX'1HB\f54Z{ޞa 3xrhr:lqyZ *ExѼl^ }Q]s 11N6ѠsDo7I x}""2هPZԃ P[:̱!A]~0"-pQI}q`lG&/Ս{J| `$NB̕vCp/Ʈ$7LӮyǪJP8Cx޺Z<;né#pqS@KctI F< f ?ƾE68[^D\~7سOFb6D/ խ~iݸ+,ܺDhmۗ d,w|Z#SQBٸcLWDD;STVf.A @|r+<b/n=,I l'B۵TqQiكKUHJf?0];n:U`AUV-}pWbo:ͦt4l`:zZ5魣 ֳE+N#i+Lk =4Awr(y |ܬ)4VS ұUȘ*;l eqiG_ .Gпh4xW|?~J6TyM']̍ȣ1H[c}qO}@=% rNQ-X*Łe?{1?=5|Nn|cWGA)Ih X CNk{ Q9Le7gEhFhn~zGl%cjZRb/s`nu:p9f@R;uŇO(\X~LKs$Qb~?: *$7Ey * $G f3`D1Nq/8 ek5`Yz!v 4UӅ~{ ص 3z0<)rI`צȠ̷ E?jD | hznk1A &Z,T62KܣcQu*]%DAr*D*<p!¥|)bX+&e 8i8iB 7WM`] + a6]>7pbD3@S[2E+$\iuO?sx"@$BDŽo?GL m󠛊mA~.xL9'3f/ GSfGM< =b).3o8 R4(؈[L_l8g|UoѸLd3' Go"J~)m{&LɤSudͤ!sfZdŚ# )0іnU ~3r t) {*3WY1|X`TK4م0Zcj{]ogbĬqp:'[2ل VQj:H)^?\΃t( ) _u:i\A@ThyQ-CI1`FL-uYw@ld "_6в[CU@-.#E +Yͼ ^éJ$TL8f^;y7WdG\c W&y2pSZ<'\锭qC/+9R/R[DZGW;DwBS`]?mKR$ o. GS *#þb4^b \P-۠BēKF-pl*Wa܈?◱qܢ|/wF,l9yLT7KK]Dx$׏9`okwpzmg^&`ߣC DJo2K5{<^Mԇ>Xp=dX|&TMQn0&DmۋWe]+R?xպr7;˃.5gfVB<+{~2)(=ػ >0rrѝYo ;u2)mzw#XtI(ٖ믿772fQfLosFێ -2x@Mtr;N~m$ҰK?GU!H%{j-Ǜ0C'ϭkYf*cMLԇDMJԔ3\f1w\8=~?~{;ܥɒ̲w'-C ]3R4ai\N;DYvU)i}aB!>El(.0i5왱˷|Ʀ//Su8_|a Tխ#O7jO[3t:whF-!'qePܑK3a`OGpyGVtO 5@, nX=[L#ܸ.d\}*Ex|~<cA(. rら&_0e(#- <_ѕo7UcBF[jc.BAJyr\/H4T3B^REgL ǜ9iAqNR˔EGˍ}R 7*@ک6@ 5K: u3b ڡX*#ld v9)bM &`jOus%y40yf.'JK^E}}|r>8l\ے} zlؓ])=U՚R-bzji֐206ۮZo>>Pl共4J RI&8d6L - UxGl' mt/%+58-df2t,\uӹVLn`$tCIwq<㴉9h!8u0qV5t;Gd쁔,nӻ!8 H1F!g l8B T<*ala jbK'm&QzE7dtCAa7F9$L2B1;ܰAXopM|ϛ?+MzپrSv`bc ;b{ kՃZNX#]0?w߃] qs>DaG4n "~0N7c3ϿQ7`60 i'~a㾞^hf:OR13isNsַVܴz@^fE.QOx;'u ~b~X[>Ѧ[шHUʹfi1G8ܫ[/3wkBu9}xo&l qL{kEԠeft#0pmѯs$PK]uVcv $setuptools/_distutils/core.pyZm۶_#*q~x26mN "! =`d}BI7mBb`ee]?vyiZ,~<(a$S AUr7bn:c{U%^!QW-p|T?t+ڲצ˃AX)DiH׹/jgUeJVo,J4M.kr!`Y;Dvu+LlTb\.|la\xr7տ yJzOZc]x-{g36K8bke潰{?^,Rr&1bIʥ@˖ù@X`{"0Nwo^3ԇ^'Ţ(gE!w鶫Xy1ŁQ`$ޡa~<Rl[D{%UV"]-Ot%P.@7I2۲*rXҚqphal[+)Yc:8uɠ`(Au S(0lM=9VZn?"߆/}oF+?_~E z C`=焥܈> vlj(X[ )znue# 7`]}%fYz!R pa[Z>Sȥh UA$aa!K( \j p`iU^#EĐa*BBL %opn*Mn3䵮COqqKh3@5+ @A.S G{/12#R2pp"Y1̜`<Ձ ; TX)X8Ҹ։)4`Z9[?9h~_Ƅ"hYu9Wd?{G-^5 \i@T`-!ڇ9cq^DwfAL:HT&-Tb1 1`i B7 !?d=1\(/̗3$^k12Clh,bZ_o ޫ*wf}:c{2en %W۠G\Fk42*z=&ĈSHks\$9lCY]&vv*N _]OR aBbtlpr9w_ޭUi&}usi~c5NH[5/%*rpF3 ZCr|QBUs6y=;+ܕ`4缁Vj 7oOWϖLm -b ֣CO?~ZX$>cDlhri2+K4[p_RV)s$NVHQ2D#D ,,F0E;RE jPѶ,zS$ĝ-%0wo}Π}|+xL7#!8gt%nO2pAp6j2W8znY(6/݄-q 9.NP줮?y 'q@S`Zg]!U'~e U"5J o5Va` !IZ&Rv03ʏVʇ} 1ȃ:YG t:=;ٟsrP[%Db6b)ԟĎ`9%adϗhѝvLALG>o;}ִ\vz1#.~.wd`|vK łd/Ea Ԥ|OJ"zx4*H? I#$%Tt>h(BRrAV*a[/$dU¨}hDV& 7&)~ΔR;T"e x+,cK?$XP~35Li㽡FXr:~A))dJǜ" lQ\"C>R[ y q1>e%egԕ1>-D;߸Sv.#ZLE-8WyGf P>Xq`JCh6\7)r"_e+)~d46 G^sU0u.AY+ )upg:pqRN4a㪠(@$ |U0<$7H9 Mf/"1?0p'H\L;tf ⥤V*,Q^9Ǖ;&}wT.쑭s3zooxhWuYpFL*kϼmnM&<ݓ7"rJ~%44f!*0${$L#I/:D wCc}6$|0\1 #.~10[-6hB A{a('u$t}gX_;q4-dyW`aOH [yWJbk**r7˧烳]L-./PK]uV> M.(setuptools/_distutils/cygwinccompiler.pyZ{6ߟUPH̤6M MȣE6j$z, KٴE/Ll&(*+}U˴_益(Ķjf?ucUr g+5ZYVԹ 39\Lۦm]Oi˷l "/USL{ֳMW=^=]X"-gi^5bi~lE,٬j0iu~{7Xv75?vy׀;% (8i7зC?Ӌs· k|]W^yžKOÓs>Vh;?,WzOէ_|h}"xD?9{}=:vzbNO|zp'xz <ώ:~>ҀCrGa;ܡ)jx}uIf3̰kkHj)s(h*ޔ˻=}rɮ]o11RKC{ T+6Ixb&*?}yN)aT[Yd0&s磌67"x?t!b=yo}?)ʼLV3IQF y0q-YľйdFƙqVVF6T ̯jN^L0Y@ĂR9uk j}迁uPyr6)1cҒَJ?T$!$v2-(bqb5)~͸&ThneYWoq8_ֵ]Si"k< sNJȸ -Lj rUMgY"y^~]!~\,XW.0sqc@:85@*9RZb3:#XW&KWu7ҘHc?TF{>}fAM?QELToGz$Ғ._?m31-jtJe#G|,4x/)gj,R40X!+N<8C76/:0E| yD'jE(yǖ{߿Oc7L q}Fqz$ +bɹX/b8dK, QL 6!/ aPRy'-;Mbs "|,+QN[3q,Coh{A 3se]"(4öcqaMI-o2CXo07jmzߠ4%+\") &ArC@Fx<@aC]?8;y𡃱UyBbMhILJb:$xyI:͡Euu ǣRGJj Qk_4<sqS󏼾8[Jw9!*5R!pJ)@| R/mnhXPt`UJqQ`pA}!Sj PQ[LP!PYI7 <R&:A(W 5Jjʭ'8BR3mP.G"婂9W=ADoEQG1ZPK@.H=AI [8Aoxݪp&NPШB}.2]FQ'&/ߟLB=FӔz꣦B{qqyH%![RЌ UhWn0l;>8kpiFWa P$sZ.|}j4 \}i9я~x"zWo^G|])R.ozXx2C, 6_--!ykPR[})@{vr?ó,eˋj]q)}z/ Gvva\9/yX|?e~NjX(G"]H!?{Vg=$ȍr࠮ϔ((Ũ0DH%;j3`(ZJJ.PykQCOOh*Є.&`ٻw*oU4mU '0:F'?HlZDKp0ӵ2_oͤcc: gF m* ~Aڀ`èxGwqxNr6QRn|H"zGy25i`Ωv_6MgmVTӯ"lK$OrGJ (ӋZ#foDmxBN$I#Ki;}݊xA [.ًJ0Pq*ox N=wFrmQqU]ep T41@F%0Z:*ӿI2dqj;ͼ;LgqC//QR`ՓWo?{I?ˏ鍭+}C F~5QxIBm{R 0R*էJyWIfwa XOkt!7~c\2l~ݵ`.?(s)NuN*\?8itDhTxf@ѱ !:a[- U?(ȶ3{9\E#JcM<$\ "T۶k\No݁tpD#ۑ2DUE "8!%O,7Sսxb'=B%eJI5UF(qps"c@hwENܚ";z|+#"J\Q&fLSi N! #cL]uP__ĪELI)p7uPvw,5ҩ$x,wWaओCMڍz1=qow ݱ)LTwsO\g%eH Ih6#oAQnEl8QuC_,mk:.iGDX&҈1K4Wo%xS.ƕ(?VT"0|t{ 8QFv[ԾGtv$C Q{gH_PK]uV_H%usetuptools/_distutils/debug.py] 0E|*HE*pk;K y)ko[8㜲"cNp_޽wOp(24RA8놢+~XjgD[=XsIa\PK]uV5V !setuptools/_distutils/dep_util.pyWMo8W*(dc] @ `$&"Io!Kvk# g޼aʲlW[7OE7~+͠kv1VȫN:/Y8IH]lhZ Z3n|vKuf5R-WVث B[eYXOk:ZKkuOh?WEQ4rCZ]83ؚ!VMAx Im*H9ꌕdesgQ8 [Pcӕ_% R]6߅(N$Lt0J)P<,8nSޱ1n [s,^Y\}޹Sލ{]<[`P'AЏrϟ1*ʌyqeup'ԏivFT|OPvD%Ze:!{Fs۠Vl -S$E<+Aagm7-9AiC )<$9LDNkP\VgQ3~ Ss?!In@<.e=m.ЯfW?cz6o*.hc78ϓ3 '' F^8w1BG=sӼSAwU0N, l%%+kZ!pj4:WA`PD(S4)mDArn%F{`y} =$cCcr˹]&`H\9.ʇ;1U?˨V~'~J,MHG80{ f~INpjD]'%1-D+Alwy~|7#/Mw+c?z :}rs;SZ_ބ z *erX販[+j>ŶKsxWYVxC[](~旯zbBlfQ8WWBWB&Wd*rUyW岲X\&URW±r*ۺq-[R(UUsX+^rq'~[,*YEW^Qڨ4:Ⅸ?o7 +(Yom )2x3T`"PG FX""# K"m<YYd$6]*ĪDk$)#`5\{(XoRC4dLWu8{wSSj8Mf䑵j9)ӭ(~T;KnI+bMȧŢ퐸a+uU(K ; f("szڊ,` Ohc;]}jAAҐ @VNJ&❍Zhh%%p:ڊuQ@] 7RA* M(qӇ]Qe!&޼*MM]tT s{D%a#yjc Ծt6N3i"q/ƹ .;$bM-]-Yf%ckI >}Bx$E¸V(@Sp9蠙0cA {= ! ! kQP@*]:bqnUi{ׅS4GIF-G{Ec !0*vݗ>;)mUZ\|W;>6riQ4J+ellʵLIVΆI"d#={B)`BF%efT͚V 4$32IXe2jm|3{/V "R.+=Wq2>Y:b~MsW tOg25n)9 ;ꅯ%N X!yKdﵮz^(Z\{ɳ` G)@uթjԈT$E^e*N- @y 1UaP(I2R_6_a'z1^fCͧw>2=9E?6'“ܫG2Q;>9nt&2ـ;O9dqlaB6k.efb AS¢ѵ1EGa@h P;FA(>F PiICQ**P!rU4ׯ8*:8X`Brā o7*;͌&034P!|GG=hl䜀mƤn(@"Hl k8@0DC/[ tp8Eh%| ZgPQA>O܀PbR$=y3l, +AUF=(jwjn"?ݸ>%8Պfj9 ڽZs;zKH5Cf`fDɓLɋzHY#_[7򫞐*27m.LJ)A]1IC=UbH 24]a$C5F(`*3lA:/u&V~&CSAql9(/ PH4{zr =Ć:K|7GYt<`na: `G9DG_.eRjڑ {hz,#D{Mqe:Gn1&@(<hzڿUm8<Nk44Gs1ʤAç3ۧt P.y-j:@#펢n%.t3ή!y4vM9K i$iᖪ_4&@;P@o?/ p]bāI :1%}, z B-K ֕59A <\O+jʯt-Y`j6UYQx=ܒ5)(&qg~@ѲZcI:=OM݊@ti*>~rauCZܘ#~rNW:z4w15 j|F>ø 8:k`3 9/wswޙ)%<tt_{g\/E٨<x!ϵWNjSk*r*ŸfTEBUeg$@CN6wj;ovnIUuəSKٿftPḙN<+Yއfizmă#'FE諸eÑr}5< R>qdt._CWg/h(0 ၿ$Wۄ}UL}yX@@#of 2c5AzD1G}-ɷ e_)d<0zS/W =bgQ/NcOd;CALתU|p<{ہOgEF*9qx>5/fw {W<1]W(VSr I|Nbu2񃓽7czPK]uV*mL4setuptools/_distutils/dist.py}kwȱw > gHڞU8gM8'3-M  0(WwW7.aO|G8w(-neFpBnԿAB1IOQNK!"`^vam\gW% sU -|e3M8opd SyCuc C܀Ư5~$ *\M+'8%]Sˢz6a_i7Rl@݀U; [0Ĺ6o6g!. %7q_\#y&Fɯfz_ZސkWoS82ݽFuU߂jf6 &@uk>< fo ӻ퐽 ҄K)0 ;+KS/Uwy𛳙aGQ٪YEPgw6wCСrЦ'2kaJt(27G{favmQyC֐L:BP t@m1Wڂٮ#ߐrHgi|B ynD KBb>-7{D(Y$PR~Dga߇ uCП QQnv! b `˛5h]״gSQ4{v 5%@MkɄ: 2ZJA(iLy6.`InG Oȩ1!NeZXXX:< [P drPYV1Y0[{=X 2@DvP_r*-Q6<(z0U_#W#  o.ʋ(O3.f|W4YO< \̉<X\/DϹU"β (ӟe$fd܃9q)auB٣s2/vjZFV`x$#J㱿5h4]Gyʘ啂RVlA1pvC,sp2$Rp$a_~mb8isxV _h$_obcX̬岄 hy2&xlEѲMn>_zo/ 4;'{:ʾs9Mrܷ6v%6i-W rꦀ6VJ?Vt]^mD"7ܐ }<Fw*E@%\!.}U2F 쓢M-ڷlW"yhd0H:ǧ )کUI0ne*s^t[6*Pw$Zg#.xFIM;Zǯ1o8-1t&8ȇr4jP '7@59XL!1pv. &J@Y2mjdY㉎>𐸿J 3Վ,V Ʉ{F+ &+gK% \Hu7Dkc@/] ѕ-Q#m <8@Ыb #~v #HD&JAwq+T7*ce1zg-n1O\kп ѧ^oʋIGl lg-;M/+qb/GB+ßԒ-9hzrc}uË6tۯ,8!edǚeRVU9V4MЧ6C-/K񩲳KjB\6Hj\}B4U!dL!nw8@pZ Y^k]S];A" . wrA(EtT:]_?j\xZ}RV |Lx .h":0YqEBòU)©.M{طPĊ_AŸI/9j;#MG!XZ)(LmQ̆%lD5PXs +Tr3/f`9Kvv}"/#ݓ^\~ͽ(.6!-V)G:4StS^c4~a]ӔRXE([+0k4HhZϸmKSML]O0b~ !C$,˓ 6HCBq'PPGAv/@)c*P-# ;Y" UC%ƇݙDJD[`4%YE2:5S%[PDT],r }ӂƢ)5|lOpsxxPOs.&2Qd/G׹jŇIPCܐ`1K$Y,z aJ.(JmIQjaAa3jm穙6;?.Ze-F5o]Gc6)YnDˊb~&jQ 2JYpWyks3~zT9ܧكv= tܧ4aHi`qA3hTFm¬)>ث8$H$4{p5kĐ\:pSPggcC;1p|3\~8Ƭe~HG[#_Im,(QqTg`7(v \1&dSRMX"0dmѪG[[{w-*D8zzBBD>Q>27nεulI8cǭ5ȟe2Uw6%l #lvN$?H";i<]wx8@[)4]'4| PQU#yŸ[$˳̵N_oP|gD92; üCيAI(){1wLH#L?hs;Hd鯦Yg}t#0{\Aӱi%^k5D X(T%SaC߸G?yit:B&E-G>s=C[; z=*bV $r-Ż)ۜeʨsZ8kZ/AXYw11 BK!Ҙ,lCKEV%kvJ)@' EG[fJ/~fY/$9B%ju\΍VyE>z7۠Y0[,00bnE! O;8$PnŅMy8?9!*+\L#c:ұsB?v;s5}{x/Mo#"3xƊ3(/9C&8xlk@ 0\wg;-Q*Lw?b@=]m`qWPh"cMQw Ž%2ޘOf9~+[yS]d+5_J 8|*JIS#l$HJLɩEi: R;hYGKYbtȴ8<96[P֨v'I39il )!aw.p> Ҙfgs>t*Ba[ 77dxS6"$;ژ@XGnG)7+4!8k-dAOBP`IKԐ[Idr$QxLjM#[rJsA50wz$7wde͒v2}>lL!faADWK$A"F(PO ]2ьr4WmYT_^ߧ@.7t4Et{LA*ʶ/v 7Y6L)ۇ:a,yQά1r:b uC]gOxuٴ]hs m#$Aq82eBllabәX/`}bW,v6N_H:>-m!4̘-'*&c/圬+_b^H/rQ~+yzP8HC"7njs툚=BCC*1r#Xp&X/+oT]'/aK+-5Dc߱mBo0!' p\&”9BkC:)-z*)%;ةCn)ӡ+,ei R V)涪 R`>4ogGMXIBdʯS&[t 'Ip'%֟]̩Hʅp,JO%>^L"6`-DJ:lW 4/~u6?ʾ Ausx }r58oϨLjGIMHk6(6 UL]qfӒC(aVco7ٟiܲ FPO1}Eu>iDt!~N|>cEp8oQE6dRJH=PY$8kAXGA۲}Dq4u?5wP`%L6 _{j&B3{gv;A#|p{;RL/vfƁ/ۆ&fsb+0&Guw*f1Yi̲0CHqv-"ڷ '+xT2fd-R֌.2J 43r6SUk/folV=98ZMLPRVzz*Xv +I %O?7yX1d9붶͈rja2Frt|ֿi)hue/tqA"NBJfMC^(OkW.w:VҲٌBQ*ST zMAk/rGlmsmi v+2</~4dPʻ8F}S2cуbWTCOA>#Dɼa`Т1sPgW.+E^ߓOŧ$7Hꡀs='yԜ~VVJ&M'?*,MӋT"||0N,cޮ*Ӊ.U8ibnq@;Nibi@ ibX㢨"SD:XE[. ޺ƨcVŚڏ',QyLlؕi"! 0~]Tvc8\Ι壥gaժPbmu溏Tu{/elѠn없'h8Tu JWWvy. YS+)E{k)zS1{wwd|[RXjj(2@'M^O6lDT^S c42s3޸kIi<ם-t0l]~ZG4LE{Λbw~JrVdlP\Ww16PpR{P_{ْf{@̞$mC荙pZ:{C*Vf?8??Gʺ W5r)HRn\,^xe|çD?8>N q%0`q;T B8Q5F)iYмg'l-6p^`~eoo ׷޼넙6]EC[ٽbPhI'ӓTx,ESḇ$N#'^ KmΰOޛ|L]v:QA_`XH"?JȥҪ7UeV5, deⳞ &v`iYMk:!}4r7kdi>g |reBdW (4'q1=֌OR*?0[͕iJ]ဤTtFsu%_ t [#k;T-w`ce=}vu{1JTŠ AG;b#oenXj< b캥K!2.lQBIgeL3TL"B?^h55u {JjL%C䍒8(ߣ34փ*~"vIOBw(S ]8LULE(>Ri s8쌕˾MۺZ,q%I#WeVr=$fp/3|fr} Go?lhƏDž,uF@< ME FNyWңkB25\%N"GTff[sUI2ށ_>[B އ(v[ \3PqxG׾ol'1@cqN2ag% YKFyk, ד{I\6aaQDeGXrLTj:sqX/6}3p{Fx[|SJFÚn!)>Gϣsj/c@; O'xQ_:sc%PFAөdV5PLQ }b$C6&n?i{ܷ#۩hz} ˠIkeLbc*'VLYw0u0ѸG~(4񲎲6䶱j*7հpy=YR$5;)h;*6P\ͷ8Fϡ.)֐|9@ApIMJ%ܐ(}^DlQw+u309y^oKtDlÓ5r;c~ЅluȈGޕ#-  thgm(;EXa~@xlLBR64Exn8ڡ;bZ^گFPE :Rp*A dl> 9ٌb@U_3aO/]/+O1O[IPs3<ӞzУ57v Ƶb鹹' |(NߒĚe0;q?i夢y 9_DD׼cAך|0OO?MrW@Tmތzbd \X|hubaɵ7*Bϒ;HH?UɿrX}Re:ճ42Qtb+WX6 {c+N"7:s9%[>LpzmЃiwS5Iw1(nI!eҋ'[>C'l9 5zx?ivt Peu9^DvIL?9 !It~*xS0AFXy)9HV]%`Dd 7ɟH*|TI}K*&|F񖗒p˽ #+p 0A%MPjH%$}Lr"݈w[4z:7K{dB݅;;2SݚItGq$ 8#N$Ebq."QM|KETr+aD|nٻ~h[ j[ST<>Of NØvDiDA/Z*6~ꜫ=бf 8u)λߛ(S\D"'LB*9O Jt('7WZE7W#TPWԍrV#T5u0KWr{ *"3 70*3Ӽ| ¡|mJ.q@dtjnp 裡iݱW)7-SHZI ˱L]+8Z+[.ۺ&o0drKbۀl-Fm0p tqw͔>/>S{3Kv!a$Ndj|[(x>7q * I%cLI&:J+NV&+5wutwj248X/"%&t7#)< Up 4RBdzR^"<a k^t .ou6J Z$ځu1%ߑg=4T?-w }K)ٳKSw*x趗"T6+jVÑ̩%tᥒh9IʥQ"&ڠ'rz, Dـs2''367Bg?QӂsQձȣw*| 1y%2TSDJZ/OI,<s8O.>'1ܯ$=̎zfgr*ҽǶ HTٮϘKOq3WZS}J4+Z bdH,@]{nGt[@N}hurW89[ݍmF]qp{1h.8 tXYʖś1k!ArelG\3kXQ@-;i_k+uJ$"ʅN6dqc/Ϯ~48P/M'*fU@JGCX{-1!%w+w=4QLP=H._zo/O_xT!P4daE 0MiǾ  ZbR\~ģUJ00E8o/fojTbѽm~].N`( +qi;zC) 5_3i[)\/5JiKgT5`#`hr,زwÐ6H< 7BZ`[Llڶ OklX-VmΪmrףvͅ$fC / ;*?3P ~G$R(HMϯGC5zij?46C 9@LdX; Y?}n`XRM8 ,46hjFhAVɅߐN!\G40EJQb0P=)Z^rK+f 9+)@@YR)@SO+@t]'*:N0R]vhO $ﺇ)?nA26@>07MGdX9?;XZNR?ʻSsaٷgx*p/&'ߪ|_߈o:$bO?n9Dxm=-;]@k1eq"Z=&>F-Rٗۅro51m,xwjMBzFD P,"xmj}Ra!$G@p׿[25UXUcL\p\a߁`jdXoYVCf9ECQdby-D]i~cܴr1ԴmbUpoz{u7<+b k0}T=ElP)P^_F պ*5J"Pl8U& QJOXiM' nR*iOƥV+$믂ujlA۵*tJ׶j%nSK6lv3c-Q9y]2m|T|yT".=0 }[89Zݢx ;5?@3WHSnz>f!aޣZr0:M^Cc#|h@mR*#h-qm7\ @ 3bF5 6DrwzHs!qxS$F7/wIMTPnH1S ZAjحxIu0Z-Bm,‘!$gje,bnl$_뒦e|XGNRjmY\_qۦ0o/+w4fin8Ody4JV|oPHs|1S=cH0ƩadB}>? BRQ,L)+Oa{RE8^pJ!JTM_Ɖvm%S @_ƉG!;eCM1ٺ1` $ <|8S_=&QpNH'#Dv#^"ZPz ],%cQt`aӏ]by?H7b"{yvOWyH+bh&_m#`9aHh@+^c{|X2?FGH٫\L=Ǩ)CO'YBV-L^*⹽larL'{vD&1 7rHj'I>5>Vg%~14L&/;? oy;m>m>~;p;܏\%wS߽' \k_tſ,}xwyˍ]o.q41[\?KgL"}Y1YDsCľPK]uV~& ("setuptools/_distutils/extension.pyZ[oF~ׯ(L%2sy["6mE[4Ia#r$MEqTiY&nZETeUFT&L~*̝NFoH2iLTV4B/~vhLZesaUYۗ6 `2ѻ)Ja:"N&F[GP2ˎXG&)XV:Kv2OgKM @;U[E6APo}1j`EB|gN3` 9!RdbңXP֛m%`eKYc @@=`;ӫ#)&D+-+ck.{SFZTPEad-D.⠐IY=hrK2kf$32?4q0U#AV4dVWn<ɡS]̎$Ây@ 0HYsGДdd|"vʖib JȲV%[* {$5$Gf\3 l$ܘZU$CH V.3P|!Yah1f[T)UF aHor[W+)rCώ H75՗Ƒv@͠a"i#V*fa]`aL,R:Գ s}/V؉L6WVe!A"X:>%I@ȚV{L8^%z <&3*EĬso6*yM/SO=(ws0JɆaмANa-ɮvCyjsɤ0LBسΙY~&WS4BIP$>"ȳWLZqq.\u _QCQCin*(R'BYK~4UΤV?ڜk!K9`Q3rN[ [#q$u. Y}88KQL[Q0Ԕp!ʴQ;% ^FUNv  8;Ӣ%SV3j 0@ǃ$x.k$p(Af?OZ *U^Z7BH"8SWykXӷP&uvm/5gBSѢՉįZSYRv!-Z}Ƚk\Sϟ@Ҙ =NGɚ7Yw+Ϯp#DXu@NyGթO\KΘ.į2uA~ܖEEoFBD,} ApتmS9d('bAVX@Tj37힋ƳiLeV+{gM[P`}n%ѽnX=Єnl6|"v!W <He(Tr'VY9uv9+kUA)C\˦Ԑ{!› 7tO9 8޵1|СL=뎸wMm{aq-7)4Q#z=AoF=U{Vуux<^\H>{Wrcu٫mUYzF<>FI9$QlyqD>ō4*n5 f< 3Rrc]IёT|%}W)Ծwt{=s e9|wag8IoGsw׏#zn͈jd3b'| /?|>| ?|V|RSa~qUa+bAb'7:\,fEуtH*34h^NWl 7˹pif\QuwfG^7\QY{/Vz{YX \[E`g0,,?iй('cpK8'k]U?gԖ2.n^#5wa ZKnt_^ ; ІXιrQ>s= H*{m4˅ #b -oZ?%A񥳞DEK^b)كu󵎲172kS); ?"}PgC )RS7Z'ә80<炒٬i$mk4E:ݎ\0 뵜`ȳywAw^MN!0.0b ;=w)Tv,r {GE7aۋ,aN;e<0F@Bz>5H;;nVF4f؞ayq{z堩ory>yzĽ Oߞ⅐jfD<ʈcwӍ8 Bn EG={]銺kqg#z囕eg,Bi˽'hB(S8AqtQ;䚖2Z-}ǫ7#CY,\(1}Yq0n% * 95ˌM~:N]da ,&ع~kwS3TNmuOu\<܋DVۄAL]~vi=Z4*_? u (zH>14^]h?ÿB3XNt2XX_`zKrV}dᾤG|Pi.7* i}{UX4`~D L0vCl d",J#pĸ&w;QrMcǞQk9G%͋A-5RkS+ӺIE-NƓdRū2/PK]uVBE%setuptools/_distutils/fancy_getopt.pyn<x:@n4=ϳ\]rKVlc5oX҈X{LI%MV+8 %-qg\ĝ( q̫,ɲElǓk\{A4-x՞8%a7sO&d3zHn9;T/L RE˫6OK؜˺k'=+ˤ9yHx۵peu"%bb3O|>CȓZ[oFlɛF4顿MsfOf.="xhjN= S 𒥅@N$-[I OATo)yu-e_/_90,\QIqr <p[$k(@4y4%DP} qd\^ `Uʗ A֬ ח\__Q`@ɗ(y&=ah`b*U++OZ@%Eȹ%Uy~t˛-{>8rh|MÅy DF`mT@iF 8Yj] B؏x\\ %QOzY&7v . 7K ꟒i(]#PFAl@acT=m>ьwBOZؒIŶH+m3 1RbY`>rE[! {Ÿ!56Z ,qLRKhD+6kP (f zebK _gKwlɫlBɋBc&@QheQjf0xi4 ):`,tz䤈,DװۤS1s0lԆ %#rxxή5:r`GIJ@#P@CzPe<,c@4Ir`BT'Ɂ@Q\2e @^Y*CC1=^%qS"괗zK;b́(BRK*Y7&sRîFaȃ["VGӈц؇vX?B/ `"S; `@.@г)4T}vB+l&J0.qQKPh%89 E؃s )z 4d,"9`D] !C#ЖNxmf =g^)'Lx8 "k2aha^ݑj }$J|Bo~*pWi+sDso ?=92>.1Ct`)wg'+p²8=ЬȎ%񥬐 _cc%[8K ,vq? 5RT3eCTmhJNNyNaRS !r2-6aB-9 QMz 3cMkʎ*i0+LRڙڷKj FD1]0[ИV߯tOPWsg$\cN{Ygh"I4hx6,L;cȽy^Bs_uBJ*'zqSɳr#X9GNǁW4üv( Ӊ:'CGD|njtAsa1q3+FY%YWtMá\te5e *ORsCJUB|1ĒG&0.;9*x'Gң(UE`-RL kbl{gS-F/-zC$<*hM&S9^e>Hr*]Q"m&ߨw[#Vã-j*8ݩq1{!ɉWm",V})vTܩK:Tmm~2l>+V駟@Q2HG~wiroybA>`!+=,r6#᭐/CɺK,LcARDw +OyJ0:*?/aA/}Q[@@_Q ]&It=͊f#OB}7@V@uHPE>Fx(d7]~7=P-D߷O")ɕxӣrWХQHM'd6~.hˋ z< 1˒t^Y(!Aݕs`?HT]#b!L+*]SB"M1)wZ0:11+SPŅ<](lruvRCV QvBSFo¸Β~AxMo$Zȉm@Id 4צJNep-j̓02`Hf$R6qi*Ռ0HUZ,H]"X3.B. RI4Y|D?8pಓϟUQڬ?'}L{#gnF3o+INBG_+5;qPbm6*7/I_^”5 LwVUa =Y "U|tp:d3|K<" hO%cO$i[h" qVx>;(|FMw2Epe]ݞ9eɎ ~1Uq.l##%'-Zo$?naNrmx:.3]dmRQCߞ(ة=z(FT!Qp6<JGjh mylrl{ĥ_ Cݗ2ur)601mCԵ3c9kW5]"Hk̀o7~x-@Znlh6 'Q7ς)+h߈JAԀ~okXkKͱvn$RKI8` _瘪5YEj ˕tFkud~|jǾ2]v&[.5'=tՍTYJ$V!{(P`0*Un~;c,)Q" ^ɐhfuyZ4ilH1p"8jj-V[cgcG-9~]NvqmQ4Ch6#k3 7S?@G**XtLN`w÷X}O _(@kx!~;42,Cł=//nQε;~aro+uŞQ4PK]uVV^  "setuptools/_distutils/file_util.pyYmo_VJ\թ(.eZH53Cr_$N3h< 5K|uf ]Kbԅצvba0ke0pl4G#Zq5+1SD}6Gqee"\FGەv7΍S4~xkf'y6oUg+SӰgS--)*]_nJϣѨT qګڻTOżY,t?8yB"Oŷ{t2FBE&$eOX5΋yXQy &ˁjBEiHp*%dHYTčՌ` vʱ}`sЎ>a0!^, Yͷ^91X8y|C+#$^=[RKY][$Z6ꪩMqq#ltKR]L@$X⹕vKbeUaRaCQ@)_}M !yKBol!P92JDg"qxTYIvS3-ۖV^ %gk36Í,@slNkV;KU)'go'7,(6ӢgI4jKzVlT(F}0bpF;=;֡.B$bgT{r̪:i?ɜ$][Gt,r_|){.Qwvnz~6(|2rFU4|=-65-)5KyU\,%)JmU |:.סDJz`*[5'У <ڷ X-M.*)UE//Ѵ(0ϱ]9˃Bӄy-&$3|9mxvY v8\ޑl2seGD`,b]IOӪtL/TKweQ|2yFUUZvitM :94Ji3>MU6jQ,fe6NlMCqJd͏r'sKt<= T8)8 zLb$g7OHm*I93DLy' Ŷ+GFaQf.'3LX`{JFJ+EXMw @\B7!G䍗@$ſIEo3q, NeMrr" 򍭑Y)ȫ:8| u1d4w5!N&SfAHJ@-rqNE-%+TxB}I5b!3bgx$D$GCgͤX!(bBCy"/VTRXb ҥZZ~[!ŧGo^>{Ŕ^vW. %& zL˘ʸ\(3$ՅPA`O+(o4B-p;w~6:tg.\,U9+2-7^J(xF0ĞT:Yxz&N\ m7W1"؛cؗ %(j6`aE'L](&. cIjsY!ȁܭZh Mv>l^HSx Ӕ|r} yz'q5&/fgm5EQ4RexWLS7#NYh59ՕPS}PM >1T_8H߁b/(WTm2DLAwukUhZ&C NbHq-2BvYr7}[io-;!>&q[qKN ujE,i(-Hԧe'fMDeޭ61 j8QtfOMG7Eal+ sW{)ثS rD炠BKu4JaDm}w> ۱&z!$@$XblQ0]~Hw{w zzǜ0 L)y$RU?2h(ލ{׉s_5G⛆_’yXf&cْٙWrբ -q5R4bGmUE*HPB*lhzf=2`Ԉs6J6iE9؆jS] ګ 742Oj:QWE)9ɀۆ] B=.ɏf<ԅP LhF2NlpBf&YNk@.n/tF- IǠLxP@~R_X@ #K`ϫXjl>z$x^L R݌J1b󒃯* 3DI݇<@tm]hKYr{Ua"!`JׄQ Fd'tlf[8 D# |!2#5ǰ*-bZJXǀM 1 Fi_n}b^߻|t *tA.t0 d&˶I?p|#/TT6b}*.8L=h `$\l<gQD:1t3"kڠDt/.;.r-Bu??@I+edrE"R|ޠWcgeCDщRuUo*'1#lM CCFX+UB_owDg(@ F1 6M֮ ]eZ4j3+ $)6yyFɽe&R9$4c4q^n6D %h[6(m&㲎V3%vnĖe # %N{޽6(y M rKYk'd b*! W$畱6'V3Tr BXUIjOfC dž,a\c2ܺ-"/@ n]iFK 1ڇ @ky *roBrq1X'^H+%(n.7#٧3FO7hr':H^y,cJPm5tNW)zU2Ye1fwp )챂㠗|;{To 9ۺ0ck=GGM{'NRm7SA=^ފ?n*OlHUZᤛ\4;5z }u$|_cؔ&3kK,/4PXS@Q|OY@iA]W(_+[# ؝ǜwmΔDkŨHWm +R%jdI%4a˺hKqI $:qUuuW"P @ BA]nr4BpN=.8/.^`Ռ ~:FMOG鋳eUADw\j8T7\5Sp~l|: xwH +D:f!1֯ayWQa sA[|dP:Jm({upY1-eă! ~EG>==Q#tBi!, mzй7קRL0\pg( S7iD#eBaLB!/58(Wy=ݹxzwr jjҌW@NKвwh/!Aţ[+}}(@vtD͢` [\Si]? o8H.wFɎD8$ս졧 { Ro޽~|N!51^,_l/P{^s4ŷZg $1prHrG]|a@:N%]L)0t6rdzti~ljk0l5TTU齢7E*XWP6SttrT>tAi7/ʚF-exV(tMsYM9jVk},Ӻ9-;^K@2`bp~$Χ,[%و^`p4fVN[Obq=%p+=hӀ;?-m Z-XÊJ}yYyeJkշ)0)`EY1Ym@A-5}fIFJӑ+?{rhjty>Ѱ#U0+Uc*K%Uz\bt@VK=/bmg' 7&|^qˁPe\.٬_mr%ػG~Jg 9ഛ eorojqkŢc H&dl_F ~ZY. PsjqLLÿ TRa(9U7k'{oN9tpw0{t~kuJOlۭtl;4W 1{1JkY*gU8˹`(n@,y5/Wۜ8O Fi X}nYOQc*/_0ϲ= ?},^}rH'-p?Vb" 98`^'@%)*p"hg(o-U|g4+Ѽf`uaJ 1`tnǷqYYE Ն?0!l{˯sa!BR80'`E*Z6,oc PU `=-l:!a2 \PK\B+][) b^s0}tuy̕0@ҝ+@ Zvv-Oaf"b+F7ȖLt$is]~ @/ 5RMw5ύ@O@N")=@:snXlo8@""e\F㪋m{WR85r;̕X%`D``T (q]]6~mi J(ޠ-,yQS^6Cx+>G#A%eO 3ǒv]Ok4X˱6apحx6+ϢujeTHjs X0 TLJ}, tU6IW# 3Ƀs6H/r q%=us> `m. Nb 8xjJTfX(,`XpI.> 1 l [%7ȷіj(m 閿pbl10 rTFND* jLļhƏ 4mC Lam5ITS?K; ݹMpF\R_Y*`1<H\ x6=Pwhϖrp˂ PQq61rd aȈ;RlG/.]lp# KqH p`$E(YjE^ be$W;/$ňUdn#Qp]T vw1;>_i`NG UTͳ,(%c V;W=@ԼxiPZc/^rT7)(28-& o_|:pi bKa؊d%EީQ7d"LXD!,kx.@Wf*"[ @yzPSɋ;*-vx4MZA_;'GC (BVGvuDTR?eWn$<Đ%> )`s;<րUAw]67|͘e3ĦHQb_^*b~qQ,qܬIG8:~Z|o`nv` xNB и DCq~U\"&i#W[ȁ^"p4^flbQE`mZ9p1WSJ IŠK s~'Z %""B*"z YB?H!rt#]׭95v/時AUD2~<}#CQ`چT$Cdeo$vvrz~vvV[QD쨻N1PFtES2L~MuGBȏܓt>\Răiepםn7-e\h*r&o; u7bY-S)KK^ϋ;lM)ƘH[7yW&̳0PѪe#){k]1 Y f^f2nUĮ5U|jQ|MRv1zTwjsW{8A[CY;<2' ӓ;e QnJ.Ζ6Yx,f^duFي'Ǘ/ꉬ!jc!0$X|r.G`P֙Ʉs?>|l"#2qL 1"Ó3TM]J>:\.5rR@Yv?*t> jDE6cb;XCRBAb<})SzWklVC\/Rx E.CpiG`٠< e5j4јSlՃAlUNH \2&ճN^I@YWr̅pZ'%XwH8-`C@KsŪ! G2]Mt.t kԸd /FOdU5Rh;:֣Y xP@Z.#eƿPt2ak>\;gIYE`pQ.2DbuBW>Bxͷp Oc{Y#Hѐe休ƣd"0^"MqM/j}-N|T a@ '<;,*aT\qIUjmPkW1%sq\9~DtGB? ƀR}Wp&=?.~PN DpUa㻼{)!]1RtL4XgU02mzp|:^S#DR_o(u+F "xo.09f_sG:D CJ]Pީ}MIE*b(<D(qU0 c"at/2oIAz[þn^dN8mӐ 3 Oq ?T o?$ urvڨl0b7H !/-YK%rWP "IM@FbS 8/ę {DڛR bf^Ç~qEWطjv_. 6ZBM]ãI`2.G9Bi/Mja~WϿq ᚤCCR.n'J%(L]}`6fxxCn;o}ͳ;x:vmA^'&^`cڥB^؞+cBR%yVbQ 3T^xRDC22hgE5:'?{,_B "dM=&V lTd{!I]ܫ7|}a_}f+;6 L2G OqY)2%C;;,u v1-q`1򞿔'So@r3o5hX>ȱEٍ7 a|_=Vm8Ӻ)pvbix-G!6`aA)Kw&ta"܂ޝ?ߡr*}nɺN?Ee* Te!A)e;2uJRA[IpLH̝mFI|>fՂ@+b̂ԍI;R0\\GŕHzGCT}*Ha2̀NW .<.UTa ?$ˆoTq7ߣYW C707_ns^ptbxkPF, jy%B{fѓ ܝ.wCѴ:pyC_yk;nb2oj=?kF\#z 7PoP+.2F۠ZjL.4xE13nt16b #zEtD*&%,SOMZ~n|m 8D<5ʞ>f["4um&HSA14Ptka0dC𪊧|}AaWD=mĢ v;(~S 5o E n7}?y3g0<8;;>vza A|VNd%5?TI~T=qYv ݁#vCZJr1.D-}/⤦-#̭߂ ~EF?U 7_zz28^V2w bYz72^|nDweZj&q^{gUҨn՘gYbQ} $k ZkQ~:yIMX&+#}Od% ۛG;K1lhb̃|׵4GOM ^Qb {}v^svЃ|ñIlj5N8 '$U l>|WDIO 3 i촖.`9O\`w:o .ܫ>#V;@D ܙB1c4dEzg\ڟR #"/)s "Rr)=E͡4妔@J$ТRtzcqYMgIG<){8"[ Wf,XȦk6c8[*"[]/_׃eEZY.,R]*%ɬL}vyxgX`d?K$4)yoWzqҭ#t Ahf$օg4(xk.LQ8=zլqQ8}Xu"xg?ק9O_e^% 0Yqʠx42dG*"Vua;ϲW}C[mP,lI*j-"h-dA)-%0|@i8g >g9'~KrNHy4<^g\ U,`˴Q$L t’*B I_A_HhI+C0_VrXxn_1 Z|<*s|88ya'788?=ejid39x^YX-E) c4E n/),|U}vPpcAs N>Ac}`:+YOUAg@T́R*֮׋*!j!q΁ǔ%JY3~Op mN=_څlCYu &Wfu@e+Zlt1vZy!;q^ɗ>;#,_tc G=\EC/0[ an#eDeJo8HH|VѪUtJl 1b= 7ivPnK$ߜ9a1[0b1˪yrMFJJ)P(-ya99xW+tCl5Z5IxC -1۞l}RJT&x\H{ߖ*ZFJȧE+F(:/y* B0:Ґa0aL)c,n vVѠHѯpsifmXha"F_$;XhsL9u10 O (%0ys2 M~ͱMDUO:xOsK2ud$կzt̿KoRek@9/t.Ʉ〢Vt?5W3G@E}B{JĥWV D@p eqAspQ+K֊38XQ/Ul b5.h#nHvΕ+RϘRIEFuzR._x ߚY"Y{~I(C98iA|0H(.A4nʅ 0iR*gYzmX@!\ U,l94*+`\ߟԧf JDv HDiKch|x+@!>`۸0 cď,Ik$tMWBK2 *g$&H¤+ȵ)o˰g&57 1k,4% ju|^es[HA`xcܬ>DH}[ 3\*6Hz;#.ivF@D$ZO|5[tR1խJWҨQ鎳UaUpp4k~[jLAbiX"~bXԣf{p>z|*7!ASz9|[Uje 6B>VlKUv&bZ j`ךU6h^y,nކWS^Y-4D4!u^AjN$%o>N/_?~( JFn4r>9pG] }K_E~xm%Oy^@Rp$CGO?\9pڿrLn\hͥUs:yW3.a6MEhF=&*,Mn [i u@Co|j q)Q²kxSqDq`*UIM<8$?_Ee0`L_r by)Ƭ53c|x_  cc).5D`W+ZЩ$ ҼU:*[NԆ+"IF]D🉦p*نl17b3M,!mn\`W'4%Rm߿8}Ty|h!J&HrzzwPChd^* ʀ[ʰ"uS61,#y5z9c)as*ЖOcO$!G%|-WznN#THkf͜BWZ㏵ce݆uۊhmhPήeks~aXA>iT 7?j![&JLj̷`M3^Hrx 7 h(dVRU"|3 َjle b,}1GL(iTa(/p~]| 3dUЩHw`>W@C5aRKPe| ʶ;g7 O}s$|G=~`Dg&"tEI$iBT_o5 py.9!Pֶvޭzl’l?vY q|dbG@S4N5 At4ߊ( %=%sr@0qpSBk8wNbh9ٝ|6-QD9ƎwjǹԨK1B]#D_1QKSf8BȳB變Ҕ 0g <*ڡ!juVìIW"\ki7p Xo~kGP!ɝ` 6ASy>ګ}Ưm,.rўd I 6X[e:fҦcB8Oh(EӛFVCc=!! @ڕ)V`ty9~]{MOۜw&FAm74L;-xБz4$S&V&dw 5U㣷Tq ]bVt'{;WͅJ tK vq޾ e[k -bnxJnڔqOl%}o"Fb$:oB!ß֮ ߌ *+Ă}m4{rP&4|X/j%`pjOwv<}yO{_lǢ55i+A]"vĜ|Gj Py`} ?W\[L]냘[f`}&#tլg!6vǏzMEa}V|uøNk 4 I}J$f9.OiX\Sxs^%<ɍZ(kfkxEYxX2UH^/ΉV XL·rěEh:t]q?7nh+:AAu;w=;?! (qCNy>w˾=~oS)S[@Бİn2&V6nûkz{grA@I^`4-Zg5ݚ@`"aB{ĭ*5!ȉkx G7˫B{xۿ_7NON/?A-jōɷb͗ԫdX=/˷:{e.1#-ť5e*<->Y}3Q7*L?;k֌s\Q3'5ӌ>޾MPYWcgEgD_u&hl+JicOT&kvA+ Lox0(2op< ՏwT*Tw':_u#:=Q:ˋIZDm~( i7oء{ pjG#lZ yįǙn&$=04|YG+iFΎu@ cپ $&H7LL&/Ma `=6>% `BDw8ѼN#3<8ДwTp{ȂvU&@lreU_" .Yf[D4) (uDxF--Q6[HʎQKq<%~{fR9yUkA'kNBʃ,ӅWF FKK*9hk+cĵP*5pj; Ap 5[+PA]āB:HkghzItԒ|ݝرE+7U{h"fIXG3<х$ XHkuɺAft $I)+Y-2U{i7ճJ{\Nӣ;emKx۳QvqR9A 0M#d5)!Z`H<#,(_: }XMIJb\{3c,;Y3bJ6!Mʻa>b@8(AaJUUhNrN5!FBz瀽$+,j#&uƈcT]FB$7vW:('%h(ɛ ! n#-ws!Ӿu}$t\TUhTG+CY;h"(2/(L4"(M3 EQR/;'wmL䔗"YFxO2J#XKi +ޘ. \(] 2y&ҒM36/} ?'dm&"X 2g{&-1p\i ~F&)ܸN*Tltj<HU;sW}w?޽}n6F==5%:IzΦO3aݟ;C+GN2 5i0e'֋P~|P Ӂ!45wIG9 y~t·wnfY."tOX`jՄY(:N<#jOn?!6?n|YlڋG%<S*>E2lGLHkpޒ2{}&YvGdI]x/U3r.iǐFmMk._%1:tQXRNQL_ϣ :D`M#ٓxT*M-v9>)뛪,_F9%OOH*WؒTw |PNεI|1JwLà)"V|{Ir>`Wty ^8PK]uV9ZVI"setuptools/_distutils/sysconfig.py?,UB&Z485eh:۴lY.-=GBVBW*Y2;:\i!oeHUT\+y;PABp+lNUSgVYmT U%"yQt2LpݨYf̑ T(n_xrցfiT!Vc!ċP}絼QK:m3O,mT֛f|6˶UY>>vuufu]nE꺬fn|h,glNor LCLeqQj6{yt8AWj ",o"ޖiWFfpM"J/m+kKKK ȴ܂$g/^~IK!QCVZggJb/.>%cL8Vg(AYY@.7xS"-4 `;&j3 <rx$|Z+;V!{SI~ it4` n\ʰ3,u\AfmmD]LCƭ5p$5QFRhv)bh*k} %Aѣڸ'r0B9,ܢce8m\= ;x= &u_E} ba$ghZx˿\-\ lZL[6Y>ȑͷY"k)iY@^[f!ڻLy&v,¥H鶪 ^ Df {aGGDRC>`hǞ {@ȪJ9p= k4RM`:MX NB^`:}3&BVW;GIVqO[Kn&ZE[l1@.vu`̆dr'[Й;Rg;B @?NXo;yȳc@ ak{GAffuU}ueKD#9 |wz1ؙKH ݌h://:r+V];[T4s]'z.(7F qe֨ x8:xeb$Qt`!Sr9> b#$k +s<30ґxպQE1_)?JPFH6d.B$ KU[3~E^q7}]f-7+nԎ< iR* Se}+5;Ҫ i|Ƽ1H/D 띪/[]?^e!f]} bU.tŀIH".>9}Ë@|9`pOhEOÂs`*1p C $ȼM v͗\Xi=GZ]Au~{-Z 3Wvm.&z}0|9M9+WV`xUGBVqjO#A)RYea,R(Y绁կ;l;:(I)JY ȍ8BG1gT`51ܱ=!^;Y=kч@&Ih"+YlM@rPAc8l\ioZIw0a.o?AОl5nhAI*?~frҰikb>qܻ5?eo(8x[70yGWtmdz}$$Y[(ٙJ:@ (KZ{dPO~*5 {d䁁Fv$#9MsǞ9 d]? `\WðƊ(O_4϶ h'Fw ?cR>$nm/s>,QWy,NyK2ckaDxuzj1tNp^yӜCyu=ؔSMb13 O4``6ym L%7qXb e|]UM]Y&۪1w|GSta AIJt`ygUmcstcsD|?nS fb䮓LQ5qt(.85r]oQf5(5$Dst*_S@U< b($Ef,?`k &4U $^_[4 ^8;w7vld^Ż7_yC|Wgo/pR GfNnk4=\|N^ܮ`a!5d,?^P$>x%x$\Zd=Zj!iaūg=۳O˿_5l{vf8_0|b~ u$:r5]Z"Uqk dBh`rz[}Xgi \HmH>)3<yqG[p ?^]@D@lg r=&`<Aم֍AFh؅Ī8:k˓ɝ2xOrG" cX>=Ab !?h=PcV¿p-?N{S449D>2˷tJ P 4{E$qMJW.zοXYL K35LƤ zTm(O77b↾M_(XNjF6JDVd%7Pwb)Y RgtYWQ oUn'E9PS+wӽ٢vvAD!BnP6uV!0cmDL \ߞgAZl;ʲ׷V ܝ\&* `(W$ C#Ʊae [ylhboHWV48Ea* ɠvϧ[h:Ovt00~ f@^>V"8)By*2(aؔzK,议!m8|6k͵ L-6:L;^ݕ xkj[&}P!fȐ[)2FӸw".uK'ZXeHܝk_4^UVM8kOz!~pWFik_Nwwm]1;4Թ)ypFLt?лCQRzjDqrEʘ̻/f9v1pcw.V2aAhPq 6q޴g${& s`0aW=rDuȣ{,~UuJsPꄨ~R>{G3irU޴.i8Jُ-S G p;G}v^;8lXx3>IwL=U@Ɏ;/G} 6/~xݓ!_nZ@`D}ӺP3r.ZqN|MRSs ]uVC47#H:tl*&$9%w`r +o3n󦫅7gt}lB7Q[VoHȍՙjUiCN J,.`jԲo8G  8*$Ȭ. g=h37MUm7(Wv/2qT%be-!60ܳˆ Պt2K$t{ܪz)'خdN}Uf*L[ql<=j%W1zV$w~Z)^plVrՔG:<6y7JoJ./Jp{Ms5-Ւ]Ω.ܾ׍ؚ>K @*c3d{0mU->>>^  W.<::c*ye,ok޲. ()WtG/xmf8.(CY^ސᇾDH6;Tg" @ s֢eDJ6vH!Ąel)Pv$VAئ5$a*k]>ȻB$Y0pB}=Bqxj.NpD$_z<#4U"_F e2NQ24qDY.*Cx"<Y$;)eݻs$$)εѫbnY$XW4S(!1spNY͏8AfK OmkU&z&ilًAaV-;iuNYd=_)# lYG"Z S\%em[1&clAY%&%l(2A]5NxއJ+Z0맩l#^, -x~Ld9lN_CL"ru{zҏ%. l֣j[D|8 -CQq2 P#q"@'zn 3o6#@@p}˖a;âA!%|)=Tq #:JD i)o\QҘw:?sYaѲHb=L"HI ڳJȠji#(Csi%e6N'sB k|S>1t>QT,|_Vxx <*hE`ꚻNƇwvhC˓w zhQc鋠W{;N=_ECnS㮩@:5g%9#ח;diE/rƵt+}*>s%):$;lY󣠩<P\M&l)f)"6Ri\?-VO MLg(Mo& LhO[SI5w;| XRkFjPĽ׳Wbr`IyIF|/RoONGn{Fib;ӊpkɍZ2$DSiZ[f.WvA?.:K{Pƍ MD5dOu?Ⱥ972\3F~*xzőxKlnLUGvQ]vh_\ (#閗~@i Ch2kB< A/LoH4P 6 Y}p!Vhm<0<'3+Ǽ?_$XBRΡ,CI6cM/g@E^Maxls[,߮gMŇޚBU/l_МhO6Ȳ)ܺ 'rY o=}a$-_`OC{67#R<0^n uˡ摄2o ­;m98Y% oGc*\?{?SM%N1W *@/`ҷ&e;" \oW?X:y!hVo(1 "vk9O?[ds'7n۪ BU[?(mL`>T'C46+iE#^J^.hWmL䔛&!oRY2e}-,=FX!Aɔ*TуxA{'E6ϲɽRy |*>cuYԎ/,wn䮐C|$P؅lw-r(KSv߷X|N⠪ʈ.!>M=kƐ62>]y WDH(6s 8ZgYtv%@Gw6nGsم/rEByAPE,^ex5 ȓ=J?՜9Mf4K8=?>"ONxAQJm-ڥYR O$J+bQAޭ4]*ʣ^ALLY^jE}Х K:~}S?Mv.1@!gŴeRM07^s> <=>F'Vn/+~kۤ-2h\:lC44 SvFYpZC\ռs>;D}7 r8W\\zCQ㚰#(+W[48I|%+u2Mz*Y-ϐ`H;Y|*x/;G-r:Ta޵p7ns5RDOJA ͛6q~z} Bί?f i?8?Z[ܷ &IW!&  N\ ''STeB$?JE`!#w5qmWD~;"nIAnc:|TpGz~ WPK]uVn<&setuptools/_distutils/unixccompiler.py;s6)Ӓ)ʹmsqR'M3禝8ˌQ!S miwtw4"y&u2ISC})sQf,4w} ;Ҝk3tg6wf;^d3R<`,rYvg3ƾb{VJLl`4cޱ˂qOk8&"͛L0-xX&+֪B3]Tnd  &ONs;Ȃ4 X*^漊8𔩲@0C$ebL׼iK nJkΏP@CN!JS0l>fr_fJ'}l+ѾZTR6ڳu*6rkg\fB܃^VO`}1ۊbUVT*^i6^XLT^^P,fRۧ-Ug=҈# XlZL78ܩNKN$Fłmy@Xq %`q Eu'9J&`(iE\ piQm\9 WYG_uCP.4eR߰hYǭZР뀕Sօp#G Ȩ82G`5Z 6zN@wyf#p+;Tz<3j%l b41뛘MkY,4Ogz&' 4ǁRށ:g t>okק7;?gs@87aow蚂iMҁ8 2=鋑Ä_ʜ`9{ Dr$l!@V?,2H5$R˖ <cС nJ^7wq,#6lxƾ[T[ѴYu}g,AD.[tsuo򜿉JݫJVѺyJ˗W?x%-gwLRBeVLF 0MF#%{^;;hFDZmI=D1x!@*O#tǸL J8h݀1 3odO-x؄4=z s٘S}&4)lb7ھvs],!]3?Ln,Ý}]4]dD=xM+ կQT} 3bX lpČ^}fmœD[,`5/R@T44;yJE&3S`1uU]a0S5(M8"pcf?¢M u}ZCT2IYeÈZ5ew>f6ϴBLI,0taihb| 8ZdZ(n$6yx*іgz1Ըշ8fs[kN̓ Laj\YyEHҪ7wR`g3^V<];|04_7NBa_N!臑)&*\Cɬ,[Ş:R$C-!)אH9ZMΝFOpZ3*b`M52(Lu˽QnTkUP0Cߎi >05l!P Γt07(]NG i@rAuH@u߲jy4g"nG8{"Ȏ86f!UXA15^g#J9ðz[}5o_/rNi4isz܎y@5`ֹ"t6USGT6 Á=[e i)`m0;pǑ@tG.+h $@KY#&Hz``*pp<*||A2;q;6>53H0xo"O㓘N&N`ryqѯh֎;OD2oμ t$d2 pܜނ:D:nMǐXݙ)@G!%9#T;|M{rFc2ٓHbAOu:N%/B+3TSx~\Bn0'|Ԑ:EJbJ}xN'ތ3C*WN<\D5G6\r$l@ SP'#2:t>?r^lih{(E q'z)&eO#OH/b#BAF =ca'mz{t<I%X&k<)~b_'pB1{3@8x?ahγ( #T/MqȢy.!$,e 1p&whWt7,t [<7< +w"X0z}XԾg너Y.Cjs /4 rQ^FD tLdкL4Lz4v|X=2EP~ʇc(~,_b_Nt^!z Ɗ[=Ɠ\by X?UqɲwIkc;BFyԮ ;,5KA%@ESA=txWUt[tФqNqts哒EikPJ1kk+p`m=]Vk@)@(>BC{~xؕrr<l5Prƺ.vqk;Coc;Lc!݉K kT9JNW_]|xWcfs~Av=t:O*>PPqɜXI~z8LRQŎnͻ45wpx5ע]^hx8Ï:5)i%_',[< iey;؂wY$'잔?;r WfX0I2g^xԏEݶKל-I^{o.Ÿ{,yЍsVYt[Nlرx(~WN+cW𭐰W?פ60Tdj6[Ȱk`4ͿKט+L9#MY84c{6@>R?tu4{ݣF)5Z x(TL` (RD;ȉ웛|BO;OHD'馤2\ ۬,5.ˈ|$oL6`Akͧ}ϟrW.4ʹĉwՐ2I!0O@-nlZ]~~Cb[ %A^tw-Ze (Ni//8"%8˗snwzgЦѷś_[\sx,x ƤDts("VNjwcE&R=Fy½ /|njY_0dV]^VJa?\MM.qY`C6MBe1xA_Í(,CSķۭ:}%6R dsl]lLR{-"Ri t93ݩ_!".ލ_>CV%o;PwN@M9!8vZ`rKU e@,!kC@5e :(7bA7x6i Q2j~^a2u}<4 BnzmK}gu{h:k]!\Ł5WKR1]O]rR܉\zru ??M?Otv;ƶcQCw~Zg7Դ?R[?\x}Dc9,1V- :*R`WnpϒIe$TUZ|6CLЪ֨a".quGӑADrr6{Yʇk^-RS$d{+Q J*Aa믄C3^ Xr=-+=6>ћޞ wmyLx3+kҚpn~" @̏[qKmʁZL^R*($|5AdL!͊ I4?PK]uVDXFsetuptools/_distutils/util.py\6} .RDعT2ݬ<8e{gU iC  A8QU<| _7Au6yQyHJmZѳ9u[MnZ-*)M^nTs4*3.FFecNLY%J'*&k ]'A۝ЇJ&`]U]MQvLe}/SS5/>9YWfb]UEҬM}Jlwyݒ;:k%ҽbY{˓LF7S7˝B48QsݴU~׹i[UxXxL‰Q5FA뛮Rj"CJmOʌiLm^ZV9_B?|^MA7;S묣qjv%-!Zj776xsR=;2m[?“Lz4:f^qXV T*&_=IJ|Mc;2/FgVv2juy² w}%v҂=qaMl؜~Bz>zNR?OO[]douIT*lE8tHGv9݀0 xB5`%>i:#DNQz&mNb2ޮ=]_?}|lw_;AV 7xGoﺫ&*._4/~ {Zv Lj?z#1O",_$f 3%@! ~"iVj+Rc,zŷ~Xs 'OٿV )TZ 7u?V,zbTi̬(b6Y%R^p,i7![iluA@SXV  h"ux&1̊|18c< 6 "X}8]Caмr:c}` C]CM ʭj'9 &@*Hdbخ/Lڢ=)mo5zvPf1Wm X+UިDYwaJ`t ·kஜfm*sFn"Ֆ:렉epl;a.MHS/SSR6;< $!'#J 0$F@s\Н)v,~DQIyp+u+{%Ӫ\l")[#.5!@#dݓ|+\C5Om mrjo[]z }c1ƚK+o8#Wgsw:Y=e;hpz D`Xj[M%qpEֲ;v\1mBW2r {|] 3E#`/+$q#)0l.A0DI-OnWϊZ5W3n%n10gvF D#7DGl(XoImDKX Y*`<,!|Z~J[xr8x J1), ZgJ i O*5dL*)Y:߀^[R,_i*00&įJ+W $iӲh9[7?sR7qW?a=#&\Ќ!KS^."$S$M 0} Fxު#Zov~:>φ/2 !UmL#?E֠p-ආ?s<uL$ɍ6l̿y:}2|HsG$N֪$.Eee ]b>ĘOpw@"H㵴9& :mި`0n2\QgRkAyܒ{úEaP2=E7:qIf-a&XS5ٹx}TEG&,pX%nN#!?dK/MuB任}6,5>go,B \lv_j7Fȏeʶ?W##>UY}.p#!soIT%u hȟ4Ɋ NHGvq3$96&G] ȀFuu&]M+J!@4-HqVA, EȒLKJٝ . {ƑޜSdL,C j`aބ[f0l W!; =H پ༑J9^1AUI /@yReqrs Ǜ0geB=eQ;D!3Y,V?8'< {;9V'4XcMvmb,)wurѫ1W<ǂP]i |W.d <"ugGYo߾w2.:{.t]U*Wo_.wy._ߟs;aNѥ0ź'1] 3G5EiFvbľ@˟epNSuEt*LVM 2't\B+wKZD:K$֒M6$%H Me46gzfC)#LƆ9Ψ6d gl݄t_} wN8YReE{O˚̻ٛKӍ~㐏LGL 9GpAI̳\k IbU;jjUq1TI{ͤIBCHJt ]P#s_F0^?B-MU.:C~P>?h샆~]%?ښ$'|ڢU"QpFuId̷DFm`3*ِa$e4h'J Q ր:teRMs@Ç^`f僄ngDu$Djn%1լ/ϐWeՖg=psM&m!&N9Q`چ8&R`\M!k%9Bv m^%j#!6&E.B&>tC. "EHn5^$B/xlSD/N_pQ >Hf$m$KZQFQmZmwfiZd=e+U}:MS9*䑹Ȇeۏ1ڜ ^.)].E /čgGv1c ;PNH],]3fӉTug+FlfݗYTYSP>hm#&骂ULs ; u0ܩ z' ִL3<4Z_S"<ґ$, w^]ﳮXRՒ\]\ MJRc0!E`y@';RY:~8vڮ!2@UINV%(JB=L&;,wd=7jv76fgsZ/9x.g\{} ;G(ejBNQ{lJzLtxC*/TjЙЭSYiJץMO[r.BC9&O Λ8n: QIps(I{#qB/gsCJn:71:DYN HMRerIJ$]z&s 6n1>ՉSvIDҵlh?ek|!aPo_xź}6 $a|E5YOtR]FgVَ윪(YSO M_v}6"/-2FTs3no^\c nsDEN._# ?؛ |Q?# >y??> B,N>`MT8 l# l S"ꩩI;P#O4t(8̙O {,"FJ0KFWI6ʹ/fSox&ɋzfKP$Fjɼ@K-~[5qFж('yt3\R=~I?_L2{&Y{3`4{{?VV{,pK;1n #3|8(,<-a~R'L9@/)Z Hg͐RIY47DhT&Cj1B0Tغ?&Uޞ8%fzn#) Ć39lס Gg (Lu (V!h}T.] bChf .Q-ΡRHЮըfO PvnpJlmW={}'0;7k%TuJDkӬt1eW9UvY.؃M.:b@6\sn %ORwS<ǻcS˧4&e %Ͼ2:۩q>i1<|shhǥx.{ \x8RWc: id土;<]~YufvTDK°B"mG)f;>K?DW'/bSB?BlTW'.T/-]p=鵩&(Z7e˷t2;$47XW䰦yCm#;p,UXOW!jTYڝs%d&G=1tD{JmIOGa;7躮褮=À2 Ǻ1B. }Ix[>6K&JnW阣u7d0Ҵ u @ѱ*̻z6S-fNƺ,WC6Y]~ejw?&~CvFv:n/s vL T'w ,?<^G\=UjR60\ <1 X/H{jਏ;pVu!n#,2׹ l>.r2^8B!(kyE6Tuۭp[Woa1&_}EVܷ4=Zȗg|Ԙe^6/ )鏷@Uvb|mڊJ[\MTӿp!= )35㯛+1[ c04%`</G X|ϙ]|/q! 1$ٮGm*9=,i5c|_U'[ѯ+[r4RsiN?xv᫝d㧎-[x?pxَxO2ǠrIm~t_첺i9qEmU *R곍Q58 ʣ3f]`2T;֌N7Qu<ɬ^7Tr IOL _)Uq ]{ Wi?A*gȑw %.B}3(Er*NTNPK]uV#q<2 setuptools/_distutils/version.pyZm:E"$WRN+DwT.d Ih̓ݥ<f0Cr%_>h]Fn\^\DujWEK|Te*Țxnțl*D\?-*Q~sE`D7 ZDggxFTŽNq**+e,>FL\ ^i$w{U)!THQ?4ꖾ~*߂y"TF?DoacJwRhc$8 VB㸔Qc)H/ a@&٘"SL.S$[h1^:p;JjK/F*(V=ƪ$-v0o⚔o9E#e^xxos)TdF$6kXv!nKP'4*^mn|.Y%TT*Y&2P/21IvW(ghYRqVbV=(Qg0Z?+4Y>KHf^IMkGցFYbq =8_Lh Ll%&.ppb!*%2K0bϩxی99J< <_hø"D^g!-|JBe@2kR5iIж~}CIkLm,0]V"KN`6.y+y隸G >DB2X9.IQύ"J̊Bg3 ]|iW*Un#x"7"El{jrG͘8Vפ9M=3 oR$rH2@H1ǙLuCI0!3F*֑E| ´v Wgw6M_ljs+tiƔ&pBYXMmrBg=3Xsq4- ߂Wm-蹼os"T||(`vDWoU LC JZCWX  pfLYal`ۡYx Ql[C&?&ϦH؇w(|/o߈iHئ$=tEÝ@u~Fh&J_H$j&2ӯ: נ6IC~VN33b&^t:_ynDA9AH'xy7R@H<ldS`'Ż"l>j !LfSpoW8wzq4:g)௳i~,shg (%`%@x=2 !.5?kWeȪ6|sic>>\u l fkJ="SSHѫW#P#ыhX)kW#A6pCX: Pnvd 5[;J"yEJF$Yp!x~-/\u#\t!;>g9X~L 72omb˂[]l.bi )<]Q2[NM*I}Fej@m4:s 0K1peR6sHȤDZ<X|'\"O6uDE  8)PUae|{*U}cK-]Msa*W[{?g d]5 Dۙm0lDCהAŶԌrl#D沊\uI wA?ͧ-19ssﲹR J&IܭQ^F- (ŰTFr;=]5F`I2bRRL0mZn3ZW5lL!`}p> Ɖi+a7頟;{ҭ^_F*t 0Su3Uu?H;NxV p [;!h 2raT9Ap_;ΨW#|ٳ5lA%Iv ,vnBC,kҶw FAy S;ɰVԶD@2!9FnY  "19E̤w%4 rZM)N]Fq3B"Q"뒈Q"=.jQ->5,Yq(S2]̣U嬟7$QʾĄ>67ΊS øm F9p(8 Q^9\ Y%@z=S#piJՙ [ʄ=~ _廝agK:isdƑY0oc41!5S=#*NXξ^!j^v߮Bo-V9^UTh~.춽\ZuM5WJzV)Z/`bWq= E]5G|RLކ`XeKWV'*YBTs0h0Aeg (&D&׵oHG4b4tZ:Ucꢉr;z1j@' S12 ۺAGh[w`99ϱk|]FX",vMnGV# Eq= yd>RߵD=QR:nAwn[Q $>xЀ8Ye^ |F]<>> _O.DE8M{)N x4KIEH"."|}D8?KqN xRZi,5oQS]Ofӻݓt3|V"EijAEZ h2!/ $sqߕԅNG|sO5NWV(jƓe}FReKAC>uI[}[y3uϡJʎ&Tb]J:5WBo*kQ^D#vQT#Y,.9;Fv>sY,T]kz5Of5Fnd< lʤ dn=l6-+0VFe l,NFp~?qzJg0>])X2W ^n@Tu4Ҁjeƨh 6JuUdHP?v aL%ŮnUZXb^mrYJ&rK#D ,l+SapkC,,Z`zhOJU2_ކrebYRF# DUilK Ki-61K8$81-Xw 1[nlJ&p,CkzqNܰw2Ӈǽ_+ `~ *S7)yq*ԍ%\_ %ʃdoB`2S1ip/bqx/nwA^Z@HJ.qY^SLa>+'R[7ɏqƢ:ՙ^M |4uB~UڌP/(A6VL XL[4A-&.{Ñ[JÌƖ@8Qֆ 5@ekTRZfčv1#p[R?,\*rx@LfC^p|*N?_k_lв~!4EjPl5j|*Lw!*C.UY#1Ot$D`Y=t*HR3S;2XIgg_;]xTSBDV>9_Tk( Wg.Gæ@w*c7ɥ?8đ_$">+)? FAtϜV֣Lrk#=p䲟K^b DbB=\BF;ǒB@]j-(x]Aa51 sR{1,rCu|5D݅* ltN˄[WV bXnb:[R>aIWhkEaoL;k/bs=!kc`0 73pKߝݹpVYQ-EvCHXJ97"+ЩХr6c8CrGkJԴdҢw۵kV_n{oFDtxUj5't˗%ӈҏ٭z[w{h28;PK]uV) )setuptools/_distutils/command/__init__.py]=k1 w YJLЭt^ʝͿSpwڞˋ֎K+K9=7 68> yj\9g@AmA堵Vʘxw: 쇙`mW kSlTKVC"Q.[ a2(*.k^:I">PK]uVSOVN2setuptools/_distutils/command/_framework_compat.pyTMo0 WhRgCOf`C/Mva YdHr>俏g%P_,0 /l)U͜ȅnOJmZWؒD+R1xJ@T6ؽz\Nk9&`+?U)VHy'4g| QPP,PDǀCB`k.n o;RVVZOq@-jTA66I;vFbM6`*xm-|Z2*lm$oFt;LId&$BMJVnm(V7p3  bGu;ɺ.1+A7a2[,OaGn5Fu)Lݨova3[xX\[TL0O'+А҃P172 a}z dΎt6yH7o:!9_e[Ɔc+gBqpr*墔lewrh-7vR9v,cP="Ol;QdzIk|,AjՍ~R3Bon:05#v.}6 ĉuftQRs&Qv2Qqk $6?NZ[EαL _حpJxkbPK]uV!&setuptools/_distutils/command/bdist.pyX[o6~ 0[<Ev -2H*SDIΊ-s9߹Q\Jg<(--uLM̞7Ė!&$b0Bɶ!w[.:ȦH( ~2DjHԋN ň#IL)'vT:eƋ׀^dfS;bQ{ywjt^-7 C*K+q$lRDUK{QbGE~@ּųwh:n!dn-Hg($rw)r^]}}jVk1MX0f ,N9ٳN 9⭾RH^Q J@$ύ V6z+0smAQ}&m}Q+h6mnLڪ_mf\ tF' |q7/Qbc9= # K@L0`5BO\KF2/W/*rȉMf5mM>08^ƀVs@#:gxyB-E)FP?b4FE,e sQ!.*$cA{&|Pe ޵Q s.J6Ĝrl;dKfYնVŨ0 [癚D6&-iuAxƊnv- 8}Mִvv宇lT}iȣA.6+rUE!Qliޓ7W.ǥm9sr++^o,afKB#ɧ̏ R =OۢwRmAVp9$ñׅkU)rEsDic-~Vӳbgp+soקԵ.X4f !RT'b󁉜.!~ԭ*\pá6ovm,,78Asnc_;fط6oeHmpVO#7/Ȟ>ة9-#";Qo r³^Y4-"gfߡE x v&=bWh4?jLqE\hr9qb?a/gďhO/, PɳtAFŪ=XR=)3}"饖,gStf2Wlr#㩧f4t&W¶"u^iu{9odSՊrμ 7Eށ-+=2h^q wOX{^4]_9FL{[^BffuHg`ha#^e6x}\+ڃ_I(›_~M 3F53R%KM6Hihc~(|;aFC`6曀&Dҧӏx[ά[>t`qwͯrR='W#$T{g-&q~,{~*9,'@ZR.0C`Npxȑ{j )p1kviwS k3+r> ڰWn?PK]uVj :+setuptools/_distutils/command/bdist_dumb.pyXo6 ~_ lcw^ 6 ðm`eHrk}dٖ0&)}"?RT46.r]]SUC~[@g43g`xeZ-Y~T 0R0$ e{&J(w xd`C8AF<3 ʽeI".TI4J^X[PƯ?;P^D[n.^^ U-u\#TFtzPP)@#m@)8$k)q5gU4ƩZ9i0Ic˵fqm0|jG%z;Ndi [$uUIkHO"Ge;_jPquũEwd V)vӈpd!1cN":tf ZF@b8Cknٍ.Rv _{*irl8=>#vϳIY_D_oA)k<k!# Mj.0}ktnNAΐN688JaA&8'$G9Fj]dϽO)[ݲD-Cy ]3}zsv#+_mB #x+_;6Xq=PzP87&RgXD[h(^]Q9n'FZmy8G4km  [c< yXH]R.و.E! Өk!M@zy^*H]fC'mvȦaiM帇H}A<n;gEX:\ =+XkMDjK7gyn4| ErEEF%PX'+&GZAX#R~ÌRtQ϶H[-_j+~ c?aqL נnT64^Xǔ8M__cqжd᪎)D5b'ۻcI=xu"r^\Wlt^0{rS䦨4K;rp5/U5<Bii%ڦۈ7zg7,WЫ|e/TsUFmb ^V߱?~w EBtqzt H6rtD湁 Goˋ08u|^Kӌw,&6g],/y"llb2!;5{ju;/E/PK]uVăV*setuptools/_distutils/command/bdist_rpm.pyl+.n$@j&%FrgU]I9&L96E98xiUjer󢂇gYE64,_sd8Aɳbe=ϋ,eٴlYyp, gTu-^7'~M,H-ج@xQdEfrAfyVˬz[dDMt7xj /J^[$Y _$,K'39: Ž#w*Liq|]"?y؝,SQ[la4Gؼʘt&c ?.42:)dނ/:Aإ/V ^0D6uY9geΣxFV8-Ë \@Ywy]%BLO QKv?LD-::Q8+<,|ۭ8X]Ru"w`A0j8 z:c o@Јټ06!Xf54A|iq-2 DIt+ AyTmz 4kHQܯ&ykM T'NʖDHב(FoՈ.BÍ.1:r$IyoO_\Zgc:t&#L`LC::Ppܾ$p_-/,(:XU[gu DzED#gHᖆ^ ͚#Y(G:kQk )lgr~zwfPp==8?{ͻ:= a% iok?3 S~~ E#%M ɫo>$7".?@,8xFO a돁wGq :XD>vj zk[$Spj7Wh ;uaD |%J,}t#6@ݘѶʕĤ|#5@ɽ%h]C#UfZAUifQr*+6Ì}F4QǮcFf:f_#=u7Ue 2{2QǕTL چ XX\7M"Sl:뱨[Pw? =Oɝtl#sw6h<5%ѣ$p?LBXˏ>]>kB7d@ߋbrϋ+kģ¡)[Lzcմx&ClQ +UFT.ʨڂ/WOqz ~F2/nCEGJB&oHsvNqkkԊ1G`v=,]@ Ҽ$][e9vb%hkS@U(򡭈4{T&MśK Ä57fUuנMh)0]VƄ*a+ XhWuˀ>ԳOW#9tJ-†6jbN*TUɧJi XY OBJTk H5tT-yU\ȚZk3@ -&f/4T mK}zSHw3Hx/k ڔFYD1g-d n Q=񍛢gH ƺ> MDpx]SzQ߶pB GSUӠ*@#˵OReQWp2Y]8%@dzڰLK;EOMM=x2`*7<љ`$8LC8R9 ٰ#` 1 ܂iigoOg{_gz}jWV\EV?KY.kB]b9{?@s0'Wy9 iH c%-.p^WT/ n) vxYR& FCb^I$,501=IJХ&Wt0b xEo[n9+Ec~ 5LqzDpE. śI@jd κ&p|vx;B$*bx7/k P}yyz`4nh~TITD2FT}d dQ5jOU=}:W gYzeLLR(s|  0Go%Q"+*K:<|T^/lc|Ȯ$ȧbX牤FУnf|k;,'G2a Ss~ӕq$܉yDzf=Pn F6 L0u6r[G׏D{x V9.jy:]f%( ;xuD&*#c K#RTzr)~$~G} 7YvgmfUX__=sG< 1ʼn6 ߰u+fT/&Ghɧ4ll{^ʺ#t9}KnX57m(^0##%C0[^!Eo"mm'kqhk؊sUMmW ui\w:>4WZ\ UUV1K[^x^6F+`֪%;/4 8L0l22`>0<Ӿ3 vEҫMHF±c2 evF00a \f ^$Llmq[4"^@Ypݻfx`#ԺMEj!@g9{9/hT*{6Oy奨m 3R o5Oi.O?KJӁSLvL%X!s2fTgFGF:;a#0X+f\T<# 6In-vȌjJau𶥣#%Yjڃ2LO)Cm}B'~rx?1~:ǥA&ŕs>v#~l&~e|ZF\>5WUWuoQ)[z{bX2~ւ'E-Gn]k]tsLk@t[T_rF?؋%*B˵$iWɞ[.,no[;kA!Oh{>UC8 kfvd}0m_Hh8hX ,LoP ']~$D7MgoV$'NQ)s*YӻZ  ?|QmM;WW(%.:l:e½&>%s0V͉(QCrIiL KzI'PK]uVM&setuptools/_distutils/command/build.pyXo6~_)$A3`Hm0Eam3HxE$rhjwww$I͵ otYɶ.=o׶kX˄HjR$YxIe>QNɖ`X19ĔJa?:å$r{f6]CNvZlGA>l7L,Yxa!WVvn W ՚fq0lk+ŭ$V#S'sbOc5 mh$NLmӠپ's[mOHT'voU$_Yz`.[b3B(Lŷum.hXwz !ZEowHAX!I"; &  ⥖ 5l I*y4esn" !(]՚pڀ1J7Z // *㹄8Y6 Ҝ:#QƆ5VEnATHX ֚m=*BأaBےÙ&h#QL,ݡYƷMӤ3Hv;AYc 6Xlsap~7ۖi0qVʆQ1w/y`Mwp"Hy~zFKH\pieaLf)m1f P[⒎)9Ph(P#mz6ax]3A.'C˗ 4qkaYpӫO,{.Yjji_ZYbVį^-ԩ_r<00BCoݻ7?b繶o":;`LP,kL i$Ad.jI&m&5Ψx䔔+)v|#4t-R=*/ (yMZ^k ve .?@gGQ8E > dBɋ$E 'ُM l2'ŷI9ýƙaXYV:+ ӟ$%Wt'W! JI>HD6B4X &I)Y!aݑV=P4ʨ1*#bOcH%UqgȽGH84㬐ɩxWGRҋ9)|6@hٽuC#pt[ò3jy2=JiyfgN_S lh|!bV\FMs~y`0#'%zpT^ U>⁹(ּ41fM:^}r!<%T^"AlZ 1n$IǞ"9oZq\ g0zb^/bv6x.L9sny0u<8D?kuy]p@R o~L#8;JԸ|4aoCrmobY4@S2j;/ddZa?)XΐqE?6z"@g*1Mb>_?{aPHö?A#8ZBc ){UЃ]pPaAZļ Fn-gM.$2 }~.8qi79?Y!@5d b5as7BLCDL.X6RcfE6z3SRɨ&}^d.xSX.L w9\؜<^nr9益߆KPK]uV?p +setuptools/_distutils/command/build_clib.pyY]6}`[Z1Š(f@EvbhԊԸ_璢DIdK`b^RITne]ZۦE#5̞KiVʆ猳aecO2iTeW'U_ FFth$1%*.oV('Il^C bNܰ/km͗L`U%X'VTwp`v\w𩾰C@O^S2wm'TGKƸYwx ^Cgr+SkQ:`3JB ?VA^RKZOWhqbk{'Dd-$T Z r2(1HtK;D=; Q5oiw] aBA=aRB7f)DG& a9A[w>@ >!2sdXѴN]i4]JyҚJПd;K{bnHؕ m895_ǥZMdQ) ʣ"|T5]ί$Vpq2",^h] Oy#]ЂҰVBr0I$TJ^[#gSJ,"׆iG8 xu&d_xppyChܹ28ƿ#YoBG_]E %W2#QUs'Yjb8n=%HDnTo3jx(D˚ˈ 7w{M7bCOfi6{8PC#ʩwڽd%}5k$ e5>6΃;uZbݛj(C'bDFyL(O^$Ç*ە8v/?M[K&o=b֢vFP[])? MOG)es$#)eOjpQS̱CǽXs2n_Ko:JLgs YKlI* NÏ9O}3/@$*%5v+=C 5eG''Y=Tԭ'^bQ-6LB)+[g^vz&g/7 CS5K~mȸ̔M,J%HT80 _*ի6"'~Bm'Lpj7Q$q8WfÞtO Իʛn)1 "b *F5j"~҈kG(lDظ H7q=1vg/+1$t-I߆,)D4$!Z)u^1vg;}3F b식pϱYn'U*(ȴ4?&} ^d&}sO~qNr1a8}A?|/vuB%ew[}%,o;[b#jL]WI?`1f\z&;DrJ]B˓Ng;FH}xOw^-zqFF0gTohqL%{0d9Kܻī7/hG7 z#qӢVoa$-S$,D fdd?C,c(F;efh^MPg7O.v;1Ћ f\Sv"8qҵz<P0'}ixYC~FN261Wj p-ֽr:~ b‹"j.k,9c[Blp0\B[K_uEc'wL&e|nES}U yxbV4C "y&n&,Y]Bϫf+U<ڶ}]CRWje2əiϒeRh[HΞXur?.OgI}y6ӯG:}Q)wzklzLtHYPp[?>O4g 8(E*"wXʄ%gO-nfO몹A^veՉv鄡%?3 oAL/EQ'wp2$g%KQ$-MmJ/fI\7ð6Yɪh~>[5~`0 _mohGwZDn׵ꑐz!hB]Ce$'X]!JUQbE+R&,XrŢjaȦ @4_@-&BH\,d[}=T[Xhx+̬(jE= @ tjM 6BR聉 S9aO;QuC VWzBӤKʂ6}u#l ( ޯ}' μAҬ B s&kFRMVi%=mN(EqP/{0gvVɔlkxk*Ox r);sGʫs8DO`A R̈ax>$y ja߃Q[}| /O#DK2Et*-m MtvGkȒaA<́cK@o?u@oIQQ1x`T#-U\T~6qg qOmfMq|UWŸjO4hF]A5Ҩ[ oOl|ul H R?{2VsxxUM$A{Qt SEQ3IYe5Bj:C`m Vj%}OvAo T?AC ~HkDv!KhP:<B'-W:>~9VF[IjVbF6"MĤ$4xo3q"RY~nQ +(\.IY@&)B?۝'ReW߉U>(FpyA\ע`f]BSNNvIW;kɛ-8!OI&7UhM#@IPGmHz#)%RYC}ceI:HL؁Uܲ#hledjb(Eﷅ4\nD+:CmQո, 0=1:Uދz- %OhT5 boQtO_Դbah(`YF_|1Rfa|LBɝtzoo4^"XUD#L{Ɂȴ&g8N~칆 0G-,"#󔙴 ɾLrMC+hi"/{fl7R3m#Li3lcǎLc!{ȕxGiG\zc Զ-Y;0w V98/V 43gs~wX'r+i}X'jʺZ<{ȴD_T`'{ \q,p3t{~`` v^}Qhп`H|C_>'_% hJ5j2LY՞B @? {5hbЂhIݮp_-ϟX 񹛠w!4^w/aL֓ 2,Т|tFn\Vk\+ɝHv@/mdu-V7hf`o-=hZѽPa 8p 7x58օ˓y%( :vQ`co5}6~筌/MڍTA1/z*u xnMh׭ׂbf`>{E}ns <%L􃂰K 4 bOR |ӧ|@<ҁ!J18;ǀbrNbPjV[uJy|r(%G~w,7TJۢҫwߟ;N9_^|❯FP6{LJ6vwD Ё2|l7내~p &)(Ealk,d`]B!e3q5VΌLR:e tl;b]=Q֊j]0̞tԎN }]i"TW>tYU3<9y%vCsd+^D6H"2\@DrЀTlROw:äBtk)Uٌďse)qB6m}X܁v4 j s 3gVӀϋB(͌i@@eec2 *܉ mqwƄy$ &vZI<ϝ/ Lń:#57 !S4m ;i+&Vd6׃KB[^sg^L8/OjGqM#`Lإ2Wl>aeq?EJ51;Qp8uQdnъJ곝lF:b%qV33 Dp|пICwhï_x2:L Db+r~JL,Ȓ7 kZh3[^FBftka6̊Q 27Ǿmy0$ׅzṘ;EdcTR qwl:!-vC]P 3Vq?Ч =X~>Ṁv|}@>|282!zwț7\pROCKmm=&E^7@^ӊHBm1[q p?񨁩>`Z"~Y$r.A~;֮] 秽 ܿ~f%vQ&%cDH{ 2AB#NZON$XeůD{VެKIJd|AؓFțF*1`FMa3iY g#⤆4e4v~s/<"*p:ﴢCptXE90R.Vw/c]_W=L믦o_/4ʌh UlH#dt5'NЗu]lf$ix }i:iq %BseCGvI9X-/HmydwCN^4yp ֿĠG\  غ3'E*^C .C&f\`ѓM)N.h˜ }>`zq}=u2msxk j97soxpƳX?pΠC>/'SFB6p"q`5y_n!4),4%]#;p'`:!0AT SỶ`s$-#J4)?M!\~7;,#6*[CqD]$yuaN}0h&YzH! t6䑷s؞L hOqi 0N;iV GJ%,P~+_74WJ3Et(AU&%E: ,עPEԷ#::zEǬc,<9aۃ@=V}-̴g&^5uR 4*"pzn3yOHԔd,I}4t/&7Gi(iv/0EYΛ(^}q tfwt\AE-U '-W@m`چ4-**{A3)v8LjA'lgیxdHoHC:ͪ-)Dr'ր䈶G76dՅxh. oWDk1ta+AȠaWN]ھGra&QC%z{ijh\ uu;{}yhnmȹ-@]42!&W}]t,6B'L=gs)]y~̦n$ml0aLjQ{ҝ=;!-H{j|GЖm$FNR["o.:T>`h+C8DF ggS=HW$]-y3atĿ }eGOpAΝ)Is9}'f| $O,U->XR6< E'UCej HY XTTݓ%7X5W7h[w@햄jyM(}Í& ,0ܵ> Lʂ`XPI(P`Ӛj곓d\eqZO;g(#ފ)կ$tBE//T⶧9^ue1$Vkj 2@c9[8;tM< rbYt*p#]6Lk'|"Am 䧢q"lAM!0mϨp5BKZ-Inhˋ NûMFG 60845U 3C01gYtd0߳'OkL9+g4㟾XlٵޞSS/舁E1d/RFk[c:uӗdْ*R(\#Ex&%ypG$kGȰ+a][,xإͬ%r؎LO<}is7$.vذ9n;yZo |bC"% >K&syGSHc4z?ki(rghi:, +`əMS܎0K$`ͰHS$J0#lJBw*8RV&$8AS_ƈF䓴EhA~DRj}$?6x_i ID k.N?| >p5dIWѵPe?F3Aϓ(L,umQru(L rYɆtkQ?M.(_Ma|_7BR`7 I\nSo#6`[:Fil< 3<_O"uߊcd"#|L?+l_i/.N-k^ɍHBV8Ah{~u>}4 qUy$K!oA"f4}$ +M晉; y?>fFRSX5WAq[9A6F ݡEW]s󯨲ꃢcUIRATX6q+5h6k4ᠶqH]i0#I0hD4;_*w F|kd:﮼5S8^2ZME5<榽tտ>,J/񪛿rE%v)T TK|vL{ZjX<;X4!@[,=@ML~wkI 9# C>>5cNBӲ^pe O{E ' >WqGEnm62ɱ{/n253iKrrB˥.KxH{ <+__dL~n.z.pNaׯ/~NÄ GڨѦro;N +v:P$˱pTGUd2{ť\%8h+*tS, we_VO=XOh0tQXbx" Ұ\l.SJrʇ< OTGH,Q$6IobiE? = ӻ;YD*oR>݉]̟AG5@sѬESҢ_UCp!zN"F@ޏ  ݾ9t]L,Y xVD05ʄH0/~,@\{)cIBt+phGvd  K!7``.;(K'm K9&ta"RK^&G+T\8֘0s,?͂!y$.x>\;bZ|E?1ymx?Yiɳϯ_]BHz7LN1*lM''DHv; &$^,:B nBX͈m4+{v)B>f[VA0 \Z\;|{WשG>@?]rςV XGH ۳K "mf*e ļJ$aEr[zr/pk/o^N\c5pβ8|`6Cg'p'4:2[9 5Uqr^v"&Nx(+O_0 `](/ptq?|?,.O?~:GODf:G9 a!>\ht<\B"[xջW?_ts\ӳ__/x|40V#x_}JdLj<f~W8b?>oPK]uV "RS@)setuptools/_distutils/command/build_py.py[mƑbD V|o˱+NJtwZo`, POw 捻+Ғק{ժn|Nnp}ۮގ竫c'%}čɘZ8L3K׵ N4ʳp flLBOaWt#1M$Cqn>,_m;A[ G1ۑ{|8긔l7\o|j!% [?xx0NHWxfa:+E(Ŵj#~r%5&+YV?+;). "*Jhi gl8*B?9zK}[6-n ?vG uh!`+.rj=s9J-[Úa=J?;(ʃ?qӠv5 Hn U$]wֺ`QL߳ьQ4 rQF6wJٻa]}JR^?  -eLam-@܆~.E׀՚W`U@U?[cǼzgۚ|nîƌ'$1CJi ż=0EmGfJh_D/E Q,te| fwvwa aq0 K>Z 3~faCôT%QpT؇Cb}-\\"Y$ʮ/S%`(4h%U$Wv|h'uޔ UĖ؇%駟PwgR,۝N.l0{ FL}Z0g %&=aB!B~PÊjA4qL?U)Ok9 {G)?[2Ӱ@'Eh!-R;*q$J N| R?iA]ѩ:d X61`i< h5WLJْ U`9Gt$SRhv֕鈄u"IyqAgd? 2oh/o8lۑZJ_Fh+*XdD mo}a(nصsmVAX 6 A8*\ UfzRQzL +-D2b;;58|j~E/;Ҳ;ZPԮ34\wady4\4Q.,vMu-%/вMz3|B gId۠,>vZhAUu{H?Ń4v\QGNhЙqfО QVlۗlJPOnh GF9⣰&XFrVQ6oI|tF0 C/$v6@ 7Wо"o^0 n\ ~ҡJh6J-o#Tm2#@{lyw\s Y*GqiF8| cbո2"d f6L"[\dsHxXIXFJt<@x-龜M0^8IFj 宅ɔ e,Ĵ+2bı4WlUUT;7s6iXRПkOn9C[!Q2]rU2Ӫt\-57&~ZM,Ip' 8}%řJ4qsc~;7w/Uj ɁIh%{?PU4h}>gM b=SNl񛢅zc'%\X 6~W :NT+bIVRj^ .)7Ctm2TKrU-\w S{cK}5F3TB6Dc/F6E ggLx粟,qņj/C k;I[v4~z.;0l`:;Υ<  _wr(τIZI@'[pb|.vl%+L-XK<|W=tA}n,`U ;ه؋EKL5dH0(%rܪ*'>SC@J9< $Mb&r5ō\4Z EP/[ $5|ڨcL)!x'<`[_IjFRۜF_} әΦؿu؀;C* ؝`f΂>4vUUh4gUӕYY2wJ׷Nҁ'75j(*q4~"X,Cx}! Ch!D8UWQ2ã%cڛ Hu zwm\U:O|]\!۵BkpR=Jp֥8Q?~Zo+/{~%@I{[?<;<~5*)Nj.81Hm MK ׋ׂUi(ħRx'\ŀ{ 5W&a8zr{? QYa5ֱ f$Y,qm7J*&*Ð@J\CVJ8rўۏ#mAOo{b]ʎ E.xM2z1gt|1V2V̹?vg&cd*܃rnU;sŠ(B@K:J%JetY"]! ddiQ`#kr'QSariy矌ĻVSQ }_AϻU1\ŝ6qMk g *)=es孝hV]i] Uht`pS5$?RK G@B&YJ 鍘X>@t-v:q%1 M!yl„phIL b<_ xHҦ#{}C|I:(%O(gMGOuRheKs`~c.li檂[UyԱ^--s)y-vMBxkkF]0r$Re%@* T1/HL{#P1 R\9 o~33w!4*8%jm 4u6(cQ~EQ`^Ƅ~=ih\T3_W3K4{_g=+X}(nsd nE ˗>OwOur!?z=:I ژ:e:ﭫ>z(W@q#m;,W+PrV%Q9nIBPMU}\أYRܠH>^EQko_F)4':mgku/ ݍut6d\7 Jщu!ȵRۻ]/^RyK"t^(&'PǃλJw`++#՞nHZ&]ϩMp?!mFCy+$}l"rS5.n ;4C;kXI◞]f̚1 5Jc|GPǽS;9wyu NmmVp+ziR- Nڞ@CKיb;k!Ȳl`dZjv+Y=Q KB.=a/ٲO]ES[-PK]uV.setuptools/_distutils/command/build_scripts.pyXKs6WT]I&q_;CLd:4DBb`0w H!'T>o 0Jך*nGZ>KD䃾J?}7FrGuPH^ s3YOdͥ{xɈXwb3\0]WZZC5_}`dSM!td@YA"LFMl ə%pB!l,Q$D' s׊Ty .KD9QL ,sF x)3ko|U?A!oJ5dhcPJ;՚ fAX{,i`B' 麲^ ㍵W.k5+yg~m:"Z^. S4s$s֑[_I1%CM|{ `cpܛC.aD0ypj?iAυb^*Q)(W ˺5)\ cw{!:؞fۦc;U.׾)IJw`CĒ7 CUY 1Sq Kx ç$4XY`9";Y!zRV᭥!6dL\v7Q z6U}վ; !XT^~#o@v QgNdENj F)'̠'G;ÚR052$ހn؄J#(x9Ϣ%b%1gV+s!/@~ZWf&$'IV& Qce]-Xb3€݌Rҽ"lm` rPF\%U.KtE4fUCw6 %e t8RȾM/ o+V(6]qwHFsSwӍmTQiģ% ^U8$ǓiJQ<$&}ԙaF9;c\\(tUl7:}s& 9f72HLVⴷ'*/߄4XA2Ço7^g 8Oi\ו3ӫ`SmF/d>eƫ($=)~rF;>J͐6ȱ5Gf0ht PN )ɭ䚹ǧUAnV8 B(mGGs`g}9VX+!n>8&΀cQ (y zPި uQF  a-l~ø"ߓG'4xڬޕgkb2yORS'}>_bt!P~bE뢉I{af݄ۗPD` K'>΄}{c>bҭxIPK]uVY49&setuptools/_distutils/command/check.pyWK6WʩW=p"!6H!X4PJRu_X of6M͵ ڦa.3T߳c h@MoPG@I6MƛUϤg#!ˎmHlp?WTt$|w]=qsN:Z#G}6u[y{Mǔ =QϵK֠3wU 5z3X2PT~σSÑ%ܔe>ڏqMO^U0=Se)bzsfb\ivzjk.O{t9Zg `$DѦh'}NI\05;55wS]'$ 4eP3wq {8ܑ7ՙZĿo?1uҷl-Xa&0kz%g W4\xiLs`ry\ȩY6vŶ>4,Aj i("pcabZעGǍn_9ZO퐬A]pJD|E3yu|5zH'fgmFtFȌqV+A/rB voaZIn!@R6Q! ߑzC^rWehp/juZJ[ a{1Ҳ+$ \)]گoNJǗsQcy -cŏgPo>?%--,3vN !tI>4:g=!vpD2-שYs\VP/a- x`DJi \yn(jȗj\Ew05='i L0eKfu}~Cnu T+ztXGrM%:V7Tv|l.Eu;ϲ;Ѕ+Ö~\_"TK'*득#o;Y<~7yd ~'.X"'@z1_98uT)!hؕcaZԮ9l%l2.bA{iBzPK]uV U# &setuptools/_distutils/command/clean.pyVMo0WRJPB|8jē]cGӲzw~@u<8qBڬU LE窖Xrc$qG 3"orOY'/BYB+l(y:9-y}~~;{6DUk@ۨ46m?Ņ{^J2oEdBg^DFNhW 4589A)$Z9!Bcu`)ď?˙ū$ lprl8K+mMǘ?t )lV8/s*_(8QcMtV@ {f~3lg8tӏT5leH:-utS3f%׋yZ'hՍKAi?&^O~nx8[wjĐ妷y B jU)`X%uBO[AWеir##^"jg*uJʢ7tJⳅ:FvfYMp4>7V5QYӨg.@奧]֒i7N %m$ʁp,JcJ7Mgr/)ps0 T(-΃A7T6q&5{еKwuFgN 描QD`3t&Jg-ZWxȲ^j<`Raj1GCpy/N9:9I̩+w8-u>V}gJߥy=Ss82ӽSeim1,Nz*3HAx).v'cO/PK]uVGl3'setuptools/_distutils/command/config.py[m_p:׻EIh.ۅAK[IDijޙ!ݻ b{6ËI;Yu[ax[5DifR3#evE8[VJwbja|k莼chVqY M L7"{J})j̀ނ7M)sNk_I69%XbL30hAlKu i+/KV XAq5|pOki!m@ ·" Լם֊`zmKf⻯؆ (aIX,8l^/ >y+ IV4%zYݪknh?tvf{U΁1>fЈd+Z]9Y) qUV#-IFt;JQ-x0[ Z khU.[Exq_(ѵ4o#DD mK{JY߱( ^!j8G>X+8GuQFEt-w3^d艡QTSL@kц[k9|P=-p{kZoq^p CŞ\Ƞᄪ JFɺ-:į[opG "g jusf0Nxx 6;e.3]=spl!JhV 1nE#j% 0Wlz_j0ճAG8%6gAצaq5=$FcS* l1Eu)$0?vJ JmTdpYkE~\7]iRȟ oLrw}=F8ԛR3r; ѡp y2^ sfU_$;s#M,k8QkVDE1X-N>U?Y9 ||e] g AWUk}|dz3#k-m8Bs6p'!1óϣG[8tsZxTŐDNCމָ'tWlLopj 2^k!_;%+5Q{=n=7H\8mds}je' kBҮ]wxBu4\}u>3\"ߊokN>Q?oKx59SXF^K&)3X.]SLCXy*rB0[⿵F/ r>1O_5"xK8kȐS9#:4iy}hQoDZg]qn3ęhdiTUXo3Thjw[n+nA-Ǖ!2kMhZzs"r:-Ccu,.*m:q ־A*waHFfP8bm YE-X?/ԝAymTޤ۬U6Wg&412}'ةxZub5; xиA<>F <=nfX@䰠[qۋ'L^laXҼ+93=T;Uֈxgk?~#h8{q ]/9_,ۄŐQty:@ژBN/u.dfnXwB+ΓE"|"69.}fhOkxhg dth= ]$v/O3??>)˲IVCDxH E_H9w۹338@4]vw!-_b9RO ~9(30L C*`ň̥䜪W^'b|#I)? ܁tOX 1.ǫR܋ҽ%w( qļ,J6ϺZ;%}1Ie1^|4 }_rq|ۺx!rq<(TR[{0-?Z5TVzANE*oざ ,;5\޽n>V-? d47>.qkwt2_;7mt Vro4b@CQVST1iLa@\PU.Hdݑdzn`@l+i`Ax[v7!t WT;t{/@jO]JcJôI]O4HWRˢPC{R0 j+`Utt[E%DQy{|P7El gw@O*(1rc`GF6ɘ/5[1MD-TSfBقg d&>4pz^Ȭo:cb),zKӮ{VJĿ>#G,N9QP#=p*}1AǝC$QLoDr Tl|R8'~d>~.8#ωX!o#'zq{޶W̏-]DBUt\߹^ymܫviV zNKfjDlϢ&s2/߃1_{*Wm$CzDM4F*.xN f:o͋o|7Ce{ʨzCzB[c㝆ů1MEJfm'QVr5#K?PK]uVu(setuptools/_distutils/command/install.py=kƑWQ@J?$^֕,ɱ*6[H@_w3"'WwrY$03L&Rt}WVblۢ^ZtEU*u'Xn$ad299)81ߖM]U.fp]^eۮi*qrn-3W5S|= \?~&'_7]?7fXwsdhn.+>u} c} 绢L -7E}mt8*un{޶M+5^_>}:sAwbMB^c-K/`/~ȫo^|bn~s//|ꗋų^?0$ K>. o}_.!X+HrxǦ^x+/eկMqe[:zo /<{ gA(?w-_|:TcwWx0>Dl7хxGxU7#$Ƨ"a>r]&B,m.96͖Cנ'5m+%|˚Ӫ7@GWRXy%&WFztDHj,Y5Rpųq}G0N,ĭrlE4T~ysf'ȳo|`S62%i%fE}`5߳5k`|qW9K*V+ i&aXh-z -OYj([ZbpK^5= #i&6<, !ҧ]זdݩiy׷uPϬi=#LRw9voJ FLY`50i_( :vE_uZR5Ұ4C%@b VQIA><., MÎw1ᙁP`y,3ЎF>M! `JjP& )y}x}r{wNנ&6!(j@mbctTM`bыؙ'v`S yת9ZS*UÚ:Ǧ&z5GH3bs"0ODf@-2&E$@Sx&h;rK ΀Y|0mWy"Qϥ\A\_c D"y*4yRc"͟G>Z\ӻS{7h2ht|Rua:a6]4K`  hzZ WRuȳ'{$yV;c]#p0[ACQ`*UMHxfS>Da(S6'qr4FkG5lG6ktd̥1`m_ZnND빸ʬ8ޡٻ+c\Rm=MX*2@hUR3JՐK@Ȓ^sA z2e{ݖ5 7\ fo{#iCvH>r$bJ-EhNbH7 %c]PݬzX٨"2,b, "jT-VfH#gRe)}30qb9Cw.a ;Ya(}1OSܖ[*ƭ |ԕFtsWcS ,'욪\Jٛ]1yC`՘'.1Â],f8 ILZ;Ƚ6+JB hc%Q2B) (i8cϾzuUƜ)¤Kqg`LmxcUyaOSwvFH |K2YVAPֻ"%:֨}׎{Ko/!9XDDf(z%V\(,E.NqS`Rmظ$GG`')'5{BNۃ؉LH"c;Wy::u*߈/$(,AF(Ԍ.uI%뜜6|-б zEx,Zԯa;%*Tm=t>? |6~%tyuk,g(#цRjifcByOSROS墄OvJSaVhv?fz_e6r5}E-k=+tn_1_D[Z-JB8\3:"BzWkb 0kU-2&EL^:ɠ"nRYl)P?vsTRᩓ>3Y7觳c v_h $r?VM-U#($}-i@:~8b>1XzL'Y礐:q$ *tSe2+*aT{X9C d0XHi2 ys}l.m~9 -N70&g?[ޖ@Yen*k>"1'[p:R4U)wqa,̤GFGm:7J_/׬~;8'x\K;kA2KJq+" % s^Xmu'CZ|m Z0ZRBtO GJ4)kZX/i3Wf*4Y|Ck,g8tt$ wG6^{6gq~sh wu^MV,yx< V\+k9lXoUs-!Jaw/  !c_Ս'WOBCA1hffnQAK.ә`0:9g3 2 c/&-e),dYQN'4`0;ѼǸt|ܔss;YaXSʟIWu ;D0l!ڞJ#c P"f#Ƃ0UBjL8NI;gC6Q#´t3/*po|+=4[1L섗=n|tXDOQFIhr7~dȩ2N=($x|ʞ嫦-30u/1')jΡSR;aF]߃fxjfB[58T#jtqG mDrtwo}}?eZR}{q-.Af8-̒"Q幸ҭr>Z|U0_4k@3mcUXZ]uBMܫC&xfns*k`bã2Zl&ׅ+%_%w s <|Dm8շ^KPUb@sh>M/ D B+qPHy Н |Ao {Mny{MgI$ $~\2.D6{,wPLWA@g>-ç͞9 !]5ޒiߪⱷh9UDFD}q>` „#E*LRa+%Wma nAtR;6Q!Uϔ@kpd(G,aC28a`r;N.8xsJȎ'+>`/`X{t71C^ڢXfp СPFfA;}Ќr73uGW.+tp)Ō(Z){ B`E߆Yzxsib䷘#}I!3S;O0Sܹ.{;GP +4eeE=ޖCe+30Ώ[W.&ZUc{ [zƮ .u-mChKU^mï埓YUl~Fk\PK]uV&KW -setuptools/_distutils/command/install_data.pyV_o0S*@QZ/^IU9hl.38RߤiZIz'9Heh]%HkE,#l`.FToIG[TvH_@-4Mԕ3r;`/:)|kP*hD6Mj[ؐa}"61Gpgv%:Ia-=G1IV`dVyޢi|JeFxpڜ.B;X/}\x]Ncq*H!;#izD(Xy26qVWŦ0x]+v^zd߆D-|.Ԕ`*]ac^:dK:oOposvl!/}œy3q!{ܫsݮv#0Vgb1^]v9kui̡`N3y19dzC4LVqFqGp/Y! b:`^@/b.+y#ѝHvPSZ%+ eس żRJh0*PTB2::Ovk3O#ߎȲni5uS۔+Bs|V4꼍`O2H~HwN O0;aj@5mtqv_﷋ DhaFZ9{7hʻ`j;3ٍVX}K֯[x)| z@!Fz'7 ){\!qH:+i*nߏrejG9TD]6d,`3 OQޥ#o739rޥR,y#9xu;cjb}JHPK]uV2L 1setuptools/_distutils/command/install_egg_info.pyVQo6 ~ >gu u( 3t}Ɩ I^z#mKa!M#?"i}Ѝm[i4%n6MkE<S &5$fHd(y_woT2HJκǕKIR;ۂU`z;zYiWr{m2Ii /J ni@VZP~@.hkfoJ;yB>򳊩/uLџ^ !Ȏi,b5 uв_1<6Ĝ~;kpsC3Fz4SNL\4}59 Jn #f;z֋7YC _T~1)\j'ؒ=.k,g }999ba'@ ;*v1IbzpP[WkWoH]T|`z9r-_1HG  ;l=*=>}0sQ7tf Y9jtRjCQ|u.Ö q\rs|9f~ V}-XZts 1!WwǴ&|j#,cu(CT*Y1ӃOg&>טyG'*U4UzmCs؝yG50":}m|١H*vPqcciGck*@`^!Ci3)yA2Cả RбSfa-D9>" :wǎ/ȟg<\~!F`7/wC ^ȝ$(f3uHii@)hk 8Hgu nJ \LPeuabi⑂d[lK\[yz& oxP OUЈÏ9($~p;[Hh'~lHqL/`_qK<(|ކR^P(0'7B_';v('5`ƸR޸&V 5a@ )WJ4jiJveuM~xa4η}m Wbe= =TH\rqkr/;8Z}꫌c?)P֎FGV`0FnD;@+z. -iI)* w$" ˙nTlGc3Et7y̙4¾m;q{,~]%dŮ+\_|㿶MPЕ"ԙމ-GXEϡZ}Ͳ` QSv Hdfw2_A[$TҩC75,v#ӏ+uqZ$B_2o8>&{+ iT+i- yaCu"ƣ)0@b[ÛnPQsaZ&=Nj:VX(%|Tey,Zf맨u=C_* hw#•*+̤MY!8T́n4zLJ´y\\،}ml sw {ϜȆ2X"Ge+K)? %wrP+켹|8U :vTL2H+HE-KeJ'M1: qy ~ֳDS7O/g8"6*ILJ|V41!l '85{ˁYq̆m$HAr5ݑi*f0 EZh`H#nL>@~tXO,a=fϔwvm00Bsy#$E=$R?GfpЫ?0**ƹ7tj7S|*/qن#z@ ?ׂo~̺X>~/w\-7ũa홀Y4%Rʸ3I u}ǥ?N/SzB>BWLuqn  6`ikWU!?o=KضYګ$BO?~[>toNn3IV Zc rl*P^"&Cx.Sѣ@2qHYa` &=vb>bu٨rљ _ _ǒD!B,Ƀ|#@w=U4jNvxƫiUX_(ʯ¯̮#m`d7W_@WJpFP,y+0T*#> *kr6am?9dsՉ+~Qִ>Խ\*8iu.g|֜Rg]U5a e|# DCkEy6SN3!:Ls:IWEti@&qQ{ j{]{Z8, } w4tŪ i:C\HeӲG1T0avԪ7X^3ُGFjyD0iwA';3=sVru4+aőWc4j goyEnt)P8PLd%He3[Ҳ2TDշ+'st׏3^~@Uͅ`rE+/NLFڈ2Vg&>Ө%J׃̨h1_usRv}p/^YO?hאm PK]uV 0setuptools/_distutils/command/install_scripts.pyUMo0 W.:ۀR (a@w+ñD,4~(rbg:H>=>RLǵ0BUEm[z%謉m'Ee =CdCCFPI跊8[Y-GxU+J$ .(mGŠYjp`:%RHLņo(di ,HG>WApkG cr1> fPñOЭ4hwS NrHjjh,#XE,?CBS#8&`NlõA4{NEg+ h@n<@ Ν QA-Rtt hV;ԠlbN̂0c4v~mcT ٌV#IE؝<5)W'ɲ| Z\O3줕Rр"T~v7rl1\ ST~_Qw,FL/&;Efʖ[e IGF<'sVWַ9>cr+ㅛtP;M}3iYg@jឧ76EW7b]nl8߮ixZ -,BCi\Ks/6q/."Q5^Cni ~-e?^Zn]H.Kn1jqjH'P^E_jEv3?_*T+#Bj!'PK]uV>qp+setuptools/_distutils/command/py37compat.pyuR]O0}߯b6L@My4f)Ʈ]",EҦ=s-M3!5\9`qH3=@Fr脷xIF J5e 6b| UMPpK$fCg.<4#RGVХS(.n%ֱ=_Hz4G/`2Ƴm4t+OfIג[Bk#~wC"dZPW~ahC~N[7>\v<IQ ǓqW1 {iWz`('H.UƜ@Zi{}Xz`YwpC_e.xaCg=p$7޻"0S wPK]uVK *.)setuptools/_distutils/command/register.pyks6~"Ef,ǹܹ鴉ݸMj^xT$6d5~ JI;~E`/,$-J2*ʓPq1\_*YeXjmjI2 p8 X,xT<?8M/#AZb ^Ti~eb Z,Kg:% e5b6aY|ȩd\M\o/o:U>/F 5(L/c?;#Scm<gXVyGT6{{,Z,uUuٹ)h ( 2yS1fK=jW4V$S"}M xT`Є)5!SA O,XC]SAcZ(J\rָ=Q>D|-xLA㐚`;=0) 7lhGLVOtLs%C^`e2|}H\3]4:]Ґ%2h`Hd GӲ;y hC&Ig1X͢Jhbw E2]oש {W>XL:DSCޝ 9ɦlWC}md,hDʂ>eTN09D\`{4P_|Wӛ뫗n~|Dy;j~.D!|ﰔ1Y(7c]ؚLȥjtϟE1\\U,s2ʓ Qz`2-,Th0}yߞE1%۵p'*d6bsTrywTՏ8]G ܮU[i#<K4MY5$&=,<~\$@iYMb*QHgu8C.QF28c[[u1H* Eˋw{W.lke#Hye"B+㐑l@ H0kؓջLK2YTWK"WTo zhJ :Ջyof EȈ=cw8e蒋zd:zw(ŨY A5P̟ղu9SQB Bv8Xna$ܖ#-:N%`! kJ}Ri5 *~&yS?ĨG8in `&$% PX!Z!ciÍA)`qs*J3͡a^]1Hq)<wg(ȑi=?_\ٳAv670NTqI>k8][P:7=Ϯā9+`!:l/5@+u{aUhk+BVFE(,,8rӱg^]+/KE5ԣ~v| 2#B=iGM1S?zlV8#aL'H>~B- :^ e:˶TK"J4c׎>> .#yVL4v@]x HT1Ӫw 0@ O |K+쩓b@yGB[ vGԈٽ2p]zvnivIdPZRxO1 ?2 8 (6嗢ԍ~GvX o|gB_y:Ri0bmtZqWT}L#tpSE5a Pd? k3Cл G$V7,;IUE/ &]\J]ԣ1s֯T\ŪWe^֫G|C#BqV Bi7ܕWYVtZ#mlf_/Sd#2E\ZPH-x6%զ6채vukdK6)~aF%<䕺nm<L }?;_5* W)N]7EjGvn%.X$IXlbͶVOʨ ދEOCclѹTXj dwYΈAn>(4\3䀖5$bk=8K&6 (@, aBQG0v@NrYA2PjÐ/^Y-\uUhN&sSdn+Eɱ@m[ewkp|USsbŜ X9Ŭ,MZzoCbԍȝco݃DskeroOO}<"Y O09ƦA\ۂ}TD4R WP<}k|\E ki>̋?TOHd儮,(gxѕwd=?<{ŏOG/~ųǏqz>Jl9 kj>еOcwHAs+ٚ[a7f~^w@P?FȾƽ1W Ӵ$ۃz,;oH a9TM&=(WʈG~(u{#vT ^[Kp~{YCpً(.Az{\-٠hЗo}ÕT E?PYYs;~6'Y򺈢@=`~kj>/eIIG"5JZ<y(3ٱ>+ cF4 72!g6D\])mÂK*ˢ> 1Z$1}k@C#"Hjgp$'Zh&v Εr=wT`yVXkOhZRZCup(Q+؄1 {ّ#ρrǁ>γ___퇏IY VTO%Y \D~BZMa쌑:OΫn5K.*8>>7\MO(tz?n _qAgL#(:eijErW qgR^<J t Pnz"Fۡ㰍7]P-}\ڨ˴lPoo_Y}{ئOwc_x?y;vG>P1U,`AFBn|AW sWw|"}yug+CV%ټ-L.^ɟ\wZHK񰴧A7kPҭo3B0>X]8;5=w>{Rꔽ ,D/v=|+P>4$O;7\` ҿi*|ƝY߰v艾9(2JA/nI]־)M))2ne:?꼍y;td*ggYqEەEуcsO0B C`2"K+Pl7UP@+b)w_{aAWN]ʅA` &Ĩ`pGDOO] +;Ԡ Tk$| gB|EbsRؖrffcwĻ P+f Fv ρk* Rf[&leha&UCVy-JG AeQ9>pʹѽ8[P-QܸՉ Wexэ Ғfc(%V#@HJ!}; q?&hLKdj % LThqk?6$h85ZaLÊ1BYH:*;ݽ*@Ѣf6bjsNO-NDz>/O\X8nζFeQPǷkd"ͭUVczmL@y|=wVR(2`"LLfKۦph{fme{zmYNja"lTPEOG3QOK6G1J72,;ka% ?T x'6 cjbU'3npJP&=WVͻ.imID eIu4P-e./䠨GCz۾!D<4Ѽ4tQJ~M&ZP`a!4U09ZY^j&45Hn01ږwjD:YNu)⺖)gM5tyA̔,'~\=+[!R9`KH')J D* "ҲK[3$)UEgBz)`&j\.@JFB 5wDYCՓ=D2%YMi&n"Cꌯ %u?[Twq<9(ry.DDsqb }JBtۍօ?EEȽyݧh}3̷\~xۦX+]xA=C?:& Ϥw{;G%^V?H!Y] aޔt聎dMuwlر5l M,`SS`ʑ$ul!-cEA<ԋ˒Hkϓcq.Qtf0F_^t;X`$XXÓs\=թYJ6Eϊ~&Y8CCv!8BrUk݂fx)vλ$by {5]vNg5ـ^C8%W+gLa,4 #Rg`}n}] ϼY^&ˆݦ Gj'<\o-tC*x\&{aIB e[:yL4'5m)اLg7uvv1W\Q(bQVuv7Wf** ; ccv: m 0 87LMEٻU#o:Gb۹U=v$x+ uɤ_DB G]; /as} fdFҊzۦ}cg1~$⾆H}W8\0z=?Zo5uBY[jPmөJ|#ِ ٕZ/^ZD]!@6ǿ~bLЅ94CӰZq\ಲ[ N kpN_~ƳR{L}sChyrVQ}WM"/.TCRM*&2CV b+KKF:\6:@E)uD|Ŕ}p NI8֓/VM031̵֣i$B]S80[Τu=&D^|a/]=Hҵ$n߿x hTb췷/J;r<z/&3/B*9XӴZ5OI`E N !YQV@L~H~$<-KՄ}o@.PN(H_ˁ^zu,;s'P=,|d|C/ٹ`;(ٙ2txEln'lkQ4_}s:3 fR~d_/@H-53e62^J!ƳH{^tca-rYܪ̖idj`:TbEM"kS{ql}@tslzu3mKd&ߺ".OjǬy.xB0x;kXm UYP7@Mۛ 䛆ՀY#6JxfHsU|)֍H-ǚQf,-4uY: sf“;P|xG%H2'l'eP1V@b+> 4~>ƤUo8mMJ'`Ps FF%vf̽:ŌY$7̣᩼؊g.IotGV;+ HltHCͥ`"ek+ G(Z ޮiMZu×@'jQmolvDr[z[5'-Fi9q@eaLr򞟲"q3C& ?J/Xߠs$eN.CgwlWdY؀aD$17HPXá;VZyZUhV]+#6`+iQRifSwdb0ᏙKhsaPj,-TܱF5SM6#5DDŽjFwNUSbPOmo(tH߉M[a.W/oӀ`m$`)V8՘]-kC%I|s{ES2)EC]/|\ŵ%\H'&hѪlqe4xt0zg@[rtc%ju..]onUx--& 徥:e>Qf/;mO_pk?V,;T'_*'^v&T֞l^2^eؔznbrHNºa0a9h2y?oW?rPK]uVG'= D'setuptools/_distutils/command/upload.pyr6]_CjK1c':zN&vwҌ$! dЎ9AD3q \FB&Y2Oh>omY$QFX@&mHdHM/t͈ /suL7ۺT~rS.jzՖTǤ= R/Ǭ̪F@ IeaY+w77BTCAk$ =V* VtT/wW_YYӻS @ÒR6UrɦFI>9G'cU/DS@(U'iISL*~¥l_,ߟ.__|9x|s,ȷ OO9_ł9Y3EQX1Xl11@3r/jTJb/;O4Lp,h4%;7\90(`m4lL>i D2I(X/D-FB@u[ "!͸-㟷oEyr$ғ蟍iU<<V\qZ$+V`F+O!WZ%2) vDTm6i]-PPo`]/f_ sEs>6Ϩ`DpL# n-[0˻PFp@.b9aW\oJ1ڞq_^gOGv 䱶{#XJ%\m| 10>S-L$*a-Ml^{'\1_xR_nEeQI4sC-Ɇ}5?ұ-ǁ~iIBP"6)/RƳFm Uv#J8%~O0/ 6sSB%Y=`- F6GʨCv6!u:"^ 2b@{g1MKW[Ν{@n EhU= O8Y ׀uc}gM罘VLt S JمMV_ !>tԺ )?%/x=stڬFz<\7閛o>H0']k{ٌ/c_.LŦboiweY-g_RKuto[Gvwsh(XXlv:FX(5)N5(!l+@g2 he@{T+1LP|ęgc#r,^ X\1^U\֠_%X2qqjܡqh%E d PuAA"S5:exY! ؐ^[-L^u D-p1;1i,8 D,/47Vjù.`2HQL+!hq)` zLڑ_>t.BYZఫ:q+Yljy`?1۫|7 P.s6/-Msd sUUDRӦ8Jy>jgm~Fȱ{86\Y_~4by缑IkN0x40^y.bD]qt㿜3>rP5H!gLT%# )Bm^ajA |wbE4Т.=7N98%Mh$<%q-*|(3 h"C^C3(5GbWJ|vEͶ2MɖFQ\D̑Cڞ=/ɧg2;mlA%]\PF*|DŽ]%3]?VVvZ@d,Pʢn &;^܋10|D {t`ԺF v[GV8lɗ#xU^!1jv~\fN莋n0#R&ml{/sD%󐤍)Y*Dq.ΙqYWO{R#b-ʁ޵n"vcΤW<޲gCi=G RcQq>A=]ڒn+zJ!}v0Vz%ڼn`_}B%(| q^A7=\sot?a4 'W3NKyyU9t% w}]h/tAB՞uTJ vad+yQ~JS/7ϙrOXwwAPQ@[@\]5{68(+*~D ]ꀵf S3{O?5#6i"&-z¾fkfM-{ A1\/-QowV(s-a"W 6^:m50ᴷT kH7ru ! sȦ;Cdl ol (Z Fmr(t s[HY9%؊%bA>s_dT*I* -"ZƎ$Z]te76p1V #)ZJ\\>+֫˙b ,.}AԾ سA5B#S7s \SOT^PDonׂNs}R&c>&!h[zˠ#7 {k+4F+ۼY,-6UAoJltb҆RKPgNv#\@4=ZM}VPQQT_ko'uje*e]]Z ^s[x"90{}~ j8;W^%}'T )Έ6Ԡt Vb|5#PW8)Ρci}M/W{q| S)]PJm uQuHe) D0Gzhj|"&r v=g9&{&>B!$i=5>iǀG Nw59gpYЧuf%Lؗ.w!eEͽ'c,+a. 5@{,AHA`C{h O4~BfeEU,twK4Gqض"@6t#B[bC]bIDȡ/`\XY@9PudQI_zN'*[LP6oQOi3q9"1P+; OKO9PK]uVaLBmT'setuptools/_vendor/typing_extensions.py}ksFw tU$4c9Fڊ8YW+ٻө(%I@\_?3$q+H`g']t/|HUgeHWIWySڬbo z6"zu>Fi?xJD^2*ד*MT$Wy6eZF_R)Ft4vW9t1.Ⲍ.e.6uZ%QJ2},6=:cGQl}|8<>FO{{.yU?V,V Ɔ<ƪҸ۲\F SEקޙ-47Z>FYMI. e2};`tr"^&R A?hBzb Rђuގ )fdqlϒʅ.,3;-Ծ*AߦqJ.́u/ҌN.'2~2xhxHR>z^2_Ert'T`4Ő?M wKѼi{YƦ7H.^3ܳ˔qt1Fߦeu<;?7"4S~AhW3͎ D ot~ m ܱƾi ]P5`_k1 QPب+DkPP7dS Do9iL+(P *PhymI!n{'vࠀ{'mن` F&~ I)^UPHiB}gY2 _\ReZUr7e R?6HR0ED++ά\'h[?Ԋ<+$P~Kg[Q vecc,ҋEXa4FaMpidM,eϛ e!%C3]9E^ S"\bt{MU@=e᎜1g'R2i%JV[RPLD"y ^ Ef 0ll+b*ճ9~82ɓ']jDC\y{NkL Ef,\gsՋl07U\V!'lmFwC ( MdYQ*ЭG@Y~wYzlw'jN^ig%L"4zcf0 ٥a~{593` jր?< )+I1<`U 1% [DãMerG߄jԮ a"]BOW o],I@`O,W+ CѰ9j,հ3HW 9 Ǜr @S_kY/4;-B m ~jhI~ZR/zQ(XTjvR=.^"F ^2 2-q̽5OBH*J2W1kِF;wer"mnϒiN4\FtUi/5pP!T0HLpIӰEi zMfm/ s9x2m#4Fެ'}l2@o ;l]{m {$>ܦ_u]:w`dY°h r\*ڰv;v)]<9L Niޕʹ%@l*\@f $@Ts .=6^#OPKGl8n= ՞SB'a Z;+A@ڪY,ta[Jj2l!QMdP xl'fתGu=S6Dlg7[P"Kc7l;өNTv;2tcۑ~DMѤvUuP75@_r.u;4֨x%kHeEVLhoX:އTtrIF0UI?%=n4Em;4(2znPQJ-He!\|]S2][NW" =<|Y4OLa~1  {{j~ad[V{m:;Oj-uwe][氩eAlT9pN >m-i9=9cvfDǵV=}FPEE^\ࢠ6黰%aB to'#;< 6TZ#xvS.lޅm2úZ[ A˳xNуUJFܮ݌JOiZqz]~JR7# H&m]0EAWQyl$Ra1~pÛ't OWyC_6U,DPDrl5?" 骝Ns8m!DVT'm4GkAQӯ}Uo nĠA5ԍ4 w'/DU'R[H,rR0č{m6P,r{" 0Cؓמ!: o57mT!XhOt.jжcijcri{J=SOZe * 'lwKpRO_/O^| ۝NjԣOP(U aՠ+./u7h 9D%CB3??*ݜHN7P*exG Z0ӣLJgj;{@U[7(qqBYg} -@޶T) ;|_u4ǣɦ#~)U9B G@EU=Sf~xƠӎ]%"`ii擪o8V 7CZ"Q]^[^_b~FloNP0ҋhx JR.O CMK|3Y&b4RqQq *:gIӹ8Io2cn޿4,Ei?gKUgFFrChVZc,01Ւ$tD]"sJ$lvD Gѿ9YKJHvZ+zEH d]%%k9 ѕVTPN,"ЄO2ҫ/nUW8-FaX(t|ˬDY‚( ,(~jޖ=bvnQM4[4Y,8f ]:,KHٿ ,oJ! gm_{O8EQй*u[6'8ΫoK'/!.R@=P QL daqՆWfl0TmcB8?q!8RGf 58p(9f$$Oh3X,"D+qoMu?X(BAxf_d_=:5@icc'OX5x N&ؤ.286H^m07P*`Ӊ 4zr։O;ƛb7=bBq_]"3( J%Mæ§v_0 sㄵ*<7[=6' 5{.UU)63t4;z6\5ߩ3YeyJ]pF.:m@ ʭ++ )V:=Ц"!o,Y%Bi 7@Pxp0 5;Mjm5 h ~6:fC]k%KrJ]!ץJ<&BKBD)x0~*R3kc#P3 /{'u@6p!Hoao{L1@;EC%uF9981NH$t^I z &AU4OPzX(rUCMN #jkEx"l|Ef]uف,\/3lZP#9d|XÅ9|/h9 }ɝơ٪GDy7C3&MEJ eRE. poL\{[~:3g D9 iX*RrK]Ќ4Cv-fth\-CJЯp/|XWmrzjy(2K(60_BAmFrI5J200!3FR,WLx۩G@4͖krnN7g DEmi1̣l_b%s(]بU3& ':5SL8'R,gVZێky4׷ \d(APC^g{@hnqCq}6wm T^j|VVqNLSWQ궮} v9} Ӟ*cux}3*9רYX.  RDb!^LvFB̤:. KaNB%< 鶜vz"pSq.8LH3)mc;3&[Qv閺+إ^ 9D`[Ү5YBO m9$;h1]nU/6N8LcFbr`,~3yE7;zYԒRRgR1 Tt&I n3uQS=}Z?B~b|zVI^#TN. c;J-^g޳0_S*`0F9cJF%x.Uz|e5W]ɵ~ 6.ƶ&+hyV[XlbPw@ Ы'4y>.w6ɏ#1^,5Zv5{0~B}hFSQ$Si#b@tuz}aCtfwKb%L^怉;i*'-8GR\ x= }LAn5hUM) F tZ?R~)F'/r 2Ӕ*yd{tFߢa>p RRX(kȀPãcj@ng4xY0l n*/uDhVK] ns V_ζζζg[mwb91jg$\{Fp?PHcCL#kP㝢r&͉_ [% ?@P/6?SpӇ* {w.$,r0naLw ;ڏ;ݴO/?ZBhVƼfSx|/ļv:>:e_ dc);U)DQDCF%B ;L=D]7ۢx *ď>N^[˛`|3U:/Y` uBgrL/8lXJlw -zq01nn ӡ.mVkӲfo(6.u pkqkJՇ=.ɉ:VnfMߵ8m.{)+K\%, X:0s&<,YX>Hrtk_U4,Z} % ~Rjh ZIHUWy⯚!eI"%J/7I6^Ri}D G^t%}^'n&]/{|R S!t}^޺ YfEij:Seyæ%L{W=Et)&["2G+%2.D20I/tL80/mT Ƌ^#|ǟFO>:WY.h[ޯ*VاlۏjH,P5;' 8W.WUze^w@Ulg9k]8yHN=" !ٷXv&+`fit0 ̡ 4q'4E$xM/.Ě̏bTV3>RqWo$ባl5w ;Q)3XCAȹ{{;bc>{X~#V7yR,S?1mkLQ=S11­CzW}Qѳrd"`/p_T,QIwY#YZ%woD_b"]$Sk&BLˇ@?o˔-:ľБ“e2T>m{7ͶRUDe5ueyAڶ6n7E@ *YrO,>P$Yp Dw7_sLc#&޽7*2?uǤsЮUVVy} 43rUOr͛ߣ$^FGouQdGƴ*+y֟#wQz.>'aM3=_JEA$5aYOtS´:cl蔴X&A,2phtZ#oj(*na$Cr xv8NAJ'D4 Ӭ*V.<^FY9Ei~alfUvtp|M**6*+r2t?HL X=+G*k]@8{!M;Lis/Kӆ$yJj{A;2%IE*1-hᷩϧ9Ō1(%bf"ds`gz4>+ :POeK ڇ~q.#&m-%O}a?BCx&xO=t#=h W6)jw  ?'13+$鶚%Q19<-ʪG+3M䑗u OQAmGc!]v>7C̾YaT2"bC;BQE?o$h* qa0i"gxgBf׬ 5@t_ G0n:0v1'WU>%|@xĈb\|vcy~qB]`<d[GvY]\,k`;e#*Bd滜c;Gvh,i!I2Sԑ1>@Pk*mLnc9J;.nOw_%{?-pў2|(D8g?(y2XK8qDUM )P,ҿOI>Q0UћʪݼQP)sIM-ҷ Kzf$*2')O]Imuƥ߬YR,6:sf3-]Q|5ȶlJ9gEF}@%2P&7hp"]X?>K@s.5 ߷5辩na8pc8U^AL~ +ѓv<}ބmK:N':Qx_ Q@f`_lT5D"SNZgi:|WT0po0O9jq zlG 61\|ӱ !\_,X*;HPX l6 rq æf<9-S;GяbӌA,}g Qۅ[[IXiXg&`WX_[!ԆsQeLrvLL0q#I]GeVO¬="aYÚ@6Ly؄1D;I$rРh?3Q-F1%uh+NBH}zRΨGќ^/4 H%XMHq5_B3Ex}~JQew4Q E5`s7qyY[k=\ܶ5fMU~+bhO%{tUYoR ҕXtb博1J ̱ԏ"WwYl~TRY(+p`` M)|I9bgRPocJl!GWogl[tlV6SСIx9` (ϢJ]a7]%bEd<{'HzyDKoɕF\z,Y:jSfJ`LZⰦQRW%Æ0PFD{Ĵkf|Uxe|K{,NP$B#BI/-&!=2>N"Ǐ2d~~x=~L=$KR{dR |Ii`@kwe ,̯K]OP&q1YKV3H"~㈻4r:wuv 8`gtSi*_?t+!mp̊A]0-9QM]֩+d2/^2010XfҧJaA`#>zp);;|NF}Cqd pZOpQoZƊ̵LqrrsGv2+QX rsnkC$)YVJ 1>-tkXUt$23}†{4тPT%=<\ol&)S"e]|TqnQ p_A,iQحhI2yH%tH9&-(]*bw*w)rmlCqK0&Fփf"&_9X T*m]'iuz0ؚV #A*SCBfϨ1U& T#Pbe#>}z桖;mYGxz 2(Ϊj5%}0OT6 2 l/f2ZS7ת#7mN{&3'eιe)lodA̳-KfkHwj\,X)^J=au$w"e8 cIf@k){Ě-invv$1!9t4tHru+ !+̊]|΁B" r(>N 1*|X~mv`<=B`\/io8;Pr<%͕~Kf09ژb;3 ^6:'nOO-ю xyaz(xM藠]Q?*i7V6ա9gQҥ]|Po1KQh-|E1[P1n!u#5Q9dǍ q Q#,cJ"z ido>waK(nȌr^TV{i 7!eF*E(M +|G$8^h55 *#i^8˨#5Gέ1a wQ]mûmx°1σ/ƃ/72p;Ax3|gOi`hcku(&V@p ngo#>ӱ7W,?|{7H5GEGOt'%&z~fh[ֵSgnG; y3t6fgTXv/'jhԑ.}0ક/~m,$2լڜ?'&0tIiD 05H}`i!9W5x|짅6IpSTcV>J" N3\[=C=@OZT2HFGڧj__hgpZ@3bbg'+InNSR6hA+19:9z|x6 ?S`i_r=|Tz-!P7NSBL X3a=/\ g jcqzH NAc3~eJC?M*e\E֝Y+R1R<ٸmO][#|`,NL:vԷ1o &, Gl?B(!kƔs5l)nvy1WtEǍ&~#a~c}[0lKbmj9˧w]ɰΆO 6|nC ے6`a#Oosbomq1U6[KU%SH~~Ay15?p]f{˭mvL|[[w' udiPnr2-;)#?9݂ :Sy\itGI-7}^gʟNC:?nd+͒d%*"Y۪l,q};b(z`+#1jEנV@dHw gPJ.WV̇[WڠLbNF"WEyF*n 1\f~ wv^JH !j?H!^ZiDb#((-t] QZwZS j]6X1Wcq^A q3?0u;F(Ddp^~9vJvf^ˤM@cn.[J'y$bc`C'|A_ta:C<ԩ1ჄgxG\kJ]XBp 8;H%e<x~z32'D:3"Ja{fv:R[u:2+1ޟMtܹ_d]-Hu0@gUשy)7} b. ٚ > P_=6pM|xN/2(S[S ӿud`2u`"8XZpZmL0u57 q Qc:sCsr`I9] Z#w:L)ns tԠIؚda ѿ{TFn;x޻Mv{܅Tnϔ1&/h섹/FbA_0 >J9[+BjQBǢ%yܮu$$-yedHҴqؐkPU{!{"lVW9b8C\,R-?ֶ}iK8sۖ[ +BΠsamo(8Pi˄e;ᾢ gps֫1;f[T30잚{eMrQ jVlY0⏣bEvbzdr5U$X@oӫpzx)_i٩lr;g_f;=W\\gJf;zE$KnJ:Zs _ B[aMH\oҵR1x 8-0Ĭi[ QfxH: XzS `GW&+t2 )??5.HE&ٹH &6 "IncrdE _B.Sit'gcHRC cmR։(}~U+ 1S xgeZkz厴R%g0'*9{mmjGxaа@Yb?2vaf,_7xmz3EOCzt/z70ryы^2't:|Xdۼ'O Dz}AR9wC&vjGv^W3GyvqKֆ7Wglº7Z}Cg1]e~"gj=a]AwJAF?GgeqYx*ebіaḀ-G]ScVf@_ed S]f:IDcp#+k_y +ܚe`qJv_Jht>YӷOeLȯ9s / EB8*Ŕ15(%wI* EvIMϭ$w; PAt.ٓ#YM8!׫HSandH}Cs (J u/R$Ʋc6PzUw921@|:`€ `RC{;_9hV lzY[ݜɃĴ}đ ģ9n8 }Ď`{{~Ean09T>?Vҩ=6qS/4 qԦ]DUAWX E`grn bl}jpװ]DߎƷjmqĭ^0jdjb%A|=DA|= zQEPmc5緆:?Ń| _<Ń|ǔ/lOޘM-ް=JZt  AQvm Q/-@rBnm{lQjzD~I(YvA;h76yn<в^3ٮԵJ>u\O+e%R^mmxҕ̆u$vVAJM*e`ѷ1zWm aU%r Ŝ۾(\DVَZ)4‡{h^B,x/"黺2E>qc*{DML q-  wN݋F>ZTdzѓa{ґcmfcv?3zn H@Je@K*bvIxϒ(BfLDJ"Jy t^컶!O$ro>qkERkZM_GrF6VemrêxU"%t)!'44;Kyŕboںoe"?dwggz:oMڋFzM+)d32(t#?;#^ׂlx1n6D(asD7$Z+|b@C!zq}=Vy|/1'v$-F|B?)&=a|^pͩi?PnKiSLFĀ^q%=rڜ#ζy~؊1e cZ[Hp={3!3\*|FwGẠۘiDJw QC-mG#y 0MZ j]EgʚMA 2B%O= 0% CjJ:X%|gjqJ(**0?c[D3_6'Y$7(*ySZuΚA YU ^dNQKFZ7i@ i% }=ʯPXUU`aj; M:QvwGowwvs󠹼ؠV]+06^ˆ|U_-W wwr xK:[j -H.&Mi@2_ݬsBٝm?̮s/#JSwzlܸ]]"2 |l 8af=}j/Loi_.h3C6oIrT8 }ynF痧9Dxl3:fJɉ1@ő==z"}|3Ld"n  "WR4 5oFmR$1mPxxa>=}1DPu驜\!D4n SgΞLouFiF SKj)7)ԢQԫB{6v& y;۰Gѥx8OGGxp =nYL-ǘj3=ozqy+|EKsg{7nɸv9'̯gv_lĉ u(KXS5v"BP :-_ jzN@_]Թu s;Y8'l5L Ðz ӫEs*sv/[eU3pAAg73XSïW۫͆0y0\ FG /a+1Pb.$>`h EYQg_jp8?rt$q0|-rsLNx\cB,}Û6 ww~у޻G\D' ߧg籿 ./8*w,pTT zY.&Qsn;{y)G}FP[.g(fVș'+?m iV`~4Op6RlZOg|ꁡg:՞{x|7%KtPK]uVs u1setuptools/_vendor/importlib_metadata/__init__.py=ksƑ+pU%ǾϫJGKVIJf-Y V_?R\SgXڦSoPu__}g5("귲mgJ=pYkشˇSσ0\wn{wuY_hRc/At΋؋&*ʦH2[@/N (gJ<|'fXD3vЭODrge]nAeyUCo627£PeMKnj5|^]#hX+j#Vm^wx|PWG45N̝jӏmۉ^=>?]S`2$}b?' @,oA&ԧfEEKV/wYUNhѕSx^lgXOM@^Ё 4e=w۲ar*Dz3q™3æщBt'/hw 8::z{)"/c !eAbV@jӉah{XԨvGlN|xl;^_.|Ha AށG͂7ۢCE= }DѬo T#*gD>|>Ђ}։H<kIf~l{)4e܋x]O_1ZqQ7(Z7A聋 A[Ͽ=+>0fݐj% q?Gŧ37DM`zpkL>` ?=ӢIH6j Q,$ZҕSsޙ~JD"H8h9&YMtHJƋV[W<680,[uHq͌T2Q!M֨;%=/"ʬZ۸DG%*5bXr 96\5iN$".fVfTַU9P$I-]p459 1uˇ%4& Wء.$>(_D]gۏE$>e= þUatGAL&8l[N$>8 ͛\!l6 #Nꚫ h`o6 װ΁ZD&os-pB 'Xh).Gp5k]3˘qP 'nX'Q'<'9fұ5\ Y3b lZP-h;\g"1wb&ZI#LH?; z'Zڞw eve`ZpR; sRމ}b3zY qՎ# dn5W>7BDh6}8x?r= CtVlmQcn}0`=9#̥+ 0(P5Bc^gg_wc^  ,(C_O1rrm ~\UE0ByCz?sw,-ח%]yq iށ=yvBStǼovÇ'z@?fDsѿ/0M'9a*!KQEUI(&؄Rq@ oQm\zb|"c-͵b.R Sl= PDunvV?Ky˺Z _D6VrM_Uk?k2 ]DϷ\+O*d"P$Eڙ2d7GSDRhxI% OWx'OAFLJb~[ κ-"J);=IHT<fG bK^t.{sl=1tˌV*籋:^pz*LMڈK Σ"fl[4N{\Bـv6[k6NFXk `=VL6Dcutӏ\ 6 T%@"MGӖl˂iM4ݽ R-*UVj'! .4 Cm}J0QL{<t2;҃U8'~'2+Dsq $mEa8=$z~4SPilɳEe_R IeL@gQ[441Y7 ݴtE ]^_Ԇ0L'fM-Dg^ A=:N'6E}y6mr|_EkDr3Mt=8w"n}A$/C YYz%,o YOo;O00I07Fj.oO|8I6jB|12]8=(TfþGN) biE2I {!Bza5%M|&S^^(j6yr6,u WPL6 )wVH*fĚAARyyQ@Cփ7pjLY j64 <=A1Ă.jYhlf +2@9Mn4O$Ce&iRM_gjk]1[~x5;؄S -a7'1PLh_ׇ 3b+JOyؓB_w^?hAD1 YDs2j15h)쩠I0PKE"ĕ$Ծ9n< U d Hܳ.; L 9Ey%{/"Jpcl*DYa8MDXX$[l-yZK^f*&CvIkڤFdbL=ج8HG v9\?G]<^e F(f:?쥛mt IJ3lI!)B[$1 w >wKn7B~dw3;[hvzxC9-p7K:EI.B@%t=P#J}\DKO$MY炲#vaUq 'H6ۦY뼳c"x[$4&†Ǡ۽61wZ0SVB"P9 I& O oKſe#}ˮH]e1.)8hM/Aޜ^s4_zP[hZA=lUXb'Klj]RaV|5!Pg"rsz#Xv-tX@WD#?'ctXShO0fK:g kt=ar{1@{am#K͂mD#MnGGբ_>0F1"@5䴖O' m Z#t҃ sH!Ȼ%v039OI[`qfa6dRŇcGd =voH  8q)*'v'J &q%Y G 7`ԑٛ}r)dgz?wV]_Q _$_6p֧l6 Sc}U&Pg@#yT|n"` BtSS%8!&]EβvyK-=He2j{үS0b$gGr'9^sn9:0j51&Y?Q'3GRf"^?}'lKV\xx^?~[qRSΒ3.,4{O0Oz'gJpQ2o !K'15ݤc J8$"|,n=$jQtd C~47y8 .d=2:ϭ@zAJFjKnv'`Yc3,Ve_ɹE9WP9;|HnD!(vk":z?6\x33H#E>~:3>::gE$j4R6Eأs3c _᷉l_!><̪]&M;&kɋcO'hh 9'd?U3OR"^/ qb:JeLKNx*EW?s?tt-k!Ԉ^3nPD+eU,U{*(3fAF E򨼛+}ďƾ^4AFuXU4ld^4p?GUy[^,9V^+`d3l:MZGsM>%5I9&3{jt]sxͮ;$czQ; ])Nra@ ojpuKZ.i-^#H 79V!r@k[tKqx}P2(i3#v1:H τfqU1{r>7-U3i v(FAf9g orۨڋNqNܔQ!ɱK9҄Y1G1mOi-6S9'6¢\;ӄXT;&3Z W|;eJ:/SsA]70tЫrE%!Rl/˪DmU+ܘDUfY{WYhjZnk !d 'c]5*]*M=u7mePp`+>hu^8{?74{A:):h"3/] lTɝ9t ;k3N-"?4*tH8%^*׏%C4Հc! N!U9ucEGfB-CLӄ;c:Xq/J;uD||5g/#qV"WQ t$l!m*7tR<[d3_7ci}t{l΄.Ih U;-)kEpjbIky#: mΕ(}QdTsZIl$o<.@x>h8Է8jX:)<,FۯY?x}VHV5>{W1GK[e!YVFT9-8^aP ce[qT0QƽRTIQ%֝큈>P ~wMm!^=\e* J-8AP*9呕 !oܻMP}2Ccl +QQJ+դ Shd75QٕVFt'GjhT0F> s$_`7T,L&tQqBMVebrL_=96T1pOsnBs^d0|p̲6L*-syP>M%E FՑW |qXWBޞ^+#"@t+{9uN9=?6l>M)R@܂ 3]܍;Iωvn8KW;\$_\r>e.=kƉ5:Vt,V2w=ED;?~ 3n/~@S%ꗪ&-y-)xHs(ՇW"OnY0Ktп89.ְ{B&R.|m%Wc,UN{';;a 'i*RԒGC_Hks6HpXm]` iFVaƁbL`Y bմ{$%/طRG:{c}EtX4z늬-u+1KՉݚ;u+.wDwhPC,2=ভ&Fyh@26J@OvH8q6cL{OwPK]uVmF2setuptools/_vendor/importlib_metadata/_adapters.py}UMo0 WAvCEkb40TIȖ&MGv|,:$L(2 Dӭ  .dR|¨ԣA+c>̽LrkᡆG{IsVQI'Ĵpca ]nEý/6a@ÆW$:[a)TrP8i337?nwS'[ sb 4%@ϕ`>:L6~~roܑHxEe.2N Nn ߮-ExqiZ:ML!(#7pe}TDI B>9OWupDgQ.0fIq IXM`ue,1@b7H&CX܉ b0Q5dխhPejx\_^zM ΝPe,P*TRp"͚w:I^ȀvikWYDp?8$BHm9f-ϷNx}p̣ݢ͌о,lX{.ѵQ|c Ewmv[U_U~LyYx@ǩ98~xw]h.ߠmq ܼPK]uV5setuptools/_vendor/importlib_metadata/_collections.pymMK0C=4%x\'x8sYMvz7$`EDwiBR#Fo^EStVT}[`߶mPK]uV{D$0setuptools/_vendor/importlib_metadata/_compat.pyuUK6W K[G@Cm,CE!͚"YrC^0`}*?:qkX,n[xVʄVkX}LO>{6l NN'E(xo8X%NH-d&M؈@XzBA]u:r4=uc#g*@[q,=@]fL^Yv hzip1$^h`M Vq{62U3#5Α徤ls6g8zl]\?Ցr8&CH)S$H6OgAl&9ͺ}P/헢W"{Z\fIv4yQ*_ܫ@pYhGeD˪0!Pe,;V5F_ϥ9ŘgmۏU:is&5S'{J{ 0=3VuXnQ4ǩ ꛱LUfKe6nN2?2/g*5ˏ )4U9ֹcd'7M~幌cf{۟k&Gce&rx ;s_q%h?%MR9g(2Cl ds61~j1hIRbwm v<59jŻDMNH%|/pҳGE~Xh{TLgZKmH?aٹe$r߅ų\*oo rKO6xvt%ۋ39;#|<ˡ̻UލpjNY&PK]uV:3setuptools/_vendor/importlib_metadata/_itertools.pyUMo@+FȒ-Ijs"X,1Ndy%ӪiQ[rɜe"7y^lK{1>6e)bcXϿÙtM>Kcp ,hP?'(7Z*֨Ay\tΎ*t-˛[wCX5,nO&QBr}G`j8CaN7X{H{.3niLPqJ+di8Np0,Hj-1O"n $aGjA\:s9L=`3HT%1V(nW+.Dmlֿ"X +ghf]x۰F%uKr(.0ד^,@Fp>k9y9!~ ꐔc.I)x8\8S!ZeWRXTXe8';h6)X|<Է FZI9'4ϔ-қ̉C1<&F&LY+7a5bzKN554/\}cK|9ccpAHeb}ãm- j-ÑcȽFa2m}l{vwZ;'%SL9l 9;9j7}FIg.:~F _5_ӽK+}pp\u:l(6JxNpOVuPK]uV..setuptools/_vendor/importlib_metadata/_meta.pyˎ0 E ]L8-`6-:i7A`6hF]N뿯83Ε( d{ 9Kpȉ[`%{Dw P\X[UU3M0%AILsfϔ|l6Ӑ[T VJ"J:nS.7z/Ωcwf9&J,pG?L-n}]@-0>Umekq(\/^&="g$Tr8cMҞ jԾȫs/N=;I ;##ݽhwF.!˄ͲؠXNJ=4FKpT8ϭmHH&$j9{$ TL+irijH;bawEـ52;}{E-\ w D*Ur&4})_;#s˻=ɰ=!ú4׹&7 [;M6HnmQO#~_ߘ k$M\`LK49io_&}Z4̺͓^PK]uVoq2setuptools/_vendor/importlib_resources/__init__.py]A0E=Ť41;4CZ֨RAV_/, kk h,23Fl,nn3G8> 1=fkqԢdbr#YU܁Fwܜ Ce0bT|]+iꅼB){^,yZ`ÒòbٝѦJpwhigj!0RWe}ژOӍ ##C]Q }D/ w.eV@M96EF߷9%xh:uۋ Zy!xP]bJ`+y0Ƣ& @T`eyxjm8"2\cw|@%C+Lb|qSRm7%T@a;]G`ǹzN6>cSyO%Hק9:1I$o(WyO{z9x/Jr0C`\skk3Nb!uE-vi 'Zg?73 {F'΍ŷ'x2Vw~nU>Waڗ>."հO;dVCW(!b!z_7[v(Ѹ ,~x&x&0Y<5P7<#7bv Ò71H8^6j=7n4ʕ_8aܸK7_wK0^2=af%c!O뤅D=zCkN"|nH/qMt{T\PK]uV0Q} 1setuptools/_vendor/importlib_resources/_common.pyV͎6 )al`Emr6XlCMϨcK$軗ԏf eȏIaƁ Qc/p;c&8Y[\FËO:mCI- RIhd|FѢËhɣ(]5z و#6YI4'q@x |񎕿v{|Ͳ8HAx=|,! ,сX΃wT@42&Dy*|҂Q/'lq98厘|Y5 @:W)7gFb 4GlNpDPc#H8  tMU% /^Uy>Y(gih GDvdVtaOlES+hAiv6잓:sX_Ѧx9-@~1aR#eYZUGίp|V M-J5V5#J w+|XJe \R+RU5xê.k{#. h"`R"BK곍F| \8W< dyǘ+E~-|my6a-b$ VA:d}ԟ.6'߃ލ{4VP9}Гj}Ul-4_P 5GOa+O >z߫Qh|FcҡpE!‰(Au7ڲsCyP1>aSPs^s)8L5aszµ*pǽM[4N`Iu:HZw_"d 5Z&d+WѰp¤n<0?&q=5?NBsTwѭWJ%xiuq-!}؞'s)<]Ϗߘ^ACьi'%GЌQ mDN{S6|f<^QOJ o@? -ͼ&(CNQy[ġqIZ;o +3> ?|V"YX=q{ x ϡ@xd%sL{PK]uVht4setuptools/_vendor/importlib_resources/_itertools.pyuRMk0 W [K)maؙvOvYW_bIOɩAz@֍*(U 吱*StB)S:D.zm4Z.zG]Vm\TI@2E8 +3XI-c|=AU1֔bz9:DݩgŠ|WMqӁ|Ґep8 py瑀 kXthOaƖhFPC 4b( ]\Oftb߄a˛M԰-Se^"&,;i6qQ1I:- zeS/4s7 ,/̔V*!A)axɁ 'sMl[# PK]uV.1 1setuptools/_vendor/importlib_resources/_legacy.pyVK6WL,=\$mzC ݴE`hͮD $^7 IC-qy|pdiUnl"Â:ᶍ n( zIRݲ ?)U4ïZ9|vhrE*apGFbв*uj$DH`{Gw3huoJGITXCR8R4[$@1bgDg̋uhal77;~/%'_X[Z)j=rO 4CAXa\gy131L[:XRi7dкyobU+p9cg >r6%Zd$?Q6F@; ʭ,5ǃ$ (O \hG?OiӊF+fc?1 Q %1WP !|.B#.$ {Arڡm 5ҥѰ_ȁ)3F~?AfO`+8aPP6Lm_  E30bc0<5\ V̈1^q1`Nc[(B E2SePeKfC_Fv_fOu&aP&8nF͟Q`'g?MifGWHH eg/{ǂi>^2%k}&?ufBB1vsBO6r7ȷApSs+,B9YRgګT+4;mp;Il8gC}C5]ԧh3y (gHhޅuTh׎@4͸/n]?:,e-Kڳϡ*] qi++4aҮ4[SG@:-| piZ"> (T0NL}c/$͑1wG{{gKE*ޞB 7gcY'`'PC;po,3#@\t Ul0u_pfo޾xO8P oBPm>~3#g"ە^r{aҔP(\4^=K9x[oOxvOcnyk wj9 BQc6ȍ︌xyԩ'[2NQ䠴3R3y!"{^~%PK]uV}>m.-setuptools/_vendor/importlib_resources/abc.pyW͎6),E6A$Xp(qbV"Uڬ޾3(Ӷ?mS}ei7#v8Ƌ2n: IͰf ]]ێ(nzd >H6R7Y ݂ս)lq)77VWMacB k4i 03d;b#Lޒ >"Z _@tj!j=!^yWͯC-(ƕGflM# }\^M"*[0nz2x82@B@f`kaOP:Eɕ=@BfG>пߌAZpiAM/HE4pR֡yLWyٴ]dV#OJ!ą%FS9ޠџ taI:+jG0()2|>vD$ B>.v@WP(xtb,e%1$,Jx\wM'n?5(5=$) ؚ=!T-5 D} i0 #|\0(guޢD4=9`hb_;C/▽=,8O{b.8‚#Fh3bc}1h'8)(ɫ4BEo$OoI{=d(csao!Gd뗧{YJ2OAs{,$܈GL2t4xG7 !S/3FվV&d=j{=rR^62=\iNЖ8g9Ŷ2)|PK]uVD 1setuptools/_vendor/importlib_resources/readers.pyVKo6WAR\ @ lSEbCBKTIʉvRh.}jE{qRBVv#QA}|=lZ;VxT i-m|9(Xjݡ(B_ͦf襨_n~ (F$GӀ敨]INa% odQo NL RUB WUԤ8T$vkURy 6fj&OLdzv 93`vɜf(~YB^2_wB1P+8+պ˳ާ[g9C/)H녓hbn vd: 2-_?;愷V8Apɳ2+V}4ЊGϥ^ݻlDzY^/7Aa12,RU,;3.i(Ofxw{1F-LraOkn/ }g®^L~ }~=Dp;eB(tfDtVã@t@?vàam}Ċ>5؂Ntcpӡ&> Eiw)raN*+TO}N#2ݲD{$8#QY8h6 "IA(xm4¥6JvThK(= =CQXGQA84 j9qi8|ׄyҌ`yʝ3KwEHqShc^A٪]Ғ@+p>iB&bX o9,-TmqnׯVi6Vr$iY3I#ntT~dn,IWȅdx6\"Rq^ǫBɝaehtM:j ݅ʷG|B: fk] Q_-Z* 6,#mAr 2/Ccf\ztwy)E 2`T$ DA5B ehBؖ#6:"neͮSF'P [bUӤ WtnvAsY>?ŽĔMyc%l&T5;ax>(%UEay^]V1NG^AD$Ih@}~:[ݜ8yFp=E)Ѳ_ZGogCa~J ^C9c!&m U5{:OxhkMVbֺ]PdͪPf C-7$ K9=[ ZZZ(/[NA;@cylj-mKP!~6u6LqD}u|~᪍H[6Qn1wl F/g@n)jhyxw<%=V.Ţu[q6w$x&%yb/eg+0! Zg=1Tsq{3I7TjM \ijjsJ¢e;,a޶"K$E-~.BD+jͳk#MC2Cu4wc%J&n%輴 SGq5#%ǐ:N,JF`-7HvdejA_*oM4+z[*Rõm$=+Q5x_ya\Ͱe9>9҉b^B㑷PK7hC)+\f=Axsd򅌷Sd#zك˷]T)d.Av4N5dtes kJ=d );*W Cx#Ɔ zv]WW1,yD/{v6c!.C.h:"@Ylꮪh6P~ֺe ­.(^ w7^Rnc86u03w5FĮX.ѫ] b+ތŽ#ɉ/XJsLxp:v.4LyJ6#SluoTeO cǑu̗l`TNa~1wΫ^)%Ճb]PRg_#>جZF4)Aw>y8J\89 PQDɃIBP0gFEۘ~{h*>#KK-=\:a5ЦGRfM:XV_WA>T,(ۙNoR$žwuhySץ9(CS{.*Vƽ버ע۵/E4r_j5} VUUL\zX^Xϲ,TH}pp]mOݗdz+{|@>7*>=@C̃BV[QҢ^z<]CZHaR F'Çn# I˵6N&L'bY\jbK$zeIi~&<㩀ɣK~tI$kݪ>{J (Ɉf!ҫ4 8gECR߷+X,(Yj`\r)6W=ޤʋ1n`|bM4@m^?d7XZl`вWizzԛE*&'z<şKC~eS/fqfu i]KL*J,NY< ?`N۹SJmU}egsұ} x5\od ΥlSQ &(mjDQR}jTkֱ1(O_@{;GD23p˰(|Mx*%Cq"ȎzB V+XZ63+r+#- mS[]/ $5NL_+S<*U]iЍ8Y*uU7bBJvfo- )$ v ʼ3nڰU|y%~|?2CYF5p3elA e`TRZkD[qwg3l.HnI8m?g#ccOX8('vc˷pΏ`0DpeG;;)KiN,@*;Q@jųz7^ő+6*!XeY< `w /UL˕OSL32 ܍]x۴[G[^ %WW@o1 :L}7 DxLMd ,T}e#1 !k$sǟfLpgZZBx^"\<Q;CF 46ZEk8ٷqò1Sm`+$~~_䀂jjF?tZU.R#I9^T["$Koh./J@߄88z` ~sp@J}򱀕( P4·ثRZ72- P|bp#f`B Ì whQffR } (_zRV2Ҫ'є{\frhHQ >g/v|㿟VtoظۣnԣݱSݖ;a|/%1dɄSf[fggbهZ K߄]&b$`+<0t @FlU4) ?'2G4{v[4e`7Wrc[c 8 \:+q˔ޒW:9l)?yMRgIX/Y ݠ_`|6èbnP(uW_הl۷:W:w:5jQVbۋwgxV50Sv q47a޲؝`0J:Ș\ukkbQ%ӈeG, ^}Y_(\c}}ΣH֥]AX;l'CIlf!-%M]r524 `8Z ̨ʲr' _lF>O.D5 ]礗N>!H*H<ࢨT~۫vS;Vgc+߰q5ͥ, NV ~w5Q-̅LY AiY,JHYo*{Sa@<>#cY۠#; KTl{V2O MY({4QG=OIkbnrTFw8<=XX;#?+LG[Ӣ11V S(lc!ˢԾrM} e3z6R,7﵈螭z(-%TK!젢c?4`B@\q}?ReJFWߋdܵY!T5z<JK]7ML,VEJ*IdWճ ye66!0rOe-8ZDI?>W1xn2 /`?H>'F 4XdV| 6Or^LHM$ 'jB:ӷ~m^O2< r pA:{?1w;+(' 9oL>v|BkJ8OC<ހ>M)Yb!31 uqc}kg>aM\Àç |CF \.(RО*2ml h$jp2)9?x5`-@lbXh3_be;r!wUy>IG%[ jӰB%u4NG5*mk.|FzdHL͑ #' T8RxSq-28Mn3w^BpJxޏݫmF[I&<ĆVt_yp'0E֯XF%\x-|UǻΓm xmP~*uFͶ*s]H\W1Wb]0baсQz[x 4I>莈|=mѼ 2\"qxE[jW gރnơ)Vc* }tۻ1JcL u9K50QT8¿6DZ93m N~'U\j݃5?j\ )~=⡣nӻ-b˓M*j> Zr1:!ߧ]0k gtkZJZ:ղ0;浢3 HP[[mǃ\ ^k`LDLw{n7&~۸q9BӬ?Krx@Ge?!"nxod>ro vvoQ Y>i4[dG0^L:WUhO/&5|vHPjX/HI'Ƀښ\*Sߛ[ X4'1.|^ph <.%?ƬK^ OYQk|9 H0Ј;ǻ&8Lnݏ.7CS1DM#PCQ%_Ik\g"5Kf蝺["\{K"T`E AC&݄7!$$Pm}rcA_Mh%W-O _ ZzP<, |ڎ muh0¾&{ux:<~.Z?r!ѢOeo/ŏ;tTDBOuզ?xr ݚ^\tcUrJ'۔ o"jvA:ZE|P3s#lAh HO *SL^|46p=|Ի+0fz'{htJ3Y׏[̎5`ww ;ynn"ռ-es=A?Ymu|7=iMqtJkt`xLȻL94V=\1_PK]uV]"ʨ<*setuptools/_vendor/jaraco/text/__init__.py[mw6_&(2s:8tM9M=HHBC*Vgs/$i͙%\7@z+u'mUc4a[{Mh-8Hߢ͗z6m+#]x(vN^V5NTXqkm95F.DTu^_?(n8J#{yt/yv(yFZU]dW@"2 mKÓ,fR7St BaWҊ]bEݬ1C5-7rqJXp" D)e=f붴:(fFX~mU+Q/FQ^lG3*D(iCi37G$Nf"&Ą?ߒMO #N{;HZn&p0KOlJ =, QC2pl+AX*KB݂Vn դ{ tT!X|LpM0ҵZϡ&+!C2R/WVw#"0W=z`Ո[[2!]y6y _;UȚh^ ۉ%6 ^4>?7N;|I!a5ƥaz ,x35z"߀MU -V=#g1 sN>9gkqʻw޽. ! 8p;7a<`eͲQb&*dZ7L3*|!-?Ðԯ n J}OfBI j _A\ 0bxҖPh¨ i,/>6,Ɂ; 7 jM8,,KE~Җ[c~oG~vK1‘{8}D=lsCHWNzAHޔt Bq!3W+L`l/@jCSIZ! Kl]H #6i%u7]dKB( P! &g$`ELf]JeJ4RN)8B QdZ岒BC*~0R].ECr=hUm-R[ -Q絃N8U9J:(jG xI`eU7-0rtKo(14%.ۍ( ^L(@\pZGTX&R\vC2E֤'Q*r#І5AЊu]:yݮ]{ͫsMV{˒"X[ׄ]1ԧ~j]@A%HǨZ3F58iMrQm}E2n1B)WS7D|xdsh"èa >\Jfdjj[,WB Rzk`K!tC"tjFnF^B9שʡ*o Pu  wGeS2{unk,mlY'Fp)P$@`x!V Zu1/xDŽOHf(6ՂSONA a:ݒӀkGΚ@OF[cdj% S ^y t@;0QJ]j#%7ĘVEEfF6Sfrg1Rɸn6ÒYV̵ V0@o[ 4[j׫n}7hվ^2t@ƀ=߀m4{F/G\- зT&GQə[79L; xkB2 M11dcqЯpɕ!;_yRTޓ2氧24(2(R feqxPU6o0$*=N>,9N 򦮶kğUݖf.-WA|?0` ށ1Cw1gMk{~6 DsiC.7u\!hbtZH5Ò>,/H:d[, *2[2+z5 _`=O{:XeX9|K1%I$i g/Tޞwar-Lȃ~Tٗ=ϼ08Uu\^[dzNaCV/a<]fI w2դTtUt8 D #])Y '3ٮsvl,ٺs-1-#5\}u!t/WtDMArR3w&+ߙyu!#:w@x`f1IqshF\$q7nr.!nfmXS,.\dnpfh;]@X9gO1(1RoӁbne|0lVrO;~*_29zƷittпs6 nӽ'O<c*IFG?ZS|pށ{N(>.f  N >8W}EO/>a%1t9 po%yMDW6֤! 6 MBHMn&]0!Md;0/i7:t4%toʛ3|"Y+-[T5 8}Vv]f"awo;_37t L8QZ"{g,T|3 BS .e{.'Ǣ:9>~]^&6]nx= V9G^l|Zn_3uujP)a㬐HJ}j>Gϐ%&|g^c)&SJf|,Zѫ7!o3{yr 1 BJUL 5Mpv1@64[N$r6IDC '~os( A~ح?IH& * 9:.@V, Y$y+5v ׋gYy'YF5Օ3AbWā@Q\z̊iʸ'r KGL,`;aG{氛xifd(uZmH·N g xyFn/Qe'O?|Q0Np9HdeXQy@)n. k?T̢~ Vu/STmA?u1ɐ[4"3/-˞jVPDSGei!G3t=|b yz(zCWAM협H J"Of)̓^Em+D}Z垔݃ ϭCu)|!Ј-߬T&WSM7 xA~/m.зW)֣ż4rnx'TKu ]N]SDq l| Α}QɓaYxNH =_t=\VuX\į=$2.3/ۧ)OTɽa4-pS@9bg§Rpn}>~ H t}}a}c6W‹x'%/Y>^|X>/e>JMW'֐&Ä_E1, 膲vժ,\l*|#;lVS26zW]M '`e푏xYևn.))!C"v :%9PHޣ~Dh:pFu4|c WhP)e0; Щ4(mԛ̳j~>v}z(_sLI!GFW!ݜ|Y{g0:D3ʃx>?_^78lX/.܆^ p/ S]mV/qy9ҰO 8zMQެsaQR\"yŮeSBZȹ0<ڤ4+eZenSnrثS+Hi_^lvMewj];0V5ٶq7mkD^vm&GRlIEdhO(nT`5zZR.uKpM٢n8&I\Oj;Gj)U ]ID|1%Z`7߷Rа ,mӇSSXvn:7g `4 wpŶs%6%k:oi^5ay(nb@=@#J~-̾A;]\Ŧf=uq I]߀3z믄 ֯ʡ+eZ?(WڑDXFhK0Z:-21dA}q sXDrPQwќlpF ?}s84n)9Lp95:)W ch2,gO xJM?Qr2JNGɳQ||| ?[%7}i8=)ur .YepPBQ%:1W34 }0P]Pբ :W}0(r/"-xA_Γ]hCgl1 Z4xPn0]#4^{,V!eˮw[{KDuc 9x ǣปq$ >:ը@Z/JW SyGe) ;ڭ<ɒrd%MB@w)=j#X*[nӢ/0y,\u1Nt|Okdj,;.쩦oKA.ⳒrKIZ' /`5Z[:T0*"`MQ0=Ć҃.YYi@ Gn:k~C#oH?~D9>P?8R@ SPgMРҾ n`p{[&M>h,euQ^׻'ml NkBWj^#?;>=8i'F]kra_ fys>)flzfۤ{?MX>Wv-nu>?z,ѩSwFޮ*~Iwa(xj@t}|LtnN(Xr-Tz?Vq̲p+ouYޤPƙKY^O6pg=a3ZYՈ \h p6odKj x59qɛkmY^Cj`F[a8)NX3e뿧! T+`r_ n###o'F:AYg׻5/nh]09,%snuUMJyAez.JQs nT(49]EۛHl9P$Ǹ.2M5Kxι2T#Ԗ¿xLն6I'߬-_ }A&`:7i֫يi-<kuvbк-S]x^va鵝 vr$j 7j'j 6^=I.&l&]AXgf.rĬBT=ƵoE\/F%!Z w- 86GsjRG|'FcLN%'/j$ߔ&]?=ovNAn2D O͌7@$H oXErmMZy{5m›~# k\shd dwaKЂ43ē YU|gi_HLS[8p<^3~)2/@Tcrn‹2DV|~u .c} ӧ8n` Ѧ%*gO)Ÿ>̋fg1 D4F,ݎO/TCUT yKI^ yI:'GAnIqApԤ H4<$$~\9#VwA@70 53f@̪r6G[! \$H2M&m>(JIF#Ft2W___ι+ C ٧?Qre[~,Ʊ,tr3фem~SaGxgGȌe OM3H8[$[ BdxE@*m8^?4t,TXE4{7+7tnKX3$D?Q&F b)t/Eo09Q@~Lx{JeO|'=^SK4Pآ!̞1s2{I0ǗhJ-c}=d qJ&XMk`ҎBN+3tTnJE T.pF^m|GtCD@,)uքd;& nx_V/ Udͦ@wD#0 boWT)N,b>Q/]SnE I{鳓T @oSE@mINl l=}v6ifP '`imaM9򫭧dLv V{oqo6ƅk=%piغ<\]ܪ >URϽ Bk*j \wS V-wxBk ]Ur |ɢ+ (:J8 *dȠ`KϞ}"ɞ@~ \  9{r=;"wXLKcir(LHkN暠i",ItJdGk3&,X'<`)J 瓞8dh%BRt⡢(0@ӆ DSl5Ym{G : VAFW,2idWIdJBz&sSً R"h8c` [JLDE!n#xuR];!,ïo=}^W~3_}؉åq3>CčuT} W>:}^ }[&_ρN ʈ7,V_g`[♰-Cۈ/x㢧ߔ6h@HIh 5(wDl32+wtGMiTW3::|A0lN?EF1DS(ȶ7>3-Z_:Φ,:7v6޷tf>&^Y;krjHyBM(Ϥ __GmZ7F]i)8{0La]TD}.;T|0[,&4EJX֏w">"ߗ .ↇ%_DI-,*y}O~LL=yvl]3;4g2ly҇aZ.G8v[z5WMngv I_tdMO/lҵk5bFWJa ޘC3P1~d77rsx|$NYEqB:q7Awh@O )^ ͙57GV44N[MNb76#WoU em? hi{Xc㚏ӗe7H^xOɝGT 2f X`ܢh~`NJSndon]毭)?smr3.K3mPm {tKiG'o/ſш,hp>zWp^G!> ːv˜6%3%m.rvyqv^#=)rt^6e\,_K~@%;:IA%2 75Ugn͟b9 %y+^{&LV E!~@!|S3Z0q1 3uD1z6w&7CX:G|@ŧoߕTמ9Z>ըC)4ބ fą[ MJ1F9;Pq:00 0:8Ȗ?7FQu,n]&@_Lܡ\XYp5vR?ԃsn퇫m LnqkwOe~/a?0%M %, Y9Kp  O{nMG ~eE[F1*?mY3ƭX/b :{ Im` rEg#uEwJ{ʕX Ye.!0&$r.ZrFGB̀Q˂hBG(DxDxCENSX09 \ NVm %Z$R]&3q+0U'p ^% n l_'v} ><4eB2l{G[08CMcFc^6`a؂Uu%r[Zw8@l;6LKnn5~heU\Yg=gBDU6dS#Y 0]|@(m.n+4b<\r8ݔj:7B6|xB?g<=VC p$˓͝]hIXel;]u䭍xå"CƬ8"yu].r0r.N+n"h=,;uMʊ45lup(Lw/zyZ(Ԣ3DNEqKp L]8ӐKJYzG&t5OG ,wUԛ'YVg/-2,nNq|OuZ_?He >dz݅ S1wI}}H ,V6ЕۅW|H9 __ZOF57!1\ZC b $VZThg/ [ |؟v5"S0FPؑXS_[JUuO<Ҕh4dymDZ뎐P *!$Pr5KZdr);m%ACl,!S4W\%^phObCӐ5 \$0ĉ̉d {EC.c^eڽ9᝜FKmK+\s41'B2&gؗI^?I,_96s˽F^- SG|&2ˉq|4b&Zr7Hod$눃+*~nɑ@m_a++wѾ,fo:Zn iw{нpnx!"ߩ4 .#~ytvD]Ȉ' Q&+G E7~[`T @CeZO.zu## ]҃JTʌSeq)X30>أ Ķ xg>RycCM<aN/T0Xt,6= Tۼ30d*=JЍY&1QT'#]=WrG,Ǡbٍ 26ʙw=iDtoFwl 6k=4C8^M5A_MVb+9:Յ ujDdr RM/)2!B5+YGu7tBǓ}QQh,z P๋ds6acyr Srzn.m?/} D,ùqAm)gশ T{s'RxBKeywGpv`64Z*<= K&Hш&~/5hVEmY0;u~P-,. %`Ыꜛxz]}Y$n C\[q$ur3^ [ҿ#!λɂA{D2URa.xQ¸#N7 zb5DJD?d+V1ˤL;PξMiNbu,,D2.6(a g Gt_g,BLۋa#AU=DXc9p؁zr)EAg &a#P U9ϋ{ɤL<9eQd1hPcd=yIIG=μD<]F)%ӚY8\9p#> hpFK8a#EV̨#A`sn6!FM3B{V{sA^ ƚADܶ)TzpY!1=(I"ZTQ"m6'+yAQ8`"Nzde_C*""?-X0To&ahh5"uE:PhߌQaQ DyLEVQH*z!T~a'2/wJ:xtx4fw„8h4fFݦ~Q.M5e[nfJJ£x! BCS-;"l~dłv_lYPTU#=⭦2ã`\˦yLSλ#tQqY 23Ԝa{&UK(Ycԝlkd-8ב96/ʩ<IGs0%EH׶uzP[d0e˯amEZ1[t9qDRBki n#ҼBu;J?/j̕7.9#v_ I3koj y|f 9IT)9mplҦj'&oFL;A?wS9W~id)/^ߋwO+`|pwt$ߵZ/`tNѕ8wv Am% H[D#b#P' #R7ѾsRNNn˩r<߯DA_0[/R9~xAP5y}h_\ q}p&|~h>nAkuٖ Ff<%\Imd+fY,htM Pt,S x*8 C0}Thks7-Ll+zP9:cimS'V~A l2?/3nc#eѧPdCP6f>$^0ӖxInN_ƻ0\,)&'9u/NHǢMBC^,9#J1̑w-ɀiE̕Y*L9隱ȉ9oAUHb-[PB2m$\AMk#!-.;PjbJ0pt(#u[9NKs<ٜZB]zR^ud']U_XuD^<6nޚT \Vˆ7-mKq^Kp\E$.X%J(߲2)}u{q5^EЪ,gnyؔ3G AD0u5}pii_hRf"4lIs[ٸT30h#8&QP~ X?'%8h[lԟD7 %e8z!toTns ,dr)e*#W;a,mbM ]eC҈MFo-Tc"b g\GdH-ybWѬxjd'ѳ|K=ֻ hK(mT\=/$O&]LGN=ЬB[XĞ۰p'0#p\*3Vӌ93 ujQ,5*dUĕBfI8m]L^Ȋe֘L*QAhyqG>}6:4Q<]61 @7hBGϪ.jE|1?FNڍ34ZcOqα\AׄqyƱTȦ.mZ K,F1/'870-#4iŧUq2Hǝ#ȁGpp-xl7|צEplh+ʑr ( IJ`%nMO}CwO` id;n)?WBUcEA|f4>Gqo2L~Gtf%B .י1[/՝kE?,k8W˰78ÔaMMQ &41Tմ>ѰӽU5y%RGCƱ /0e(3?0lf2QI^ $@%Ro(ӊ30֏ƠUIy 2g ,d%NUDB ЋĺM :> mm[/>-YZb}ʔL7guQ*NQ1.J~lZ|eZ3pGbRRXU꾔Hw"Wu9")BOhcO+zNVs2zƹ M;+_UWowǑb#.^? ZʐRs DYfSpmMO]M> ke dW6F1eLrS\9Y/{WВS2wd2|Dz0,5"^:RͼtϥaXK˫F_0@$ ӳzfc {lUݛ(3$^ȖtbC݉[/aK#^Lsz {}<|,ƘVF,˶--v:3.DˏUڴ;t=/[b {XJNYڡ:yi']nxjoTDIN/ɝx]6%9`^eBeFCH1wH"}b-Wd%o% 4^|k#lĦudh)/,>Z,rY*JI $zǓS5J_z>/?q)'lLOloEphټn31=NBedߊ E7;˺ x9s6 +!n) sm[%*"`<.ol4JEN"1xF8rI[[-K+ hp9`X正)X<0[ppRid2tq!0tZdpn=If5d$ 4lrULnCQu~xb{bTy:;Y#b2m2y666PjE^ÏSa>FIbXˢif"tSFę'GMhɬj:?Rج`;"5wԅfQ]{tet,巒U<ױ-r"ml+CqҢ IdP/3"Idc[0u~,Nш){o2jd9USϓ0#';O 'MqyEa3l20"v;Б.,(H1/.`n\t( M' NAdn .@<&̠[#^ UqE9|F49Vz@qQ\\<ު,# R`qƉFczIVmq, -e`B|~֙IiHyO,(|--q'o壖k [ .PIt)GXAP_w5 f/a_l =dE ~uHBDy64y6E{>zYwlGr(a?6gb̜-&xLem̰UҐjcĀ ܜ^"<) 98\b97m/ʭk igD7;R1w!}!=u_¾.5vsI%'l8:sa7eB]ݗ~PPu a4JyF]K*X5[~6p APûDj\^<`*\6e 5cDbe J1xU,$T-8$QW ]N.r@e)%sQPsVE!n ޠ-; UcniXeldC92UmYFLC[9t^@#9Y>b^Ut.o%v)X}7ZWcsE~$qGˎޗܻ+)sRcDմ7^6T5FeDy HuOAeFqvh!]LEho ,}N1@x%ĕ>gpp c`+4k61LRs> R ݂z'Pù[[W=;ŰEQቊk "OB5LOxuECfϿ/^9KnҖvޝ%迯˹1WgI7~[_NRi|!{/I坲97b ~I7rN27JkY؋Ѧ0"GC](Zc L5cMղ$;6߈07.1p#Z4DcüZ"j!{D8̈s #D(*i=-Vz T"\(ir$ڒ{ZGj-b7,)󽀟"I A9fnڎ#Yge fv ebx,C Aᓴj-$B 2- Lf3VKE3 B\aKӱ%b?Nt||^ڞcҁ>bn ]mgJ!NƓHNĺV0FO𷆧Ls$pOmTӽrd8f<lu$l X/A%mzqI sS5uMU>F{xeʈZk7=ߧ{2SN^?gU9 Ss:2NAeb 1%fq<`;0x&?I{n3Tl2ÎVRRQ/67uj^1f j0hV(k3Գ\&dcƔ[tc"wzFcԶ3 Qm rF5xబ #~oeD*h>RdjZ-"aq&x O5NC:{Ѧ n c*fOøb l0m԰6@ xaJXm#'"/5TM :`Sn۠gͥnA{MA l|/TT+$q3*>gՙEx_p.UnQRċ䘏lf`Qv͓|ypi/$)P0]OpRaT 7_Xމ2M%^1淑غM;OQuk3vG8#{ۇ< W2!-;׈I^}*W 7QtTCfɼ!h @dIJр{>_ 5[auTpGf>k_yHo$4W~YemWkm3/v =d ȃE;Lmn&=l)yIBa(KN^h9E0ؽ1]/^bO]i/Ъ3 n O 5^-4Yg($@7lep*eA)I \8OKV`UztxĿIxV%~ܪ_oc&8ӃX8}1>7h$|W/LL~.vANvh+Xya`)&ys# d6lHۚ"YXW= 6cP2?|]lm.K2!SembZKTKB+I\b(#KYǨ Ïޗe EIJk4eCZ8pi:D9!W_0H4T[Zkh6V41,OC`;^`t[Tm~ IկFTp1}rluΨٞ=ƃ!.E⧮ yRB[JYV`k}mzűR2uL` _ d#6ⷀ'%|C7@fta`NI XX)Tfl;[m?IHK`XeӪq4s̯H9q&Z`[3aYH3O8ZbmOcMa3*|]*O*ce LƟ>JNuxH]cԹ1jE.-J8~U߻[j;= 8)w~W|(G %[yX'{\Z6%QO[$Yk̮†!=85!7AtoMx$?=_~Z"ΕqN &W宖h@65՘{/̪{% =ny:,rK20E}nAL 4^$U!C:q!,˜??> pM+~ٵg+3Xw/t~?iFҏw6TGV?YY?7n?>>hll81=Wy2[;uL\kU5TvCOk̝#:Čѫ=CSZ#=QTp2 ~ (s]<3[V@ޚJneɋ|QjA3Sm09R7>ҝА ٨ɅGi0} ([js^ix`=5*62j;Y6u>c -M"*Ox\;/'~g5Ҷ[3cP{fPr3Fr9(7Wg:)!au DYK_GcMzΊ#1OU!A>rq48r4'_.%h5t[9YdNto0 rUmZl$Oy/Z\+¡gi"Z6MiϒNNZ6vo1r8Ukg2 uHRB!-F2K~؆B,~|GfΘ kF{7v`l e(XC~dsbȑ.˰^Sv7B '5#EpeސSo@ފ%02&_.~4nӿR=i tdrK[f5@e*O^b MAk4M| 2b4$BZVV2$1&kВ$cb3NE[J02Hf?0kƚhV$w椋n[Ü7pGK?9)@a"iQe^øvZZ7.u%5ٳK}ߝRVoDXkZl9r]&ћ$x٬ ‡އ5Ȓ9'S^s (r|>J^/Fg^3 /^;|TVm_PC=c~ok_6V2%2ڀ#(*WRp 8b<]ܦՒ^wS;4HBjbu#kTO@TAp[x/ *=m. #0?#N+O,& Π0IkJLT@M߇"LZ72+BՏz4J+߷m7 e漆u63JKWw)(cg*qÎO ٞ<)U{[}R(sr *_cJdVOX+ZDP:;p|zhyb6+[O4Mc?+S?Zx`iz\͍e8۽`VOn> ,⹲kmLӞ"1Z,d.Tc!BT]+}x1p☍2u+;SM*njw_8/VR}<f7,v3*E(0Ƶ-&^jcsi/ͮ=gp]J$)4HK{+m [Wydݳ dy2[ݡ]1eُEӿ\e6αYݢ&=PW +mjH/ְrp 㼂,9S_iQcb0j3%cb n5e#})%#)#)-{N'm-3.1K8US0#Zpcvl܏Si },Ï;5:,^6,aDAjNĭMӫxG2Hd?Fϥ2e"'KWiZN#'≴6mn[g>UCMEYX^꼜AnEED%>Pu^;zJFN~n7V}(F@F|CCL(Sd%DmK:pe9GW;%"%zG Pg$+\#ю0VyEQujb(]cJU|6g:Ԗ 2D$I"һ&oBB A@ʉȱy=pn)Qpjϡy0ٚ keh Pfohƺ1`]X~U0L䵈{:Ļ(:A+gɛ)7(=M1KiH)EwW69ZF"GRE G QEt-]\ZT"5㎞UnaWR PxIXnm 6Fb7pg 5mqx|2J>5v9oJNj'GQiUMJL hZ mX,- ,hYgᑔ_߁o@Mq<0E!j. 5AN-%uZH0 YaIDnJ"[5m׍AН8?  vKl'I}JɃ&V"rOz.!D:iGƮ_l`_GyzG`~%Pv 2Dyܡ%C5ޢ`M@ @Qm Ubhmf8 #1>ˁdH~͉@HAE*ĉ$,F3GQ%\J9%j6PN]Љ11Y~I#}x14#%}ȝQdKHr Ć}5[ع;]}š>m|vw@mcXFvj']h%H0v |9?Еb2~\c$[˱'̊Lm6`d*l6Bsku@?M!SdGt#'{Ƿ-BA;r%w,>78묭׺Fű"zqCfN$mJh εTZ UvYiě)91Kr 41ꚣlwXqq%I6IIc{(3j#:w{L&Bl=Pg3c.[ 03ނ*Zȩ&5c7'co=6JdpvٱI쳴P^CAx|EJ(1/ omn?qۦu'k<^f9~ѕUL wyxNF?'<)MݢjJ.ƨ`=i'"&˾=mh不Ck? @vj N]OZr|1)/Iu-QÐS7CFp ڝdߏU|!gu0)PhSIp,r6 '`=hxyJGQ "rO4B&X2+E΂S[JM5<:1 Oe>(0G%Lͤ 2aplIZd=0!\48⦧%r)rGwƺLjY2<Py;ZM3ǃHjꜸh}ӟN vp-l}L~c47D&v25 Sc"^zQwqPX)@zx(HA !gIA˴ć`Zs.5o!/mP,'׈CL)mYG|Z~e;V2 tc/{=T]l@'m FnMeq..wӫb7DE{]Qek2ctt;0vIڼzUAFb[j1hŸ01%W(PaQ52 CtGM ai=u. 7C*~aCy,ft})!zofc כG#≳,Y n0~z`7dZOiBPkwV1S!֕"Zk3o&&Nװs &oNO,K yT}>'?9OEL-=Yv2 XYxU q~c \SSp~:nn,581+Pe\6L0 $LǍB#ugg3@֣ZQ\sgPÄܛϑnY.Ш,_O_W{շLxW_ +G7eى*2 <&tf U_A)L]d@b'5!ǭٱѴf' ~%i`@=+[T)u)=`nY7O'ٙ4 s ea"5d΄11[L*Ou9%خMaV B(__G[jc>(/xKN!3{@2ssnZwqT1[ͪwCJ^fx\ KayA3uCq[VBUhRrL|&hyNYH2i5 "y6YF܉%qa1?F9t>x0q4 af?{hbzfCF~sƈA=߰^my]xseز=/BUDԎL@fwz@-ВWH$j}*RtF {ɡ2 LV'aalk.cM/EwtP'ʊQ)Y[r}_REIF 2@f3} .].nOfi(Ub*uS)ѕhVCu7"ƲXCޥ".i 4:ʹYC(y`Ev$"Io1\(L%VohXʁV"q=*ҾTFYٌpgԄDT-+q{&eaNS 8ι,he,=&lVqڝ˻Q5U4@Zjb2 (TYݎBƭ"`WkKK$Cjz/\Vj[ FTPb>Xc@&6Oڡ-h䘆 }>ؾGmXg(3/ $7EbL{8Kƀg Q_-z蘟ᭀR܌d́C#c;CEl,+lq쀬eLb,QW8JJ܍xN뱻+@N|is(H ʦet¼oLX"olGm%@~R5LOZqYi1;K0P6$hJL0١2W9> B8hɭ9׺&g0OF'_|-Z`3.3v0B5S4FLH篫}Iz97GHU%W),XM y-ZaZoм֖:@ę`fD̼ƏUϏg tYv1kbOmF' =pQ|Ҧhhԣ:rh=Je6$ Bb&EG j%Fh ,@1EaPz"h]PknTNM6̜M,$ Q3 ol-ruD4;O VxŌMݒ]16x}! ab.\|窱UG^π)JZoI#`r4kP^oQ7E=匩:-#f[Mv`́4/8HΏڳMu%B6uW Jsf΃`ݦ _wzxvN()e̷1wtvMZU?NA?+cZ V0SuL1q`,}x]?057i{`;Dsxjk~dșQ;v״54:e}ΟeOY(! .uՂV0#ъgq(ٚcU?"q7oc N6q9VT=Ƭo_>qEi/UkLnShMZLA÷Zwvs4h\L7E?.< ;x؅54z$V15;5nMl֟gWyQ'ɗK, M/{%` ,B &Qs#J9 ڻ.F`p|x@PI6r8f| 80}\cy6DY_j@MsA=[í)9v"Y L+&Ϡ4oU>WhWX|pN07LRH͉3쿒 'K.QI ƚYЊA9;<7;s">cf4\?RՍRS8gjF ^1#G݄ƔM~ֻA7o0켮CR;S{|NI0YJB1IBa95Q  3%)tbDC[p;?r _m }&΄WYՎA$p"ܐ 6mB0D61~UQēJ#SU]*WM*>M .ݾn@(=4;U0`sC'"_B<#l&y3o!K:T^ ƹ iT ʋ"ډ.Ѥ&^QgEbFADT Q, k}Cb7pɮ Ka.DF#*cM.qvQR !&. B9uifc]vµ:_'6ǗsD\ oD>'h55JoV D#b 8W{0h 9\Q,c܆`LBVl)^~غ "[կ2C-v}5:٣BK%HϱC3jO$WMmfEү`Ӓb1%Wl03la%G(:7;[ 'a7sI[bNDѭBڷ~ݸ(yXTt1Ej&u>让 :?4˿ܼB@7"AE Y2t% vЙD4h:@C4٢~Qz=?3\ 86;=FNȷ![s etdF7Q=F5؏{fR_el ĝW#渱V5OLԮ7- HCũ 0*xkJEg~1J*#}{v0߸nQC/6GB"5mzi?T[tcs~˖wH'5dr %a nǓlp7Ջfz{Ћ}wc-;o5sq'scLpoP4<֮AmidYU{h}Tfx=¿A=R {qI+?b'|.W+d a`aqXlHyE,/̍ku2k(jo&ᄫ7llw-tW.j% B(zė$9Xy~Ua]t/|Cꈾ:5_o䞵`bdG(qewL:5DPK]uVʀ8?,setuptools/_vendor/more_itertools/recipes.py;ks6+pN]Rh$d7x晚ڭV&w{.5Ej ʖ_?"E{|DAot7Z矶iU!MNaTuK#:oUJ|==;{SbhWտԫF6GjnzJ).zV{#WQaĝ| BFөHmwW/^ uyaw|q޵&@Ɯ=y]ʒi'{oI]r&_~'uz͸}گǼTuڔ:/S7lr3ݲ20mȨwm0 ėQ0r<;[.eY.Z>n(]?u b*yFֺ1m~κm*H="SꐫQlˈnx'T>Qiug}<;CUGG=3{cD0UIe{7]fPijVYWz ;iYdIH;YJ]L Nٚ |!L +ׯ_3T~&x_R1OW|" ϲUgT2[D⫊!<3x-8 ;WqE8]׳1Xkw5%fHh8XhVEpөx& rq!C[!sEر^V&Ё0p`U ߺphtkIHM'DBxyCصi8 z*Y'r2` kQp%b2,:Wr|*_ T$φe9+yz+ }]>k72/eߖ&0'`ћh_f|30I05EB][y(Uuٞt/*7,g:9U;3%Hs Z э*5 GwTu~}kI\j`-t<C~±U~}"cb_wtxR${unuc+Ah,}1,˞"N4g߈0*iaFye.kQ͡"'\fKE5M{gca6dQڀ6ʯeGG,*wdc쫽. plW觮2A]鳂س48Ϧ;.gC YrfR483sR`L@ޜK[t.ВcpEM1o_:$K^l=(GW;yP9 >ÔTa:Fy hAOYE9=1!ns[H? ٰGUC RZW)CZƩ%_Szԛ*byHNQ Yy1&B=eW)HY%6'F{.&xF!c2.R%{'>K >^2pmrޒ`w.+!*Z9qe+p]E"0)ЫSGhuk ޳R'z'Jq.CāR~4V%[ūo?_ JpHj/{ bF$lt6JiE4w KW{@dxBZpސ@PQl~'IM_t زJjr:с,WZ&ԘևۼIC1WQr1/*ma%y6GpmU9 >4M00ZD)̧ ͦ%(/15܍`w\l%8k}Pp}uo0Xu~Q^ޒF^sFoF2_hW^wjs LjJwk1/Ic Ppխ`L*彂44?dT9D|f}e/uC$/[1.. +wv IIw% T_ZqR#a)x(:vCɢhXjG9 |j\p /r0e LaxPq':Azi3]3] #B#~R"Y B0 ?1j6l*愀@rba /wb[1 ,Au_/C2HκNUumE&Ni0u|^,\x }-IOZ=S֚l 66ڂݘf⁂؎5Y YEѫX1#7- ;WKwα7{j8*N0; %7vbAąA3].d87@+8 tFf3Xx#>QoQ,7k|r*(4z-B3 p!$0e.-.lĐᐳ5ߜ5BqN&zѧ;o Pٯ`]!߸3>V49(3=6t1"W>[dp6%~QըWy)p {1C*9#Þns-<Rڢڻ~DƪC Qj)>'Z]=}sbY/}-zmݐu ֛ɈcqCG48vް6jy3eg%}\Cuhe^E@kT@u8);%Fo۷o߽{O3yS6 Zc:6%56BÕ)N;+$?+ǂJ9Ew{e j짤8cAZ ll<ː ,kJzO&d{f^6;PC']Lcn8lI zYل%Rԏ'7xd+r|-n4ka ._]5&GiӐYVC}L?ʊxS~FN7d 6EU4e*>ȳy &/KSҌd<5 EAh-%siJqL:3-.ؘKR(2BW&vonY,]%4o(O'9oe_;J93cw`שK|(rS _Z ۫z{ xKlMۯ|}]9s&:ӽHC.JeK~Nʻڵ(*:Bd+"E#uw30CV ZrǚZmz\/pGoC8SaR%a X!qe@oEY׻Rŀ9ZNw.Bnw1r1j0,}#o5,[kIQ XC lԞ#Qm z  M&pS՝iXScN'v@xf~3xۓe(=yGgueP\2x|+ 8j3}'>݅X8WpNZ0]Ʉ3ίA딲uBbNm$W5F=sՓr[iAC]`E oi˳U95"׉(:TX-%/Gp8B@:]KNplg?i"PȅĢV#S}!bzv~O!$ _xB3шxE } Oc5L9!%fB9_Tɩ?6^Tq"J]xmؼWޡ ;F1qE2sQw bB') зXVcN4h6bx4J?.O!g X ,c~*໬nE+x~du (QGOvNxb}~, >u{?@K4`SGL䮿+w_V]EIK @ 0m1Hc;Ƌ*Nt1ᓂ'uޢ}suuatnξa3;|zfi;^ę5qbEp}^4̈v1pPK]uVFcf)setuptools/_vendor/packaging/__about__.pyMj0 y [. = t2Fps`˃lݜC~I{GZl Fe4-6^Q0-%ljbqw)e=/UdFkr{m}Vu?HdYEU>3O^UL,XŒ)rd;EqMJ5SpNj=G]xַ8:Y*|j$1;ހI6H1,ĝ;~.0L dIWAY7^ p ٵ-mjkGuo!o|Ynpw3(KWTɘv_w09d PK]uV(setuptools/_vendor/packaging/__init__.pyMj0 E 1i!2.Ea&C77QY.:7WG6@oB]4mq A;dA}xLe9XNCfs fxy{97u.`(X!_O -ɡ t(ƺP+3y6Ek~"S^QbU4l)}*rM\@ +J.Y#|a|6= b(`QMPK]uV*:c,*setuptools/_vendor/packaging/_manylinux.pyZs` A^5qRYlMeɵU9G*%&Ҳr2ɳba"d#o4,nodB4ʪêZ$wHe(G"KXq̳K$cg(xy,iOZ*/K+9R0^KYu!X6W:%ᙸa]Y,Y"XKQzXE'Hd E'2qb#Bitۀ N,YX `2=v_>9?e? c^,SX#<'oe."XlI{ĀQnũKO3y,#k?g(b8zP X4oKi[+h7GΎo^c/ACq|~tugSn4~:zs~OQ ݛ`.w˫z>?z{Ӂ!9%ٛ)OO'3x|n_L7_^Igd.s2UR!bzDy3f[?,MhQyo|A^.Ze)aovV{!zr3jN}$|!CHlDu~8s:>rO/ݡW[D{d(x&tS%Dv|R9[r2%OeU;+)KW4CFI 6}GNiue)jfTbYVx7J|RylQ#u,b)' C(D dr CW!2 yXj9JsxP[ERxxUD-aG|}qݡ e덋|gk7,j:nc 2!1֗}g2b' M:(ոcs3vx[S,Dd7lY]߬ bů^94s ܬet~ϰI 豼p"RYSM ̶XNu D(P&fYfdQnaP\0v=S55W;ΜQA݋g@_:-N+L=8;c6;#\?\!T<,p~>FL#B*Q ,O|$\PrJX"tZ!;ʿKrCx. 8&)( i"]VR˅>וG*6h2wof?Kjn*c"H4kQnJGlGݑ} 'RBȅ.Q*U{ƪ1#(ng"&.{ʣQ?˪ɎQDٯ03|E5hN ZISU4)__H6/<,ZG XLy,P/.JU#uU`0jJC]1NUX"8]d&"j KADWz@ KzX?P͡R" lpj̲XJ\[x1qh~AeIg ܎঵>9EjATdBad/QV-WT8!P=N{G: DP} PygqA.zGelҜEm4}P#8=\(M{A =[b`ZCFbb!sFAEV 1DHE,CJH3<[Ÿi()1>רT -5n *\hCҴ0mg.էȀnsGW!3ڙLJ@?˭gl@=$Ąq [z/x}/=? it }Y fiG<0mH_96ȪCMoxջ:d"e鞍ihkw_Ym m_qQ!y{>ۍğhVjۓl/I;.k`QJ-F3AZlȺ>e)JRV2ʑuxtF!V }cطXL &taFS1UQqK#Hjys+(+jO!RMʲ #9u`^55)mz!Y@Y=-zۂfm TB}3`]͌w-Ȏ6[r8+߅HF_uov\B(;{ּग़]v` eR'6)TR2Ih]ueZ4KiZ`M5[oF ]JֲAh#k5IkjI=pFn1 5z^p1;{_  8G^}IngG Ͷ};bXCjo$-EFFk͎0$I/†2:8zZwP;uF otf⃄J?SYK3=[YI^!э;9|fy1ob]Uw=VCmz@mkuu</jc9PK]uVt10*setuptools/_vendor/packaging/_musllinux.pyW[s6~ׯ8<,pNYv5&Ƴ/HPšYLf{/mg9p߹ k86M-Ln@Um)@VM)* l " [Y5{ЭRRmzoi&T"R Uk)p×-Yh#ih$Ţ.ʊ-[)Zٺ.MP7Bs[aGXfծ]gN2 ,/>Laa=)oZt>4uh[L&( Ղiq1C BQ)=R*;j6|VAYH8a'ef䯂$7\yJnMɜTH '}dt>.$ q:=k ;WDZ.x1zPlmx#eieuu\8?9=o_go/3.(w.z[4Z螡iDfªur"m=<{ymϘlkqJܗ{zd88N!:!bZ׺BV­a8 2KqMF ,g^* 6U[2=5"JpOBb #϶R ~!BFNx"uX}W 5`l4B@lAl:+QCKz. #מSk%N=3>='3>WWWm|_gpPקGkiو?~StO>y8S[0VC[/ 4mҍ v5I+*\,N|^;r#+zߓ$UQ;[-:~JV#j'E DAsq'%"' @-dr89Ȼ!:4ccZU`{]rlIo.s=+xhsw0 α$ 5Mݾ^[A<<:d%7ҟQ}uZ0B6uPTcaHeUHүy4&NZuU*v; N ժ+ޟσRF e1: B|Ln⹞N0ŧzkyu^+u'TwG˧~Zabn ̍>BЏ pVVA r#'h(MRvߍe!q++^DJC&TkE>FeM=:+ Sʱ"Ԩ E1Mhm+a$b6UEzqfvN?CɉM2_pLr''KCcܠ:{JPw J4p Î}PDžç?W5yNwHe=]//*Iej݊dJO#c9o+됍dr)ɮ4|3҈G 3vA/l.bG% 7Y4hup!rK>qo}8&WgiN0@kOC[/^'~"Ma'M+T-M#gE349n]ȍ<ňҨ[e.pwF^hNRG>tň\gjj4̰o2 h5P<08ԑ);hzOQDW6A4$d*~PK]uV0`+setuptools/_vendor/packaging/_structures.py͓=k0wYpLh)BB϶"ҹ_YN?dt HQY̠V-ܿT$+h]O-[E4U$)]p8 1ikQm8bqA~~6r̒#.UXþ˵iӰ]L`;'6dY]@pܵyFQ0[u4DwC8-yNR[CEA9y7i֬)͈z $rLQN9Zd׎S%MMp(TJӧ@N=DN.LoO0kV6ǘ_W-[7:!-~dA xdq< 靺fHE Rdy:lt9ܘp Grs6͈@M ;IND^?{N(NϓY'۳ULi!#UƹIniЉ ZN8V)G #h[,_Ȝ0$ЪMl y 4GLB>'VQ[ &FaA-.Ef ^j N juy @6֖w In@M JIhr2A^W[,^zrYs`H=/ `]/YRL=$oW g0s|>c[ Yt4D;=l$,Z ڳIw^*G>FA=j^y+n!D-"2&"n7V !;e tf%_o%z$Y+N_ȓI*3V׉&i݋;zXfE~Ah_PK]uVX\,setuptools/_vendor/packaging/requirements.pyWr6}WɱI:LF02j+ɹTp`R@]@JE/"̮݃SAi H.X.XB6y8׌HWK=tȝsChh7J& SߋFӨT9^`օHe,8Yu$# 4D8j]p KHWhó, ֔ ,y"nuRR!;d0RAfՠCc6 &7kYPnA@r@u~bu欣[ :LY.5svnǂ'Ƌ1.xzfLY_ӞOi4ANX3x,UR66 GVxzM]>۶ ]ѧ$l,9} <ο#\\gl8 3c?U5#T*Bԫܯq80M'F= j"Fpuߌ~Ah;KYv2kdihڊa9< zPfcyj42N?"vʱ2=3677GNgx~ޟDC ꬲsyսZN gK@*1G&UMOJ0/\7 Ik*Oȩ (`A+W@Rw"!F@ 64ݰL7}jiOGzYl Lوs07*RLmK–$U`ű'XƆˮ'O^Q3b~:Hn[V;`- *0 )[eU$ νڜY@f3GO{aw*PDjr5Hz}GMIcG^ovk v¡W=oorOWrN']ܦ, 9eޮ*ٜIʺ{ b?BkŠ4OlשGZ!JZZnWtm{sF5ͪ.5dz<]&)Gߩ0z?GI1a_ڌJϞ|\OXг, TjYYSVwQVQZ^/sըh$ ^U%z*Ҧ,<cAo*bQ\Tul! "yy2՘>} v @ O/ZzlIΟIWJyZ0o6K]WF.^5b\eI7>@(oc LURvkZz>v~@#'|$m`e9suMx:*ENpDUͪ*j%TZM|hˢ}rJ߂k)ȍ2D,vaMI})E%ޑ:ز}QԨVWk@2]-P/iulR'h߻VJfcSW ШU3x (e/a!_0>|c^  a/cc,-eY0K~ܷOZpdA 1X Pb;8k6+` 9Ja1 RP|BtÖ f=d뎭;s5. %>(a Ip j \~ML*:KDoe^Z`w ug7Iw>vzsZjAX GSzl|3BAeEX\ uҤ5)od2!فw(P4}$U׺^ N}z>r hxQp`:08[G! FlhZ6ޚ79N)ڊ^thpM&|Q}8ouҬPn ԩ@Ox锄`:ߊZM^ &8:nb?|`a G-lԺ41TgCP`!z(<:ɵl^ 8Fpt znf5ؙ&)R5Ѵ+Mqno{F_F./'e Weٜ\5TGoo a-tiM&V AjN6_B%Ǎ5!ǝ@bZreL &fچryQ bAie[q䟧pΩ0onj>IGCԼY9=‘>P*@z i/ܱzCZecԀ[_>zAddi^02\(jpXrg\K:ߨJD^aB8LA=!U/%%PTMj@ udxЊOa0O = 6+@µzHXJDkP󒔆o!yQU*k\d)n`:#WG9DZ? в\Ⱥ9k%i4müt`撂k&`ɳE?ܹAX|V =ikh'd*p?FPD5ҀpbPW*52։9лBB(jF{Gd% ΛD|>a^89NKU̻.S}s-|Z4*+}΃\[HxHZE!:rͱe$m΢ UFܙW5na z/n^&)*7AZeA_^'K4/k4pWEmU( 9BBCdI7N444lS+wvl77\d%TN%K? 8~?o?{un'L (1h)5L;_=rUSt"NhRh zY9 P͟d պZM@jc[Ë>2 z8&~^ɭ#PЁ XMlֵ%`;:Mˣ{'0u`󒆿OƸc26 @ԗt!萋ȪNeeE3!:!VF*AʇLSm^VpO ~a#Lj \juW57 :m[  |J ?qk.頌?E>6(kuַ;r$ǎs]L#Zן#3s*xr5  8h7^UP6t6ZP!Ɵw#Rxhh;tUMFeSƀh d U%γE֘0vj^U*Q! `i[HlP巡w [H;i -9l^(F3,9}~üR^ߘlj_Q] RǦ/h$TltR/حMI HB`aŐ7F^Ud6;=aީww/JޭQ0Ɩ`',kڸ!?ňᬖ`aauMlJP $9x?̲^9:a=-YA'IwHec uҬp/ⱙɳZ.! tϭl>F19|f{'x-u lʵ&%1je)8wrM!/cZ€zu^LAO}S8v|~y F{^o?{\Fs<5 +1иc MSf1*!)/|`<^VoLVH\,y8FfyXz{.QEov$+EN'Jǘ:n֘K>{P?>ු+klUH:8SRWwX%',ۃh*ˣQgXߨޥN \bahi bF+?Tƒ)Bf{ WHd%GyΎa;sߠYBU͡@룳 -޳o'?[ -Ƴ#k&? Ia6京sL;N\<5] !Jm FðIM @Dރ N=޴2tb\xcuKv)0^&Q kJց=Deߎw)&aNe aPŋ8z#]-[|C>nCj!/uRo]`NɬwE;zj eCtIۉNBPz]Gvvwu <yZp]F?@ՔĿ:hnvTUF}tZZzTaeۢmW r Y:zMK(tBg9?qŶv匆߃b/]_ %nS! ϩ.xP[ ýwltQׁ5jl-jjW bEAn]G<,b]hz@UG6t "YgνkZseJjTiF`/g:f-Etq1eƔSoIs?j`Py;6S3spĻ7vnIajėhg<пQ\4j妃eMK* =(-]t2M6,S'*P(Q)}{2CF<6#[-YCR*o|Barz1 LazO9؛Sܺ@lPfEz(ͻWhΤ76dq҂C )rK p\L &>گ{GҾQ9-jsrāIp 21|1x88@*QV 4Ƚ[Hzd4M  ;j::w)kwms`'z]u{wF jh掍dͤ 4OFb࿬)o/EȃI_i/6]jQx0"͌=C`ZdU!x1U$H;ꢒP8ƞg{ITT7h"gA( S | [F  wQ?Mr1:%cF;&Ҏ: \~&:ܘ#ŜNN?]>Kım1]=Z {#ysߎ AN;r\oW{14- rB6^U$hI5%Au"Bt踲n+oAc;0 uԾ߭#%sT`BDԤ2[&*SBE;KD U"Ek_00$:( 'awZ浶2E oy?jVk$T20EKݭ͚CKߩn2#Az_.1S;CE(,ZKXE.m']?+ l.3nc>M%3PK]uV msS=$setuptools/_vendor/packaging/tags.py;oܸѿ_A( vۛR#N$N@.6N C%͜V҉={gDG{1=C;-E|'uT<crrdǣ3<Ǔ;ĚSY3g04Wތ Wq-@\x33qHDyB7@?L&b;|wpZ9 )~.fϟ6i$%;.vFϣ=˒KUJ͘B]Ո]_r3YEIɢgR=]ժPA 24`jQXGʜɺ@$hh0i^0z!@4|0:mnpNLOpLTaK.I#y}e߲y16@u5h>GޛY\ 7qX .Wh8 L)_ D)]4e*J88VWQ$T2QI5HW⠄ TAUF (/+U'c2 =MĊ#`?KR/?C%z "Biч֕ Ȋ2OjP).20;x˂.hN e_Bo=sSpc@ѯ F%|\k^3psH2N_98%!D7 p2R\R_j/Xh\Jq.(ؑo5QJzwA䕚*8:Xd}o!ӶBAd,=+FcCnhbDIs}LJl0N vT+E_**}tl\p#NFv6*z9u0]KbAflr 뙼0rgS1w:Ί,\)d7H,6U9_9Z8(ZiиzcfR޴}2O E n.}W*aNމF?WP OD5JT#x])+LX@E8kQz):N[rVowf:G}:|զT\oM<'tԙ\V*(hvKb|/H9`,t}#͛͏{ƞjn -Cy-R(fQuqG ;:X~0(,}y<6) p;=xs%ҋ[-;m!(ZmΚK{-xw  Q\ڽ2~!z\̈цAE ^N׍Vkuplg ,H#WB6~M(`V@QYtz)u潎v_BEH}KpLzr ,>rގCb~ CʸPpuIoH$ZJˁp 9fhvgH34: 03-)ME 0Dɔ3j"#荰֥9`qS]ewkTФ "["L{%62J.L(*;Ex1T6gN] 6@ $@w@Cy:U(4:&̈.r' U~}e ~:]EUpN"W4XLRHiC_*ֶ!V|?>ѣ=q) jl ЕQvѭ4Nn|v?p @ܱ.g3wAi]ɈeV\0N>Z{LJॽ兺*֯ 6opvخwSħ o,, &E 6dyWu6Cswb葵}hg>2vS%Z:l{&9@sk+u| Cg 8HNݬMa7ˤe`0 or |FE39)IKXɕkNO[9yfOU4~M:kzAj>)JtRJ4lgU޿Y=C?>J}ʀSm>YL57cԺvH%fÁm(WM˵pø߾`O^'熆h,X<[JŮ"/e|2\l N[vaz-&UTVU" =O쎍'_y6"uV7=3cqQz4~j`/ϻy*|sS/9c/7uQiP.O=jrYF`WmLW t 6T@8+6ebtjy^Wl=`GGHp|e^_\`CKy) 39}f:G{^0[/*WJZvFPgmo F2*23bRY$fUiCk| a+2ġ;ydF挅UIS*WMW<#8sc_`ՖksXHak~9|2 Wי?B b`& w9$ &G%:Wj[W uYQ/ &⒧g'PW2zJ2mSiKݚ:@nSŭ2P5EͬlvYOF1}N#A:@Q ASI4y*8U]+WP(v;XGu;Ej3\Vfp9n5,b2|;bKu&MxKo$thn]g3]n5<זcLzPʠȶd]QlZԄJ H9 َ qb 8`JYҟGg-ar=I5x J.M!Z05l6mȚȑ͡iР>f{WdEG-:2n)΅>Khm )P- tA2-wOw뀢m}ώYĺ.B (=!X:!7c ʩiVi"ϰWC :BAkJNv*atҿ3G(7YXFnXaG\9GZ;g16gg;Z. ZxL?PK]uVA3h%setuptools/_vendor/packaging/utils.pyWnF}WL"cp\@.`pIUɑ ew/wf)KZ\=rb1PY!Tk ra)j>B$Ef^odb@gN/Nž))Mm"PXJ-Tdo!r]h24uyb]JeH[(s[b wX\7%ExY>fiW&YF2YFP&J㜎u\#sZ\'\]JY̹BFf޹Tk2syFRqoB<Ѻe  >'y'JIN< E]+5,J.T2U .'𷸵E&4kDgߓ%זe`Y !ӤH-`sK!,3)Ptm\s;=;Lp~⃑Ryz?xýʷ&>_P?oR nB7<\haQ؝ne\*;sGvHFz69G}O=^c.Kvy^H8tT[ٿ﫴n\'7Z^jgмjYgGމ6l^x~5d:_&=E]{!QV] $ttst>ʂ~.FΌY~2+Q,Vi.njD&>oR|aN}o9Q6|;v;iZ=7Q5A#sHc.Gr: e )"gk>] xD_Sñ:gD:ET'ZkF_j؎*: ++60֌\ixwhQ(Ḳؙ"Mʋt ,ks-XMfS6 zc3׬7HBMKbSy{rb%Lr~(oL^ofmi ic}{hxm[:j.<Ts~" ="G>]`Pr!0eQ 57,ɥu WdJ1M4;*lT|^5sL۾ 5jX)pa UFXZ2z=^f^Kui~ij%)E$ewH1I|F)K_/N: 0NnϩMZC+V(]1^ XEqwadƫDdi֯y2QL"[$v~l 9Tg>u_ʌ.Rн)>>y[2 rs/ )e[,P0EBRos`Fm\=?{3f|okYMC4?7~me;[oC0z3a_Nn'O"I@_Ϧm<ք﷫yW}kD0ЇZd7ʠvtmSރPTlHsuE༣iO`"W'o|Y*Q^aG+%1ll4 WEEX*H!Vx YrE((Q}2J!#Li֚ǟϏ=Yʄ眎.Sd*U{Xf9]XyyHiM]T=Q`@#J 2IL:y\U};5z@&m0 4[%=vi1smL>+dGКBԊ(=tIulG~V ~YԾҙm'-Ih[4~Tw$wJ:`dy0a8muφV-3r]ۚ#'y2˺Vxt \d3#üݐ8`oiyGu 9MӞxۼ Ә۾yRJT7cOgWf4#yBg'՘u=vΦ#}{iLgJ4V 7n|6&6&HYF 8T{y5[lĎӒ!ԬԈu 8fCbd0whWupzvZO>Gl~y7 V+P}`5NV.m?4$VZŪ }Sl*J4橞9U橾PtMAWUIk,GGϘ]_+s3mmsj>G!=2$Yo6V5bId0SdȢ2F/؁ݚWlGbpeSVb 8XTAmD.Nia+BTLY.ɴFSA$RP[9 ˜qǭV]DjmětaetShNFJoF;-ǭYZ[|䤅2Ƅܭn.4JV,ѳÚn61oYPz;S7y }Xp6s-Dž΢?;wGpHNӜV9=^Z;y eg]B0kӹ]yE&{J<߽,1݉>2^?c b͑  TgԱ 25IU#;E\oe뙁.3@mB8zʹ Hֈ+õ2od7{B U@'i5N}@ BAԵ?Sr&kCLIņ[zaHP7A0TQZ "Kp!O2X`-jtNN G4è_0}ZuIG@ÿ~,7C$p{5NR.EYt@@c>R&NǓ[]o׎7^Xhph${gdll/Vұx1Yas b\ΙcIs𨼃EtSs錧ඤMWs-EҺ.;gЦv"1n@&M!XE?Ep`@!3$ OTroGZze {KEmF:CN=MYChDI6y',?paZMe @(x[g@@j,ڴx,| |rqTõWx/LBa'Ks!`LKn_#˺8 6TdS٢hCpEanpqߧ3 uaaZ3#UG%uLꏲ3׳ǣ7GH1#ׅh}$g֡":jۖ[+jG=<-DvlY&EC%+(0qeֽ.Ӭt+%zfa{WwGnoO%8]v3@w-c]ӛH".Ppgnb'٧#M:ّMxukByFڊ"J 2al,>_Ѧۨ8!19zoR<(Vt= DF-eU 3f8h\cr!Tf LO bbj-W{b<*=R1FТ(d8#ZRa<.Q?M[<+zR|) p=LC)C ~މ=Ld@08Z=I bIl"hG0dl4]L*§&|DQxK4%"gYk, ۔nRlz!(b dj}NVPN<a𥶻/5OS؁`bmo`mXU&faDŽ5: 2MfW90ĸALS?iAz)2Iӓ45ͬ҄E^%0-p Or;2u#jxw63ߤi 5-o: _`^X1|t;GW Q{9{mx YZC?UkVpoa5ly[XH,5-A$8_ΨuaPq}A| 翼Js|? u($&M B_ Va<"ʦolvjۥ1Z]P+{WvL-_] KiUWZUɴ+KHVQ<5'Eɥ p&hKJZӀ`EYKߢ~O aV[ct,Q*"ESE}9 BMW\y!f\:SaYkCbo]ӓq%m\?҃d5 Do7R'aC$ĥե.'omzGm#TJV5sM7#$ⶭV%v*ʒL-\zFtqը9o5 }IM)KNíZWhB`ЪEv[:=[ܢy]zm 5CȞ9wMc{tiVj(נxK ŋ.!oVSPcK / Ϻ>M4WEqE8';l.aw!Lí:8cc]t 殀 0Ĵ^\[1A1w{,m>8tl1OېRmaBw4>xX᪆?M$y[}cգ<%POPK]uV/HF'setuptools/_vendor/pyparsing/actions.pyX[o8~(T:jam3"E;-A 2es[I*J8mЇV@b<<<\!Ff4JMH&A4Nƪv!ZFQIkEHt$A*eDZQvpPn6B,SK'*xR"u]VeDʭeLk~gzI 3:n3ȈEDK"Sb' .|3̬KF3d0=t*l1֙<,l9 |4jS,)#5\0k?h(_rty^֎*ӶE=YVސR&Z:ÁP}׋ދܾIKcQ,] xxTFxeXb UTRktE [D:BE 9u`ZnX, f/aB~⧙(vב= *xҵT 8AVƤFULT|:|ӨIpZD$o3Nk*⌭ oDmUZgf %$/rޙΝ%mH0 [Ŝe*WvO"G^[ atVQ,CqhǬF3/6]Ho/.-$x4 Ο`iuTȢF!aok48(.#+^/_+~8G']o6okRT\*RNpv0p(ՒsiLd})Nqgņ"K"MMp$0.DYck>;GҖC3| TMJ Yg'_6THW_7؃sq0do Yf4/u`(TvzSe4;K+۬o+YL$sQAٵk#'vχ:R~$ e[m[3"Jȭ\숅iMw's M$Ef'd#&iOBROF {=њTgnݮmA&ĕ "VW5kx#XZ|>iD4t TxCR4_8@3jSt'l+oP_R `+seXc^msj W#{ { x"hԳTc:?ԍ*f0Lsn"嵷>CG׽gL#ឆi4 ;Fj0gJWː[4`Q.7v1\ԎNjB=î2G~`) ŀ7b-MV,D89w(b^]F|,zfem=ɭa[Ovu߬uoˌcix_PK]uVe~2&setuptools/_vendor/pyparsing/common.pyiw6d,R|$:v&y{Ưqo+)2DB7JB;^Iv=X\ K(ЊW;$ D #^G 'Os؜1K2 <܉良ChD1 '^gw o3.{A%i,R6[ KS/ Sb ݰthʈ4%!e00rF}s/%iL-W1Mp$}C4-K2Dl>[2+>tE` -L9M8r@.DqB>L.xEI} \y!gנUc(`zhH9:`f18)&l#' U,:Azn^.]2~% u]X~Ӏ:|ۻ/)1W>~x~ܳ˩h6\!twgW`nn\a]L3ݫs#(j 6 T'%PXc"='H}(K]M7~DWA>!|%Neg6&G[>"U1 }+Y,婾WNk'Q"d+:pI[@@1Xmx ᫘)h^O7:vlbfW Eٟ# wU9qj0--*ɛ7Rp6Ŝݮ*y=| b&&ǒ}Ɋl38jR0st4 FDø&0_CaO f\aWih[k)uw ~@P܎+/L[嵶Jl/dn cciB`HAC=KYTd\ۖZJ:=نC"IhƄGE$/$B r~˩f2GvIrsd+DdsےVTҭd9J\֤ kL dDJz"+':(XBS:tRxXɗtozK3δYe;>O{YFF/Lg#@nD󑻯U'$Pw9bDh=F Ȧoɖ'ZWl ZBZD3Y%ӕyOeʟOK Ƥߦ=~%RX*w]}!:i&"B6O,̦q Xh:l5EgjrP\r`p5rshw&)x'uodE0u(af3Y|ȻCz\ c>~%d{M/jG1B'u~p@h{a gn$0w?>d>gv܍5ɿTwŠ6K`Z*N*@f(xM*Vm<4F|>Սo7U2Q6׊|B@9Pd]{%(U 9@Չ\&#֋XCxN ģYcUxs4f |Q+LۥM {x݇1|bww>d|9;E˱Z ~K2r&K ^85g/7V˔&j1-|"7* %D|U0<%$4DFYJ'$C-ߠU#~zC % ,hd U6r/1ZTC k-Rt|݇̚3+1,97 rJˏК@\BD٨eE`᭔Do1u+'6Ha59Je! ;DT&/H=6G96j@8md|9l/s+@Ot|B4"sddQ+DA̅6 Ya척\8}qړO?Jo/,ev~o+ RoY[O",QP %f !~@ 0nJ4@ t.|\]UAzOdE'(+h_\*60)$tCE#:}zLP'''7l͐ ^CE:B!v($p~bd xdK#h:FƆ*-uX?`r%k!@=03b(rdFr~2Y 6|vGɬ_ʋ${D\,9"?]6_0bڝou[zuHCN?z+0Y?TO${+{ۚ_MF.yXzsUz%͟z#(`2t*ɭs7$c̣EAb.8Ean' (Q0og Xt{ܙ ˲_ @~꿬^oa}ѩF{Vψ(de7_h)#˽+L9'±x~=dՊE/壸*g"Ǣˑakih۩!g4`Q.c hSPDQ B) ʻ{9pv>DGhhgO,"x9d mykSAүﳧ]zF&8 1)wA[h>؋)^еW8K܅g/ q9a4lfZr p(/ZOFK*ym~i[a$y'DΚXG6ڹQ!ddZouSqOۉI#8xD,kC-vAupC#&CB#y3һ""Z o 7Lvoma‰s ΄{|2Gk?ee3k6xZE@7ȁ/2axJ;yǷm.f%g/Hyz2M"O Wi> I =:- mP=_ۘb7 2 V9.z}QVꦠ!hnP/^1AϢ` NX샎g1Te{d24'>fmؑu`2j6$VCjY)SP+GVmѾ^ ENu^۹;WGbI$_stg"Ljq pP j]2~ZG%ӎc$A1lipԑzo@9gXfa gR='\Xx>/cyS*:MVR:UBs*]'*Mf.BInRBvvv&ӅC),M8e(V.EoOks4*ZbS NoF<t&td5bgxPK]uV<ǀD>A$setuptools/_vendor/pyparsing/core.pykCG(_ё9l!db2I6B{] J0@:7%I2`e~kAgxBf" #AMdzTߏٰI \LVWE8:ȡz7Iz޼k?҉;y8ߍW2|p0̳qz9OR!?ɀ8Agzx 2yoëP/I\^ Espa2ͣ]-,~<ы&35&I9TAYQ&G ?L*,DU̠4C^ w~8q2whj{Q?xۋ.n,+a7zxƣGtv>Ny + JiZ0(-H.nK*q$Q>p:. DK_TqĴ-lu|\H_4(ΧIVňhN2TWЗu,uZ]jhİ3DUt Hp-p2q?TI:M@(켄q`_{4cfI ^4Jż%>&փ;ѫݟvW?zy]x_x~8A498v~1Qw_?D?x.ϯf-UI/ ?d G Жj0nzהA3RO3\D5/,IT`0 3vY<`<:]mX8cҕV P4,\G\+H'iuqy!T>LqC9wgĥlt~ x2D{z:)r-0Π1`vfE FJ;Q63<", Џ>Ozt~.<`'9DaQ8RQ+e9O{_?fTg!"@szN,z!MYyƣ!7ٹ.Y̯<^WV|WQ^?  3!F[rkuz3/Ptf U WSs"Yϻ}tٮWٲ7,b|Ա3Æj`w4Ky=Ez=QLz=Ur78'œ^eEGݏa3|1 "Qb "5+T"@f}S+\䣛*kp擴oc//|ZJcGC8C[<,)!C|@EfN/&"AYtȂT2^]! UZ%,&g@p t(q:ʶFKkxh&T{S)!( qp+*,!ɾK5mj^+Ŭ1_`AaUšFΔI<, P-ܩkBZQncԠs8w Nu6.DfCvjR#o#9un=:=83%*&j<:h|y燨$M;/N@{'?r/'e}h磝WzVg×s{`M"C~{!G]d^d6>~yDDk.kd<#9e*pUq>e*zJnmuk%ݍpޚGݢY"Xp J}5׋8˒HV \FD09i^Y\ "zil¡ 2K&OB..W]Z}b-p Ris49M̦3a8eV4$FEtIVn^5dOk=K xCM, i#R6}Nɸ 9[JSS UW@?J|߫k]M vq)2>4xe{\cHxLG/U<"&P$'B]ERx8U岳"xE6&*ٌ E^P?MQoh6[ڨAZFsR"eRt90MjdDA7_ jQNLqn`YO%i$7y12-c Djcо2G}5GK`Ǔp0v09"%KS\ɍ;ȡ!55֜U4-/aAגN \Dȣ.t06d֕ECqQO/.Pa39Ft~Oz#̭udL AR Puv,VQH$Rf{{ J>]Mz$uEX&qN!szҸH)t u}EWn*sL~OCX2Ywcz!Lݲ2ji>J5x 7oԳa䳐 ׉^"tyKCڛr˕9]6X7j^%z)33M}Y¹N[Ó&}aje-K\𡗁s+-M: %fذ9 |`-|Y1֋\\Z4#9'8|kZQ ;KG~8k5泽;? ~qdo٣c촢u("'MXVjO&ǴtvLLgZV?sPVWBu ۨ%HGdh30b..Flk5V%5EONvJT\<Ȇ300uO7{ό(oN SUfb06j6?@SV,ϯ5'D:|Y2܋k]E߈XUyFgJ< [XRTދ2֔`[l-뉌 8V,N=3JTg]ZŠ".iFal%r6[6"6O#$'2AMYpEE H̀?uU nRBIp.xMDmW?*bߴkAjon>zxZCgxaF<J`gUU F?ANܷvI - ߣˎW.|䢄vz3ٚ'"r <ҙjze G2pci f|,O-йZW蠟ӧ//XS΂bwǾSp ""oφV{bW*R#KOA7u  4I!["ə…a^W_=>/W_[W޽x:|g ,.MRƙQ( %j %(,"}4 ͣ{oGjE#{}* BZXEPI]ZYl3d i,A;tX~&[ul ?jr6ѲGJr'$N1{>0 p7se4ǤmOT".lR=:ao|^N2erF <1$˘R Q禃BAXgc `;f3Y̫ɰTdzD`QZNE\l!qB'=օ*(`gvOw*!A3t':`t=Z7Q҃Q{/FF(zKP^wb(Df*.6̪&b[okpǛ8SJ޿T1mGxuŵt}& pcӹ5r%# 7;F9c"bǏhѠ:N:,Bpm 6*Bj%CH;cleW+ y,1) CAY7Tr5(- ؐ)H hq]RNsd`^AUO34<󎟋d I*9b&%F<` ~i#cHS`K͜5scN.oS'rdXa\TX*C| =K`b OwZdÇϞ5EBUW L *PO-4VnzY8SGK=rtէo+ =Qcyՠ*g6ύ2Ҙ ƨ5T%_fٝU?5dU2wGoy1eTj+jw`&#P.!'f|Uv^턀õlʪPh&Y1#",#tꪛNͶ^IL1t5 ==ЏbDZ~{N%wU9C|Oի|P޶ճPŘ%n vF#G6rK}tJ#ߧ6>J۟ %a^m2> C٨:KM池M뼤's?Ime*cXDGd( pKl*(2@a ]*~#ZnR8~vK!qvH>_kQڠQ(+ZwzK|;v1 }v=?E|կ25IA"G*Mx\^ dYͤ(%lG,Cy]:xx7K;Ipy=H I◜-zb@UO. f,F-uءn} V1ǟe8W=V __19x2{:ӈ5tpZzAÓÝ\?f8G޶[P<&`Rϵc/?ú9LBZvGc?]OH#T#&"$x,nA5}2P$kM$52qo%5) T/` *X;ГUשNI!>vTC@lOM m0{%m6Llg`,O4F,ǔ('M6)*Zf策hax!BzD}{DΚBKCI!(  /f޸?_q{ʭGIk&Ļ fTum֡^׸t='noiZl46\sAvf3nԤHvBl+Duq2`]jfm%1'*Kbj~tXH= RE7v4Nj?6Y<o#Kziڒ_Z#%WQ)vԖb ņb#\'Gx+px"N<~GPKz!p[:} ӝ;ќOIoHkV31w/ܚ8} QKmӖTzA39L{ gi'g5=mݐ,ˮhPE銇j3ik qI6Ekdq5 Om1<ݰLT, +1gv'\%FZn[#oHYZA/POԖ gQzeDV'3hq@X$*ve[{X$GW4&ttX14FzYW\bҖ0(?(50rC&e!\QB!a"_^auBQQՀۑѡ}!9D{q D.G+eU %@ ')1`- umL]eR5&X8!Cu{#SE؅H96%a(G%"%^W,1f<.q6q4pi+(s ;~}1x! d.gmGlt $? >F[LMZ!4:j\$1[QPpDq,Ռme\΋7)KRrUMd6$}"pc>&#k񳣾i sv%OewPiCePk zv4nOF;M[ fcl{ĚZhE҉ZnS ŐQI~~99cvLw虓s+y\G_TqCfgtcjGsmf^m[Sγ*@l_dNt^<~Q{yd}@e֩o(oуN)CoJ ErԠ9b2%GR+^!*WHCޤ3X :RG/kf<9=sX9"%Ruw#Sݭ9`_WA.8ty Ԛ d*dwx (Qrvغ9Wk {!!Q0,eTL)fH@DJwOUpWaZʚ 4R--ܲ#o³ݧ'% P#$OSxP k%3huE i UB>LNCxh~"ГV7>Lɚ"HF<QWؙlEFtWiB1gw@ -MѨ=̑@@2-,b!t=P$T5A۹"Óc ܉#/$e䥖վ<)# w$4ysQY C+Pȫ#}UtJ #hB$@`jl3fp:hG"#:EX5%x,N=G&\ʉ<ƓNMn4V}r峊㽹p4՗b1H5c6^ enW'q(\jmEGbW/="!HN *&[v' z4b3y_qD{̡,cCw]pK}!݌;t-n95'LqMs R>`!K*2pNY 3gY۸cl]hSP bX'~  4$kM1q{iMiXX|ȚI<>깋ȩH{F%D6߭O>ExvDc\SXŖEbOOTMo\a|/6zEϓ:WdTXO((.! b`gB״)uh˝l6:$1&_fƖB{L{ Ke$!w S;}юL\=7p 3@Ơ Ͻ(KRrͱIj4o+FHµU:{!)`)W5ύ fj'Ja@rYfP5m.hևU=и w#um/!+~zGM9ё7l q6fhR]h88FJ\WDc[dxI97_~O:/NZG~SY> m2aN|޷7 6c+} j}AT^; [ N)]+>`aI!B? 1T4:A&)wSfVafcÖ ӎ<G rgĴ[㪬?n U}J$aA }N^A{N LnJw ڍ:=%g=1Spk2[{߯[V}o؈-"P[fJ@ɡu>,,l|AMY=IPq9]Z q&ᒤ/?"+5LTHsЕ7y`RXȆ];'XIP \#hrJJ8s#|NU=XCa:4츪FSO@ dF+Ur6}L2,..#.OY?ՅxaLiTD:C"Pvqeˆlg";QGwJ"/H ㄍ._,UZMO3RDi ED (.'yBIZ2&/Wȩ/I3rT{7vQAP'i) Hù@ݕ:ց3ˢS Grs> ؾCHP.qΧ.KnGexD@&$ZT@AsHKY6>Emla a[c5>sE KP9@Db(74E-X6#eّTQB՚1(9A XM*:VYOFj3]^UQ3>!설hG`8'ԄLc|" 9Hʱl1i;Txbo6I*ck6P1b[w[׿kS02X ]ړzܣܦ1zlbj%_DMiA/OO-P0=-TzE`8A6x+ʧ(ZmEEe;:r!;&'8Փh?,}=?mZxǝltTp]["$AW9Yiɮ[1.$;mbۈ%h /ڶkz7,:-J1KF[x&\i*ˮ'lvd|.,+jB%:,G*#Fs19v]> S@~ Zm0] r2Rbb.CXA ~eWԺK ?"YЯϠ[S`D sz%x"K3疯$[Z^t6@SXm*]0fes!YA0$hیYZ0ҥ(n^[t';n-pI Wk:O-@p^p꾸FCbQ}]ulSvGOQb\-@6e]*=9<khBm[gn=:[W5A^C1/`wpD5F&suu,g.f (6&RN nmsA#vO`n7%#畵`7ZCSf(@X,`pe< G,[ʬo^FOK"ly3]h  1;@ס퀏v,Juj'|̭4;qp6鲦0zo?PH$?x,d-?ճ:3ѕr,^9 GܱJ<<46j*zD1 Iԡ@x{.;3Ǹ+RZz t={kܢBݶPw˾P-`VojnQ A \8b-/fN ŧXGC縻?RxXEQ؟O!00HDԟR끑{M.-)/?Nbv~/9wO:шZ8RKj{>%)Y7w~W;Mx\ľ\0 HGhoL~c8xb I&*@imT "CQ(!Na#KC=&bg3Rwh"BT^,LsQ*iv@T(^Rg)~L[l,w-zSݲ}Z֩)ܨWΖ_SnNu[긝j6 WIܤrUPlm/N %{B*yz*[2f-<++u#5WGu9}[lRMB- I PfʿCč!td4dn33JbY:1R+XNHP2Tmr&ӐYk\n(:;{C?CRSlB&7bW=}a'K,wi>f& ]G +=_%IfVY *@dp1Kg[ nr@kt3"Q% AYbd`eSKNTs 9ԏn*Bv U@vB8gvygE%7PH 6Ʋp[eFq( z$1PTh 3^N'+=4<|, nD0*k-hG>.‚:_qOEα8P!w)&:^&%9J%_$wMD&TA"ib=;ta4):j=vO/+347 , YEm.-8i5cՔrYM%TOZg@*޴[| η- ɹO m*v[$JC[u[`N0MbJAc$:_&eh[<-+ jJ/q9Y)R,,fadD'L šMc[{'#A93uaQ9/n>QQ}2bƀl9q1_bac:?jv;˂| 2EЖؑ0yf=s%MMqM~a78ܪ򪹲?)Nu2 J&Œ`~%2pn/@O_J+u홀V;*$ Km 6*_w ScC{+52 M(λy9L2 yٮaq7n:rDL1W7:n$JtYeU@>_ M,$t6S[OϟUf~Hgds#l>^i4_a_Lӵ<4N;19Gڌ)h>d84GeZěx9!V*ѱEvޏ^rvX'R5JO#(b>*>ɞv njv[_%-TVAt>N'ǤUUߓƪϛݘ_%%Om̱1U1 =OlDIb,,46U7tWh'qr(LYTȅg/ bXpg^se^ t^^z34Yx8[T2ge>-w/;ʤ%q^Fti:2AʾC]P/b.9V̼ooNub9!4ܝH>I V1yTT ]%7e;Ph):3 VLƨX%)܊%V,2&3g9.[n+P3؂v)Q;dM-Jm[WJ. |`:ŎZ;STr0PYy+ÞƂD]eﱊ4sJƋu`8k%lJjQ<98y=7r( Z%+~ Jݦ_Hf+bhaWg%Y./5A G ɶq ?3S#gӀ ٹdQr~wQV§ ;GGu &Zw֬)xO,0Iф{T`jy>䴥/](ubaV~El+ӳ1܁/"ɫE'ɻj%\̆HIVBͫ7-S@2|߷ ˼F/i-[Y2qZyƘZ;Y" es*#^VRdehU2b5B]1r8K=SxjVFرrq"bMõ`)(= c:5:`%!CE b[}͸.uMS ^Z2 $`טmZ& ͅs4hP ٳ)[^^܆ګs+oQ964@R-Jߵs.Kb*)=t.X)AN j,ćawrt54BrlexPV dXagh+څAT1\!a]>Raj0L)Rv,fʯ$EHHHV !0W'aK\DB v[0&*nILoEJ]up+h4ԡ3D{(ȷݐZ3{mn;kl) Ij̚bBwO2m6ҁVN4Y| 4v:;Z31I2NBh)`)i_cX-FM&kTbXL!{%Ø;Imv#('1DC;e۱:iA+uoN1jԹ|Xj}QSd{JTqt2˓$8Q6R7eg0t$Ͷ_,L8̧#F`#SU!1bkk}Yby&t!ljYE'$h>ojZ<H&y׎{pPl@ ny:;#̮f5PMj7d,)JLP{*|xZbV5x0yVO~T-3Pm$$1]FeZ)R_oi*GUvveoCX빼wi;&"K֩?SxpCXdkt=7tOGkoKyP~S\hy|gR%وywQ=`_֒j>/8 %lB#>,VyiE;H !-na:l!UI!Q}#j0O#Z utĬBAMHVbĢU+W|J[ܘM,:vbuYѣUk<ڃ @~X$Cپq9F]QbI} ާ)9`ۼym?Ļ~vw;h>9bNؗC.E>=sVSSYk͙XOa~IʬUaU״mO掁v=ۡЛp̞I2}ZmqH'tqXLfQXyycOIez}L"땳gKprPWmp/Qz>Nck+I_m= 蹖I0U9UQ+@sl~ZөͿYM i6Oq7:P\ 㴠7`Tq `9zYh;y ?56~L,f5D\lh=QK1YQWRAZEsW SaYZ.U_ԅ㸊iut[%"}5nc +O,)<{k'0P`9 (~'jG% WJ1. Misͳ8KBX?/{N_؊ tJ@&s"X j- [ na4ƚP QlA-w-$y]uAU]O?~} C[4ǫnN;[ZX{9\ &#va;*o+yA zA$ziYiCim"Y6]ZtZ :@<{[(}ܰrO*ʖYRm.W73$BԞ?tuT )X0>"1Hs~>\cNg;l -UzY:x|u=mmcsu~ԂV_ۧs+m-v̓B=ʿPSr5ᖕWjA7U/HI#["шK`rBH-8s{(Ez %S2 ՏZtef M"Te(Y>3hLcc.˅; WY*ETߝ*0ii˷ӠTg($s (Dlzu^`7`-ðbͯYaX{ ,[*2γ]"Nu4E C .\Hv!%(Ћ9ف1j+u8MN`"W wDHJDL &iEœ!~̰o3"eos*ƋR#`%>"K0AA8ESOzim( ^4ʅb)ђ6{Ϋlu!401&lɍ/-7G$j{PGg;^Z2EQm:VUtG늼B *i\GJ J[C])s&xc2RN9n OPm7c{m:ެlH[pm/+qdĘk\JU)*QxJ7'5!\dS{ ڊkA[szM6և^q-cZù w:W /H@Q &EO$Hy`bd:S65PЭcD=pܳ~~n[0O;fWHel4ɐ1듊 6f:&;}|b fћEf I/9oCMp+*jfG6tӓl2㫾w8,AFVlmobGݶ 03.:x#l^ACOCT(lC4IGPi[lV~f[j{`za)aĵϮ YC1h! 0} p9C,NѫZ$$yy3m1gTohJw8g{w~:8}?QEgE=t DK IQϔDU%P^d pmu@,_:&ƃ"t>U!8̩bn yV9l>٭xLdCw5gx̆S{2vLN_g EN:yAiꝴrVrt !`Q.Ss.SԼT83 AC$ ה.=4QaIi.?X29^mފNO=m*|vm}X/~!+lԡ>\lZS$aunpsҀ&ܬ?D5է9?Rl?T@YE+;WДs3af]qm WnD Á|CAR`&Q?[*[`/]Om@AbVj[:^o$<Xw5iZ^g/GpTwq< j sO bS{-zIX4NǞg0E@B}L*R1;u#̽*/ЖŐj`GӐwn(]Oԏ b}A> נY9{ QZ|QFqnS]zy:ҡs'm5ő)O[ "T1Jue[qeK ^~RLE !,Jn9:Q3uMf|\-_U6uj wǿ좫j5+oi {ntw1Fj-EԜ1ǠĪ f'{H qNKt1֤ ZpFwl}qaL%ťݏ'Gl݊$3TvyPVڴ S\ a@ `0'}{qD BulOkwLR)12Ҽ"SAV?rLLq  ދ1&9g!JRDtl() A&ήsǞλټ$aFYeBH8H"WP8qM <˞xLO1Of9.Dy).^hM]W/ )񈔶vF#l%Q2Mh}py8E7Zz~':q=҉WA 2] Hco񌖌G 2"鰖v{e#6Gkk~t1%ִ5WO6zR_;]2J&/$ontD({v3mP迸J5/Hs_$ g*vwen>fW_ɦZ-a` w>szUkq6φ Ҧ%-SRH 7Ӌ|rL "a`QW _Q6tCS8!6*q&s*y۰) *B SQB{r2\hi=E$mv[l(m'Cyol>zW_o[2Ih[U0E;)r8ud-'R "-lӁ8#+$~^._7aU}c 6FǔBO62n=9psVi=֭4_No㗃g-Ͱ0 մD˞}"{f3G͘Ju  e2lZH=c5?LgVG$ρ3jdv~{yUmJw{SzywʚxXYwmw&#{`ļ"0fVgͬv/mCBʐboᕆU  9Me3P@`9=+tw^= Ssy׉p [6WHI כS?Pl*̉qSbvڝ' )a.K1P^_F?]Y0\CHI% DL*[b S* ӝng(ka;cvm3ScL7LCnŪͯ]~^$P6`b4բnJtz ]1 8V`^NqEL1[?ouC~lz.kgttUW2)VTgf~)?:п0zR]-H⍼ V="*kzA"VCi\^v#=X5`::޳i %Dn_x!2]ZKtL=Յӭ殯0*Ҩ*vXF Nwř~Z]I_Gp]O. 7ۼe΃шpVm,@FNϸIUp1ڀ]]a{6I ʘD !':nRSR}R𮕔hU#N"w,P^E>Wxz2fb7A3EII)_ёScW4JA8<ϮdJ?^XXAĶs=;g5P M9Gvt;|Ll"l, ذGAJMxE?z&kSc)0yKREqŔV ˌ]܈}h^Dn+(cI>}rUUrQ>,3*ϋˇB".n1}ˏUMSG8(M<['X)0@e%*Q^9 +!9}R)vR R|c=?Wiy,vhI=)Ep1vC 0L dQ;#Lrc,I F';RSH[xaqi*\Iv8Oaz,ZzuqoEj)\6)ƓwNˇo}x_u:-uӶNںuҞ2\ǁ(͢4uޗ:>_/r_}y]+8q(7Y&=0SWMO__(y.#D|(vmreCyk9N?l֗⿚Ӎo;XHh'1O1_QdVCd֟;Evo:3/q? Y-dΉ%h0Ek!2 G2\o}ja8gCQ8I,ߕĪJc؁$/02S9]#)a2B;q''fiZdqY@-lS:_1KF}~KpGz(@@0jDKa${Bh`ۍ0="o/ 9le3i7ȳi2C||.\ofy&YhzTxmNIa)C)F)FY9niQԄMXgn@y.Cn^r,7=4jumλy%HD8TX1Yv^M'5o"=\):20(VMwEFM/zlVɴyЅ7Țm#UY]6 9.f#_p<ˆ9-'S+}KNs Hyv&p\r؉uk2K(VYނ PHޡ \]Ho;t:nԡ׏50J,.(~\I=1=.s/(F$j[uFȯE 9zU hV:~+NBN{R:Gǝߊq:7I -D,n?|o@ݑÇz_&~jWo[u pUR[Ւ-fgM/8=mΚX;#Qp;uE1gF?3sL?gA? ҷLlf=ŦJ\=)$gH.;jYh$ ی[?n1A2|ma15b%T7ԙrW)gZoс ?1g,Hn,Hv*0f.:i6'bXm֣<ÄH4o4G?HYgu/Ck >l\ :`oSV}.0:h4ڐeLe)IAIv:Y4O}okϻumʞ뛑fYR Ҧr"q*=@:ݲŵRQjΏԟkbsEt55>53yĂW-hrV ڎ/P@E#GWWo{ߺݶOPMHʌ(OۖG'=-)%D$HJ^'9uWl,3ZH`/jW}ņ&TvXŇiU{4$KxK2g; 0 zPbϰlh%݂♉-3[*_B *^)H7:l*xE-[(ǫڜUT㫍 \j"Wv_or'bgFsg0l1`',Z96>Pfdd@ <6WIA0E6uX;vmX[@f2[(¶Ţ!uIqITPJfC۷oog-#iNQJ,#O8IN?<'o«mƟnzFc?}'G1=Kߢ7/.?~O9˭JwϞ^>^ 7yT~_ex͗G)9jH9Oӿ4 |ݛ4_Ro^eȺHY;mZp*bǓJD5ҞU늙\^V8QG'OOgϩ,bG.g>G:g Bz7p5 r"qapnBiS-uvFK"Mרfc/L `T=DIƓm&JY]g`gF6枬lTѬFʡZPB)w[kdJڹjwIuE|mJ&%m .]qU~T1dtobnƬȿىw*Wv%GVg%Ү0 !/T?.b&u5+5QՏ;r ѣb!vw-W>*Pu8 %|ZmŪB*AP~N'Fg'dėLkFCFF{#絃^v)xj2]NPקo YrK3n\ *ZoNOoteqs@aIݫYRe_P;<wt6sHIi_,Hb-ФURʄl-I;n@7h6RAϠ+gI㫴lz&E,6y6 f$m]?谪O񻤌[ժ!W>e,]٧4.~*M4v6H@y!HZ$4KcyjvV~V_֕lR@xϸFc5DZLTyJ53Z=]튉}2Cm *% x HߚF+k(_|IO&/EВM( K%нW2ޞ R}ǻ'x>G&"1AB Guw\֘FT+DG]lL h c R]EYIg2 fΜE[=rМU=vϺ.3PGet0 d ]ۺکdѧR,:0^\u5*up9%EsIJ,ܱ ZCbKǯxsrkMKCiYǀ_:.'|Cގi oxš9v6:^\=VE]޺v8rf 9)|vDV•=Ň/Eg I>'PoJ%ٕl9>%bN$9+|WLdP$#e`푨?*y׮v`0Js8|6X 31=g/)o8>@X↥LY! B@'J|O GjXMS*S}xԍJ訏@-B "%`68 lW%aQRd9dTc-rFwHm 7jmgAT'AJhx]2ٻ_VJG ;F IfuRVI#ꩥ=K˔A&'x)q${i+cMB:!@#5uIǥO&i^9kMmzX] d/=(ErN^u:LeH:QyMWfEx} FKV:Bd]H_NoK`$.R0rtps~ENp*gn-zqLȫE63cѱ58?kWW~$`ֿkmf3v+Рܗh;Gy5:"xdɞ^NRfҎ꠴gs! HjDfN>P#fE"Hn<^EtK}8%nuZ_A i #- ?C#䂲c fB\nku;/Dt%^ꢬt-$!I.0j)C=fT'*6v ݼN5Ŕg2|vg4V15m\@54 -]/y;Ƶt\5KN&TJN؝>!o =PР̤nhRU(g u [ΖaaR fU9[SgLy姬UAhiRꑓUhEꃀO=dBifn9:x&j؜ҹzvw%1;2da짣I!,!z%wP/~ű ʡ&3` cQ^uy.|{$9a Lj2Y)݋-Hs;]@qJʁ*W$ }\8%;mҋ # \!/Q rY~A[f>3AuM 6;X3%_AyQuu8 Fotm'ʬNB s`АT ?~ L2ܢ0~ӧ=nCdXb8B"Ҕ ~s}#p/IPZ7iFj:\W4҃d>2Uc`\lG{#(<-#GZI;@MH*`AyRMsQӹ`-5<kr5`Ϲ ="eKv9Ŋ"..zg۳$#c`Qa=\+l<+'2m(4aV+h:: R?\O|[RSܚ‘z3<|Jx/ ^S9hIw;OF(-$0A3KQ2VkT.?9frmG(-@b{Q뗖\ՊkH1 XN V[Wm =67(TT>$CLvkә\ 0%Fub&bT31lI&)Hgz:[Lȓ^#y sQCk{~t`/C>nC6qm!J&J-*f7Pr4zkZKs=MP& L~!D˱}Ā?9qYSVCjv7O˂-jbv)7^*c6 [b||d( ڷc"VBgLˈ=@03tعR!5 3,$zΏJnR7؞Uekq Z7Vzc +,뭾?[Q`Vr1 Hxz5` `jӔY5ަ n3$J!+jm:t'ՑC#ot[8)iǭ!vMe 7sSа *'WP/oŗf'A%ͮR_n_{VݯYQ=b]O[TX- >.]]~~W]] RËrHsMy\M0& A6_|wg?D?{s|s|G?Ä\Wf z d}ʇ/^Ee*A Τ_΢SyVtA9hctk{nڶ5bGCˣ_T }{dҟÏݖ]>i9I 0ĪɌ`n oHªJȚJHU>Хi+xԓ%/ R'C!`֨1fXD5l ECf9;5#,/E;[\h[&;,dp;[R^ug7hs>9ls7ʌ_(][[AQm Ikөz̵ZڌSG׻Ns7.)u}݉ye/xsCӡ/A`(@<,×a&ϥw<*Y /+/r^䮽c>U;>;C%NiH54DS3=moO%Z9 y :p)&b⭪tUgmv{(xstejԧes\:x V./-KM6w2bIk};Ж^t^$Yx&('&Ά'q Q' E ;u BęEN, wER#)Vt8Kd6SߓE]2۰z#D-y,g*xDΑթmLMHP)ʵP4Pt8tKei&?+_}hyCp2okaSYvf@ 0' :7l *BfjUJv]j&7*Vkmy8m#U* @M鑓 Rs~7QuRԙ4A{Q. y ԵeRM\vEP?ٹ#Kp2ȁ>xWG7F3fF#<:Ȝ_`x+tܫ)ssY`n2EKXzEM[Jt&|-驩'Gݜq3':s clFof0⮼Oם]12T]";Ρʻĭ\*XbT Mu +{ f_;ᡸ2Tj:aX#u9,Uu7C?)ֳbb$.ܺq,oOQSX^M@֩N=J IC-XYĭY6P`p-Z)?{'Z}|Bc R|nSf>lBh 6%ME #cKCRQnlh5\ѺI,Pؘrk$qΝ훆׺0>Z HZ^cBA_%BJ2ЊF7E2^N,oƐrGk$*e#0^\"[C4>%]OR8U)LSl4%*J7G]NO/-Pi䇎 k\QM ); H9nu)ƒbǗ,b{ 7Bpߨ`6jБ"[@KF$KvZζ DY]jr|f^.o{Umn[HMc~k}ma)a_=,C)'xe-lq^g*ܥs]׈Z&Yv$mPn;^ݾ!ޜ^/qg+qM\Ѯp˨iu[[6g;^_$Zx-Y\_x篧F?RaGg{ txGq`ضmefih ^LmZ}٪+0#8!bkR:Sk`v,}f"T)7}(p.~Xg%(p1pJ4?C!S6F8㋮箏]xwv_TSmzgYΚN۲'!7n$Z& z,.$4 @B[N(v!N E.J%ᐂJ2ׁ3Q9ϒ.ЅSG]1Q =+={-AY&-(^ݐ8=.ȸh6M:ewDiGm@u*bw7cM1]B9hZ k|WLkh恉`Q:&B&ы6`-fSSo;J̶S[ f"={ν t AR&QV#g u)@Q\<@VNՍ?޲ R]Lq1 P]q/E6quNUxɟZrBPa:  ڛlY߰ضYVJ#%"qElF؃{S }]K˩a h1!%ڢ%R t"ju.šV󮬜?'HO{M?Ƴd*ZG*TK~f21UsƯ*EŪd3{.2"uì9GH#]D=hVsZbq=IwqGp4H\ S3N[CXH~6țw~-;{0̦S[) q3@r?49j5Φ 9V:$%$_aSZ&mW2JEM,g*5b5Q< zy)2dhjZqGQld2*$Ls^>mC]y,f:cxd<$q}$Jd0Bel=r*µk Pe!*rQQu.zÜ+()da uQ;(Cŝ^hpzo8xe1ZG 4uLF5D.ob;mHy9;cα{RL^䋟uuB꽄ʑ*L)DR+Siy+zgF&L:Isuڀb%/Q)bV %ٲRQ[xLP~G#ƓeU}d8S(z6zA4wl;,nbnHiWeĶ\Pqx eKja+A<*G ~]gK+ ŇA+N+ԫJ%8S+`b4޹?,F?.q{ggT+OͷJݲ*u*-wmy U㻝!mMdd"1=ΣaS to'|-Zsӷ`BZ- V?VM/:<vu'pXO`DE-Hi0I_́tE|䛯2g\DAՖT/V`XOWl-ƒiF[c} (Qcjn-,'dH 8`o_yqY<ڏ4Gwl@]xDLdVߋ:peuv/@LAsyǓ,^8ZTV;)7`G*a#XVRI)>ϳy|SXU6ȸҍrmT]OKvCX!̹̀%lֿ\:tvܯֱQ]hFfγ š=ZYV6Δq-業ܣI/@C+/:a1PD*:]DR巊 ]GWzz'h%PZwDt7nG SA1P>&E6 Ԝk?Տur8d:nuw+O a~`[ZdptpŶWbS˽@vaLdB [WXq2qbF]U <1AuTGF *$4x'VD'5?7<ֹV@P#eZiG-l4$`e'fipk(luVinԵ F[ hUd ~il8ûmRhw~m :P_Z4mnn:˜ 'DY!^] l6P5?'> L q(d SN}r8?Oi~lOAZ jTd9b@HYNO%P:ʦJ!1 nOu)3Cf,v6[J"P`G!"NߴnK!Ւ +:gX"BtH_fx1-L]1cKO~:WcJ SgËi;j}diB3ON6g8+ rD)S)W"15iه*I(ލ70;b$hG蛤 @C}Zsgc?6Ӻ- |_H+Z8E2y\\Z>{!< t Fcx4p=G$;0kwn? +x< _q B6 qb6DV^ %0ڹml@m!72Cl/%p yl9H&i%TM ތ#X)#GPq27U^v><FdLfNc Ie-Hi?3-Y,YXVlO4zO:˞tm[:a|eu&U?@+BHƭ-X`6719<,7=]yU$ˀcјCj[dֲ%rItVwjs㼕Շ }C \+\a--T^9jjyeUwVW`S-AD:Օ@PXZB==A8vhHx{A|ꃺ ͵@-QVyct܎g0+he˅xrjzCOK9[S0a:/E6CİML-#"ڻ7d/UeJs4Jpҳx"(BM2 .U<%%S2qJ-8!p0hgfWа%<91dH<Z|.gٕ>1:G9 8=zd{wF3ϼ]JC("E0M/WHz;aʹ`7 FO몥Z`tKA37$k-:C2:N!]d\k OpFa{@@š'0hF.)QTX*>^(jnl1iN$$gT|Vu{/I6YYȤ-9 =JNjl=sgЯ$q ҩě`*2ੋNcDƩZs:֖n5LUH%UٌS!+.:ۭ+ g)}uaJobG9ȻB@&b;ݱ8X6;~[k ֔ܣL,fe0BTk+I@$>x a2 5+'k6F؞,Fup 4xPJ¡_RoSɟ"a$XNO~iAC1D3N3\jH'Ƃr& ڽa Jl(F^aO`ѸAD n3-_xSM+AZnR*i},Tr Z-,NIGRY${n$G=8IrHP;t u% T;%Bjk,S80B5A~op0NLp4=5ժ'[Zaw=L?بۡ3"S" x7Ѕ!,҈0a%sYrS_dtOZxǖh[Y@"h1Hw*hQ-7y4JY7vg䯼6mS&գ倪7ʹ,OX$I,U2w) /iJE(Dms&^-n%5W& W*ה  &뉊•N/Y}=i]# NC$WmMT/c+1#sNZ4%S .xNxx>-6Raˮ Ҹ*0jcIBIaF\,uD|1ƢLQm6/&1,Ӹz1*N~$8;5x*E&/GϱbY' Yyc, J j8LgDl_1rF}Pdpl^p,"p[]1![[INekn9~{V 1P\w5V`>.x I 񣨚" N},MH%ɐ:P7E:Fxu'D a:&NߤZ)րG#+z2b(%}_4@K%=рLЙǎEw*ޭ:K(#Z,=b(T_ r:S9bQ1-[AWH#mu'tFЕqI|dP -h{>A(JڌTc͙i<0ʼi1Ig%TfN"dSH#jQr)iduiV,E t-} Vz`w$ -MXydś-svkWIEq1!F@{6JӍ֢%۪^*Gd٬ʬT7z*;N,돣)0vb$O@k)Q3ZYdѱV+~Q , oǛwlg5kʖ['}4TM$v.q奖5}?"Svr8=}klT=c@.w jM0g!4RILϿDؔ\jHu%0־ ZGՃ}Οl 6ZF&Pat yB٘%2RD[P{pE+$5ITz>Q9- [rv2k<~& 3Ha K Dgl ͻ?ۿuZ*@N{OFڛɝ=>A]Gyu99*o~{rj %oPla٘rP7x{H\I?j؇^d3l݉mQ<;Gы/8jˢ&[o!w<&WЅ@s#1Yu,2&C"r,Yr_x״S{,tIo+]c lʈLr8KO&stMEJe֏y[oLIQ@"zyvGlgZ+dV'h9i-ZVop[OZy-c_~*72)!|~9ebO`|$NEO(hNвK u !׻`)E&[@EJծĺi(2 ]z1dX- ) 71: %yF Z*"y_]0,ۺ~g5 ! gxt)]J~cCTqP"FV.H `̆[n;4S=-5FjV: v(2o(RcM?g%]nty5I嵕+lʁ^wPn4BRd^eZB*SetTȱL\7 s8( «qr .a.4R>OIB}2&nLqlȬ4_cZo,B*') -( @rP=8N9 N/G%Ս@,@aS26 A ð^xDvrudRԛ!.-*t[٫T XB5 [5Au ];Y-:[*&LVDq}mG[h;vҘNSDg5NVuAFpsH,!FP?/9~: D?p_@O b~b!)9*^A忠v|twD(L*iM|&="y c-%0Qz#q9Z~籜+hEix.~ .F5KxDS1-Mq713$bey*grEAcW<-%.ABpLIGFUC)=W_F?:;-8d3(Vvz;{}{~Z]-y-Gwݻtan>ʖB(P7P֪Vn[*P[VS7}qŧ|x(Ɍ]dWcw,%0afe1%íM9%~1yg 34gcSQ5ȧv7Zl:NŁĠer&($g %KP(!.J03BS͋M; zLj 9> @;'\L Qϕ wx`FtlH_ ½-'o^J'Ri`k`:͊DKS"3w&p_o%IW4JNz|@GJk,7;tXPK]uV6 t ?#*setuptools/_vendor/pyparsing/exceptions.pyYm_iKiX 7H:F jE$%wf\.%|N zt33L|HEmJO&yYW0%'ݣ׹N&U,nL^v&)+r)쯬,Z$(ؖ*Q\n<2ё5Wv\n&IZp٫NП+dׄ \Χ=?_o^Eb#IxQlJ͖w!&v{#.HnaEGR@V8:/D{F?btS8ހ =RkXЛ٭΋f&/uvNF(X_r f$r`,-AMjgCJGlGk'Ù{rkƶ0KI&>X{bZb(6 MVen) > ׵nQѩ CF 6G~ b ?-OAL9DŽ$W' Z!I(Q;JB#zGRBFÐ$W80A␨ $%WHBR_dE|6t!&WزՁK\_M'dWeIyA  ѾQ-бFDiwZ^TŤ 4ߕu^W/y dh.qc2FH <žbtU$Z\vb;On *k lv+Ib2[h]R"hc 9 dTpS)Xzzʳ^L|PP tq/׽ǹݡ}c`5[X],^\+'>rB~c{ĿV a+֨?L8^!qFsֆ:=+]Ѧ!]CY Q_"WeP 1~Sf;~zHo%cQ rYݬPB*o PA)V;MaZ -*.dW-0KOH(*Cz;U/*gfV$%oP6bwj W-5CG=>n|l/ԖHQS52k۩}(N 6VcQA/I)4@B)2ÎX ziE;h1Sl]⭪:XU:x1Mi ]uk jl -^ZZWl+L3m jDǔ AJvȏoZWZ E%{ZUSb<%OþCL# Z0/W,}`Ыe0[ /lA!Vs` DK:y@( բ-N::B*2="˼ڱOv+7c7#;QQXb5#kK[ES\U,@mZ=ÁSFclAt=)[7;EW BXn+l߂Ϟ,NŊg3:Foa1A;)$j?4`OȮ6yiZFtƯHa?17[( \T?Pk="@$X)2*T'$h@Hw8\#H qvW;=\z5[=%=xx]{{ѵ'M5@qPK]uV*,٘'setuptools/_vendor/pyparsing/helpers.py}w8+0irU,ϳwwe\ٷ$fLN>IJzvޝ~%( B |"nde5\{bYPuZ҅2ͯ{yY,P8uiQJhG43'UT7ϳed5M2.I~-MRV^IΊIi|h&b&trgiU=eY/yZU];"˳L.`TD c ;-4#1) dG4) .kd)SVv_e]\IAX!wc g %@"bN$UTbO7)u*+tTII>*1Mrr|&ȡa,*Td*KbƺIHj^|efJbJM\ɫ+0Xr5r(^ *Qtu-y$ ຸ@KYh@3u?.o|U4[9w)L٦ ]T%͠ {T:qJngqnmG ٗdh? E9 lyTQ4\"gL0Hd2NH-V9VL2Y[l̍Tb~Ԫ--ү1Mk(4؊QP/ ɌEͤ܁,e2C 2ˠ'_ۦZ dL(_i(Ɗ@1fG@P:3ݤT*=t@nU6ZG}˲H 3- (΄#atkY҂|)!pg 7iOf4`ArSC؛ (&[(ڏϫL,YLfGZX_<(zf]6iwě$Ye+Ai2V*Bq"s K1( C- 6\,s `P י1Hg8#X1QB, ]/ ЬpTtF C.4 ܁#N' o/И?p`,K4K<ܙSpG88?tL=+##Ӿo@gj7Hl☵( δꉉC "v ȳb ´ϐȰLXdʼn͕uX`9C(aUvt+RGiVv>t?6&c(FE|ui΍f4'`VȺ`yDQufZ00<׶uPFH >Ū~a<>B=HΗrZgkUo&cԪVfGИ9 ά(nQ \G|(pi@AKSӲ=f1* ,:gEM]k`# \ `tnTtE&JآCT ,HBvr)ujZ]fz)FW "Y3/ v:oD-^Ty24rE&\7橕e1P[(oKyt J9Vk0/I|ht׊jY5bc۵R`5;.EE0/I7ҕ"8 1!-M%%D692Y%%Cx9D6G< x}E@; &I0,\Ac= a0ACkP#\΋ 0"3 n93@̓ TZ1x%O^?];#hy}JrZʄ^6,E|V9] \4ѡ@@n1 9VF9d^cKª3iR OA <fUŷr}"~cAotzk1%H %:|`{Tm@(w%]'_8ίeUܫ+S$:yN H =Li9)F\=Jk|#tE:x qq> pM(ar $v7GR4vIG^55 |:ktzUBВ*2 0G*`/_u8kCtDs%'@{C CPˍa2wƦO8WW wLSIM%E%]3DÄO-*kgW 47.2.p 1Ac8>ωP+7u Yv=xp^IVEm KE GMH=P3qz4>9ֿqIqq?EA0㉚):|ET鈊SX7tN;Àxj+YaUssg4F/ c3vPDBlc|`#TQk$I' (h+TtMv_* |bzSܶGeK2`൅e6>ORX(Oa$j Үq8]u[5ۘjC@+4GC.I NO <2?qk(0}(oGu35t֋I&lh{qiuYQov>+ %;mg]0) Lby~G9;_WIgujYX1h6|S"Ի'PN:"8C5XCl %tFHEY,nO JQ+9Xdl7@Ј'<|PaFԤ™E Y@@^ S+.nR# Pm~ ot"_=H}w iLS mhr8˨!^Q?VvA~BU'`ܶk* ygX^ujĬ۝1fS0,++rr7oߟ8pJTOu"tmg#yqk7##H}ňt247FX:ѱ>4bT@nxs7'L, >Nf`3#\ esT2{M8Ilj!>k"j[ kr*$TkTvg%xBS\Aޡ;J> &Xtȟuy#ɒՂ{ ¯aVܡѼ{Cn*V~:u/mڳ+%…,Qdb'0 bGdf`f`@*Pa^袴@\- [6Ƶb9 r=ܦAN9[\ڰ KASH(~s5bUך\ ɍ[ŧ $ "9R|PVYX]n_B?] +RCX-c] ,Kڜ 3DST`Q%hh{U>W+O :L D~CAZ~FD#'%k1w\158>wCn@ K?QԊ=qw ި"({$`e@*D ;t1%]/ m)E)"PMޡT1#`ϬɎ O,Ħ2*?PJį=n3)3Z8=Y!KnU 0gRؾzGi.r3ST/F=E^y9%#;ߟ/vч(52IȥCA6'O6m+ `JW6U RӁ\|ׂ !,X%wT@5Xsrϰh+d2Y.yMT 05Fb@e3C!g4P<%V3m2\4ʍٻr1p*?&ó=$Мi)!LpLH-B,ŇB#S},2047qgY1 YPOl 8M]1mW|0wͯ:Q,a9 D*9p݊PnF8]1lڶ6ѧ _:Y5{S,ňϲXv k5+6Ve@NW5rFԙm<'^7[Ya{C;d%)|}Ry=kCSȋ"G%.UF0RƊɵ=>/ޡ#%JN ˩D 5#H!1iNM<+IRH`Q%>S Ke0!z:ΉE9s>gTVюHXP}`kiےF ĭ5wT6>L( DF_]@۫U4sj5M729_aBΓIuQq6i:8+fByse'vaS&#[cOqo@a'g uqg~ ]óghC "e.j̓>{1}*@~Nk0 U;Wch59X6 % .=`CDP'mqOdSn£RX mU t=B~`iL::;:'Z$TP ;DT mˆ:BY"{3k[m'a4{p=ghO=G=O)r ޳(GGTnܐZ1ik;3HKOZݞ^9q{磂^*5aYәE]7 L|z&KUu8q& "od}'e.6(39inhY?/T҆f:M.VMRu%loT9r (; Dū,|o>:Њrow`!\>\ hŜ״P|hC2JFIr1Rʼn @yV̊ !r. nv)}Yw.lc&kvY5`Puz]}@_a?-xR05e1}v&1\Ɗܳi<3#\csx -pj*u0ﺢ4c#(C0yl6asԎoXS|0C^)I߸0+P|B:ms^ Po䗐vAPDz}?re K }B8xyw3nI7^/(pvP99p:pq?z[vpvrN{x@#1NUջ՝516'OR#Z]u*0Wh[=a k%>`b3Lumcqb6H,t#(4ƑԜ h98lޞ 5S= 3l*rcXJ N٢3:CU>*{VcPzUmP&$mdG(@S21&E1/~Q;(AƝqoT|pw5F6~UJ' ATPܐaxǍL>-aąwj7ĒE|ՉS+N~lhԄzi}nTjBjC`WL͊ }Yn CN0dJv mIWW4pcpGlG&| ֫\}D6G1*(7^Vke5\.Mch?9XkZCֽ1&`fZ4I3 dpI6ut]c 8R}6 H9iV7ӦbڼuƐL':NoS_C'fg7=(=hYPr uPųʵ~uxŪiԁV2s9 3ZwMY._a<$T߇;Q6n+^-cG5,WU ؈D;Qwwp P+zb> ? D_t^D¶xvi[ >M!V/J0!*UHi[rvÀY! ':ex)6OC qᎍr<C7|j-4ufzS'r0&?Ec8iAg=FNo=6h[v-mIB꫇ROPpQE}.7CWk9K6+V []^ZؒJX@6"!KTBWcjCy"d^.߹TOm%_+5hvvQ+Bʂq`K!}Fn?&)>kEhҥ_m4B2d\EѰH;]-?iðklبT2^d߼Zw3~JmM 9B#y^Z9HDx#"<ϼgS}[*N}^j$mdN>%\1hݻіѤ-5o ]Y~W #}S3^cPFFUE~aNgVn.8NZd;pP1Na[t } X/jr+#|'?F-7(٨Q0MifPݮ׿aHuY|ܲ2+oCVz.|z| 0O,*s2iD[܎Y&Mu.3(z7+_ ӱ=`cO~AK|=)pn ;x2FY^px12Oť=}$xLmpNd2Jo&.3&|qiўjWg/TCj{ޭj)q[Pjz,k %N:2YKijufyLUdMPX$yr-= nCtQx!@sX᧯IS9Nɮnp[Qc(4 O-ݸScK.RW%H9~wF!XŜ}tdy@ӥ)DUB:]=nZ_c{$o%RzsF=E)]Yiiɯa!شv@[" !SAq+eY5Z66K ƃfa5Hឡ95p?ZMKݧ*L܄^la#Ǖ?it*ڼyiz ۓ\;IUM5,sHyx -w6wsVF35wggݛX.v+\pIyBv;<"h2y̫bBKz6H" pש=bFMiݕtH=Q[C~&1ޜQC !pz| >YΐGI5R o#__q6DʒoAkKrew(zO`s֯'䢽5\Q7K6n ]K:9_2b\RHB$akܙr} }ou J3tbZ+"[ř6f}4\BPs# 'w00o7x7 IY&kgyߕsZ;9zzT:V]*r+o晞o".mݥ+.T'΅{=(h0'-EO0/l,=鴧v.TOy:Rz}nZI+7]uSWh\};R()?ື ۂs@YѨJ4uIPK]uV^b'setuptools/_vendor/pyparsing/results.py=ђ6XR"m<%L]nN*>hU%B3Q 5M߯j8Wu{TF4F`M^x[yuVb,˪fmd&}VF" >6XP*B﫬%l+@k"#B4 +b(3v#6LPձ"oy!QPAMYe 0X,)YY}f#x=`ıZ1f?Z\PA qVdu盈u(⃯'1Tf[^g5ߵ3i*ZluRstH؍"2n],_-Z$ FǺjO5/E.پ*Yd0alǓBf|. N}e딥I$Ɨ 3Q`y(r*)T@<Ҹ̧2b^OfI]W٪jɎ\BU9fK"A}w'@جT3dqoyT{YAD8!"bv0:Mn+7N(}@гC3R6{DMQƇ%*C?C] *_OA 7 P/y)&c zOш#Wd`dY mJv&Yi}頻Dvt,>;&.PKE*(b(;|s#)[yR5EzRq^]ͯ7E=b0_İ0$MVi€@SdC\of&Fu(j|-x.yL7T (*U:6fdw`D`\BaC{A$+xz*alӕL0g (Ņ ( ؃m9R?Us}UU5 rrYHH2')=[VĴ\ıѰ-s3JIn 2[;+j鹑|}p6@@Iedi0 UILI;Ҭ21CE)]v, A!g& @W!fE䇳Ӑ<5W٢Π=i>pAZU 3P_h>n9x~1< Mf=R h5W+m9L((mBC`YRlԵV9;#,O@"k]1nnuHS48uvG?܀@cV>HCB vGE/^_A3tPw'll` ] ,Սĺ{%0AqWhlx/tٍv&-^fU7~C-M*6 *jJ̊NxU57 &$ C=3`&/b6i!fNL:O ;-?P4ZkBbvƨ"g. NDtϛr)a,]K цx}WoM#> dqB\),p,GmqDLdLLA˽cL?c%+|m21 $ (rN:5{L.^}2@pKD~:[K*,,y~شp~cQb!0Ջ6ޫ_Q mFZ(@OR =bѱ Y(ZP `S\`W<udX3tђC?z1@9} Oq+Gad`L&J-SF;nNt'N7;s YdVuWlz{!#{W2ysZ4MVI̚gSӉq@L/:ahȓ5͒|*T/)noy* ޻wu j蚅VGX'4%a48|+N7t#&AʠBlYI6U{$ϧu?^| z\oָdČQimjzõ& AG,{e{ҶW~kn )3ύ'F ")goqXk V, i?T]d #26@-Ҹ … DZNqSZdm~8KkS`be8iWӌHqTLp̚%{oepc+Iq- iEXȾC퀫mvAe䯯f}6Y5q 2*5Nn.nN]\}c,v]'j= "ZO&U<.o`V/5 }FP*2_Tu+jy.5A;`VQd;~y3܀stʽh3p?h^i E6 uBK]4?8^ټ}A"多`i=31(E-4n^=dqHY9^/X~/?~W߉H4@N=/R-0} 79o"k.팳e+u7dsXlwFDOtA"I$q l@UL#j)]o_OsIoߞ#j KOg& 6f=$r4ʷ,:`I^y8al%dx&X#ʟEQ G)hpp7Oˬ]sux@[|+^m ~{L,_'̀FtFV<vaW7D_ۚ;_?l5M#F/Ħ9H{0nomCGrߠSUuV )WD )%GCJj_r{+T/6z| 3ǧuQ 13YV?˼M;~_LW rqe_t*~R ] xf튼U.n8&a,:?!YX\HI&0"[)5ld^zch<W9]7{Pؿ&-Sy;_6CkE)-IaDFuH_fjZgDŽyg׽`])1 Ĩ$W(VW9#WM _e#-,V4Xx|y`=MD;ӊ'56CM=)Vʅ8=XǚAs,'c=Źs1G֠l@EٳJr5INnE6ԆDE:s)MNgx \a* &Hfkr߿Oy_ө^KEZ_2UJJI}2=f;؜[Q2hW 0|JIG׾4NQ] ATҧS  ]-(ˎ(|{ڵXM`̬_Ȕ?emQ :$Af̂=o٥n%qdrxc]%6҂K˰POj T˷Z%6*w:-z|94l|a_W)2,~J8W'#?Ȥ+_0gª0gAi}α`}`rvf#*øm]oz/f_Jƫ&eu<[UIu|-kN]2[hw~ִOLY:5A!i:@Egza5FϰS3"x3C4J`SٯH}5l±*]çaGw {@X/hS$,VQ)VlUMF>C|{uƆ:;B3^j Pj32L g?ї5}۪lͲ.e 7VⰊ"1ջZ81{e[eBe,wϖqm90@4BHWe<{:`$TL' ~>imgsINl]@iV 2#smʆ}"G#];-l<*OtjoU;;hN:O} E\TF+˔e,˻5/r[-DDQ6ڻJZ}xrrTRK #})gf37%Qs7E~1A6MJxNn+*&_tDbQM[A14J m#Rd͐eBnwǒ; oNh ߒ4myꦂz1.<F+rϜmh [ hEUbxŘeLJHES" eM]b/gLowA Y-wcKk.^OMIpBY6ցM\J2Z hE3Z鞈,#q[H^<9{CтY 2"?1;*B͛ V l)a_opbxOS'Q^i;FN>r^q ^pW¡+XY( /Վ7)ų爞p6'WtNNǧB;A~QpZm=,F- pr( PX 1M[|5>6{$m}ywF?|/|4au~{{{3mHyx$=zfMs5W2T vu] ՕVÄuXN)Ke50rcG$1͈4HSגHҨ9STn\d85q0DecP٦ZfC CVGVk:cQRQ_$6F*BMx2P% B7 vV*ᦽy,2+ ;/^ TD1f~pa }}jq!-؀|{+?e`}|"`Q GƸ( :jpfhg#(ֱ`eV YCWCg]WǴ _9lCo!^ʞ@$w\wX$Q\l3VDNˋ*]26t d 6 ѣh /ـ_~s- Kr K YY&+@XTwmoh)Q@6d)Y^^b#V/I!OIזs͓tGk@N:Kd*z} "%BhS)/!ֶ;3}*ÿp) KZ)[LǠd$a :[T=v g ܿqI0( L'`1jެH]@3eGP~ݤȰMX*6-8R ǂ|,Q7e )k`8JF\L?qlHfRg"DqS82#mV˄4Blx>=Vo`&H4q̠ҝq8ҵ/4sv} EɕSV Z"'|A@>= A[K  u-zlwV' 0"d j+_qU~ͯ5 _a0TbA]20 S%^ObW4i/<̧Rɳc\Ba֍{#OƫBW-j&^$:2x#nm!:KOUnr< :{׃=^3h La;iϓ+T? #H$T=<ܪ6$tމ'@ph `4<EǪn2ݺ-Ee5v6p^ҝ"Tå:66.PCs1Q4(h8<|8 -Arw~Y~x%OZav/@|W${$s+w[,O5xMtk(Ghu#ϳ~"J aP%!9φfib)JHغnUΔ7 x2ViDE23d3wqwJ>uS"v2VrIqs 1`55%1EAB KE8$C! CGMxZveӗBBxUkܿ'ּBi͎$9Fv ob&%J^?~7Yh6P2{"(xwP_1A,V>tS-|aX4r['|/G32kr oH$4)~{NM#b6WfϿg׿Y<<C|ƇwRl#%Q:L\-,޵!exryƍ/HFSȳGnPČ f,TIlvƷ 5ƫEƻժl3 GxIq~4߲|N?X=_x>xL/}]s2 l GfLj*3^gaH+!NJ)aܽԱˬK432ﰞ ~{5W˴C زC. .HgCntETs /xs2̌kѻbn<ȷˍbK "Z\eǦ?s-S+,Ѭ |92罩SS~}`Yy¥6~]uf6 T5A uqe X+1 cXL>_/l T>jnlHM^`%S7 \sW%'9Pq׻O@傧[ 8zk Sͺ_L sX&cZ$L.=/%Fx )job fo`ai-wSw.Qp9rUۤH2"PK]uVzx #*'setuptools/_vendor/pyparsing/unicode.pyZj[x-#Zx<ݙYMU  Mi 4-M[$@KRhC~_/|[UU?5ǻ6^ig{=9ܱw%ϱ-;[I$N$X m!NhaDRڎ Cm l1#\9BDa^v񜮗H$, pWWm@ʮOnFHN4aCGv`ZD3N?7Mgz~H?7F{>~B?MTd4Û Zω-&z`}yhsl]w[1 5HI*j._(ʕAY=1,K'S87 lq{+=X:ܒC[sje͵ܶ87;ձN뷾{|Mvv'}P;z]PӇnmFI8f:GrvfXX:lY ,[qdc`Ȱe 1#g #J7CSUϞUǀj߭F:ְkڳYmTJ?Yzzi')<ʵ`"W6J%IQyA-k(:~u)U jRYQGEF+R+uZ1vLRVe*F˒Zè7$o V0SaC,QB(KӔb^ƨ8Ds FQ&Q {+N[ll^S;h)Q59TeZ(v*IZU,X+i&[+ZE@@1 MHC jk&Xf$o IڰLT6OL lbK[{X3 l!M/6lvuz;sԸ{o6?mCQM Mo&Xgeg-M~)nɩ&*"L,<ձ "m ?zI{-K*RP`B.a".$U'_ԉ'`^0gql{QV+mγežDU%3c/䝛M9>ϑTmq"70uG$#/>׾8}tyQz|z#<õO=\{x>-\|HX~{7P~a~tuU!饫O/wP(؛aWuҦ yr'g>c_;(Mə7~i1#wۨ#lwn]XMcZ}w깍;6V?Xzk)TƠ~%PK]uV-Gl$setuptools/_vendor/pyparsing/util.pyXmo6_sH#lz_z&˵@qmޗlf+*I%]r.:e3gC~$j:qNR{۾cKRt5)i`gt!vuhsS'LukD6 H6 .Dz0> HŊ$IuSd˽?uK /FdS4S[+w̰k[(0P"<)evÖ]Ygшk߽S=%@J2)fپAg܀VĂ ܮEFkq+"?9 xdH'EvbSãe雜ʲNlA[0Fe=XH7G'zQrej'>{DQ\W0kE76ig>R3څ 0V2RY'&\Z:=a;NNJO06!7{ejBP |#qFdDya!z |WLa zkH]RS|޷"3H]N.$E$QHpV\JbiccF|uSvX.`yv89hs'Vaz@%WNn-4TYծ1Kf8i@j*S6'ް77?s(p1Oy٩@HW'-ݦK'}_^\>bg<5H ݋yVл~:wMkQx 8QxoW*5#X$!q$cQSP eh#?+j:V1)We·`RBj_kj#~a\7 ej> =}hГ&>`Is99Yl;MЕ _;KRbXRIW-*:FlZkCxpݗu-N)+릯KBD~qܔ`@wK1:ʓWk۰ك[ $LW8F6Dsl@knWM5%  % F 9]¸ûk{W5 DeqѢ<[Jߨ~jQ'223L??j.9c߈BP..Yc'V@$ߘd߽|=z-BV/ekM*+m^>t;;gdmZ=RfWFȔv]mƭ3!$ѐe>)F]~4ٻ8)Ua|knlO`Voq5%6P2tǢ)4Xvۭ%"ڹQyKq>rT >.5\[ _,5]6hI켧rG7G.n]y".s: ^_5G,u\F g Z*E+Lu`LSnnpR +?l޽u=cbIo.҃!InVvU!|k'XH= d<0N18Os$mkuB@KARx L/O̦)Sf}Y=*MJu-ϲmUZ{򗟪1(㷝lWj~Ѯ|)W:$}kkYt'''<ޙs6m!LF>y?]x^e_ȼ|I}QgO_)~^nWK$:zҽR)F~g"_ULT:/?GUer2FR|6%lFwF'0&jEYmTn.EQZFnڬX>1TU iWu5 x=f(.jUA L:BxbLktD!8I Hɉ40+YZ''䙨J a'eVmRH2rDl:OZ)f2a-U#~ӟV^ybP;ٝrU>ţU6jh"t׎'" bB^_YN 8kS5 3_yF G 7E 5arT.pq%m[1/XHyBtb(dQ,gYKg;v3DD@}yySAqUCx*.y`8ǥ@ӨB9+t~GDi0e ʪ`rt˼ bHXXms ݻk 1v|iY/&;ܮ6$OAΉ 3G)o e s7VvR @8"Y,l_mcu̫ޠoT4G4UiWS$Ӎ9aj<1nЩDG^/65'BmjyK`_7Zdrz;e)#u #H/WOO&ZA.[znnK|@ySgq3QNj yfs o߼Ss$3nQuzh׽c ,?W)*4o-7xq}AV P~F\NvߠDr-IK(EF&]]ӷ=Y"띻qFƬA'j6OT )An[0{oJ~(G&37n 1tQqR,@ RwyNsPz8 vu϶$M4f;_zxVિ1(À{4`+:DKLɁ12Ki9hFduTfP0sFS+~aùUr!)fARo~zs6lz݀ #^`Q$C] U$]ٍ%kV{̦" rD߳ݭfFRyL^R3_;LkOL:c" fϛ\Fk@V&4\*U톄t^Ƴ2S?"- W6eU,EYVc^iPC9b98Ƞ}|` f.U[qx1XvblfB33>LR5E)[ʈ,iFIJ]d0gAk(4BZlZPNF}$2b]@ g&фZ;gvl?b@Ų0 -_"Dž5"9"C?P7ðWJ?!kgGLY7(2Jt"=NǑrA0 p˝g5rk0lB{<&r'Fnb.ˉ"K7ѵB#Y`tfu)s}FzssXMrL)_Ր3lH VĮZݠy$Xl8HA8s\O3` y[J9r[-a"4d]B3@HPD$/4]I(q9ܝc|`{T,H!TבIP~: 9=WR]Qt!C W{ e9 TeDnt9n 5,2N˪.;9T.**l}9h-R``]p !>8Mj\xq<f#ygݶ*0$pѨVrxg y{>ea5޺y3c;wT9LDְDyDC<']xҼ۲\$13 % 򸁣|پ%z~~qDc %C(z^ CvN7pH3`B 73]b ֿpޖ9Y94:4`|$+7Vݗ ]؝X=]km/U>4;@a&&:BO+&&Bp [Urep"DGx발YH8&"I9mOw>0T=/d: ݡ鶨iZ`cbH(Q{"cRtN)\k꫋ʱi֦ piq`)}F||}.O[-!^| +-Dy!f=};iϼ=燁}&^&,q nEԶ)yZ'e\"7AR <7(T&Ʊ&m: $`e 9{9G.Fn,F0OXO}hpRe:U͠)?C%P?MU{HjS]ؘ.xD i kg)ZE"@١jwe)Fߍ 7 4FdXUl:LW_ F,>D<Y|0HvC a߬shwV&GJmTc8 6`b5ĵk6LX6uK)riNEY kjKeJP3VĶA%̶\*f)Xߵ/\b:5)GAF6Զt ȨaO.rq+,Yty-Ave#ߟHQm nUإ)QgJLK DΕV|LW%PE`nP12Nz4vmBD<ͫ1k}]^RBݒjddNm*Щcݱf.pmh&i~KүPt> |Ѵ4ᆑ' .nj-b ̦RlfAk_=Y1,767;jUcsh19qa/E XKMfK_2uGt:ƽ}֡r^LZQA~;E j'|2zt ʛ\v-f8Sa%0 x bo]?X jHBhؓX=;";rVTLc?{z-3-~(v}ǵ҃)bN 1f&Qs6U+#0yߛCGgY9ڡOi O pAi)LJ5f\A)l6v9,*غC6r15?`r?SA 6zӔmkQFm۴)(bܸa^TvYțK9 F,P>$ӽE qwWV.Qc?sr@'# K!9qňoQi+-iЯJII}la|O|hJ2TBx[~gmȍט.J8pպQRI8>}B{@e/x*+d}!Y!7 &&|Z2a0;=q/ 0W+/ &sN3?DPZI? }ó~qǘЅaJp1ۿud0Bxц" h DZm{ǿ2`q GSIvQ+@߀{՝7_q?rыV)˿~]\W?} __y[I x#;υI~ĎԏMo;?e((om6vX/#f$HO$giف;z% 7Q9^nLNѡ|)ݘth#:XM 7CA̞cՃ>5m:>,M;vMW :ަ2HٻzL>.#"hyW0}&nQ=ޓ YNې׺8kuAE5%ю Lr|K`:?} K1 q";sɻaNɟe)^27\n#Jթجaf78uh-Y%BԉݝݙVSd+W~^)`^ S~ˏW ӝ r۬hqʒ[AhPYk$cUMް5^;_@I\MAC)XM%tc4*w}惗:>߉`/PK]uV%$setuptools/_vendor/tomli/__init__.py]Pˊ@+x `"a= c`2:]wttUuWߥߓBd:Ϛxu.s5-lrunx ZcnڐQg/ћ kS,#0QEA k)񅏰1`b.R*mE;mC4@bQ dsHN}/="/rq>gpf ֲ߫1Þ |#^uԺWu(oSER6kzҨPK]uViiX#setuptools/_vendor/tomli/_parser.py]}`y4 ieI0˳8aۄ Z$^1]Ypej'(μ,T0>={3_5ggOg֓翌.ϕK7ݹ,X0yf<鵺pC{|Apë=\ġ:2]XċnyqUjvtnzl #/M9=|-7cxwi>,#P6z.k3/ {'b& Z Wc~)Kc쓂? #HC4) :XzLX9lq{#lg f|Xt{CݦH /jb;ނ=$'!7P`!M!1 d+]q/J9Z<炒%*$yQ]k-P4㶡I/HO{_H C3z͈@3l&59`y&]};$NF6|Gx[}h|*%i/aq$ m$ ֭lL+ V[.`BSgLn/kDĉӼEF)z!Q bג!Y 3@T룖ܤpg+o&-D6"C"2|jgh! :AD@0="O7 S.OA`%d3f?01:)H8DB)· l5!:,}2A0ՄiaAdIy -}Kb`D]m K- Q= AΰnqMu ʦ nA7k`z8QJ@@H:;ճk`عPWÙyz~W~CC1t!*ɣ\t$R=P6'tMmazQb-JR;^;Ԣ(k#z (inelat"09B@J$zTLWjR68=3Yso 08RXk$LtOh!^W#]yy&QNi,"H-ua`rbc/ o@E{$Cj+Bkĵ [YmUFoi LIbN SuL3np_zOEV rE~<ٿ +jL)UcoRBC x b+ʹc"X||GRSaL #+jHRi[S_`62TfW Ƌ|.=J~؃ \ %U(90긂Bƀ°_@Zg [5o,ӰZ 0Qeb Ԡ֙{G5ɨ^Jz6R( qj&iIBH2ql&/yz6rBhL]?=OW1H&Id``Xz(SJd Ne!R](j}' DV%j]*!e 93sulGY )oIzs/㵴/T2whP,gw[ ayz |C^62enkZ<蚺nS.AQOgǬ[ɠtǁq08|j6&%h̀Z ?)tć@KCI7es)jMc&fW$e5V%,}T^Jvgȉpu7L")RY[^u%mh8xX"d2s]\ )BNDDž k `<%!^3n]jvn|u(6ri , 1PMƼoU9M66co ?83W`eU2ȇb#ʓ$8U!Zr ,eǙ:b0vE o{%$e%2)SڲgVUEzl(Y[QR$i+!]4{Qm 2 -٣@5V~EMJ+fyC< >GS0!<,@5;fP]?^0(-؜E0;zH=sq2'ďˢw,uL.6$gK`G 9Hc7+K߉8L>Ȃ='5Fz)Csږ襢^!!Eg/xz6>*n~mrS(8SMzݽخfklR-P e7آrbϦ+oy^+,[+q !{ ĎX̰AU)֠vXԮKIimwg3/ (e F&;cY,}ةrу"pΑLJAf L:@d'zڪXB=;J*G&ywQcS=bpk1'6ڿ2^>G~z'AslΊmU0z;`3oΥ%Kj4V,~S͢ZvYD&GS#筊lT6rk9FnI^ڪS HdcJj4s3j|H 9'ApsکB3t L6UO+HRm)z$^/EQW@0 JitnGziׇZ\-;hI)&=EYشItob vbsB-< 0v-$j͢V.~n@^<(漢xmz]؅dcjIDsZJ5vvҤR>;bѹ`XvЙ':eڔWgaENl #= Sђx2M4#%!֚3Ib5}83l.GYmX#;np&Q)mFfpZTl -biUը}2l-k.Θ\r*ZM4e ɪ* Ul@.izqv]%h*2V+@u.YəK-#թgcz۾ҿƽ[j0V eDDP5>duXK-?} /&|ڵ`9'?&He_\I*{X;VjP yУ\=}Erp&4`DI|*>x&Bt mGL8 ۡcS!ݫ\7%zT(UA?5.7`xas/%8=f.]UϸOϪMv T7gm*:6JY87}K r,M>`u{C_f L$RP+k:d:Q]^=1{^FCx#ݹWd}@@P#{x_Oi3t6p b E܆gJPܱ$>}Evˊˎ٦~n\KBbp$ v7bo-yVe,7  E{ޖPS3| Qu L)&C"֕(c3*J :^ 2= ޿pyUit1oKqRqUm&n׍xbߊCW)Q΋#_~G,_ Lj%ZCHW>HĝVF›fv,B?'ٱ)7p`Y'[sWJSЖo'ה-ǻw?,wym[o㫲J^Ԛ:D+'^ &a// 1t6,Qg)O =0ʉ;Pyk7l^ɦC"(wlmm DξH` CZ-%C/[" QQRV}%'=LH9h4 ND@JKp5O왮wᢖ4\><a9޺r4zn0]Mwz6]7 c\@1߬KVk:9o/PK]uVu1~ setuptools/_vendor/tomli/_re.pyVmo6_A(_T$gka i-yHU *b;نaǻuo?~svR\<1 8Ǘy/fK hoyKPWRDubbtAJFǚΗ5\FFEQV(BzC(CqYIcYZFo 厔\ˌH7Y]&~ ZGIKMR[ nocZ˂̀҃.dw&~=ͮn4ݡ15w;Y!ܟ&%ρc{bTx^B`2io>qs2vȢIgoZ'yJ3*zˎC#DfޘqiAyjjSqV#{n&k7e4NRSs<]^uӱOIk$+e5 N>|:p\8փx>I3Σ۵|>hHՉL@(C;7Ӧ(3b܍Ec-hP%YVajHF3Yt[BrHnFv.Ra|+NӋé/~y=5.j< VR0PB(1y*-Ax=չ1*FFѹxH=˚ᾬP)vuQGb6"I[T˲oW l(hTm'[Ђ.4hzAС@y>A[M4R ]Ҝ: 3B>uM>JN$ʲ++fu零Cm.*|6 ;γkijFlVkB^&ozA7U^]zAht˕f XAĀ<7N ^s2epo 1< \'PK]uV;Nsetuptools/command/__init__.pyj0EK AP]w](Ad^$r Y339X*%䋞R&Z=O ZPPjQft|Bōs`tw'֬#wl<pZ* Mn˂ s~y5l Fh$ORq% yq/"8Rmrv7(eCoPK]uV=/M setuptools/command/alias.pyUKo0 Wp hb9 0`40[Nْ']}ö|P"H~Z*tYr* X ~8i&g#ژ(N 6h[«D[Z1]laQtYC(h DUqဧ}KZHh:"{ѶϹ ͬ,qHdqhs`>V;%t0ObrO'mL'x E+{̵lR@FTP2|Ok1i 9,{ @R$~ReY+$Ϻ2)[R2 ks(.X3SH*b~=uzEe20AHڊ''.v iϱ=2LlapvG";_;l2Jx.?fa;RcL3MM 1if#3&$s.){\ 7t vIOl'~ђGϤvxӱIK3}s^:W&vdO pl3CJ]oT{n:Mr!=K\Xs>:ɸNOᣳKJwdc:#CٹwG [ 7@T fxRY=za&y~ 6$$?.=NZ)׍)չ0)L%?iK?sn͂ MWUHWb?/Re %D/K"zhogϩ 47bG$"$˥.$1W࿆edoGjDy-8hh{}#l' q|k`}qEPK]uVe\@setuptools/command/bdist_egg.py;ks8rWrH)}Im*s9j벵Yod!1#8$H @DFoA>mk~7e)+1d|[P%Kac_mC6"j۷{j6/>'VXφo|u=ñU[KhXwq5Op~罸kEfwz.C_3mpYWö {m-SZ)x#Gcy@i!t :<bhBrElM$%8ܴMv{$cZ|2lu_􍁎 e<?&-W Ud|^KI?H E"pR5EpPdGYu3j=0;DZq:3yu#-9.8 PM yeE2vukwLuC3Neب}aL 8P)be мkgX#+pE siL5CmЄqIo$L'faw&Vvq$,c t0%Ɋ&i֣(\fF(2JS_K5yxCKmh{ U^W_ ^o-#*(C?$Mۋ2):EI(V\9 W&Zzfm>p^e(7' _f*tdUmxN.cZVf +VW(vF8-|q:6~:hBAxN^BgAɖtˁ}QKI"9g !ccZx`ZG3KL@vaE'3H;磌iJқ\dj)o-"p`5}pĆ0{' rGp7YwKZP6S=+!* %CEnr5 -z;bW~TAƩ& dC\CθdFOMţux Wec  C6U`_Wz{ 74(7Y 4Nw2[\Hw,ܒag z~}Z3/^5sAXҼäRn(5Cti]' >`0h5 DDC9%jTHD|,a=d kv jZ=>cYQ@GÂ'sjbŬ!% ̲4 殽vJo 7=~ lŻ/!{}O~^^^~=#)pdQ-1Pp+axrѵ΀h:.jkQ3o{Ϟxmq=ӏwyRCNz1,N/apRlOȋRJ&ڽҤP+aa ̀Ɇ ʞ@D WmkY%"W]^,CjDo8fJI+4=T}w+eBO}7ȟJGc@4`lS9V7(}"8-FIHWn'I&I+,xC vN z|D.UJ\W&G&KT({(h{6wVYS/_G#L5Xu]c"輠bHld[.(uo4Wѫ$b9a?R}{zOclTYyce9#p640CNIDW, R/X"!%L:Za'=[2O#y64.i m p,XF`J!pnƥx YE;= Кihk4֣U?7q=6[-GIgqK̥?0uj){op̶ussoh|,Pc"'K_Ig{XN3!6 J9ミw⹞*C%.~ [|azt3?̲ 131)$ۘXQIGP7)#4(9_jrO@맳(xk>2hHE@/g!+k4n, x}x\_ӧ*z5,F|(n"Q3sO7a1ؐCc{R}[d!NTF\d0V&1z"QaiF'fa'Yax%2Xcn`?}<_B,I[/ ᖊk/9=6yh4:tce! Ǡ 4^2ß!<1h6oeYr9{LIb凱]3_ C7}eo˧+xRFd;t#('cYc9SQrx`ta!}0C淺x GZe )%r"z D%t 2G)jd ;z9,5w9&J^N 9,HaHc"DэԦ;Ue|3 rTdh3MZG-^L­%Hδf* ߿k_uhty[":Dg.}Gxb**+\5Qq`C\yb 3P 5OEo8JWrfg JgD Z9YP|ZkRby^ežr^e}}K.jy/CKϵ=s j5-`VY`!vGAr׶1 B+Cw:Gl ;4xT(/yvpַUXƉ9: 8lSgsõf  *-aSJ;]wUܴtÕd g ":VnEџ0H @B-J]df!+=VDǹ9Zu#ީ0f(jI7?ۦ2.hSLD@lݘjy4;0w.f[Q&ш/8תrBMXn_XZ&~Dzw:/[-aMD=V9M ڇsW#.wZ<èQbv*3j%f Wd|o8I@ֵBTL8}>Ue?DݚŬ>vt5I 33"ts5u;t~m4IYFalp3*0u8TdrkN2=^$9,l%EO93 Qn#ob t`AVOf\kOCWǣ}tK* ̌>kk Bq>x"B{WST(qj%b;M*RVq%XWz]V=.`cDeA:MIB&3TU-0#-3,n/(owK>k.WxJځ<$J XP} *H]o7NJ؟-idgҗ 65GzvĕIz9 aNH6OC:}N_v2\.hl Ϋiцk%&nt68Px{9D(ܷ=L!r24dv#7@xC=X)RыB]^( XxO*qb)/ Ζ>Zj( HXgO@h&QPͤt X'/?~ My._~.Dgfhvmr{wb[Tr'xPۻ<_?K\=3 <ޔ.n"[,A;_'a#u.7Ϩn(נɉٰ 5X䱚0W-Nh<FvٳO*lwl[rmW,_=ӳׯ^^Q.E\V>uH= euiv^ׄDr}~8ӻ,V600;ekov V'3٪䪿x:G^QE H6䧔I("{9MTNa6&r 潚n_ȢKXZWH,B1V"Rm@?(Jd.pT.Ёg p0X;ų&8L(SHNYl\RӃWE #jHRjr Z1׭_t͍Dms mJm2U^%R%&N<biNqQ^]Wp4PR`T$enS3ϺkV2 Y&aJ|=Tz j]Y`êJS]#SQ&ѷ,y\L |C?ul'xQ|}ZhS-^ (UN'k>tbc.H 1H0q&aU7zƪd~x:k.^Ş:0ǚ5fy 5!uZp ӡV#VhP ^!pVX#&,q$DcaՅZW^S))U$;.^:)Cc`W -.QKr# JwG[Ouirunq;,mJZŸ}ig7Y^xMA-=jVWWgmi0`6Y(ۥot0c$3{JW7lt; Hp)ʺN묿[J>.Ej"_}}UkN!\m+=p֠ ?{G|yTmˣpDV;eO> FXCMe>p$A7p $0EUDtjbP>7o8 #PG:OmY`)Hzj|*¶ڔ{tIICJm gs4]ilZ{ې,\}/^P/#:WSn6&}CTA?){@F#c)]rc Ez/q0f*ы7xaF<&w^^t ~G,;cp}`-4?* 96FhUv6:kS<\}W~~z.y4ɼK%T;F~2a0-c2 N( FWot? 2 ?[ k (Ju 8IRնnKW4$˜݉Aӊ*N?yTBJqc' oi殱5sAл>E]h=BuBOT# Ji4)K? `XN@7P" i{8~^7 9!iNzMr]0Kp+wC"!O]" ;a#n ^/|x?!J"eMe8^\<N|iAԜ'̺~Dq&߳|@^~%{|{PK]uVG setuptools/command/build_clib.pyXM6WL h@MMbaf"ڍ}gHI֗MemsfpPJ/ SUBJ@80VVGk%Zk#'&2m(ǖUbgF3}-}'kzZJ8Wh)@Ͷ+g^o,ģhxIƂ7P`v4JgOU}W )%>9/ CD]\]*HZ*LvBO4 A`C?%T"<12DR Fm| qO܁DG'8!ZDe=,iTIDѯB%( J[iQ!H"Q9.QDTU{[ޗrR " (*J}RcQ2PA4#5͛[ aG{"c+4ҙpq;>NJ {&hTg c;yu'iҮ$^{JUjұ.0m-6 P*̲+.>]jMH$`jf:ڲweDP5+Rcmхe k'Jtkx  nu#,9YS?˙ty7MFHP>@%GΔ,kL|חÆA= Eb )f慷D+!ݨ#~zsuK&@J'.)7<9SjuMnKlԚwp0obI]Ls{"OLB-Y)8)&nxw1qwOv _ {'.Г( Wbg6Po UMNaξEhp&Ov75\GX_?_)"oK*xU#YJƕH4DZ}?,~B>N>!&Qvl4nvb[zl;DzoT9Q|aCx W,[2̸/US,xDY{wɻTVu,)D&< q`O43'5Kz\HS)7\^n_\X{Z3Mg , 4{rxe) gF[yqT7R7DmP /Nӹ!}ὖ "Z(yA?&Q_- IhdIiߤ湞-30l_PK]uV}]=setuptools/command/build_ext.pyko~FF@٤Tnq4\1S%-E{gf|vТ_*\i3;.ݾnZVYɣZi뺔uS-UٶDsc]y.|z}ڢ43)NHe5`\4wyiwBhWE.udo ?vRFp5de qpޥv`BSJ M2.ua:ֻ‚,F> X͍m'%$iNKGZQɢvk8bs^[֬<[]׍ ߢKVEW r@+#BwDy,K!XG0]w`,AH<мu>5;cBiVN8x3 #i*8e@fgl]#IP ^Yx/>~5_\!U+f9m,եr .6C J%oaΎ]^yΛCQ%M$?ֈ_@UȺ`_}mH E7,e[~g [66l&X5f&I`򗽎mD7@QL|ery#"CŜwxѴn?_XKop~꽚l"9Ԋ<;}1F64Iy+bP$J)TfQ\|CpR0##e7l|l:1%2>\UA͞v䓻咷mE>}{Se0?DN24-In!'" S٭CA c4xe_Oh3` >s$}fQ#`.p'Xh}BKd#BSQA1e%Ѕ-W I\D`kMD~#rUz}K]\ypHaBFR=dJ%8K} ԱNa[h|G瞗jVdx.y-T*x-[Hύ`ݕ%H<.g,Xu Dցy, 8J1;!?D/uQ D(KtF\.%!kĦ+y3繴"iK>Ii# _5a0JuGf'{t=V ҭZ4f)+ S3g>~)LMJABh. [xK;d@&$=t6G4w(%]Yʁoi dn1Xiդޫ0b(쉹p/U-EO~@.VB䒂p=r:gC㴧jaQ,+&! `S;McD(֮Wp *Df1$BzjeHll\WP=CR&o>T̓).r} )Yp]@/KT%\$:f%~8j#';Y!\P!Y BǗ^xLP6^ظ 4q?&(\lGJE0m8w.Gz,$*'vog#9J3omr ?m-"fͳ&j cPsMm9MAne@I&d<uqN[HLöȶ#@ nMYʇnǨ{"w3!*.٭[VGf5+j XꍍQ= WcLѥHPiY-%8z>*Lb{< eɁKH`_h^&$By0pQϙ-<"$M!_`"X*z'%r])тA5z2_/fgz C j IѩʄbU#4WKf , a`>4GSwӶy;zu,{9!"6 ʞOYTwu3lHsV)~0Iy"\WÔFrٵS]~폩aJykd o?{wA]<:Ls9ԧO7%T2qpLN$4jw9niЩN,~~)K&~*}n xp!K`U;:J!AA@E|r!, )yQ67bԖ~] e42`DW’cʀfsJ&eyFDSTjP=o4}F>/ "ۇa9t ~ 2kz6]YH_lâ? Kю|xDó(3z;:L;րU5,qBQEcբJ^TTYä b^,KigiJT©al*+Fk^BM҉kn'' r&ʚ:@1Y GXpbiseaUc!s$%? 1^"?DH<@3swteNlDPsPƕ`R +H}zbͷd ˒V9g1{YAL;5Gw+HɋAK%Ah9yu^ݤN􉝃ɖ;bJ[nq̐.d@ y^(_zZ5K9%V=uT:xA'ˢ|<֘p;u `&R!͹pc8a0 ːCg-$vx;WYoxxqSOP?e-$8Ksם<ۜ@E)ZǸDC=͝C*?^ь~t|m FXu :{H$ͅ6eeԕoJ izҏ}Fuٻ?g_"R7Ay9_LeH)$q)Y0yRت:AXD(Sr .*}JcvGe=uOK4CD!Xp[R[Oo.;!C621VOr{4U/䪛!ua?ִZ (?D9A~MۄmMQ;m+;綦ufGw~nB_YYKJoF89y$>zڞw-{/.b;wR~~U!_џؗ2X^3tjutخ=,8l\ɿ1uۺz¶W<0JjQפ~.y[GpD=}ѨtXu;L!aj6j72h|sgNĶ$6OMwr(Ds}>-P^m% 4.M(HO}QD"]cEG7^mzEJ@osϱ9}+ZoI~ն zcS:oxPm +N[b}(=PK]uVqB#7setuptools/command/build_py.py[o֫޵(PHݶXqE dZm6Rw$EJJ6k!͐wI쇺蛦2BڦE+^bٹ̭6dM}>oemȢ9d]fAWeޞ4lO/<^=lw̉k:O@n5^j] yd:s \7=ЕJO}muSj#>m.Q#Y^Saeþ0TWgSߧ4Q(^:#I/ ? Ԕu (,7 ЭĿHu/߿.E%8`PTHJ@8{4 q,* 'lB*8%Z4AAɡ z}RZ"FTCz-5FߠN!Q¹B&6b`^Q}M%̀ŦWIFiqJ6A `Iz^HS- hA^VTx6PO,7{ Y3,*-p<ΆT [w qԷĖmuЍS'۪Oٖ҅'Idv]q:/nc@M4K%>` ΀C;cbKܝ{ЕAyN'hz_j9̐}_BE?Їa,:~%" 98\?'wϝV,'49;5F>0,8;}5YR%$d6l[ŪDs;UcVFLN,{7CDj&]q =q.!l!l{ed,.d.7fH`\rKyEnG ZإAuh>SK@7Q4[5`7:Na|% aN>4 ¥#\&vgvk2`[U@ ]cjnv Pcw'oos'0,LYFɛ&#݊8ji+ 2~5(R;"|JO-bbYTvVāÍ}P>o 0 1yQ F=/57` 6$֡h]ǣ=P:7 ̱ϱBLM[^Tjl!#  aWWAv(̾Fν<5"*. BoGu@ \l /wx3>_8ԆU/Z)4xamxT̛4ђCg5LgWx+*Pi0f/ARop.#vpv_{ X'N3KEISc w[؅^6y񐧂<1ɢq3aLL`K^U+x7_d /8W(X< 2bL]^)$|(PBIНB9}qL5trC\mc]Ѣ$A8q1VG6f+Q#{y =?D/ F EpBc< L:* -Ds#'<Ӟ+ZB,ʵIy3 dɱ4YJâ]WyMʼOBP0c6xb{,Śj!\"ïjy9P<՚@1PLL?o/D_5o \VJ蠢rQ8l-bE QU4 Zh0o#Vo]gcJyTX%hDa1 3LRN2+Daի_+]G5U`=P6'k$8(2 _~~n,'&=1Rƅ&?jqlXq{z7왕?apΞq̀CF'g] +Щc۴w"qXHo5>xuųz܂y+`^΃~@J}<,|#pkGBsty(M 5J#D(5/Nכu]96|SnuBcL N쀖d>xڇeh}H\,O ޑሿidgKN1vkk~[@gwg, 28X4T& Fb|k( PP]`Z86"TK8O 4»Gȸ&Ո>{ަq ,?(q3>JPlvaȲk2"4ie>>5#ܞ HDKoXY绢1A21ύ[dYcƒq ~){[<`u0"r- z3+%vkƚܱ;-H:GB雐I=Uޞ;-=uGKnX0r[&0"059w TfX@bN6FXWwXf& [=FO^*•ep{EE{W iņ'=b^&k $pv[j12"w.v뙪1$އLbsj Jf]i.\/2bCD91ݛO7.0]RlOIJ|z9^?j> !/!;.!0瓃YkVֆ%Q^c(rXtiA?fѺ26H"ihK900|Nb~?Ya72cg]ʁ\-Y\y^+فuBE|Zţ[?vegTw_,=q78tfh Gɞe)u"~.l^tҋIF}2SU'C+JjsJ)0(ʡ?MJTk|ũ H M9Ύ,~f刼o3 N㽞%f`BŎ oR4-ْ^Otؽ4~ο{RC'چE尤[|kyX z.._͜ILx' /PqL )'"f,G܉3 w`{hmj kb2O^wm;XO/FAYJ3Sg?oE od OʎZfh18>Aoj471CfYӬPQȠnA s|T-?~_a8'zHca,~xu1w_"4}5B#Xyi ^ݫ=zɷnBڲ^TBg~xwe>@j {x2 ,͡ej}E1@wU wО0q r0v*V\7T),K-H?B.9ICq M-`-h(ީSGyAsTŅ 0~ٛyp-Juj>4rs_=UrwŅyyߧOXEPK]uV&ddsetuptools/command/develop.pyY[6~( $%yA@ohhYTIj<9H${)A~ٱo4q]l~kPY~ƈ{T# 4NTpIދpm0\sp:F&QO!hLP^2)D "ݝ+k)מogs򶆎V"t?UeTPO(TZqAX( A.zmF,ج!Pq-[!662]%|K8Ѽܛ3܃#V?oj+B^Bf'Lf; DPggcn~E~( G)7pJ 78 \1[i)g1/`LӚjinRƛ\YySRNĪ([7b(3*ͷG:Ē6l2Kk8f-Xei!7h ӁAX̀T0(u0u]- 4~(+x7TZX`ar*Aز XW]e;LԘɍ)J ;廔!Yt3c z?Jr:"B1߼ }=jmSؘdSP^1R匥 u+)fWi4e7Jۥa,K}}JB72(eO8`FXnސWt %LVWXk4 RR7E~ŽmYGgn%3Ŭ ;MaUت, 5jľXewoV$3 qVyRVA.!Vh幂x|s!c~;v*(2SC1:`WL TIM+;/.~mgCe =2ߟr3amr&;cN~})j9sg謱Vwc K=xk/oS ~ };``/ro :'%#%BkxT*.ƃtzE>}DAPi Uhp ƅ>nf0IhvG΢Kh`&ς@|rم8UDx05(5kg ;ώrO\xv*檦 crp+n̘zdrkV3$TY.w'hPkPb9Mm9 GnKe7vP"0Z sn< >?qQڲ9ni4p;0z%w%lT-Bf{Eh]ױm~B^/ ^ÛִÁlfg~욳WȬv5,-py7Z < 㪓Nߗ_ Ɂʄ xFX]}nXtո~W8W2 )a7ZˁoVrWG^'cgPK]uV;setuptools/command/dist_info.pyXo6_2[ȺeXd@ l{h2hDj$ %r!Ż}>E\1jd\$<)v3M  b6o+YW~P*Kɪ1bօ2z CG5Ж!0 x5^Y3eH2. Lmm8aIrG|z?Ob'b?fס?0j䷽& T%˖w5XNZ`͠@H5=,3ypoۛa ⑩* 3F)%>V P=Ng', ԪQPi8$~l0HQ^omoN)8Pb$/S<ՈkboɉEi0w&,ר""B'- hQ0vvǧiB8uW$J'nj ¤5jt@9TW& VL`ZDqQ#T_8sف!K^+kz#t,.غ>a.VN 쉗:5n+vN;[hZWW?⮉X {bnssS?D(t?[D*UKn{z}49'tB(إ2yEÊ0rVώB0-B/ o+ж |39ROiGtt#nY3=Hrq0V99I\3NE`; 5bO7%ƶDR,ƥB*Sqi=li0|`Eu_}aNăԆKmbֱ 611Mr1,n \Q.c /6Ri3w~!O3n ZA nAaVPK]uV-][N"setuptools/command/easy_install.py}{Ǒpu(KY&t[[Ҋr!9!Afh_1 $= t5mrj`o/zʿÖodvټr\L׶_rNPty¤V**m˛kYՇj˳UVbZ6=mg>6?V4~W-"jɂha ځߴ|)  l=v^gl6s/WnTb xRjAWg=s9C;] ;`MAO“fGvqY_W>< RBE]y^V'ӌﻺ(Ho~Uc{,}xZT\ w\e4ŪLoG.J`uRkP:dBˊ7Gpals{2p'!؞ ?f;EC;M`n`HG4:㧣e6kG?\{3`r׀O0v]+vey Z 'ȴm7`3U&fyW!K6!3Nf0Rpi>fuaVR}We56f3w-W7m7[4[Q^ {l*E|c ' Wl׮h5kl, W UGi8exSroE^%lZzk쪪ĵ)@ǯ.p5rƄϲ'4F[^pt Ӱ +|1.ܗvfr AܛvizM 沆#ͳ-e{l_PfCKƈ3;  .x_ĭh5O08[XQQvTe; iVֈ S7q>M3Q.WQػY¬eȀcO 6;7 6pxLX9A?ŽNh 1$+bԴ 4 St~ޠ6;ьVTSbIKea(T %cxLdizӖSuR lU}&mi_u=w0>M]xa^4 岂un`(/_|I&WwU'КxiT ܻ0;QFk7P%>Qg&u]Zډ fPP:Dlݏj R}78ǟdKe[޻fuyvд۝|[-I)bW"a*a6 ~HVtD YԯW+ @:iZDkul_e#}Id@jmGKmlG8!á^4;\>Ȳ7Hlov&'@jΚnkU3MDwtY.rfxeර3hU@Mo1Y)ɔYL5MzF20{f`>m.af0扚n(Əj«b v>"%uj@M7K/9RwEVnpsIқ`j\Ү V}nV`;]hv ֥[tk.\-Q[jE'LB\ a tؐ2OYiO9 6377J CPE B~EJ=~nOL~7dz15ȏ؂#9,GvaDVYÖv}>Upxy^>^V#Z\+_N/^=EpGVymW8Й "=A> .*;,cs1(Ma>&l]TjA-"u_[ J==b|]ine}B31Pv*D/몟ڬn3Q}: .*Uy_c+pqQG(gO,vЙVn yfɳ`n ΍<=f~gT#=HM8~|2V!''#ġ'm+aZ|?“ 7򶎕gbRu:6aBȽ;t6& +@"기`Ȧg^3`XeN;ZVI/p |(Vܥ.WȧFŸ|,7`իq2z|IiwasP7j"S tjV6hdW߰v8,WGY|c͑ZL_JbuSKA/cDLf`upW#Fj蛵 l& ?"(Y:[e ۈ;->՜הU_U7MiM}Sf~;:,+@ %gcpY&Ӭl&!ԹϪEbx6i(RegkL"@1_u7kmhb(drY=|0js__:y>? +"\i/;GwȞ(&o+23jT# 6\67sXgm6<\# r3 \l[a@A=RTm?:hæ5c:mݧqgyN@z RftSd={6ň{,qo]lk$!dWLR)mr7"nhcBRn?^I2jY&[¡k?@;5 *5>;8*m79Xu,T,uљd˦bGZvnչ+ѬdTP@\@aF_veuL#x{/& ˮRK܇{-drsQ&6ؘꞠgVDr_:2`W~P溕`(4)e:ٯ \R S9HO˴@:l h>GƉpٙ4f]O7(7Q+OroX{\~ 0P) TT8/%FL>ꪍeA՚1gH7D|zX=gAN0_&md>>0uӏn &Qn킧4hӌ^`4Tŧm}\LE3s}Mtj%I"4@g R/y}C oy$N5Ce@[Yz~bmQ)Q^(hͦ١n,G8&a21evɘGYgHqX ޱm)+px ۉm NSO>BCz )GIāc{}&} 5W̵nh>Sǜ$18A%ղCM_}՛ワ"ԓT}!.ܝX^zavht_O59|? qN>\Vda H נ+bWzQ!&3!͜ddc]fv.[(hGq ){D672XֲqWjnLh]..k 25C;c'r1jAXBfe(NVr6g l)2hNfґ?bvlM4xLyi?!E->-m¤(w`Խp$,| H :1( ߌe|?')N eXCA1d_ Afits-J)߬Es8[{ D *:mIal9Ð tFYTr>Ww;}>"Fc!Xi\җ%A, @gLnY's9reߡwd77ATq|Hn8jW,پWi!SVK>f H{[$1/ Bi)"BNUzT3 2)FR+ݓBq.<شoah˲S*=8Vx4œ]"# m {N<(~WbBBfcVT;iHll|pcŦ6a-+rܠk\C&sb PVꚄ43ސee]1i зNji瀀ynGZbG )N^V( ^XQ~Dc>U2e\R#Uap  31d/Q b M-s%p2x]3_.z\SY|(^ &{T/)=.rzz:~TR m3[ *-0r h=*]y^&}א@I1YU۔S kWeMܡ3 ԇ.TƲcaZ6 NP>_tŰ^$wSz; o>"{ʊ>C,Js+[o2vlK1 - $M'ߓxX`b3 +uA{C8)0V\kV8rмw5G'T-s.Mkdd4- R`/Gy RjO>ꀌ3],+>3FG4nԾ|A{Fל< jS.ŭJ)qæzYXo8=Ƥ0#Uv zS5K[8@rn&=fy_lbmᡣ%@6T@0 xfsCn2虶ۢ]<3hDzS8݆ P!ٙrWmK,{d'Ue/ߡw}H}au;|gj: ·ҰxbRsA&Dm+`o?6C8z$|wp&U!f[c _K#*r0eeK]~3֡v1cqz$h)L|thDZܱ,͍~ȴŶPkoo7!:0DoOcFɥYf8-]) SLKkBFGfD|O)97SL[-]İUa]vX&J~.hަ^p=g!yғi6C"xK[>]\B q_:koQf?STʜEܫYаȨF Sh[,b]n73=4oӰՇ*j [\VTtYSG\AU/q>}P?Y6Q,ej˺!{͑7D")Ea)I$ u5CKU~Qm(932^d "?_1ʳ-^G(x<+sDվYs9,GʾѳG5f`b9lM,?9(M7hYQZUs:멗={o;V @J-_.R2Hsq@";)lOy)#f5#  +P&n0VY0M^R8 .Au$69ݗYI`uO2r5 |wE'Qax̙Sڟ{}Ͽ4{njhvd?[O^Ĭk5$ͻ̇RU$̤OT5 ʱFV%` b݃ Fu.TPF躧P$4j3t<9YU/& ɹ |AnR TMwN8jVHtoC┐wA1Dw';7 DQw-) lz_ҧڛ 6l83TM)~7C"ym&m< O?fﮆOsj@U \;o7龝4Eh;њt8>7Pk gCH@n K3Q+&@vר'Gi;HYTQecݩRڙ)-5%DNCtMz/*!bt{86Tfo)ȡ}0oAdZ[ߏ9C|22Ewќ?W^z>G6kp(Ra<M)H9Ròp%OWh6ЗlF4)@!!KjѢTJvyh3>޸h^MoX/CTm I9M;L?RAs>S@XP³8h@/uDL/`m 6N![Қȍ_\]SnVaq eq)]RWmӪA!rSn,_@h&4.Nj CGflo;zǶӁ&I,EQ0mH{tx|ŬC [)*ȳB'J,g 8u*[&vvH9J])JOBf՘f$z4sÜ$BD_39Z@1̀/M/ʩ3pDW %NN4g7u:UUTТz]y4r7̇5GbBo^!zתhX.-)L8 |X;lMFX-Gj{[+YODmW%"Æ;6Y*]&|BQЗkӏ#428+R G[8(zSwy&fm{\Y iF^ItSbןZUE r6CqƺGQ$rVkR%OT)hC2 @s~;r]iDxv.B$wׄ㎻,,sͤs5DA`3Oxީ#79 >91u0ݶ$7 zu: <ޓ(Z#粢s+cf?O~ cZ DnID+r:3O²ϞD8{ye3Y.(xK}8iRzj2zլR{j]bٽ{ a_=RF">>q.8êw1JfWݮGYRKr c:ލ՛xāX?t?3{.T= v?;- &VG.ϑ\ 8MMEx9欁\*;,`yG7p0P rpkyOGHc#kNi&Pꮙ,<0lw1 I$tD:VvxfR4 !1NpDO!'0D|3NOe ){;Br"*z rUWwzɞͶ\*BE &E&@4$UTQ #=ݻ4|S;iU`j^<h9z^gYg5# <"f9 IPpSf;˻ +[;l&MR34=>W{wԸ,69uh3iLϭ}l3=(a?eŴ nw+|Dp=I|#_a-E7;O-LA;Re(#/ M|tjWuyX (R~sOtƭF2K)rǏ'h͟]yLIP(ITGC>;dmo)Q854I M<ϏxWdx 5'`彔T)=ն[F]Zb XhmX[,dN0^V/5C[5Fsz}Ҫ&CmOǸ,V +EFQ,ԪX&`iZ²\";ý)p-vtr/۝u}sfdB,㞂F/|(䆐lp7.<N S"{a8hY!2EDe#x¼owI> xNmHw$XHDoT(fZ7Q &.8N׊BDtB8TVn k9*`jrL`oi*0m-]TZâ8>MaHUqFdYӏxiO9.LpG4' ҈FAz9w.c8vCe{S1VveY{TN2{*箃H}Ҽ)@3d!ڒ}TWUo>Sk9fdnG<6ƀBbC믏Ǒ `mf6ARm_t{:C|RdE& 7D}] d WGڛce:ր#?>_ǯ⛓wNm;C7*B%k^X3p B Hiӗt6>&DB~K^`9eD\Eٸkɏ4."lԬ]U{gWF};Rkbx)oѰxC[:[T1AܜI&L1X*x#rUjy*mtPa漌.ʅ>Ⱦ !Ip. $]32(vdѦqfˋ jQݻn*zg۬HYoQCRL(G7v^\X`̜: 9v>$Ԋ4rŷ'J>HoSlD.F@lgzF[Q%3N:{ <<'' N$XNޕ'&AqVt62L܁L%tTRvyA F\pa hBK%>$˼8,V1йG1%%Lks}L }{.߳'O"J^up s4܁e>ˋ)zk*k^ux[baG^5R.1*O~/~c\Uӣi,e-^}"գ i~uzm~w4.2EU;[ ΁hZ/c,8Sg13”n5vTi|$щb72RɸP0<сe T~ %UTt(󺙟PEodv'?B eX>Qg25&10P]{/ ֳ*/9~)*TfGdbu!lj0#/+jeH>~^uPSʧO ޽O0*ՂM?9ȁd AUozb"n / vRxcJ&<4KY_"Y9ojp>A{ؼxIV>yjQmUd.& ބ0?SQԜ{r8`}W $TW>aZGsԧ4!\Mdu_vR.Wa=:>WHdH7kks_O/f/ +r Eƣ1#^E"RC PI5qGfn{HbMȧ kgkv&'ŔGl8٪'mwar*+o A*neNZP<_Ɂ ^#!¼AZŃءԿApNoK$ [_SG5# 9d L*^ssT3no\Wk˪B/bg<#4?_.]Z^ųzjlZB}j;qlL)^i^YX4 +P7ȠBf\|?Ju .&-j&f d s d>MW,Y8EX]zBbf ߳aGًґ'b|Z6w|yNs[]]F}XmE9. z0!QdbE~MX@X%zfiVFi wTVaIzƈY3{ L-څw\`s7mms`#cwR3~/g7o޽~Iqד|ŗ^7VތI\1hBdW $#qb 0d0 xui$WN1JC*E+*vF#@gtoTz>e,6xW*&WVLFε\d4m0 *? JrZ$u,:4tvK_{Q\&VFuN(燭li2'*F79peD :N RKA9W8(OĔ9)Xť:&wuEOQ'+B3@~yV)UB@~^7u4*1eFωp0qubϾ7lŷ3曭v9 {A=uzFI=y$CϺIy^(W#@ݩ0 =J 2B |wPCjFq#Z'nF&c?2$9 U X>d]mMڡhfx0VƽmISVQ.)$0,'Λ'YΪP'kY1g^mTݴz3;Tz@ocZ\:U*$I#/qbw{rQW˫Xf.Y&K[[Vqh 6KV_QgMi`.yʺRVqs&8;ԉ(`qֲcvz Wb@rŽ-Ԝa:EVfb SfY"wFW%7fTga30qwC|]çG!0~Ϟ>Ngn^_l#` >eG[~6M-`F:q0y2}(> gŔʌ:"*<%^$`<^⍙X[jpn™'+77h+6Zmm]Kt[t|AOL_X~c Vd+<|Y"MJpaE%X-_"V$-٢FsLUhQYG,wtR:-ټ_rUa5/0Go~cvToh%@) bL>nD5Jyw?Cz {?^M2Hp2t$%e rH.(yu/Y G٤\F7ijV{2}0G$_WpCvL9 p~^/j)PO$̘x)ʡjuQ'r<5܄s<6 61O!i~(G$|[HKsscj9G^3Ԍ^vr6^^6m0c  $Ib[e=n<. cr˵.sWЉc5F R ,;λ,v2;LNH͗=9;HĤް=OE η._' kV!|>VA٢f%D)%$I:ʣl^pBH1jnV2`ҕL.Ugpۢp9)E# _H7 ȋ_|WϿy L -k3Cillz!ű K(*^*)9Α;Dӗz,Nzj5 Fޅ~ٛs %h(^57sb] 75OfPwl~@j~;D;H1=0ç b!Aw;Ce7H.d0MGpD Wd>679> 82捁PM2 LQ AI։{= ve\BMgSQSw 30G*bti{5<])Tl`XG~)rş`Q2m=_n?!vo<ƕex(iq]+ cmC>;0`ZoIZ17Rm_߼~GƣԸ;OAp4w.@?[gr O< j|bZv 8OM$]lZaǺC2) g;ҡHt$ri"aifM}u9%.3Alť(@3{ǛEH^%D~VmSh#PYM-t_jt*^I[jRO:Wuy6Ӈ9UD @I$*zPǮ~}tߑ%Ɲ-[]+3=8m(sLpfXW_׋2 s/5`QʊCԎ'lH+7h#%˫^r-dQ!lxZ00yGGAF׉j4M5hz]gln2fq6^i󅂚@걉R±ed[rAqfũzq  Wc*G䉀(¢tގPE 1?ۛNZWٟ\T*iY`b=/U`> ,\#Nk%Q> h &YцeC>8|7cJ/(}.->X*w~84-5)\'s(f`в\fعG(hn1}rGF>_ZܵS>_UǩWRLJ0we iO09F3'r8pۚe<8f"(!0 b.-xIiz{:DХ:L]0SUftp}pG(Tڑ+z|!3<;':waF[Ґ Rvr=R:Orl7:}CZ8,)sj9ɠy\5z%ٝqF,h|js HG ~3IYZ3,U!ԨrrOnj((zABs/sO)-z)Vp%@ŷs}ׯRh, x,ql24%W<n6 ԠIOLXmuw! H~$Ͷs=*e YoW ݟHrA%nl>.LͯenQnJ-&hO^=~D%Bu;)~wvCo-SsFCXuOu1p?qȍ]NzC {0:n}hy^e6lGaJZZ` vHLd h(Q.O\Q|D){M = [SZkL-]7)DJӷF4 82i񟫫FQX^X7E׋{_|gޥcg]7"W,.Jޒ8Ɣ$-ٮ7X}5쬎@4sYUco>"npQD@] $_vr84smB#CjJD*쎱M1dC0'UjrT>咕b_VϹĂkEmnJ)B%R̗4.|OsٺOrN߯L0O,hח q ` tŪ8]UAښ*{S$]sbxUgԤN ׵/S.,άl׿l4@5-3cYf<S7O.FlƁKO5$ㇽy@Ɇw!Qz5FjH?f<. ]mUOlMJ r\}?rYej> Ȃ0\p-b.+SZrg󯏛Oc&@D"רӜ Ba"ǿ?6=.vg}gsZݩsL^ N~bQ&-P@1ɇoH5f\D@PK]uV#l2#y$setuptools/command/editable_wheel.py=kwƕ+.NM*Y?t"E $H"%Zy,'z ܹs}p0콮ITnWIf7#48˒9|OL'.6,Qe;^pN?Ig :leoKki>..X"ؔŶLKrh41b޴_5eͫ7 v#O #lRTH:KH-!:/|k9e]7Iez.$B_O.c83S%6( !z1!Vm>7, lzSzОfcB8x04?Qӻ'Ё3*ŋo>Cl-z_x* TઘӃy@ FGFx \ wj  j~!r'B=1@7Yg:EBbCw~mQNS؆QB"NAAt yV'%X N8KyC.eý.2cX ^?^* @mANg“n'g(aD`'JO|be$ڟvCSaڂ)ƳYQ] x$TѦғG=˜k=[.*ߋZ.ٵOUӔ#miq䇸ek9E ul^ !~`yhb%z/ }:pY `Ur.eo{aE}AVkh)aagn%T$5sv4s'#5a79LQRCiw(&*ͧLAGf刔f 6ߓh-v3̰b, ~Td@N*%Ԗ6H7䱨rեa`cP9hvo"Њiܱ7@y 7xA+ 8д a|cd ٠-#Y!_Cg@D?ڍ_ 488\Ktg*~)|/:^Ԉ Ffآ`̼Gva]͢= D)D݀,ZľtzN y$\pԂ6jVu+d_mPNF $nLKP!Y`g\u Vد .*qHp0R?d>|`^Fz_#mty5?QG bX;W8 |>9;{t7[1 CMU7qYt:l:Ip-'Ȩ*a_8uf~!fGWl!nA ݮ_¡gq0r3#OE!i-,pȮXrgnwv2:] 2.Ǐm8ӬUl PW"<ɱL`dU=lkƷH0T}h=.[-A}rqxPrց##aȆ83R٬"ߋYgV9[E{eΘ! o0` 30#%ufQS[-h4&q}7 G4>ix>NƑAjس2Q(Y.RDs-]JnY[ؿb 2pїA/7. eVL ɓ; ق7HYz <ۄtG丈eI) $SY|'_|v;FH#^YVܢо#+ D݈dXq)fvFzy.ڠI踏,Ko\Wf! Н HrNԮf[iPܳUٗqBTsE0.Rg)#}E; gr;" ϨP.@rq`PF>6`BϘ!| n 򖶯j{C!6$':U&E0 FE`Z|LsKd3yy'؁"J˭#4 Z- QJt[~ RHhOi_Ǒ&OTBFӉЀ̠SݧF(Fۻƌ~Oyky\*igKٕg]HtTL1K BQRLmlr`Izm@6)[F+AyJ'#v^&v@}'lN&^KP,^jM6́56{:8rn01 Uc#x"|/9Ns22v ,n̰,i͖4CLמ 7x̝Oְ|%ɴQ.ݱh8R)]5ZukhIV,#=qᱳo(!Y2tC!^^=yH7 7%tG!luԼ#<}Bݿ XiĆXRw]n>bPP=^ 6G,JW6]mlS7:W̙u\aQ8{g܂@pli&VRWiMncY%0I%$3 ?e]$`qݖ}aAmQ]VZC@ɬ5t*Z% (:[M;ʀCZ 9blDMe nm"ILk"^#S5aL1m7%<8#uYb7t٨ 龲JU4ےPaUCuEtZ=t21,LZ~, %23BW+)} {QBN<' v$8$šj0EqC?nA 'gU:'1콞DF:暈kB# VPgWmOh &)[&(WB?)|'>x%vtP"Ł|Vl*V#/4y-Bܛ`zb u>)VOtS(5))q.㺛.D(#2r F7ik %ɵ*eS7֯{&EÝd'q7XO)t^jPK+obʝ 8u~q x ۞y)u4V/*O\Ӿ2{9f&h.뇃{3 |2L 1#Wf{/.r}4Zqm89>&OO(eT/:ڝWWѼl`3d_!FO bǢoFJ4-/܅N`|rȊ*!.?+*zDfԚ4zjoub-J-oG{Jb!"քFݑ~AD^x'4zl{? tNb'BC>h{+Z _;8/2a?T[py7ڻ[w~eeIfds4kX6q_n:11r=Q(2zs+t)|Ku]߉M[K#ovC1t;P'p ݑltڨPƪi,ޢa-JuHny\.؇A{!:p٨>X3,j8jծȜ7袠F()WS^fn"`WaF➣>;s^{u/qxhAaXE^HxfmGUy@CŷttQ%݅=> #XQ$]Nhz+eEpHVLy]L0 ol#܆ߘt'IG iv BP"UjMJH_lvaJ _~~sʛJCt_בt0RP`F4oԥmb'8a&A褥ST<[.L>zXTf㭇NlNJ)âG:h~iRLNlښKRhe XHacКXHj z8B2.ηHH̹$ ['q^jk$Q횞 Nc" `:#aRE.0kxn-u*ahGV=OGbO6$ZB1C3#%HvBYz1GʋF+1˱%!-Ӭ x~䗆nEJl:Dž_/&M%"aؚDOqlļvw$^Q8(]`# qqGŜp/a^< 34v}17I)ï5d?˯teܟ&ts2Q]M9!%T|BYȧftm"nkONS`yWa2=S=olvD<+]ʹ(#TM:"@ְ~Wc˰kQ+~C޵po`X+uܰzbw 6>g !ZΦc!St@t!ي 9 x>]Q?Щ|)}BQ[)yt/ yd |s fBa ՠ vV0wl C^u:DmPVEN@{̢r\&6&=ڄd&d:ȯ[pom 2w5Gxϟ&3,ed_W>K&6|Î}]P>L~0+lC-RBv9bH+C o_]cMiJc_ rdAG>l{:oOxW֪AxO:ïqBb^}M(Vvi,ڐ0n*qoћ馄5wH܎G:Ƙt&a+dPm汾EN^Fra`j腨xTM,24"!ũh :نuMZ݃M( ]\8;u; لzrWkQls !GGl0a71;AOx]d2ij)OD5.)<#mN]>FSx4> i^dI^E'è>a0Rx0M7G RU'MlA-N[sc}B YNX⾵ K)Y̯&`؍ȶkPm!>`G@Fq:[\6&3ܐ=ق-m".ԁa%Wtz?q%>:`]RD5rqy6w l=jb/ْK0I}\HF;Hϒ:OQ&׫0kvFl8]Aq8b >N̙f 1:SQ]U |`GɒY٬"ɓ:jR|/6a[>AFN(GZxYP2ROLqډxV2\N9W.8+;OjAbo2?6fF1s/6Wr̷,lJ*#B]">/¡bTT7_]a,H%o\9y H[(Rlh* e;l"A.c< Aj_Bs]U@GdtFWpP$CKt&elMԼNbᄂ"6gXۗ(WtiG=T"VRTၟ^ԋ:x))] X]~BG9Fv#7˄%%Kn߳;qٿUu ٓ,Di''S†r0"~z)?Š9zǣoV[£GڙwKB?Ą/KԔڡz%o&|$L*T t.HđO{slDЛъ =cf̞0 L]A >DzU7aEK6h0/J[=Cj-L,WXWsq z!/qAyA7Em1n:Q5V}v0v3uF&֞Mؖm n_1fp69z{[h^=H-̈á!=:DP JBɹG*9$5E1GJߐF:el;&߱ޘZzL$RVZ31>BL O._#ǰfU<5?˩9,/߆ 93oFҏPD@LjO?1d'ZG^|mK|zCuW;)e?KTt4oOY#A[7h`37B(ƿ +i\|2Z.S[y[%tw. ""21jq>ATىnEoO?918yDj,_&})*͝ZzOxajM6=:ՍaZd2#|OM(_{\{2 |8~r~v_؝Ύ/~DO~([;Gp"9Ѫ(h#C埂rp;\\.ֶV[(gq?PzSxK)ȁHbel*QQ,;CFt­/j.EdzdV;_R!uqk,ǎbpxS~}zA>G !! b޷ QG޹=y)+>3=)pۥmS'˦3fvl@úeMۼ#dߴv,>NJAc{TriW V1Be+F.DuJ  Ȼj~}"rp@h[ySȔedaj,|lY~A0KuY$=|5cv*k9tycy.[ر ,GGG\jUv͂SňTGn~Nųz ԀnJ/ "(n[4T6$2"zH5>\o=ਐ ù)r Zd}~Xu_;%ʪȳ@D$8R *砂*Ոfff?4L;Qo7Kىf+J:ř#HUBU Z/kXE~Lʔ2!l70%,ȯHƄōJ9O K'؃6"9NjH:,U,x.䎄u1O??n楎g"gg&N#x@Cp%?pQϐ-oiGB \i`>U4[&k,5kIcIh82S6yymRrdQy0X}zcBX?e8J߈k]7fukh\JYÇd2e :\2/V$N 4^bS`pD`@T:IePnFnG9:lQw~LuáD=ĨGYײK-%,77e yg4]<^L ?M!qs]~\iZp@PUP\_5[p!Zp'E?۷Y|hp1iVuX4g+ظ==@Dǟ"h0[wͶ]IxZ;6u֜z}]%* 0zK 7W+!I}j491Ki[9{3Q5yPSk^<9=Ƴml0䧧:<Ӱ1X  0;0&G0aXK<tO2пV ^7x4 0EYӁ :vC ]bUeku~WoϿ:B~W_'2cgtg @VF?O5-D;v<%[7٪ilC'ks\3|lAu$&-e LdzɂQ {93?Eg#@9p$kpsIX L'gJp*PwŎ*qHLb{c@-nq<&#eRfͣ}a}\2=ZXBwl$:&@ J1aTW%%N ėq9C q'㼕.:2 NtښzVL1Q ,[C#ms-D$ِ{Z<]J5D ̶U 2ZR`lp0 (M+b'O~䷿|qVDo0d\0kSy٣Q>&ZXܞ#@I^m by}ikF&°bvJWOC%j0,2d r}$9n_v͍S ߩ\&.WRv'3Zh#CڥƤoFZ!XżBG)m9I+EI*'2dI1 bD{A-0Jid+0|0O<_(v,u 6iK5{޶;`GqcO̿.UJN1{l$F kJMz4W[8/Oű:ܻ$oĜs>4Z ^8=.)#C3e |F:&>1T#?Gx``_"P #(%\k@'ON2?oJgk wɛq&d8.(8Əޔ؄]<){lVfu 9'Av lAŰ ' ^faVbϥ1@ET% EoC1y0Vųm:|`f3ۥj+4쭬#[F>c@Ǿr aX7NY}4F2fSĺ ]VD=ƍx? U /hЅx\nK.г gVt*  ܔ-͚֬!ЅY=1}ѕ=P* (B<%Z@ɖ.%? &:Йu5Pî(BTW~H=pKp5HIyݷ^LJin}v:+l`^7> sqxRyvo΅dFp昇UgZO{AeA =@C>vv2~h3dfbBGRe(^8]@$!jGm\;ĭv$c5RH =k X#x@ PUd4(, JM TusgoL=xl+5k!l3't m֕Vxuś`}Hӊwb>?MqJܮӶͮاAm[  !z7'~p.Sib^~r`,4zS?qcQ er9`sh%СGp|[۬yQFވB.۹`` Iq HDJ{[y+"[Au'%j_~t| ]8E* +Bl 4d3*YFkO|L݃Mջ;ZAnՃPdpushNm_x[v\y9մx,p(yޝY}8pe 8}PC>1- c-K0G>QY2a>xA?/Ѝü:ަ`?)m]IJG;O0@ON'Du!x*A'b'}$*oPUW9#ZEsaèȔ,Vq<W.Kbd8ṁi|< pd^`cwF79Fa OGM1 ֫u)mұ(v kfx-'䦹8S- R$ $| ,āBnKxdC#. %ҵ/؜Zq (/'=J6Tᅬ.J:|+ZDhvјw"=J({"rtʟO(x5& 4ٵh[}h-/Zlf"3&mC_Mnw0dEϢ.b)ն  g p1, rƿJ Filu _Hty##<,yzW6}Ja@# TJvñ&D_7[H_,˲3 +v_@/SqH=P7euC˱e^ľժK`u]KE$8`HM4ٓ|߮RR2+P<~A g̸T T nkgut&7ݔ]k /-ܩ1+Tj;ULs;2 dErGcyAc&~{V-y(ODW!jXP Ǩx 윘>TBo%sE+JA#~1\:TFK#s}xʜ1e顥gC00'kŇ(Z)ұ= KpAi>!ԔSߨ]b,!ռZiK"fn~wi='-z.3{LWda`>"5Nާ~WPR6BUz92@i龎ZWu|U;rmM@aū9#=&6%~c"q;LASQEĈ[5򒻈NŽAJ!|C$dæ+>#ы*CrOu0U₿8?Wf kM:SH*fcD'*gM<|gxh/s݋zLy<4'FO0fT{9A1ߋ5i![Cåf־:NyыMJBTL%$GD$⌀k9=o<)Րc,=yR"-YW vcdlK'd(La>. QGw7fw/N5,󿜨~W h0T52Y(0 <]pOhRn0k){;؛MU~ۖ^Sʇ{&Ә`bAs{:Jav3q,x|⒴&S$3L,z YjeK.2wW+Ҩc~Ky8CP1Nj={WAf?|W_HOfM ;Wy>IwAE;]r_+lzA}FC|79ui5}DHb^x" ~48L1Uwqp]@m҈Exw tx0gyb*,wC6e!M~Z;SdVAGθo+= A 6R>@~gF?Q@DR>~| C4Ÿ炾($ g^ELpx` G12Rޤ꾅Qny}֭H߷4б"t=Ake 7(t~įFCGk+F-´Ђ Xߞcqz4"{ro&_L",|lXH̒#'05ȁxmX {eP(_ en4~= %x"1gE4La4z#Ox/KI#WI|o81Og'jN-1Ȍbr&q]k$Xx^C`JObDfn$I:Dr֝쨏T]X{ЄIjb&O]zo3}pTL?= ?Vre ]ejؓĽ$<Ć:-ξaG0EO5rPK]uV@+setuptools/command/install.pyX]۸}`=gmM- dv7@P-b0hHq}ϥD=I~D{]ݲJZ;\eqGsVT F#7J ]]MgYn[`7 i# wN.7샑0pbF4RbY2x+6j"+cP`JsQ\g={R;un}'텽nΫT_,eíe*^\?,>V0iB5W3?@]%:*JX [a 9= O~`}Vn- |oDiQ_ p 0[7yp6b"V|q(EZڦ~`^$N lidW UBd%KXP#>Xmj1/9<!9*)d(*gt;宏Ἢv)n&A+*8}i5:Zv$,sd@8PtŘŜŘZn^YZÙS9J$ZaGSq8F]`RƇw~#JmNp>{>v;=> 5$:\*r=*g?~Ëp! E~n5ĀC)[EBFkun }d.|N è4^b.1@,tb{A/LIs8 ;b,ޅ0}E|m+if6vspLӜQfe~-RXTIEM*jH7F(WЖz}a̯e;OijI'hhEcŕnTq $g/xrlRwN)qimjL4\cY62B& S:_&q`1"$áHêAc I#T) Lc9˭t n4/Q8UItm+*Iipó%=7hQ̡`v fLH/CFL!33'X_!Hc/+eC4a #[՘K_CZu4Z{@kXmk)w'Vc9OBJNI!.=-2d=_.;a-)7nil?=#C=wٟflA.ph QG5Roy|Jj8Wq~8 y]mC(W޳!;ٍvK_D^@mQ//x>:nHj^tD$i &9yxWj8JQ.҅QUND謩_!nH(!L%_-,7n"jxq̅E` (ԚxFEHxlaswӍKd? FX-h2(Eg}7ߟDnm)#++%%W+[:#SbM>z?qVt{JxOT;.{5nǭ3%Hk3¸՛ͷIFӿgFXT=uZ|a>}b`Q{҄B ?F bEW"5% zҢ6N#MNPl7O~~CWPK]uVw͔4&setuptools/command/install_egg_info.pyVMo0+,PYrOSz饇Vꥪ]$k7a7oxTִTzu1uJ6`Z4\9ci[˷l;YI{Ms>m!Ywҝ<4r35v:,F"dP׹ҕAqlHAOӞZd{X1@ZYCd XX9ex2r67ɏa89SpsKI:D*TNF#4T5?3#bhX(*M@M{Mp^}ިcVclՒ,sx(fJ믽;j-n jf:Z(zUu<3G Q/D¢V>k\z' e%NW=oh0¶U%; w Q^:[\<+[);hϽ%4bJGUtV m;z 0* 6 uPCL٬{Տ 2@f_(ŋ-ñԾ@KJR1%/ {)Ƒ= i3{> 1 ՙPE2yRcΙ0l bIh@9_9R,"kƽ3b@hRԟfSlɨ1޲䒃~NUnem_85Cjt[R,g(0p{-N`&E'oPOI MJHm-(+TX>[HEWhȪXojb j%Vtvȹ~!)LNmyȥ>,U=lmZSf9Ã!ѣ'nTm-3Y -fndYc&%0B:RTy}8ɵ:$% -X0Ey@RBlW )a`ԜstK#kUPQS';e(%'+@PK]uVY$Ve4 %setuptools/command/install_scripts.pyVM6WLSm{K!Er F6Hj]w%QNAs8o|F9?z9P`ΜDm^F(V ށt`:gm%Zci?00.e\Ytf5& VH^6IIj-VX{coY՝tzP0['`En$ |n \KGYi%5ԿX]pî 5ڢſ6dpiG7C~1Ģtj6YEFy9_vzl$f?Vw=]a*̓W[ _n3|,;ICMF0 *Bm$ATp<3wp:)_k=Ofgf5. N|&|Y6WW\*+5=,X\|UplGHW]P6h*ޱq;S@nzcީ3-9l6hy M 8 oqW̼ Q!fĬ{龯]E4Q?[1y!Lqp!qkUM#ؿOv6˞&̄x\ 3}2S${2uؕ#f˻.] ï!S1{iEz n1PK1xU])B7t(setuptools/command/launcher manifest.xmlOK11 !]kAJ҃B1;gɮooRR*&\ޛK g[AL8 ]%wPA.E覤V`% zu۩$;FYt ^׾ڢt菈j+ 8GTh1RM9Qpyk: }U_ڍoy= +`iV2DPFZLFQGC&pg`m4xyDV1PPwq\KazRX8ܞ,ʔ0mzBhU\CY4wH;`,AZ >Aʇi܏pdW$v`uLwhq: Mpoi ZB%q *& _F +#$sDD &$\]J:u~∎Yo|T IRD]ˑ ;'F4g/8⨋ZV鮎x' {.K~]B9ݦҰ.Ĩ 6}iy)Ubz YWzWJd+k2D"i5Q^ @!FgH#/'+GeYMm/i%_^sj -x/z&RzF9U hmt{fN.2h_TEcݳm7:XDS)ŌӼoߙmGšc$x!BeCa;߹F?|Bk \n:!#⇌ HܢoMyaB-6 0dC0=Th6VSN=z@1_%4m[viҘܘ4+&8\4&G؛eURۿsC6,Yn& h [&4a$=QwuM-fA(ިZE=ͩٔlԕ`N+ez9h~-6}!kꮴ%eD2@ kê7tη2~67(V]nFg5/n0@'ًoʳ[h8Izg.ވ/)]o7K(lإ pJ~/WCĭ>9)MNv%n/@^|jorY~> <}W}bXݷG,3|?sY8L]}.iERKO+WPK]uVvsetuptools/command/register.pymAN0 E9U+F+4HfTqк& ;;< .%98Lӣgzކ0NRCMJ0/1Md&>`z[~.=T?۔UA[Zgb<$ !hw;Xp]/FnM6*<>Mq&,9??]ba3h"sLm5:C<5]R#-6 c]wZBǧfubwDR/PK]uV!Psetuptools/command/rotate.pyUMo0 W KP uu] PljK$K(9Z_$'R9{8XѿS'%V~Kc,5vrx2k0&7q㓻dr3gbV2I΁5^zd1Zi>a4%Ȫs:2&ѫDWX݈^;kxòR9ܔ#[E)}iܮBUW (0ZC`Ea'-I䷙NSNZ@&TL`]g|jh)ktiOp^(>;?dc (v LRDXM9PNi.#΁D7;܍ayNM1N?Y/Yg|s\+ΩSIJc9)3;y6ty3糬m7}?RI,T7<9͡A ]QSmZ8|SFכ b>gƉ\Өic0)^QފP 'zg"QH,҇2z294Y3M~?;󱌱;YtkP;W ۰D*ǻ9T*C7q>%uK PK]uV4Fsetuptools/command/saveopts.pyeMn0 ¥r9EU2!0UUu2a4Y!9:x D [+s/.>DPQzqxF:5f;TwB!t0bU"0 ubPM߲^|TnikH]2Rdx;eM]n&U{Y(i'@[Xc5ܕN[- Օ=WVPK]uV콬Kƫmd% kRh&㵪7J30YwfydmMժm#lې2o$r5Aj#/de: Ft9H^j01ΩkY՞@0РB`w26aQavbsXxgɟ;skQX+2بVRuXJyaD5G0x|<&1{|6Z- Zpo\;$(E4+)b(fkZK*Lo,b 2ka`8Juogk܋VkHR HOWH?)IJeN-pI`@{||2_~yYaV%No}Kt) f{_̥Z j*E;t[FT insYoT2̋} ZVXq^9"Gaqǧ{2MP . &' pOl>YD}{zsͼfС@BNin,`IGyC$hQqZQ[sHn0e ,ʎ@yРCG<8Ijn2qE ÓQZZI8 6"yVPi$H9SuuJ%<4lFKp%4uQ/+dO0@J@gp傽^5D j&Gi'?|?Γa4J? pW'鞥yiWؘL^qQN(4*ݓ6e' Xk"-;&y @G3ÍZ17g˟xlġ@UЫ I_ N#W7jDwjKCe:i)~̄QpT51:m#IJͨYN`v= кB)'qq! 94Z}ؒY=8GgNPMH 8{"߫tl2Y2PUhiu|3zq ߈i9?鶉m@?sF4^ͽ4|QTsvqb ..bp^H  ]hKBʭ~vxXT8VТwb"`a) E^,lL?cqj~BPLtrqբ}%kXй4f|+;uk-ru#'!Z= "Zn`h8C W^nwLB{VhXˠZ[p iCHVmzX^<$']VN; $#VG)0uR5wG6?0S-TX$[=ePqS>V€$yi!? s=2e6[-&6rJڇg~$ zz;}}kl*?el%;|y  :2e ~ex&eIH('F^{/4Ucĝ0 Q6 8r^4I֭.5itr]qvfb0Mztcqm ^C)6;nRxhWXVT`iPK]uV. nsetuptools/command/setopt.pyXKo8W Ib@v!dZln)DZJr$|̓Q*5r^ 7}'RtCᴪ9+Rwǫ 'x~ŕYs:E?TH&Vڂd]75 m*i]9ّXkY!1+w)J &_V%|mꮠuެ\Q2A6tzvlRِcgb;#h`OQ827$P;/ݎJ')*>+cjFAtU呡#-ddoa3K^/ęDPy=y/Jr(Bow)-3BnHh+Tj60#X;pQ( jȍt@ oqhAXJ)[9Ns6JO f&0%]ZBNmt@ӐP~>{zN&LwǪ hմ9<ސIDZʠH4Hpe|`Lisr`@n]=ɽ[{ag7ܵMXnD; 2(>ka+3P*GJQ!iHhӲ|T8us7#?xMYl-fH`g(̺͢cPYVQysfa=;͌h3HϕXsù[t~pk8W{]AgSOyK~[m}QL e``%] ä``L5ϸMcּ 7ջ׶Bp v"/#o&<5q|@/v6CDuB $~]EWlCQxUGx3.֪>yQTz$XU'o3"}~]׫oɝ>c>%RlK\qiՄ]g,;+g/Npo ٶsS<,TFi F[Sdaڹ=ؽ _g- kZ@'oڷܲμ|PK]uVO\ setuptools/command/test.py]6ݿ 8Mz KmEa,ZʢJR~ )eo t2U2eҿBstۜR5V|+ m#]#.vZX%M.V0mWCZo7>WB/܁a_haTK1 rQJtU|eE[{TA6đ8p <:^U/qIRPL($BSbjˊQ&!3ާIޞL9 RJ ]b%yODy3 6b/EsL!3UZg\&БeӝǸ,xbxt+2Z4)gWB()ҟNS;ƞJ5 ^- @hi` " ZhiO3Ss|,p-LjLV s'r=ZA{a}~`jԊ8,Xac(n\bfN:RJ1Y_/`8]CuL|e[Rm' ãgĔZR} .h(AoUtFB'i^:K['X5S ̭(NzeLDp$nh2k"V3Tr-GD!%r=ĐFз[D FD)|, B`WF!P >W䲧~*EY$d ݞҰ]ҧz{DΑ-dl\6Pp0 ÍmilsCܟ!?If~n,`b,K`AklO^5$MLA^(`/E;a'1l^4f|^spߊ$"ʒN噘}Ct cG]d :8 )nK0&+Rwӥܜjj0509teIu RX Qu ZsC44ʏb\V}˗QE9TiΤ}7u3/ /a oexUML5(,1mQlfRA3wস&r Pl=~8xrl `OjURdXJ# p n2S j7~4AL 퉕Qu;|wX#8ۮ!4ӳs[7>d3Ab5A|K<B#7u%QH v\~oWW^no?F9+m< I_l^/_}4pT/d_CܤE@& (g7|KGWm.t!'<ҭkxBd{cDPK]uV"]Ksetuptools/command/upload.pymMn bEGꪚJ](hp2#~Zdv2gY&XO!L*r k{G3) *3;b>v ꗍq50$Q1rA9W ü@!>֫SMk\deH!;qzlq_7܁v9>ɐ? q1a)[J>vmo$*n~% ;jR*(?b{ \s(2?)a?PK]uVsb .!setuptools/command/upload_docs.pyYo6>1!i;V.'Y`ӵݤMvuGxXKJRL:;:~ 5ϛ4_"Sٛ.X*%g\FB( j֙(KZ$/hj.*Lqz$[Z8-s͓t6g%YS^^Bj42__*9Tyk#,Mʤ%hF̽_)3z o5+{jSj!V֦ރu5(:TsH )9SWL7yj2߬9{ jwT32Z( YA"@& !906G<܄*ҎU55rXۊF1#vȚǷ׫\7Y}N=5Oފ*?f3Hn,MN (WT <_u^?eh_9MSrn9*/6YZV%S5@ϯb.Zοz,`?,ɴs.^" KAsݚ5*[ĆlZz˫/bơ(%rbt c_p~U u.QW@\mS̊l^(X< F;/%:y5ʼu:D8;e浦ukȆW%6cp[XKا* Ba xGixΓ;mN{=]OaDg8ЏވG>>Qy+57P::2@ +'0[&%Lk ;5>IF$e0G]XF gZU,H?攣ԤC}lZт% V rx}=/gZ4 G*$ʵO4rP[ =Ũc+h`~AL 5/'ʹ0zX-BRl=־Ҵ{uy/GPǂ@Rl[g|m^ տtOe,~h0sGЉמ>ſ>U7FU.,Qv1ONo٭O2/DSlGRR]+^ ;)gɝFB:+  Rx4ZS{to殍I~WDq_.~>{'.~_<'Ϟ>? 1Ϊ#}SqyUr;h1u1B rP d ஧ߥiPC#eބ}_!кֱ305vI"{ܓf[ʫ˸JIP9I|:bo85:Vצ޵8{vve3G[[EOC78XzKD R7Gr%x'SMq;6ɖo=)͐<:5;"BE=w@vÍmߺ,3*i޴ Z`JcZ$AEi ug'd1>)$t"}8HlvaiTLKuRuɵƝ  ծ;g;0Go~xߛv}}6w1M ֧j:O ㄀8G򻭶@G lزJe[VR.D@] RvI|.<N\ @Cvg4u;:w*b,hEQgpTVLv,T}ԫ|eM՝ ;+2QזF2Zf֤8ϱAMGa-T?&H!#ԣ;V(1nY1OaWp)$j2xCM9sjB%cb?`:jz}~yʶ.8Pyu`rhb}ݶ*EWX;TrNb/`K)QMgMSAj|p޺c1rLcd<[V(&˘RHɿ`K;kPh%,L^0RK^81osfkܳdPK]uV* asetuptools/config/__init__.pySM0Wri 9p\7$\'d 픭*;&_b{=y)`|,RS dGP\h=aźcXl0Ff0zssXQL(k\"BϞEJAŽ-)c 8[d$Eq)y/'_a>5iÁG& a?e9>;hا⠋zqT-ۖ˝ceq'v5['x!Y ,'}]7g@_. 2^Ytw.sՊKdM4oA#L h =Yg`ӝVsm9OɧzG!plXE߅G (Gj~ '5,di*3.3Q0~md k (Q9GF.3#(1?RcSF G1M΍O&nշ%5i| (LW#]ţ˃-FekDn47  PK]uVE_V4)setuptools/config/_apply_pyprojecttoml.pykoF~ņPҐ龮(8QzKl#VZ@JbL*ˇPD"wfvv^;3rgZH23;^;^>rn[_xT(ϖɊY/m`q""^qX,C456 6ySpYn</Kop\/dO$× m’-e$[,r2ߤ 0V^#$;d6O'O'''#v~s .xdd,LY%gy8d͋j3\oa,|+KS$ .b 07akƼ( Ӏq *b*wrteCXE\E\B%x*ZI&ݖ7E€A)wf\cr˫_< E]ʇmCX-.81`=8ِ}YkA̷Ƞ%Mέ{[&adh}I+M4Uc5?^4tv4|~iqGCͦ*QYQy&|܄pH*\F uq[(&ыLGle03964<5f^sr1 .?~\_\]LHjАI"\EbǚDvMbuDJQr?5z6 -O5U˶ P^_?o'7'ۦE_P$˜iFt0<diYC,Um\϶wt(f ֢-Y\~լWCk#8 !( Ā DI:pKL+|K{=/VیHd8 l2H`;Z%ОX(ҋ4/A%X" #ƹ {LW, adyMsS»7AI;Ȱפ(Xlym4.K`v.&o\N|'@2MGԼ$n%%x `hU_@;@^k1)m2}^F#Rbu6h˷~59^( # MÈ) c---Ta/IaYJu<#3Zb%rZb)CK0 | 5mA* 6/ tr5 1e픴('֞6:_;Qc"*RW!K1e⡼]ת֐lAZ l=!PiR_Ttɤ`[D+ZyFUeq 9S|(#N[@91M4k_@zL;ViXmt,Ņ.tAr {?#q{R&:ǖۃ ;I[PP>&C1} "\zѠ0Us(a6Apͭ"Cؕ`3pyw"^jX1b͸IeHv^=:؞gLKGњ]V,Ǩ`B@j(x9*Ru3椌zmkWMOOe{ 6>.n{@z~0&l4fPq&W @FЦkYᠬxc@*Gn>`ͪWW4;$l}*zLʟTv[[m^''򐝜{IQ"Q|:4v* SV\$gGn3UXȡѯ`* ,qov]~Mꅚ,o%, %k_Fxi;(f]xdSi[2oɖ2­*YS sS:EQGk&sc|7sUW[-f}(E#Sa@j?9T%~ WV#N67q[`Ѡ^sY|(L݆"Pq7F pH;  wba֎k)lxM{iX NU#гge>CL:L묷ނVmҼfS9HcZ:,ΫU4#tFBz|4_mMP^HPgX^(Bqtk6hF$X"=詞[䀴7!9t0:ggwa ;PÂJļM×zKUa㠵i>.Gji(|GV)mh͂|bB*t/fgچ_3MZ7.265+0/3,hoK6"];G-?M |)) @ڗٳ:j B-eLJO}G+}BNI[0M`SCNByKaN`00v۹tE%5i>=~A6$iHybCy8أ$cA yD͛7{Y\(j@w_lZn8!G~PҜ\&GΏl~ ESR\yhps,!{zF>;!vߩkKXPXk)ZISU>9Dw :1?^Hdokk,p@sGP/Rt0^5Jg͐({ :TR; ewLQNveY+~.]3YKR]8ؔp/=Wu{#gU!0M\uejOo`s.?S/k9dz,^/SR:m{3PYR(W26jd8zruI u?.r<U}%6x Sd0:`C7WWSZ2VaJ(aeNpln1S/l*S y;s }XPMBQҁ}_2)8};E*:MRE6G`n7󨝐HWɗ?gc{PK]uVv?setuptools/config/expand.py;kƵ+ {eB7qI[.pU ׼H_3gks8髺Oj?4e_Q}hvl}u3t>P0^vuY/AYbVV]cl6Uh`2[][jj`ڱ}܌W5Z2>l:!R}X\,jWƏ5~|3Ks /X`}=V>DSQN;_A[5#W7ԻhN >B ڑY#ʾA()x;)8Š"E-+&?۪I,?4#s4ӡ]b л) ?y@v 80`>X86q,pҐkiꥣk',41EQgXd_S"(w4Q y}ʁ$"75lQks, w6g4ȹ$Y쇶YH Co͆%@c)shƁ-*e@,|Cq2>ۃx>s'X9"GZD224sEiGަ~WG4mkn(l'. /fTmIwJ76?EGii)O X<qS;׮B{>V4UH-4GbN[fXQR]Ē{ÌԇK)hCwڔqIV7znI-'A6ZF-DZT$:g-X#)$9rpp'0(Tr K 0UL {/0qg^qτJhY;f+pjgf2q8:_oaGyk+ʊ Km{/Ɉ츫7? K)Q7R |F}V͝Jc 山w6=8OF>E\T P'MaĀWtEqlɺ"ۍ 31I3*'lgP+c{p'v c瞛Ҳ`&J@scIhaŭ.zi1U'a) ӥX*yJz_- L{洞cƏ%S)pA[2Nid6-*d/,p4 ܞk^OۍaE[VEF\#Sx a "!"& Zk*gD "c3Q5_"Nv!8rc@`JWaP `7r@wGQp^%pa5>)ldɪrB]ck"*{c0" D%r˹mT'iBN<&@"2{,W0;Ck0!H +} 3r9QrU4m+S+E ضjV%s LzBaŒľa쌪}$0\yE3S𾄼(*;0F*pzwZ#v Fاj8yiJn#Xs[&L~BH{D9HNG؋\qT8ܠ M[~LoCQy0p$dOCIqc#Η`-+Jx$0rv0Y.Js#~_?ѹ\8LD뉰oyĕ]L%ժii/u,n&u)r.4H!:T_n+pdJ-սk$(B ڌ%qmσ`N8b},xCݘzuRkCU]MdPp5|@Ru52AiP<-cP^JPr T})J;_%š_Di#,`g/o6( U w)8@5H$2jJ7F"#?oRWrs֖z1xC̠^24-DJ[t#}wK<Z([ΞX]}J8ڞq\TҘb$#$vaZ0x<4lb߆9,cKf )&sX .a[yXDy 8V_qway$t2yIk|K:RԴ#[{I!b5qdB p>كNb%s<{9t \MXH?gRPrwwXKatX}4WMBѤ)Oۊ? j+- 8\[dA_OJARsyQr;&";wdOP^qC]FK.OL- +^# XD m?2>4 G靌pϵ0VNJ"`~qm<*8ԉ|`&i #pH:FxI~3.pcX;aŮAvA9Z M،$/H\FF$_{LXpR&e l% g~/PmF_-U1whڽE3λ2}){Vi酯#UR:L~[(Ps TF{W'?Nxk+ 'Nh*.x-d["Uqz2Z3]KFZh(vL]سN|ykBlPc'[>G1 唙M%QEf]u/KB^v Nj)ݔz_{^@HW ll _{trQ2ƊDɗdz5`vU ?p O/E4|i\!gu(.3܄]H5d_ aGFM[7֛@udIyK?y R h.Ž7P ]vt.Dv̲erן.IA ?HlXY!!ܯA~ѡcSX%״WU>+=_ж)!ZX.>klc sKmFz} H f؋<l4W6;,p\x|Y5DV15 wdr);z AJoYKY"kպFN p+Vv<6f=' PK]uVzkK "hK"setuptools/config/pyprojecttoml.py<]oHa@bnf p>l6q$F`Eln(˦lWUMder/g 4]]]U](&`۶oJͶٺk6llwm}7jd"L^t3Ƿ_ޟ|`/YxMB/wuVn8kjL"@ܴM׳)g#}AL#@}Ulvrz[jY9ͺ*ߵPY؛Ugm3ErdՌ8oƾf"a&άto4CJ(fu2RVK z})tČٯ|~ٻgog,VMt]ɫb2)>5?}5ܕ- ;aul2I/JB]3oz"y#ד޿wqنt2| #"E-X0?ed(%țQ"{`%zBKiym[ES$1%MјeUYd="- 8#bޢkxW$:ɻ~y}qDSvJ4%HLd}C@8}ۍ'| .y*߱Վ9m'3b"78ŀan%KcC|Қtn6YZ#R/#HԠL {5%H֋SGv I֋Yw-[ж 4m.7,fp`!AtnwwF$*K !utbVeMc`ZզK.+g@4:z$O_GCTM͕^I<&h,rmP4Z|)&mZ*gJ xdox<Tlec/[.$8=>]v[pP h X3~[W`ꔰxSڕ^" vSzHt$-L9Br$sM.g_ۺ̛;{"?l Q1w<9deа+,o@tE ؖ=49o6UCqA΅ TqqH&͌zUEϙ†5kv[淰" doVhESa&ax%Tn_nZlv3~>O\eg@6ɐRd[ ` j(RWVw|;a{T;`˪Bj=XYW_I}ki.am mCp96˿Ax+X晰`2, y\"*2`).)0N815< Wt1y6M9d+c HbQ?'JތUQO|=!pIVK0Μ"h2idl KËhp(~ppՌS ;ęȤp,˾(Fz|ˆ dlUzu8rap 7@}QjMU5@Sh>F#>o[ Q3WVbW^J 4hrAK' d1,_a̛A-9*#$Z{~!&FeWx)ĕKg7+j` ˜˲.Z4NJѼGNUhɆ, `UOЌ(qA S!GeP,i~ӕ7i@|iwZJJlS!(he ćK x91QZD@[y;UoF˳+i=E+W4 <'mS'6Ufju+L6!=XmGi1i'g6]mL̈́{1@5 i?~ܰ$[٦+ƃ]\~fL:٢Q}zK(hxB,`e J! SU Xn3 >jSi3Ol_"+*aGgBe뗚sB }!q(/a o3pKrJFbETyQq{&ZKIX ʺ,6g0(TI8)ѡ U\kbtǟg2!ʪGO>3&\ bCj20h(l*_gBG5CUkM=Po!n w$]YNb(=5C `>#S< CT6_w-SSȱ#k7T*@۴*RD#[E[vM`|[-shrMl!uv RY825d]e_x:hr1)A. r]ս.0bIyDQ'% +%=bz.s mF ͿUW? ~Q' ^N)HkBU[A!VS Q`ira_ӈqU,uȡ%I2UaM>>Tj=P5{;4NEDk8xǗj(f 9U}W׃7,ik1*-C,!2#!kbJa)qqb㸽Gdc<õG=e ):KPılMmrHBL+OwJ:4u0S/herrb~~ TZ-^J[:u7^S`U G|ygMw$(0Nqݍ?EOj{)7RXEP)!#XIIz׽J~9gn4s$YUHT4h<αX&s6ܜňmWЮQiETuĂ7]mS>XK|Ȋx}FCNT wA~Oڦq:9J*:9Wi(0^B=Cp㙺KޡSpVwdlGZ"B-zruu 6L\]?f=ٓ101x:W s FglKD=[F9,!ZBcFr-' ,k"ڪ+n棷U~~o:mNpMm"y<Э&E}𞍥H7Y3rAy]]C(ýydž?0yCIh70XZIfzx3S{@6V)[jڕ>O|{;`~@n:5%κ^`a N׀_ urDBY G;q+n=}̦ga >Qڊ5x<5rQ1EQDwӥ5%]a\ÿrFs)lh^U7>Dl1XR2qa)kS9P_$hWS{OS_ńE"1`^C#ڹ JsΌ/Qߐg2Y˂6Eq #.x<zxElՋc[5b\vqd?D0nʽ^:?tX7TՎ4ňD<0sIFطx}xJ=߁K8t9(CI ^ ۦ0L׊-SFfes@.|6=݁'7PK]uVTqnbsetuptools/config/setupcfg.py<ێƒ}d '8 Z∭f(RaS3߷.}';N,V쮮{UWk4}PﺦX7ܷyW6شVV>]o.W+)+ҳG|!n˪RlB][e-+)6nGFN5m'ufJ|Uyk<ۺ/nJ/%Bn}1ۮ̫#oo|w,e^$yg߾xWU~QɩxZ9,8_Zz*^tߕϏ;<~C_v׼_jx19c4 JmbY!w\fZZD;NXzF+UG>_bO"#w7^vyw{̻+1gX)hM+gϚPfiP0Er1#-auGbȕj%*ioh6Ϝugt]#x¾dEbb{bm  {y2a"oq{%[vAVB"OgWg) #LjCiU$ݗȶ`_ 0^z!HU=.4e$1hjY֘`k%΀\ 0+o p6y׵$S^l N-f_$@{ ^SΈF\b2z;^$+ԨZ8*%22X2`=.KYùݮ:$ejnVr`& 5:3kzzO]DB!XJ[o\.Q7} Rib@taL* H44(;m".2og6ZЀ%17a3¹Ц}3"7+X;Meh#_"P-nD/V+Ӥg}7[_(?q,]oQn̪s0?J AcPDKm +cҴ B`@b\þZ/yw2U6h m[>&g|+5GrA:Bt8DTkY&ڞ wK4%h| du&8kyC35\-ޤ:zySrO(5nJWmw7؂{lrt)dԮ: q8c n]Qլ$Z.9w`S@p"M`I0C{:Ey|\<_ awϝjvj&zY2@LdP sP_(J{Ю0=o"ɷ[SK앴@(AnEsh+);P[\jʗT nn$>'F!>C2w4R?/x^kʑG⬀ʊ0:!݃? ) =o[9Z9斂'a#.ZQ]gmt>S[f_Lm|. ռd}m|E%U~!+THsZ`pA?:{6 j`[ yl`TFy/ ͕I"6ًlw8tѧ#U\?RZ[e<)ۦgehkZVe Lٍ5jrw3 9dK I7!L;S~K, S9>7][xL *L,;[ueޙfcmVZZސ]|O ?J47 }dv A}4-* ж&Z_.۩^Mawi< c=Dž\W|#023}22G 3q?  #[s=eXF)d >[ptF(a@JK9TZVTNa8MFFMSe9($I4 4HFx7W+Hщy7oHsl:_b뢬zgKڗ>H{>'$ Mg Bi% V?ıw@/+2-x@ ~f9IY,H-2weAh^qo2iiӔV% ] 2( ׍pY0ބ.rеDb}]s -6a-㇋/;>WaMDsmH52`Xb2dY[tRp-Z)Nk E|N%6#:!y ^y 㳇VBO'KXzx)~P1.7U 9գBd}+̳abh!`$/J̯%JH(-Dþ§I%&aIn> &(0S:ӵHLǀ,aB _֝hf\;98& gT f%0~Æ Ε2AC"0斁M0FD;"FrJ2&%vh+0i:ptO,Jd1˻N#1 &1b<{\Rs ?F?t Ц!*{4g%arDg3XqgKg6;)bAL"D,`,BϞEK$ 8AeBUsKj6"?u'-%j_XO<vi$P8U5.b5_"$} bP  yR/!8 b` 3P#׺5ȃ:df&POHbk_.3HXݜR|1B<o! 0#?=S'V!2/j}5,A 2%PͱFR(R鳖g%RHJ/;ode5/X 'y<\A!qeHK($OO؞웧?|}ݏ_[`j׏qLڽzU#T4E+QZk8-@O9z P Zpt`P׎6P_XR{&WWr;Y,\'~ʂN3稚f)ؗj:0):45˦׉ G}B8lr;%#x/)RYٰ%ޕI]Wt~RX&VA:0^ kA]P"FxRH&I?~p0ɢbMh1SG/Iz5z{{AOHgCup1$zx=(_~7B۪2Ԛ 734*x Azpa쯥[Zw=m梖 @rNjK%Ɔ!3']?R4]Ss p&wb&R.dnB"|j. (t܆cw}>jϟ役h~Z =}soۢcRۋ"YĶĆ!wGz|WVt`͖Ld niz=75\VʝFt5<$Sl^yA-ѐnGIkudz950Um%G#wL;O$G`l-]:>rTԴqm^w=뢲rO af\<]zAo>ϛNy&Μl ud 1WkO2nvbq,O( F#@;~ޣʆzԴ> ^&UP-+E2p< 8=T*&sUKPA:4SAy CenST]*7Y9$yq !6=\Stv(۝. [bE ўQ6 EwX~ G_]gz71l%# t}bo ==6#j{:ű+4Kgt`Xg_5[ y)31޷)X[{ &[mIGto? F5b!/Ǡ$x6Yp$ >>HBO%E˗Vt8?j!O!* ޻faن ?~G/ -yTa} h³a>^5og:j law)sRE(QN6&}ƟCv˞r#l5y ]Bۦ-N=1'ǘ01>&+@˯l/rnc$!̹{uj•mtUʶco#!%;18QѤPM؞$o 톊`9`x6(N}d~\\qpg8{g;bx *X@x30ƍ{۫kP~0\遫pqdR͚.ћ ls; Z'R8_Vؔ̿Zihu 760wo񙏹=.ukRToa";' aoat]5ت r U_~ &3ķIDh).Z5{QG]ȧ/pE(q}tYDVp*QIJF1szjOoP/-.wU.~5Hgh#[?fWJ oyXMr^"rg ;?{Jgs8\o֒H}-w蝊7V(*Uk䳽M% ZW4*]7쏨BgJcCkpSr : M! [4165yأ#xozߒG]iˬz[GweS*w7҆V1,٢# Pd)L }+L'R[k2ꞩr Ji@$|q4rAo_zuOVG'W}Yh&WebOxyKf lk ލg:^qvRehkE̓SsdžŮg* ~+jcFܝو}.yGsJK.LU' ,Iu`?V,{`N9p?=.MmBp/(sgLL xMCxp/޼ui ;38O"c-,# 3'c0R<r!CBlH]-yVU@Z8+ҿMLׂD!V]5X=Sq ̨c:p7^ $,\^U7WLjbf9$K@I-JحՌ'@McG5ŸE4{:&{S$Y('fXL$wRo^7uo+}ÓQ_dfJ,# Fkvu?y'`hE gލ MEwM}Q$jSRw^ST&ٌ­.\]~at%Vi;ehDȊy{PK]uV[1setuptools/config/_validate_pyproject/__init__.pymSj0|W,~AZ0m -\9AQubKAs-l+8CbfgW5 TޘځjzX*{D ^ԵAIyR(Zk,ouJBXw DJѳOo?ٯkߊOi1_J8(Q?%Έ.;p1-H2Z Z{Ax)^ָҤH>`B˴Fi Ƨ8x:_JbUǯ9~}" A29t1~Of/`}\8M~;x~֘J1* 5]y&,BZRtdB.H8H3^3㕍*Tx X Ar'kcl%D~9qud18_|#GN&2/|,_>.5W[w`wB@E"f@k%-"ؽn>"]/ &eD  斕ĺP†-\Ŷ"L -#`Z=Y"u C%{7p"6v$n&DfJ#bhx^VDzd&)? !$mVE D@o=XAraX,JJ+X [G'}zj }p/Xs@J%V ڢ_vע){)IcaCK|0?+J( y;"!A6aYF]!6B& ODCs!U (1. q/}8!-&oOjr~qɳR\2_CS.]?YȘg|1Do}0H8JoKHCR<s& 8xz|'֠M4 4k\c* Yᔨ*Gp  yx[Ԯ1/] 6Zs:hQ@Bg6m(IB7O6mһ։Z wRBXrGG:Ԝb/v&D͉QX'4[^PpgWo]7!x iT_|f=g0v /-kQaQ֠-6 ]~֒f${ ok_Ǜ%~7ͥ=̩9m<yQI#!<*+K R(i9v]jڞш V5QF* oV5%Gkn;>-'L<[Z!Õ䘾& t\hZ9x&#uD'Aj~ϣ'tۖ_Μ^ ԆtԨh)6aFcy#b -`k!9Uj/!lİw^.%75􃢽z \ ~ Q?_%ZG:,ep2|{1ף[j>Zlm8Vgs^DSHvqLF6_'gqT{udr]|:uTdnr6JGJ=fv]]K/kɢBdbLs%C+Ya!k\#^ 7K ZA^ӐY"\-y@bGGG|T $(XR{I2>Yذ z`+С$.<{f T@H]|u H-L֣&RMqoS-O{?TAe`fJ-q݈p^d]qD{%(JsjֳZLl~KnP۷DQж=Mx¥ Fr RZdgj:~-} ~ZlHXk:5 ܸO'eQE%-#x8t K`#ƣ)z2UUvh@GDaNQ@7uk[I@L!iSUAR-t)Ч͐sbL3fM_4ArԞE%^{s?"o}-i){ fFP׷yW/I囵!{F?(Ui]l9"-K[xi3-MhS;># f{ IF%F"ϟAem87P7k?VKD>b>z!uZ-^QVYz2>,EH+zZayf ވu""vi(7/R\lgZVZlsuS7qN˟Bf܎&C7 d7NwIU#Ssdij= g ~HZ#&1C񥨬xGh.QX~'~ut%X:u,/xm^ui$P;pE=כߝy A= _12fJ. ^HQ'TR!kg[&^RO{^Wĺ2iT#JRG)ݯI7ܛ:IE];].S0438W-'5 j~j+L|յ)} %{Zk{/?X7EN=5Yj] g/="@A=CTC~o޼ o\ZIm4 _3u igrɊXgPK]uVGA:setuptools/config/_validate_pyproject/extra_validations.pyTmk0_qh_l` t/4n(%UsM$w3%}'Iǘ>=w<;qjS d(Q{YI(Y /vEVʪEB>p^lhQfp[_}'8uc*ދ;b=,9\{S-5wB!tX9B?ɒ [/92N"ss7gVTCE t]lxeQyyk|S+z˶5HBNHq{"TM[Hؑpq.p!^]#iAx`|Ԓ4 >m_5^u0?[3t&:uG0hvyD43c2]İe]H/X\%ZG g*_$^͓iS6O|Xu-8x>[f$Zd U:>y}L= vd? ,5_chfC "ĉʗ‘qͦ ,Bƣ]tqw= yFx9JF884Y\\x:rvNia"cdɘdɿi!,^E THBc"1'#>:|JB'$k2vrmϯ_ߝ'&piRZ_Pl#߃s0Uy8}MbP~Lc$3[\{(g7|.%'~ ?Ï ;`;)DU}Zp`~X- `z80W4bU>]:rJ\)]l!3BN8)^E۞":g>Ѻ$0bsߥtpLEP oh$_cSgH콿{ɸaJl"~p\'T8߱90u;Ie `2HqԈ4h'B`gOXtX,gzB;pD|9G5\q?xP~ѭǡ _N/GJ25sI{g''K߿2L171J'T8z{ ڏ?pCYs[ ֍:&IL&O')1dU8*pXѳΆSMH` }9N" %0烟TU W#97 CC{&<z?@,q_YL&_^]fC0PQ A+A?0}Ҙ Kted ӕOjL%fc /Ȁg.x`ùIN\F?άn7;{] #* 6 &^T^Dj0ɏkN\)T] j'R9SY#/i-?Uӆn ؉0fzႁ=, ,!LL ID<& (7E(7ev@HXL %3| na8@ ;#Ƌ X8P# ]\peh1!A9?% L'Ji|/PcC;JLw7$/B?$p1]yw.9q#Ds5Olx(ŁΗ,<]hX7OlvYhRpN>t:`ZΦ ׏Um@e6֗ 0%.}}08:ofM7:它݀u+=`QVe6[݈iUd. R@F ` *q_1^"}g@E4mZm2=eZޣۨ{->&i`P Z4(Hߐ#Mȳd\SF˜Mq=uc#4Gy-Mf8/!ci.4D!^$vhw'>%`*ߣDNe#̍/̦#Lb4,UXbV@YXQ/#,4r5^̲՞feLn 9['/}ͻgqMr/vmjOҡ)#$; f 5z9ԋ.&8pIB)fMD5f)_fcӢLB*LY rgRtcCSU!CWXg7IN<ضɢzVO!3GC& .n]9ѫ]W_`cP n% Y\<)?|A73fiH4cyy˗a&,1$ ;2w='vWpqh;Jz`ėٴk}n|%N`E/yֺ2n GYqnU;yb02˨TСaI]&t>BOQ^pk< r^%+(Ox܃Lk$QQϓrN7͆7yҙ 9#`}w1M uy%=r).6YМ9NEIJ\ge{x豫fURrG3dgm"&US'Figϵ\W4j]koND>lrJ2o> 痋Qs"C~}ٓW_s#xb[>/:蚏!sF\o\=~L]oT Gi_rm ͅ|yNɹi1P`"O( y۷'N_=z͋'\X_g籋N r+'\.0jcKi)g7M;pҼLM.9(<&ˆIyh5bc[J/yR~(PmG `Mp¼9%,ȏ$ ʰJ~)7* sHѿ0ȕTՂY[:}$|0@lwQg.|às:// w-I+v@Uȝ%wE+ceËKnYD Lvj|^-q0N$(3C,ON?J/iM `)">17zhK {3pCC'X=x9l ΞBuYcEt:K`ZF\x91 οKV=ǹb}#y>9׬GXTXq9c:Iy|ai(~q.㕴D0'sX K⿧qpYT "A㜖i%;1Gspv9eIyf%x ;K&-FLP&,E ow/J>-P@+.>+PyyAO3Fh;..2/LFOdĵFu4n˄GuJiHW=$a)h7(fDa8(5%CK ["Fͦ,v{EeK@=/P N-1#M]1)Y>],<&}00e?.>@` VA\R~'Z [*$#J{.bWvWwұ{(,PjEpw턹P).pԨx`]%/fHG2/V[/=9أp ~u.f8;|DP}6Q+B;U]rW}yFI ob VɉA5_vG̟;}YJqT5b8s[v_Fp1~()pdPtmx"턃8׶b P =(1^!W23Mm0xOޝàU/뜱Shb 4E0({/Ch PY/ /'Nd $%Nֲ"b?C.'\ԾPY$`P?;yӓgp`()x$1赨nf G% (=RK]yZ'&(2;SKt ԋlYi0Y~qX]ۖp2"5)KIa,Xvxʾ26͠>NYA>2Àŭߠ0DCќdF7,hXuX&{ E$׍08(dL 1Y0e ú<Ŧ1Ur6Jdd$gAx` %4^yfv3> 7R3mG^bT7((PN}sQz4ю3hK5?qFb f@)c!^Y͋OO^d(7WO3_Ow8Yry 11!}"ؒhs0`[":S@Q(nf@xH8bz}$F UzԱD ڛS&xc7;8)ݹͨ{lY$&#HN&[~WyT鋃O aҳ"#9F;oء1v"S !앙V̘-4z Eɞ=§7/>Ksmď ٣Q͊`.%Mi9b _G_N ' b$[d{Hž pv̚hVYAZ;h#ӗ*7 ;sɶ_e:c >+e)aЖ/QuiT;ƞ>n̰k?ּn[ ,pngjhtᐢ{x* hxW0Ƈs;^> mp+Kk.VS@&e]_)qu?Ű}<>")A V! S׾<+K'suEHuC>& hB>& hB>& hB>& hB>& hB>Fэ!MGф|4!MGф|4!CGgc6-} Eר(˦6.ʰѯS{woO6TѳL^D=ʍY9^,~{w::KiX͵Tw6(BgUN,OW{ 5B P˨UNfQ.‘Õ9Pz+u]<kllx CSM<*K`UVyY^^v RViU[CA^tU}Nk%vƍv3'!lg;.;gu[+'l:xrSjecsL֋;v ,&#]]%@4xJ|5-37RڼҰBNbe*8-hwܫgЪ2klE/|Cޱ t][h"7yI+=kC )XԭWhfqͯ)p VW) gbj鳘,'> 5}BL፻]٭dCe!0"z 7A"Zŗ`-X% ?fNe⡌U(cX2VU(cX2VU(cXhIyjee;t^y@gq*]92-3៑K{ZhݞLI x}K8z׵,x#Zdj X9QO"9ȣ (\_7!(M{N`,槆 Ǧ3Q.uJi<(E2^Pфg4&= uOCP4= uOCS;BӺGi{Ꞇei{jh&P4= uOCPC( uOCSaJuOCP4= uOCP4=}`uOqBJ2!aH`!aH`!aCC0$0!ag !aH`!aH`!aH`!aH`ދ!MGф|4!MGф|4!MGф|4!MGф|4!MGф|4޾ǐ|B>EbuC>& hB>& hB>;Gs#*Z)Ud G76kș?߰:k4c5 V K؍v|^Q("q(mݗ"p!Iir&!Iir&!IiSb*/v B@irF_v(4 G%4 9BN(4Z BN+iRl$di+ˎ=!V@n[)J)jCT--ܖܶ{/lr+\0s;> CrB7B= (^v%iG'Ht\~IA)[ q~ň8:߻{{H ݊z,(tWWnٗ_avLko,Ѝv0q>iIV<(3'zZR k>2ilJ4i-%z@187G{x@e%4auuD`g]fa]j]Z9dq_I:HD볊4.r>v8}_&-ȪtmI<&]iZU)>6>{W%`[I0\~+1|=k@.|m?fFkZV6nêz{% ϖ: wN.]\4J_4!:4]/'yrzzջ g=&LKaD6, Y!opDEl[F&0Ƽ!4kzjhAKV]^:J7YޣNTA<*y_ q~lJT#Ԁ]] itBShVK=G+Y%LHF3׋e6s_#LSAJVl][=Nǟ¶A]94G*l mS"$IL;nc7=XlMBM)-[U hX]C|8*=(v]aEBXK$TaaN̰oG2V4?G:cG'h%n!v)7hj0Pޚًn9x7'ܶ ۧv oU]Ik}>= G6Ϯj謡{;` 5m"쨤)-mL:~Ґ/RkZp#ɴVD Jȩ*w0oUާpka-ج8}N)0fZвUֵuZ BU{ykzj _%YkWqӪ=E?Z/(:xf/Cl_U?- -\]%iRȲxΗ(d[Fn9Yz5ѫtrD pP=X#]F;^c:ˆXK{ٽxb[Lz\+GnX,JyrLڴXcE4=$@#jmRASHV9pSTYgc ON!ZUjkMN޼=yPZ$^Q T $V2WӑHjESY`Ufٮ&T8?0a8F"8>0 %pm%I4(lXQcN*vH%TU TČra)B@R}`6LZF6OЧwCԡjЩav8 "bi)kɭfj+ 6;7R;u|}*˳nee/QmƀnPx8QfZeTek?P4ߢ'A @Uf].q+( IǣV):uNwplwRadWF{e.`E=z={UJfA:9KŔJ`t4RچvN\ yZ] jX(_fkESևVlI :-*ߌ2՞6N4O1]m kz7?Jhn( ʛ1_vi-ߞ{ՓG ! ppyL W:OPs⌗~s&ŕXE/ϟz>)YO_Wz} 鿟z߾ۧ,}SypEcL0f  Sw]Ev6Kښ7kcR ":6E3Yi;ʹmN}^-YT7i{p2Γ#;WZVmwZyjbm=+6+j׊fgD"<2g z錏gY4+]PX*'")Ez̈ӼulrkW'!4 IFdMv}؏#DGNρ3_$F+W/jPfS .@`~E.*7x'١BjP^^o3%Y.E(3 t_G_N $ @o)6w;Y%{DKglE״ Zbx k)LA-0p6uOµ7ˌKOZ8rKXmd ǡ 5'[gtp2nYx|P"`ż4dLM({&CEmǨ~:'>%;ɀl1,~/4}ywnEWk^wېkm:BK60r-T'Byק` zb#Vu+ `miM^ҡ=5ۗs Lɺjk Ek_}QG/޾TKahoc1=wae7PnN%|bkkw1vzц/@ jkk{Z}?nk.ں9XSwL,Q zcAdGuIm0d 2hڛ;͜OyU7m׫fH{f=-5g~<\G~Ӎ7^֮v\D`Z~n*PǷ n]iAu֛JkϼMaArUxu&p Lr}lHmh\inčk8mÔk3wVjen2дY كdTY+gBڲѼm amt%}*-`Y~51e}Pni꥔Z;jt#s ( :gr-}>d2>X0Sm,kmWYr WÇhO$<R):khC@7kI/ݜ%6wN<"v:@㶳3꧛X0 <`[~(Zk~׳jx'*o"n?l&]yu| Wy뫹yJGs1,<eyzUUp +ڪ`{ZD*^8) y:IxOvӆ)5 h{F@EzDJΗx\&sf/K_mq 4:@K?|-L1;hgW&|0"E)VIhdzbeso$Mi~Eupd$_B`k}Tj2zQ|Nr2ꅱqCwrC ȺCxvh̀v"w_9@% $]'n€2dtaX^3<ѤT^C99!z}zrJ1VaH䊿׳tDXsZWtd>ѮȺX3~3=mABg~EapkI%+(N2u9KX*^GwJ}mMW&?9׬'XVuvNA_0g( JK0D Pe\q3V0)[]qnEQ+>h}D&'cpO'k"b2X`0U:g׾4Hz1$TUwDJ~5%bwSM u:v;0簳bBLPbBL,jCi1}x !_&b"e[ i w ܌6!DB7BF}+mđ99|81Gb8#K*9mtO3?8槯1P-p"9SVƪ?lEc5+Ý@Yh|:gVV"T؀F8vZЋ3*J*g@pg [ l+d0ALg)CZzIU"*-eʖ2j]* Utdˮ1:#YQ)[Daewe;Vٷ{AX_Q9Ҳ|y`QC4zYrh泜%~4z۷'N_=z͋'ܹuiC2jQ-/i@ I{*a/aN,dyNlԶ53_ߝ2,4$U./|W xA& p`TZ~ݱ%/_Vu/'.Z !XAڋͶ %#蜎IvA%Hdԓg/O4h?~ !,9x w-!ﰚ*p;$3rbU"ɣg ,Zq?w\H>-xց_O>iOܺEI @o< ٿi:a'p1M<&#*. ,_)c(⊕GvS@{p>f逆I|-ֱXMIʌ1fctQZbɏ)b&9qy^qhيeãw?l^e^j3 .)@872^.T%蛕|D\9JģeIAb6 gȢQ˻[Z=g 'qyQ)7\hEِofX+KZVӌiCP7ȂeK3-e+N}5 g%3؟8p9V|MRFHIFX y8Ƣ|I^ۜ'w8M0ҧZeYO3cԀ*ޣ~VMl9x݉Zʖ,p%ia]&"*% q(eDxtAd5`B%˖Tv"?Н^>ZJfQF4TL][<7јld Ȧk3.̯*l{s: {}:Ay5Zt/}9#b6Y %wS?.Ȅ#),DՊD' P J)/| 9DAj |=],,l{t^$נVr;|CYXOٜZ +0/75=t#1,[E =lN3:/-ҖHUzljfoRmԀ5廒">v(s(Զ/|߱0d@f9%) x5B3SkΖe}L / -7ӕn٭-<ٰ~!nʽM}dc K*q͘@C".|9J"J=aɭiC}D%"tQ9G! rJ8:o;1,dcK+"~J9e6nAs = q}~cXFT[d3@h}3nZY%]W"|SD0k'diJȊ_;K;KER&{o n05n7m9 q!.9%C\rK⒃YB\r ݯtC\rKqlÑ G&%zWKq|[B\rKq!.9%/̊Ý%Yd))'NGj6zؒSبHN3V*i^ln/5-^yo3Yc <"ޚ!iiwJvΔ\2_vFj\+:ί"mNuxgYBb@{ά\ac ɳq5wO8*6;jG۱T#TO j1ՠ#uɵDp_nXO6R 5#F#$ٲ?g'$/(vf"H6t}Mi,~ȱ~L<[)q]OB{龜#@9W=[?ImPxt{o Dy$njC>NF`"8pןɮJ/ǽ@_g^KCΎWSd}uP{09=Ћd {d7 1^-@R( 'Dk?gyV-za<[fS@Er|⯹^iH/Eݩxa ܪx> e;‚F^GX2MaA#,p;8/al$,IMͳ0"b.e+ Q0Cǭv#9Xo$כs_#unU@iEACaĭ{h#7X}ޑvTAAU:D_iIѼ ^m#DC/=`]цb #G8Z#]7B.]dc+PsF|dcX"T-Ӕ#?CUXx/31\J47 U5,Uj"ӆ [1uÐ^*hA 8$(dCF:]Hm$Aקּ._b\j6Y x{ S6ߐ{lliFQxjRxD:Ԋf!mf/"2И>;>^vz5jMOlVXfCt17im̬:Ayb9'۰ 0aoHRnfSk/j4rs_p7+qy㿗zO]sJ8HN[1ɀ1jYgىiܻ/KP+x8eߡÏ6qseB#F1w)>~2A#m*٠'ԕdЩa $U lڙe\W_V%!@pu{$ LЛ  JAdn&d/]:KB~@4߾$V? W/U sUqFق9'v&C,YA1ae~18BHoφEwX@3nFM*R;[&ؔǏS)=ϬT߮+_kuv5,RKVQZuJ.oUSB2M To:ѷ=ֽ.-kO8Z-,{A>f*ie~ 4UZJX&@=l?HO>6H)YX~@M0˴ֈg\zUCc9zIau6P'R^ѹX@qeۓ_Nwz^OUn:+8u޺Q6_zAa #d&TKleM CHA-Сv}[Y&x9n<*e${:h TfH׃xifdTD[]'-RgOEv5(F *aQh$|7v_-mS;jfz0[ *PuR:UiJJ2K7TTD壇h յm>Z NiUK:w}ڌjvTJK|kCPVk&< v<3ԦCP0T.g /nyioGqŠ^iC4[&־N Ǫ.lcf()U >5=GJ /1S2l.v$JiC=9jUh8V[ny3)-ʸaBPU0+=xmXQiX4P=*|=]|A]}[QY}]veHunK >6©5*ۦ6@piSm W SPٜɉ}VvfƏb;~Ύ|׈%Y^a+Eޒ]k*ǒZ*a+A#2c8'oaAx!(x,'k~O<&yξ )r[P*ߠ!JD<2*T6FR|ֹ~*>vt8|ެaWzxx¹#tuN۠e:/$'9{Ilu̓<mg7G~\&PA!WGj,!] Prtx/ʒ1fyKL? m) i;g@ZSްT295\1gIV}0(p.۷dhg1$9iu: pKgH+E@<{$7Gih[G"n~#2ǔC̞M{5Uyqt!Ќ4䀧|9X*1A Ւz%)K\W(FYvc Ͽ:vEHvᚡ|/.=03Y34%axIX0#L82GFs٭!s@W`^yݰζ^m(On1|RDٗn+d'|VO͘@C".|9J"J1 ;|XrrsZPRzAkkcG<;::|cyb A:|' 8;َ;,KusnwKGsD݄G& ˟GI492\sK,U6|A&Ͷ+g=^­vA;H^S;L|avvفNU!~ڕjv^KRMZNMz&2%x< |U03SW nFs4+ǶB>}sKvrV#3wQe"ho L(폖^+X2>I06l6}cϴH:H/ 4DY|~.cic9t ESzenhHM4T_=>h5Y-9)iw'Ոܓ o&` zDŽZʬE% JaP RX֤4 ~aO&ME*ʲ鍴UޢR>wsk;U6)+[Mr. V}uyIf H "ik"ɖ,,7H{}gF= #}QZxaKMBETӷ-q65rTU5>͂oDVn3uiϬmbY;ok`!mnTXmUp:#~ޑ3X1Sj:g= Sݒ \ C PK]uV{ "0setuptools/config/_validate_pyproject/formats.pyZs۸]LX:wUv=QGGeHP™#A; T3goqDz͓ǴrV"7$6ç0OcEF `~ʐf|g{^Bcy`&3/'gt>\r˲~7'bU'X߮oHaC쇜EL+٥A˓c,g70,Fޥ{uz{ѱMVw](>7:Σ%ngVJx`I Ov U Ue=׻՘s|rPӇ'C?g@yҞN|mÚvS GOr~~1P3" &:b/&&?c2OGέϗ nX@Xç?YCe 3XH2!cS3Q ivc*M5! I*Y殿~0z7trb1{q:7BzW덮^TkІCj<!VGN=s."* dɨM1:iV!y2 9}Y&TM<NJ$:c0GVm2Si%AvtASf l|TkШ jJC\F[eWR&e@B0WzZɋF< SxCnixYrV .jM؟PiX\$yLa=_JKp=1$ Y FP] [4/FX!L̿y>La8Aǭn ϳ FNGZ* ƺ_~J`" ,)*q /XdR&dCѱXϖ C@@ Uֱ"]P$.¼u1p x)wzFUG/ ;' IԪu7T9>[*Lz޺8S!.sOh婌xٹw6t~/AƧOOߜ'?')6՝oEk̊ր5 JE!6,F,5 h}B6͌vY UUa5Ol%F PTh*Z20Mx@{UECqAq'`[QDz`1;Ku7xG`QO:kk~rrz&-!GX SQK_(CfXF*1TDM `̤R- Zg~[أ~q~;8Dڶ:I1@6 챃\jK< 6-syҵtus,ʡ&bz*7V{XG_rC./8|=CK8v"6BՋ( %SuZ\u dʓ|;Rp09_2;[meqte/>]'wN,S|O;^L^0y3y3|y뎮={ޫװo||wqi2}y{`_ya%^nKc;1]^C:~+!Qjy]3X4bw!V톸]ўjoBA4\rGtsxكvxBپTu閙{ =TZ(3%KQK(ZZYF.x@$&!X &FeG{f斺[n!Q']*ǫ)԰ZT V}t eVOf¶fP V3r+*K2,{Ĕq1V4#}*`1|I"ӥzOʬj[F2jO;PK]uVM8 setuptools/extern/__init__.pyVK8Bh$JsAE{A$n/mlj;Uv^n8m43r&㏒:%?޳@wp g}`~{Y3"8Č h{ă. gp".H@Wqzg \ՌA8ZK7њ-7ƥ+eۯFChCͪ6\$E97[t'J胍D eOC]ԬI׷ .L+sµgNޏwl@gÐ8:}aq1mi&<K &$AuW̞).dbt>О'GYb֏J^y03b KhL5Fk,5++\E#-zi" -j\JLͱɰP>{F٪|YaG9oUvY9X+i'hsp 48!]Tg .r.gdX:jY !RI'ς|07ir" ̊-?o~lזӓayj'9/56Xm]wA;Pn&5 _@y-eGݜe4')m1i {ơKY 倁yc5.DE@H kp5&qcQ`%:Y%NhhAE,?PK`uVYď`#setuptools-65.6.3.dist-info/LICENSE]QK0W8RDy,nC9fYy*(6E;WHg{ͤ:SWz &0VBpXuz;NS=F%pM zW؉h|k75(p2H1puu5Aَwt -N"n꽵b&ZHpyxk gs@ίξsG9 . tkbbkGBa~b18=gp7ϐʅ+uo?M;^%nd8ɇwfc.Q#RkZYf"H`/F 9Z/[YԢ@i&e.dLOB\ r+ $"|%si [KSZiPrmd˹rKU ϐZ؊,Qk Ն9I1CAʃOg+*w) \n?E3FP8~*g)ye%ZVhBzZ'"LBYh"8B]% !eBH\SV12Ŭ쾴GS~Q)9>0vRHa^<\S@n_;%ZI|H} c +H~[tkD]]H!ܢKEVR$*仚,y p(]JW(Ja/?I+u$Yx<л1^ ;a>1q5Љ,d1X@` ;Af-sfȊo,(C;tPA&AçN|& Vjl\ÆCHs-}DH0Jc!KMČ4"E}z22Fc0%~/faQ9w_g? [vAH,۫Oʿ,mXEkר Vu>3;! 5ަk _r^4RdS kdR)ƶ?k Kxη"Upni!`:ѩЄyQDz7Q\:X/r5&(_+1ՋjIGW0esKۦNQo8_~~o\je^ ^L47& "3KU[KR#K(RhVܑ#muz^wv*[;] 2@Ym{ٛUv_W7`鶺x]i1ˣ¯OxrpۗRa?i9."peqƌZ j@t723QX5 ,setuptools-65.6.3.dist-info/entry_points.txtV ͡!V*{*I;`LecfgfȾ^HkU.w/__xo.B4 Z:54[IbyK0iz! =@4#R4܈ӛTDl%fA:f@vO-AWLE-織몒Ao5Cԍ|'NLtqJ/l;ȒE/jqZYZ+ĂDj=kGqgBdB+A^5a!wm%ς@ mD;p6L T;wWxXϜB?em 3Z凎 +I[w oOu$[0PiXmIgjM.~D8C8gp%3su;VwZq`X:8. *gmm#J=AwX6yQ"~I^Ya u˸c;p|?y/ ZLb#g}wvfv19xDwVDgqZ~$x:~$A$`?ñ rTDc%PXR"'6v(^aLC{~Ŭ3e?)PK`uV;*))setuptools-65.6.3.dist-info/top_level.txtA 0PP\3z9.1J;\A{#PK`uVLFE$U"setuptools-65.6.3.dist-info/RECORD|GZvG͠ @B B{_k,۷(#ȵ}Y$ux^🺏w]@(Oiz-BPku R; HdmvxvR&m^ u }nSv bqU:; W AW.qg!j 6O`w{0KSod $XA.=XA" jh{ SU0q bl:~M#bAW+lg[GZu&=1p @%>ՁwX>I00¹]< E]mUV#1m>5'ۃлu(6;*_1Y7s#=`H.o!SNI9̟H<}#zU\cIhb};}r?C܁ B1i.Jci7>⡮ #~\ɂiU<ʚ!0,]^?Ok]*\t8*nUa"[ڨlj:V.-R8m#^=Wݙv#TpeaUig&xwA7E4CEPySeO;dDp"FСuý!K<]J˞|vT'~w.R҄fgOQKk8&J.v]%UDuBp+jqnBU뱚}'AMz%XtG=p+NWɕQR @c}#]up$U r$]2 π/:ڢb"|yf*b"ǁ0Yъ'OPDxvnU{sϲj֣扝v8țxTTkׂ3G};fcyEP-֋G5:WE W8-?ȹorمv8A^Es640#_u{`M'JVyĶX'Meg_1/\-WCQ˔#TG+dA! şڌ]J[&NGRPnRZ@d}gNtj e9lޢTF."݃)T&\IEFv8q$l}wVsO+Y+]LSb1)^˴w ?zoeWS SaDor/Gs凇SfwϏW(ao麍kL˚5#W'sBַ^y&u?}Q*z[<ɿ0]V\NEi@~)7uw'4h½=X CuqP<Јvй^ 9&K?l*n6-t[^`OyƯlon.p!;[|hrނDr W]4nNq<K K)d& {SĉҨ0TȦ=F4Snպn=(i=` EFujJuScil6#&h#.s;%{Ֆ C`MQDQ>q&w5^b `w6g)_iD ̞/¡KBW+9A!_'-Y~]'Aw'Wͱ3ax>pӠ }saWuGDlUvծ.U iWD2>[-~`'O oZL,Z1V;W憋|W1[wFb;g^z8 |O!}2=t8nwH 7=䟰 .wG,8zS~Kk嚘tdҙA}C0R{KJ!O5&{UKPKZ90(n|)O3 U-432-ŰtA8$$A}#a.і^Y|p qcBU'E{%$h@5m|Ay~OV3=zc  dx#rߗGw]мdax8Եv`MqF +;MeCNoBe P0Pʑ3eAl,Z6Ƥ$caGoC,LLw-΢$dfb+ &ŸnB}\$nTl} wU~H BiQmQw&~%y@Ģl3|?>T0$Vx8GC; ~:Fn%65<{ُu)`qtzI6I]/]DsrvM,L362,ی;'z8vBIᢦ\uJVF4Os1Z&a|W+.RM ؂cΜxapI(.FUm>>bQD9iE`v Qs_|Kۊ'Zk^iq\Wo,P@M)y?1Ѽo 'x[JϦ"n]@+[W霭y=:51]̽8ބ 30';A[!7tD}ʚ_rY%aPxį?6؈y{ޗ]rPA evSѡ*z %>s< oa=2\pZ)Z+/.cqa coۖz 6Cuhwm-V^XK:. E~ݓ 3k5,:e@NP_ޏVċ/l)v]Kv,}Tʜf/hK,}z $=疝(#r{UKhrq2^:4D"AQ7c- Dlr I#CQf oGpVUf{)ٽ?N`?z?ly:xdK4pJ E:0 5TkLǜA8%-'&fȬi2L.(y9%%22{ZDˌG$2Y 1,j$;8zx>='~O>,[,[݃&̣ɺQxϑ|_ӆtFT6<4M񶉹JVHG JU'(PO[EAvZP#4WFXlhIDVcwžT2]<dmI~ݗ*k?Xfyf掋^ąH 'u?K\*ckH\c<r'[/$u s$Fa $R6o&U_`V]:J8hIt{/^jӳyFt16:)#SC؊ xTuHpm>:ݦUoG6sTrǹG{eYyLZ?FG]fpp&7mxaJ_ .:8Q%u-B˧^G^]+]^I1RіqYWYU'O>19=ߘ̰"Nbdu*gOȗǫhvzMQn/cRRA>/7Wr v=ػ2e!`= uqTʓx⎷;xXNv3n+S@>ʖ9xzM % "lR.>v;ŰIy`bDKx53HfT:~'j%!b#vKoiB im?p Q(7Y#LLYnQ0 Od"hG49UDӡe( lK7ifYRg$;Sz1$fPj&>d A/E}E 떣E-y ̝\, 'r=s8Nd{;7uڏ׏v̦ś"+Y$}Xm~ֿ ~KMg.غ675Z3|K,Qew )]nA/^`-R|-q+g>wcYf ."/t_LfE66eN3a L^D*o{m?_R&;] U*XYƄ @Ϥx+O ,ݶAAtڻ%&W1r04vA𹖑˹Oh 0~kis'"-=1 Ė)zAaAI[wFLM<ڴ`A |DJ}HϷJ k2\ s%:'(0/3݇=4e&>D@0Na?+׳weˬw+B-)WU'`IUS@jHcB]!v8I7M^!(cӌ9.ҩvG8F`Ox}g1?|ԛtp G,S0KZ.<3+.Q"Q,Q &F1/}w85b%Ɍ&Mj$ZvƑ.sO7I2#8j%y) c0=`ܥ d#o%Ԁdf..:jQ&}?2{ŏS[f+v|fmMqfYur@j_:P- $=m}pUawPK`uV}a~distutils-precedence.pthPK1xU.__distutils_hack/__init__.pyPK1xU .,r _distutils_hack/override.pyPK1xUs pkg_resources/__init__.pyPK1xU!}pkg_resources/_vendor/__init__.pyPK1xUaQ}` }pkg_resources/_vendor/appdirs.pyPK1xUBq8u lpkg_resources/_vendor/zipp.pyPK1xUoq5pkg_resources/_vendor/importlib_resources/__init__.pyPK1xU't6Cpkg_resources/_vendor/importlib_resources/_adapters.pyPK1xU0Q} 4 pkg_resources/_vendor/importlib_resources/_common.pyPK1xUkhw 4vpkg_resources/_vendor/importlib_resources/_compat.pyPK1xUht7?pkg_resources/_vendor/importlib_resources/_itertools.pyPK1xU.1 4pkg_resources/_vendor/importlib_resources/_legacy.pyPK1xU}>m.0Rpkg_resources/_vendor/importlib_resources/abc.pyPK1xUD 4 pkg_resources/_vendor/importlib_resources/readers.pyPK1xUc 3pkg_resources/_vendor/importlib_resources/simple.pyPK1xU(ɽpkg_resources/_vendor/jaraco/__init__.pyPK1xU2& ,'pkg_resources/_vendor/jaraco/context.pyPK1xUܱw4)cpkg_resources/_vendor/jaraco/functools.pyPK1xU.ɪ<-ypkg_resources/_vendor/jaraco/text/__init__.pyPK1xUaʥuBS0npkg_resources/_vendor/more_itertools/__init__.pyPK1xU:$ν,pkg_resources/_vendor/more_itertools/more.pyPK1xUB΀G/spkg_resources/_vendor/more_itertools/recipes.pyPK1xUFcf,pkg_resources/_vendor/packaging/__about__.pyPK1xU+cpkg_resources/_vendor/packaging/__init__.pyPK1xU*:c,-pkg_resources/_vendor/packaging/_manylinux.pyPK1xUt10-spkg_resources/_vendor/packaging/_musllinux.pyPK1xU0`.pkg_resources/_vendor/packaging/_structures.pyPK1xU 0!*pkg_resources/_vendor/packaging/markers.pyPK1xU|%X b/dpkg_resources/_vendor/packaging/requirements.pyPK1xU5u-pkg_resources/_vendor/packaging/specifiers.pyPK1xU msS='pkg_resources/_vendor/packaging/tags.pyPK1xUA3h(pkg_resources/_vendor/packaging/utils.pyPK1xU 2 I9*pkg_resources/_vendor/packaging/version.pyPK1xUJ_ #+pkg_resources/_vendor/pyparsing/__init__.pyPK1xU/HF*pkg_resources/_vendor/pyparsing/actions.pyPK1xUe~2)/pkg_resources/_vendor/pyparsing/common.pyPK1xU<ǀD>A'] pkg_resources/_vendor/pyparsing/core.pyPK1xU6 t ?#-pkg_resources/_vendor/pyparsing/exceptions.pyPK1xU*,٘*pkg_resources/_vendor/pyparsing/helpers.pyPK1xU^b*Jpkg_resources/_vendor/pyparsing/results.pyPK1xU5[ Z4*.pkg_resources/_vendor/pyparsing/testing.pyPK1xUzx #**6<pkg_resources/_vendor/pyparsing/unicode.pyPK1xU-Gl'Gpkg_resources/_vendor/pyparsing/util.pyPK1xU-2U t\3Opkg_resources/_vendor/pyparsing/diagram/__init__.pyPK1xU͈z )jpkg_resources/extern/__init__.pyPK]uVx5 msetuptools/__init__.pyPK]uVJ"Xzsetuptools/_deprecation_warning.pyPK]uV&l#{setuptools/_entry_points.pyPK]uV X ~setuptools/_imp.pyPK]uV6 setuptools/_importlib.pyPK]uVTXVsetuptools/_itertools.pyPK]uVTRsetuptools/_path.pyPK]uVgsetuptools/_reqs.pyPK]uVsetuptools/archive_util.pyPK]uV&MJLsetuptools/build_meta.pyPK]uV+JΟsetuptools/dep_util.pyPK]uViy{setuptools/depends.pyPK]uVi7:?QEsetuptools/discovery.pyPK]uVHA*//İsetuptools/dist.pyPK]uVq setuptools/errors.pyPK]uVu=̕setuptools/extension.pyPK]uV޷Y setuptools/glob.pyPK]uV~( setuptools/installer.pyPK]uV2S, setuptools/launch.pyPK]uV|U-setuptools/logging.pyPK]uVZ29setuptools/monkey.pyPK]uV%Dd&lQsetuptools/msvc.pyPK]uVpm!N Csetuptools/namespaces.pyPK]uVN-iHsetuptools/package_index.pyPK]uVVDvsetuptools/py34compat.pyPK]uVQ짟 8Jwsetuptools/sandbox.pyPK1xUsetuptools/script (dev).tmplPK1xU3jsetuptools/script.tmplPK]uVT+~setuptools/unicode_utils.pyPK]uVHLbeNsetuptools/version.pyPK]uVVz setuptools/wheel.pyPK]uVqousetuptools/windows_support.pyPK]uVr7g!setuptools/_distutils/__init__.pyPK]uV`a%setuptools/_distutils/_collections.pyPK]uV O#-setuptools/_distutils/_functools.pyPK]uV=&-$+?setuptools/_distutils/_log.pyPK]uV];F&setuptools/_distutils/_macos_compat.pyPK]uV78L&hsetuptools/_distutils/_msvccompiler.pyPK]uVGJq |!%setuptools/_distutils/archive_util.pyPK]uV*/-!9%setuptools/_distutils/bcppcompiler.pyPK]uVUl=2ϸ"setuptools/_distutils/ccompiler.pyPK]uVǢrEysetuptools/_distutils/cmd.pyPK]uV S/Bsetuptools/_distutils/config.pyPK]uVcv $@#setuptools/_distutils/core.pyPK]uV> M.(A0setuptools/_distutils/cygwinccompiler.pyPK]uV_H%ur@setuptools/_distutils/debug.pyPK]uV5V !#Asetuptools/_distutils/dep_util.pyPK]uVm !YFsetuptools/_distutils/dir_util.pyPK]uV*mL4Qsetuptools/_distutils/dist.pyPK]uVdXsetuptools/_distutils/errors.pyPK]uV~& ("Vsetuptools/_distutils/extension.pyPK]uVBE%setuptools/_distutils/fancy_getopt.pyPK]uVV^  "̮setuptools/_distutils/file_util.pyPK]uVqq F5!ʹsetuptools/_distutils/filelist.pyPK]uV9M8 Osetuptools/_distutils/log.pyPK]uVÄ"u&setuptools/_distutils/msvc9compiler.pyPK]uVTc\%setuptools/_distutils/msvccompiler.pyPK]uV(#setuptools/_distutils/py38compat.pyPK]uVC cq#setuptools/_distutils/py39compat.pyPK]uV|V  setuptools/_distutils/spawn.pyPK]uV9ZVI"Lsetuptools/_distutils/sysconfig.pyPK]uV3\I@/"&setuptools/_distutils/text_file.pyPK]uVn<&o6setuptools/_distutils/unixccompiler.pyPK]uVDXFJsetuptools/_distutils/util.pyPK]uV#q<2 dsetuptools/_distutils/version.pyPK]uVaU)usetuptools/_distutils/versionpredicate.pyPK]uV) )|setuptools/_distutils/command/__init__.pyPK]uVSOVN2}setuptools/_distutils/command/_framework_compat.pyPK]uV!&setuptools/_distutils/command/bdist.pyPK]uVj :+setuptools/_distutils/command/bdist_dumb.pyPK]uVăV*setuptools/_distutils/command/bdist_rpm.pyPK]uVM&setuptools/_distutils/command/build.pyPK]uV?p +setuptools/_distutils/command/build_clib.pyPK]uVG#{*0setuptools/_distutils/command/build_ext.pyPK]uV "RS@)setuptools/_distutils/command/build_py.pyPK]uV.Ysetuptools/_distutils/command/build_scripts.pyPK]uVY49&setuptools/_distutils/command/check.pyPK]uV U# &0setuptools/_distutils/command/clean.pyPK]uVGl3'setuptools/_distutils/command/config.pyPK]uVu(z setuptools/_distutils/command/install.pyPK]uV&KW -*setuptools/_distutils/command/install_data.pyPK]uV2L 1e.setuptools/_distutils/command/install_egg_info.pyPK]uV D03setuptools/_distutils/command/install_headers.pyPK]uV9Qߩ ,!5setuptools/_distutils/command/install_lib.pyPK]uV 0?setuptools/_distutils/command/install_scripts.pyPK]uV>qp+?Bsetuptools/_distutils/command/py37compat.pyPK]uVK *.)Csetuptools/_distutils/command/register.pyPK]uVJ&Qsetuptools/_distutils/command/sdist.pyPK]uVG'= D'gsetuptools/_distutils/command/upload.pyPK]uVqsetuptools/_vendor/__init__.pyPK]uVD*;!rsetuptools/_vendor/ordered_set.pyPK]uVaLBmT'setuptools/_vendor/typing_extensions.pyPK]uVBq8u =setuptools/_vendor/zipp.pyPK]uVs u1setuptools/_vendor/importlib_metadata/__init__.pyPK]uVmF2Isetuptools/_vendor/importlib_metadata/_adapters.pyPK]uV5setuptools/_vendor/importlib_metadata/_collections.pyPK]uV{D$0Usetuptools/_vendor/importlib_metadata/_compat.pyPK]uV:oikO 3setuptools/_vendor/importlib_metadata/_functools.pyPK]uV:3setuptools/_vendor/importlib_metadata/_itertools.pyPK]uV..setuptools/_vendor/importlib_metadata/_meta.pyPK]uVxv.setuptools/_vendor/importlib_metadata/_text.pyPK]uVoq20setuptools/_vendor/importlib_resources/__init__.pyPK]uV't3Tsetuptools/_vendor/importlib_resources/_adapters.pyPK]uV0Q} 1 setuptools/_vendor/importlib_resources/_common.pyPK]uVkhw 1setuptools/_vendor/importlib_resources/_compat.pyPK]uVht4Gsetuptools/_vendor/importlib_resources/_itertools.pyPK]uV.1 1#setuptools/_vendor/importlib_resources/_legacy.pyPK]uV}>m.-Tsetuptools/_vendor/importlib_resources/abc.pyPK]uVD 1 setuptools/_vendor/importlib_resources/readers.pyPK]uVc 0"setuptools/_vendor/importlib_resources/simple.pyPK]uV%&setuptools/_vendor/jaraco/__init__.pyPK]uV2& ,$'setuptools/_vendor/jaraco/context.pyPK]uV4&V.setuptools/_vendor/jaraco/functools.pyPK]uV]"ʨ<*c?setuptools/_vendor/jaraco/text/__init__.pyPK]uV!Q8AR-SSsetuptools/_vendor/more_itertools/__init__.pyPK]uVP,xy)Ssetuptools/_vendor/more_itertools/more.pyPK]uVʀ8?,setuptools/_vendor/more_itertools/recipes.pyPK]uVFcf)}setuptools/_vendor/packaging/__about__.pyPK]uV(*setuptools/_vendor/packaging/__init__.pyPK]uV*:c,*dsetuptools/_vendor/packaging/_manylinux.pyPK]uVt10*4setuptools/_vendor/packaging/_musllinux.pyPK]uV0`+setuptools/_vendor/packaging/_structures.pyPK]uVXQx -!'Usetuptools/_vendor/packaging/markers.pyPK]uVX\, setuptools/_vendor/packaging/requirements.pyPK]uV5u*Q setuptools/_vendor/packaging/specifiers.pyPK]uV msS=$* setuptools/_vendor/packaging/tags.pyPK]uVA3h%O< setuptools/_vendor/packaging/utils.pyPK]uV 2 I9'`B setuptools/_vendor/packaging/version.pyPK]uVJ_ #(R setuptools/_vendor/pyparsing/__init__.pyPK]uV/HF'9` setuptools/_vendor/pyparsing/actions.pyPK]uVe~2&h setuptools/_vendor/pyparsing/common.pyPK]uV<ǀD>A$w setuptools/_vendor/pyparsing/core.pyPK]uV6 t ?#*u5 setuptools/_vendor/pyparsing/exceptions.pyPK]uV*,٘'@ setuptools/_vendor/pyparsing/helpers.pyPK]uV^b'l setuptools/_vendor/pyparsing/results.pyPK]uV5[ Z4' setuptools/_vendor/pyparsing/testing.pyPK]uVzx #*' setuptools/_vendor/pyparsing/unicode.pyPK]uV-Gl$ setuptools/_vendor/pyparsing/util.pyPK]uV-2U t\0L setuptools/_vendor/pyparsing/diagram/__init__.pyPK]uV%$ setuptools/_vendor/tomli/__init__.pyPK]uViiX# setuptools/_vendor/tomli/_parser.pyPK]uVu1~  setuptools/_vendor/tomli/_re.pyPK]uVg" setuptools/_vendor/tomli/_types.pyPK]uV;N setuptools/command/__init__.pyPK]uV=/M  setuptools/command/alias.pyPK]uVe\@ setuptools/command/bdist_egg.pyPK]uVȹ setuptools/command/bdist_rpm.pyPK]uV.I  setuptools/command/build.pyPK]uVG z setuptools/command/build_clib.pyPK]uV}]= setuptools/command/build_ext.pyPK]uVqB#7 setuptools/command/build_py.pyPK]uV&dd* setuptools/command/develop.pyPK]uV;3 setuptools/command/dist_info.pyPK]uV-][N"b: setuptools/command/easy_install.pyPK]uV#l2#y$! setuptools/command/editable_wheel.pyPK]uVPCh1 setuptools/command/egg_info.pyPK]uV@+ setuptools/command/install.pyPK]uVw͔4&+ setuptools/command/install_egg_info.pyPK]uV,,#! setuptools/command/install_lib.pyPK]uVY$Ve4 % setuptools/command/install_scripts.pyPK1xU])B7t( setuptools/command/launcher manifest.xmlPK]uV3|R setuptools/command/py36compat.pyPK]uVv? setuptools/command/register.pyPK]uV!P setuptools/command/rotate.pyPK]uV4F setuptools/command/saveopts.pyPK]uV , setuptools-65.6.3.dist-info/entry_points.txtPK`uV;*))u setuptools-65.6.3.dist-info/top_level.txtPK`uVLFE$U" setuptools-65.6.3.dist-info/RECORDPKKk& PK}0]'fxff)python-wheels/pip-21.3.1-py3-none-any.whlnuȯPKyVS:4@epip/__init__.py=PJ@ W^ta*oRf۴ 3Cv@ɷ@C I[JޘC%@vvcjl`poR\/A 7p 0*˲ߎR;? ~> ~Wh( "pP/G)X'kQ/[DTyޒvVa]4sQ]-CI [IҢWbHigv1"_0 .l?PKyVS#mpip/__main__.py]Sn0#rXvEHQR^r!CS!Zmlo &7o޼`] =wRhsIrCOfCTαº#TkUL=ȝn!.ڜtCc/:Y]g!T@{T\s$b 3>˩'|kS'{䚔Zk趏|_oj}GeiÒl}Hߊ*;d[屡I9&71k6@Ir(UэxjWj'Hѫ*!vWLmw'b"",USS*oNn,{MYymFL$K6o69y~삘 Jv)+jf R9Z)oEdF,Ձ}#6JZgL8kG37*OBȦfk1%efC @Xǻz ԌUTИV P mAXcfEbs%KJ@|"\=z[b7O"._^;Tĥ ꊪӅxJ(*6 _u0PKyVS5 pip/py.typed%N0 E~X lo@b6i-%v8=N-sHaIw$QFvT+>OI H.3Q<̧f,t$o Ƚ9ŸX| hdr1hd7*4;#)!*W|J߱7g]R915nPKEtVb=_Kpip/_internal/__init__.pyuA0 e*  i9rZeo(ۺQe!A./σl#\< U)&WBl((k$]E$p\~0lxwte ɶztc/]{"]+G/Q\'&R ncsq˹KFaB1ɨg8xyRkalgs?q"BTNPr]a-A;j 69؜VPO"oP;b؋/n^1GK &ɦ%venZSio}DdW*[]{D'p&GY PKyVS_4  &pip/_internal/build_env.pyZs۸_2!SM{zP.'vj<f5Y ZbuDHrT)R$˟Tv-̼+A-Vù {  ئ)*LV+=DA F; or.V64X*b9#K DY+EpB]RЄ?,5?y eʂ\Lȼ. lSCeI6` mRI0  q_PWz.tT%`W""81l!#+Ȗ`aȏ&B:qPj5WߢxA ("0ↆ yJvv5ֺ +nn$*%~nRkp} XA |îiu {Ce+@y A\U+2(À/CM3\KYBq`x;AoG`#hG Z8auhR #,O(翾%}A. !?HmLlo`#9L=-)W5Y9kwhox~kaUQ\!Xm/f/^Ko94TLaYrC3Ei.2E(гrK5U1B,t(ߦBqO>W%cb&x&f5*/դ> Uְ\@0NslHzDȱu0+F`wI!bJ+EA(g6?J*蝢IeXu!ɫ @BiBSͦhBp8찳gu'dg(sҙLV pңlqP `?J5&3Tۆu7:"YLW -bsmnsPbewE"G5ʰpU 르@ۮքI 7ǧX>Vץ 3W7qn%D(UΆ3%s"35fY^L֥RRFuPE4gӼ,aଵr~O4kZ.3.H=-cE3Vʜfve5.?aeUѻ40݂4Չ3\W%RfGBsgos>e#=Q1*\Ul<Ǵ<:ލZ~S!r20 EFAX 4 . #gqL52E,Rϧ,c!BmLLضG+@l3liYC.yX]q48RLg\ [Ҡ҂;R 'PW%yԂakV5ܥb!u C T_^@Y.psj{@3:z5lƲ^#}[{;'fծ!ٝIYgٟ7'K8憪!VMqoajuyRMJD+%*HV :v@D-mw (t0Z5963yVĩ&R`K.UJ=`H;}XL\_lWfҰt6nIu$\Rظ9|>׉y-9-Y2qs SJVh'%ӛpgY^Wzt&E'/ɨz]bk,nNxf0LRIP%MSbQH3V _dVY,І$7 }Tǃ7+t}&x5r)y1B"T=s!FW#?[4 2T; f.Q9L.-C }~C4<{VwY+0pq&Hl` bZ53ENsȿY^LhD'+.X,7>Ŋgר|e8K]+QU##WN3hC )ǯ `D¹on3px9(\PD&`N~<|NP&-!Ϙݹq`'NNo^?9^g΃I mX"3g7Ջ-|ǾN+ixvܜix _/iZU}%Wa@JCČ9MZ }E[mB{(u~n3AaLJG5 PKyVSՕ $pip/_internal/cache.pyZm~b 0UȼH]T $ι6EbE).]Y \M6>}yfyJ|/+WGUbqui8H{({`M.~}hծ1GN5FE:w:w+F[|vT\]-^UiZ%Mێ;_ ]9ԍwVɣܫƂc[˞d.+S\'bf0Lէ\1=J/ux뙝GSҦ;rSƔ<>n~d1ԭe-fNM^<;^4죮 {J ڬ;dd"Pt.{cIƪβUv"#+K5;ʭuJn)~>kJH m=gϾf!"A՜R_a9lZ&:ͻXUFBhIJ,֋Jʶʤ͵KCHĦadezP W%$f^JkGY/7b dGn̽.gR@#p"QH'ۂ‚0ZG,ﰷ1ƑZ~T/1*-# 931l5MmzdY;,KlCҩ;vò- 4}'|鉐U!X6CxSw@.;{m˲'kU[*dji\&3r;&ˮ/e?+PeɍM)Rm,I?q[6tx$m&BO$U>yC!mTsm Nb%bs9xR9o=3({5|I:ql{6`tz0hw,|!+ +GWBfQTHCNiHD+H7} 67(@߃IL##o#!Wx=WM\NQ#%y ly-b͚|ؓu_ Ж]ܐoǴt##mEz>K#6*/BŘCxrm$QWY7tDZyWetȈvz۶n"Gd UP5 14`Q>Cn/SJԺsr#@ouI2K#jCvbu}5@õy|a)cLTyakz;SؽRR 't 'e)|Ա JX&gU6TpxGF&rx)/578YWc.c8VZOW-S )򞶍bю# Xw  ?$F.tE<o^   @=&{rZ86XŀbOn-h%aouE]?[1o^?icRz SJw`T1!EQ– 1MqRaAf8;b6x(galR,czQ.N=*/8h{'N jF*IkՀۄ~NϢ h1k)7'I?b˩s {}/cA2T ׵J̒ ?X|1M=s?E1%g6,2AeXZJ|#CTIFFV<@z @g|jBV7%Z˸nׂr0%zʯP> Ni:ؾoxfaoCu\~/Id`:ti=1B^lPT+>x Jc>Rk( Fx}ZPSqMn8vl5w'in:ZU{b,.6 j,}hy)vL.Ë MjGj^m4'/l/QWg E<;_\>rx#c]ul.FqvL=gNĨi, $ c^̄dܻodv-ޏLSCfOcj yKHìݙG+nK;~s}}8w7.")bOgfR!cx1:3Yg&'QQFH`r(Ĉ d,ڛPBs!]tVtwۿ| #ԐRn|uJiZXtER߾IdV 3-oAT qI%<+WLj]7Ŋ˕ȋۍG,OV|؉b[)LlNBd\Ehdk\Db.l+%K4#vw~խ QPĜ ͗w I^!L.l[Ȕ\ͥ0wlP_rN<"XxBAX̖2K,+IBojU6O݈y5bg6#v.~^Ru 5RQ,r"0LY!͘'uqT^"Iy: YW"H2R]ʤ?]^~ٿT vPsj-y z5\'[M{گ3vH$O?< FaS?]^9{>\XXBo`07nT ,yVv=cA@Ŋ u46<%Dqj)\,ĜV=|}r~emU7;U$RNknO%*O? Yl&F"'i7z*6zsv>'״K:]p($>\Du_Hp|<9?;/ONˋ_k.8 2REX^ei4vq~2f-g r*K κ J2/^!+L ?c8vW;&zsQ= dwLm7X9g!w[HCb -4$/d*NCZ jMTJ #xqj~?`<۱R0.+6H2}OW "}~ ZZ#Va 0 7ľWW%!-0cc# 8QˤTlW" l_h^b $[y-PU#},[:(2U|WCF1ېfl0Tm=ic"p蟅C6b-/8d@W(""*> 菻s tFD{&@C :d2  GB#|ܬj_hdr= ŰeFnZ>l̦mV º.GGm'лyolF]rvL\bH1 BhW|#~fRJ~)H"/@?: I~T捘auvLѹ*$N* !` u߃ ZˍO.IW%9X@9*}[IuQ{4fvA6ZcxvM-گk>O0m1Q%d2GjtPyD[;  ?#g3ԅG2Sǁ_oֻZh 𠯄`}|CWE 1O6|v ƚP,dI=vfj[/=M&ᤂ6l%pcH8t - Q,~)1pMY ")d/5u?hi ࢀ85_"S:~GJS)V2Vw7pCvIĈkrH`lv8vv;5ρg̰6m^ү4zѶG8ÏYigg41twcX`;U"cW|fk\?V&Z䏰nw@3I{W]|uk%]}mDmMF_'x_qKwcu˅MEzj+(`DCnwQձNeJvtW%dQZ%m>aIyy0 ֺSz[j}[ zЭH" >IS=WP,r;!d ӐZĚ?@.|5L;B5XV6- Lˮ,@~9Um?/K,zͮ7kqRiйjF?DRv"YKK.sU0exHQYdدb^= lYU|yyN^2yYPj~󟾤/{UǁAV/p0Q?~P婾(@2)| Ε!vkSnlb3f_~z~rɝnYe7TRa`+o(vIgT$8͟K/?p|/ 4j|3p S3sG/q~iT}9ՁތhRVwP>LjWw0izqio{Gx|kt:m%orOS8j{6@n,!}pX#xN]PZ2@` ĺȞiJKbii+v&3՝@kњ&_ZK?h5Gxݦ0/rOC{ƗƯIS^uf v\5WCJG,kV>|zVa]4Zs:M*sGW>Z7*|%)GkI;R/ m pWzeSKD_ 3;{.2\,DbO_J.pS9h<8:|:H8׾%]V:vc#=(Ma=ΏAa)$LV|.+U=M|r!r;3KWTZ겻Wz2P|1]=]ѨX)}UB8^j8the{V]")<00vz]H$X/=|dKV굻J\5-MmNQb';1E/A<3qhm^Gf_ Dm5i ĪU v{.~N6mHE𨏈i1#;%>O`PKyVSyfl 1pip/_internal/exceptions.pyZ[s۸~2$9my<fręS[9."!c!H;:.(Ӫ*(F_n *LF4F^WYuSR&wr(:96jbJYU,+YZ?d-b"V S.QReVt1?8ûx%DYD|-N/2߫"U\ޭ2ծlJQ6Fcy/v37;04<[K_cTOp{AtM Q3AXLCb3CP#xwJk%KDAoHV*mg8, *&V)s #zϡEp g+%g)2j3al'!T1[fl㒥Iޤ]alH~`yGhk1Ql=dHu?ƥ^Y:Īt U F0*]P,Yy$x_ӗ)SՖ-t-LHa]|IHz mB-Iӧ"Hm#[bAjeͦ?݌we ZұIh&V>$i*#2q W6b5G3/߭O啒-=)\F\iip;֤tdRҒf2C 4²9A$v7\sCvgGepe$9sjX *udy&^!) qrRR݂Hu^ك~TOD ;:&ڑYٔ3`uV,؉Q3l݋cQ쉃Ѕ[3s)5rCT25h! 0au@{Hū"Gn%kYTMadͤEPحDkrEhz25a)I)juz{ky9 DD4(1ԅMP(a"0qt"akڔ9%ӝ0}n([Vie"y!EK= ]ڌMS$'c*+-jYD7):bTuO?vM ԅ!ײF'v4V;lWﲁ<@=w v)G07yp_&F*тj,bRG =-q'[.@-n:b,aO;n6?>[ݩi.7T ةusINDZ<^`;ArĨ|*wr;s7bzg^N#[PQ6|•gLt?plDxްᄍqh7Y+䁙:LOe!PKI hH@3>{AXRBŽZ CrӟnNVө{oEx4>],.}#> MDO:)fx\Kuv7P;7''iꐷ?1lCBUCܔb1ZdhvP_S+, MP6~~ѐpDSkX4`;Ŷŝ=% 56+ޕ4P 7ډGd ^yPgu'?S:K VeOG!WN!$uwBSgTX= o~b~y5^vka+UB`Ͽ #@P4!a1C(UK%àxl/ѯ Ig̗{׊{:;Rã@/d<)p1S $i,CB|H{j(.ǃi V掹ṿ [DRֱ сiv|KNwʤoݻ0|uEJWZq\)J?"aR.Y͟䛫{=Pnݲ3ƙ5 k}(r3{PoBanqSpNWcxLK1|(Q)NVUV.^Ǡ.HmX;W_˅tvٻ/Gxv}~)NQ?_B".^2*WDz BB$G *rCCH@$SB|Dȭ4ޢ@ihaA3Oo77M.kwq%~`0r ^o0jFWyVAdE}siw} Vth&ʁ*(0) 4{koWXa~g,F0G~JBEWDo b4 `,3KwUr 1oD?oT ~_z{=-<~t,9̫j};Bx]|BvL4UMl6ʚ>w[#&@jeԜEmKCWcjKc$s82/"+}l' +c_ke!u mh52Y@ݖw6 8]# McDխ+G!{ cDFb]O[u읁'uiE\YRɍu޽h q<_o\7;]2ߞ.^ve5a[!WU'Ġpwluֿ2rx[wȷʸrΧr.ۋ+܅cLt?}'vnRK=v3^j4C.13#G6X|z >E'PKyVS'Tpip/_internal/main.py=j0 ~ }l lǝv٭&J"pl!)y)HH?HV<\F%q9R~2j{.59 J<ekhg",(PJ+YKBNMh j]%ȼۚ*УEJEɌ a$x8^_ pŃzORlrSsȌrEE}sPKyVS;Ւ@ /pip/_internal/pyproject.pyYߏ۸~_*[H ;\.%@ (YTIj7B{gDG$pf871'* ZݎiU B8q4M\|U" L&=,H~ba)cx &Ag[VTqR/ow[ ɢ!g6 ~KfiUh&`MzppL5_yb:K-O뾁twJ(4@zh CdqCpPqὲ D(XFV@pFvC,Pľf)dÊo6MdݼҠgHRAv0j,k(DC3G K7HQFN-4֔$q=߆@mrm CǷ%<}~e'L&޻9`aRz#ΆǩE!$+;4"g,m ,1=/Zw3ʷ/GW+4\i25@ w$h yFK83l5b]$ғEz_@rU9.#:"5Ixd^F|5p9HzH:z}.}n`4oBrdRF\auwI;eܷ!m`'&4g\)ɹJJ 4)Cc=ۥWqHtZ9d62%j#&*!eHU-'@1>Bgڗ_r. VLD6{!epV5Y@o+ rh4Vr\H!rL "'Ԟ>7 Jn@3c"eq:#}H=WZi S*Ch \IIm6 xG{924+O&O FFkl ̇t^SB~,U.;d옫H*CPKyVSg$pip/_internal/self_outdated_check.pyXm۸_*X\x"8{.&r mq:DDk}eQ޵`[$g8H6Җ +y;wQm_{އGَk3R*uã9UU_ {# Q /8]= mjql-3ٖ)+T]*8<@=[xÐ< }ms>JF,*Em2#H4(wZT&0 *h aqi=(6fj+z+qs%k_Y*^~m崽a^97WV Ӣqy4E(ZkRV7޾wWoَ%?^]4W7ۋ7JVEܰ Z-oDq f9XD3I6q2c]~+V|?Qt6Cdf_*I^<^;ha{::HRZ,vC[yaV2IT;˜N[2?؆2#D9; j<# %{ē42Qe5l\}!OJit># KR9.^G\'% IT' EwIo?&kBВ]+T$nHK% YZk]^ Go~jYH[;>. fðPvTeZ /A0[!yLRG^2[PvY(4̨q Y5N! j$2Q 1^0GZ;tAَ'  m ZE%ŀC@{-e t&!1qMg"$k8.馆?FHrK ல́3 S׿&D-<$rdw=zC fjŽ0\C#S ,P:Ic#R8(&acV@^{>정ugl8pK,&~܃6T+. zd`3}APϽuຝ:(ww:{F9Qj;)N c!?ků2&q`!C/!oUX`䙺i&EnC~(woy RSi 1q؝}wR [ CpuU<8X]xM1v繿Ow2P"K^Ҍ~xs,^ٔӥE64`~r4N'N}McMQ-tɢׂ U Y+UC v}=*:Hp?9 7ise'/甛OMk?]:Rn9\5 B5 iB37Ot:uO5CuIvřoK%| "U. :`aBL.ē(zK?Z4%e_9>]$dFSܻ:RraDig3Y$S&1n٫vV4"ُ;\^3 s ͒sIy$f=\vnRsݺ@Q PեSeVSnnRUPKyVS_ /pip/_internal/wheel_builder.pyk6 juMǝHm$}X,ŵՕ%Uv73|S8$g837E~b苡 ]U]V͎9{ӈw7VQVաkn@g+g7^>jysLKؤy ߾ĐPMQ|%QtUw)6HD[w[4mSmMq`>O-4wZ$GS$U fī}#sK-'&Ej.c\:(ИZr:8B VcXJx=0`4(xų#e5!s]w8L̡/d8DC;&͔IiЪ)ay Jlzjs8>eB vE/ >жȉkPܙּh߉o\8Y׹^ ~ۂ˚{C֬lZ-92 . J; L8b2Fx$UGHY9rE;[b\9m4W?/F/S wG?53NGUdPjŦ8$tQ{A2IB4H\V RM2'˶yJrDiBy)(ȣJO)fQxj`ŤTN@ީuo))8S7w:fr_E>d>%R*,HgDJyuuI cT hPba4VX(AY/b@b

6]6, ˳ DA$&Lۨ)cDSTş*>[.M ?K N+R$DoaNj 3%>|,~{i]\p3i4Tי*GZFcIV"N`J~Ѭd̻d:Bsn>9tb.xb0&9\F\Mɱ7ddNӉ h|V\;DѼ>k6^ #"i 82 d2h!pO~4]¦-t8\¾68H\{}PKyVSȽZ %pip/_vendor/cachecontrol/serialize.pyY[o~_(,10^}&h̓a)p-H$9HvhGɏ~3oZiSžp铒?V|:ِD);Vquמ)2}ǔ?n}/k~`oxY!j_77?VЋ5wԲfSov%+dvI3cpp\f QUp% )&d3u*E~f5u?B{ʾi08A4iA 殷$ݞђulmۖl^`A?fe;eX-L;Z+|5J&zSy)=p@aϞyOJ)5#491M$m'l4*^к> Y%Wp{u*^cGۖuk@_t0ۀ U- RDj ,!p_~BVV*'O(pFFTg=b 7Lqa(k (G{B8f1.h}'uAZrVBxwQ`# ~t >lLsW`\f? 1F"&y, \#0>W 2>t{)7gbĺ4` n 0gK,8Pq yy5k+pe 1v p .yU9Q0}d@AHXX@DʺҜ .iRG*%S辏Bge g Gʓx䠧|INC*p>1>&]ϖ2,W"*(1.v'S*>$ @Sr@7k֨8Y&ku 0^PRݫlIs=AP2oNJxf2=f| l54}G;'щ1܂`-C %P'>N3$l |pGښ8J:Qv/6Ż^#vV@Me1"51mY. RA{zh.،*kSUdҾ"p׹im;ܷfg=j8`]E '3-Ya5,yg:JҔѧȯ_,eS5{Ʌ>SaY[mCpaM"N$m6h 2`89%H+8s2hݫ[8[7l%mqlz7_86Y(l`09L̈́ςtq{}/.nN%˹(8Sxg$5 9K `L 1@:@D:kn #9ꝋ~??W{Ť,ԫ3rkȁZ# C@ITMV۝ο j0>C3C έՑBdZܬ!8kΠ_mO [!zq{^Z{. .$@A2(ie \q&diF5T걯RkY5ƺ:Z,Mdhيe8vhpiDW$ݏfHae脒BIHک )`pTrD9˨ˠ \q6z,,uXQQ +U i@ePM {An|mjBxCenîEi4l%32C"6laVJ4dzMNQFS>j R;dSZ*dߴNB]QwS fנ!M;n̗[&4Tz/Q<>⥁2"LOӑv:0AYB;_aFz-%u;8ʁ6;F '/E .i+qk@U@ݦtuӎ&wJqE>pnnJ<C4o>V3vXk"Wo?;-8i`~Ԏ]܏ZgR4rd|ulE. y(0 %snʷ©.+9\<J7q(Elj OcT)Nfիߣ8l],2FAGezsSz~Q>OU\>< {T n]<XammNN-  Yc>qLO1 7;5 A:j/r}_7jnK)`}(`*r$Vtn" bg^4sDh}puy*>3c',A>?-~f~mt0ԗ7CXO~Exj^ISt:"m.#M+ӟg6Da[d#d$J~FU ş.,i-#l"$&>)E7 {?x|"|mnPKyVSAgX.pip/_vendor/cachecontrol/caches/redis_cache.pyun {J5Ua9%(K Sj6DBn2,dEivp~27z5oI/YZYs ܴo| YdY{EE,5m[]Ԏ-zFkU_nge<4f۽0A\yX ҘWEgc{b+j vUԚwLO# 8!F;~grfB:nPKyVSU@>pip/_vendor/certifi/__init__.pyK+UK/JU-/*QH+I+)Q(H-J⊏/K-*ϋWUP22023036PPKyVS/ pip/_vendor/certifi/__main__.pyM 0E)Pn "!)Rc[@Kܤ*Bb#-513Va|A $8ů3,{~i^pe[soJK4Cܐ\o0@b,X (FNaNXJ]罁'ÿ^߅PKEtVL pip/_vendor/certifi/core.pyuVMo6W CV= Ů нiiqdsM*I9;CRl8Bx3fn` +>ReY ZU)םY9hapC!(Ze hۤB#@OM@|.EQ4ZxZkʧQ t'c/927Z F4xFA@sVQDT~h"2RhkzOՋ7CQNPR(a3(-A9k:ckrVl ƆExnta3{:\`MZ?r*u?~z *Xm1,kʻګ r~$7Řӹ~Ws]JMہA؁@$BChbL{\Q.8iUw%O?I%rh"Flchf處F =,O/.R҅Q(aߡaŗXϢ0 ) 5,Ȋl{jw%ϱAz '|5i"-괷<ФORlDGlf1ipٕI `ި؁qK{ L>V5&pnUx^w=~|nn0YI̖Q/c!chs F\i\A?-G|DyPKyVSfw pip/_vendor/chardet/__init__.pyV[oF~WDܰJ[T" $VD\EQd| 538/@blT꼀gwƮV/xyWomRɊk\1*D2420(>O 7DT_ˀ[A f0jMg7(Py 4vO06um <JgKq~Fi>[ƌ_-NAo!b]t+J4.Rڕ)RE0GH4I {or;N=xhL֬$3fL|G)+ńRDn ѾzF&x pMi=n8cĢĶNJ+qHkI 0,jV 4_ z<!M6v,wXm'n>7Ɉ`L.Ip)6a>5g͟Ϛ13ɨV*P5V-,PRA@s#Oĉ]~Zh6?|{g4r,|.)qdY{tgsF8 !3ͷ}yUZ'''o  654W[[KBZ1Sl a@an&Ĕv_ؚ t3г`d;2 *0bߥӀ&G]q(%UT 9ǖ_Xw^+qi,ϾaV=45),k7D D" w(Cdb5G{-ӿ|n/GBw{>l:WnӾb%b+4?;ӯ}wI& )BXa랣 OnGw*5"ؚ(HwKHnP԰V/TaJ"S&IdՔ7~fm6|1|ClHX2'r.5Pm5kurbpΎ)O9l'k;;Cr#r=,QoG]9E|"ٿw˄-N~xE(jy2v\`KA xn׀z-8<f}]fT PKyVS99zpip/_vendor/chardet/big5freq.py]%EWZ--%ѽ3SZآШUI*~y,j{a].xkw?/ϟ|_}?yn~=no>yxyݿ_zǟ_߿xq{E烛w}}|Ow{7ڌq͋?_?~|DWǻo^njQi_Q?[9#ͷ}ٞ޽{'>s 7CmyoWx=|}ؾTwwۛۋ篷ǻo/?|x_y/c8o៿fMtW?E|wml?ڛ1Sёl=D;xewOZb{ju{c/6C} @ ھ~~sϯnxO˗<>ۿ~G_<1Xlǟ_n1O%>O>'Ͼ믷Ͼ|}}gO>n_}/m35g&||y79zw/âE~k _=㍏cB~x{xV7Zކѿv%%:>go޼(>RK˶}u}şn6aϿ{E_@=N&6W?>؇?~q*~x?|/˟?x/ضm:KT|XQ껗Vϗ^qKGO^>Y4{4o+_~i`tFi#7ү??=]PScyf?l?SӁy77?_zG'_?{o=o~!XO~xaQz!*?'Sּg_~OǷ_>ӧO݄,mtyܖm6zƏۼ}{m֣n2oQz|VW\۰`ZgX9v.\m9ZM[xzm[Z8-Xc8nŝV~</Oo%z4vk[<_ᛣ'#&g}ɸڝ&k w->;ormćob{3s6Sy5~ÈY~Yt5ZcFJewgz"0Ah7Xc'}#:w졸Fh|y=nbzzO8m?͎I{o r1hϷigqnJtGP?Îӈa;)Q'lFSQ&lЩ+9Mh%fO=ºK=cnig7j=mѷnŒnl1XRb~ce- >ob[cVW1xc4MwTka{ktj (nK.;5ߘXk+k1U0lq>ųʁg1oY4ڍnӝ`ZX>AW [9<;g+[gC.ɚ]ij04&9v;4ŋh7j8n D <~h(C)|'nll9YP<5<=mFc,,ř]y`mT4hnßUKL^%4V+1ѱxLky.e t\ˍs/Z7-Mw1XXGkxLP[<< pmL]n:-:}ӈ;8N˷o/0aK"b:IJupf_8g Ǣ2щpH. *6C c5u,T2XE'[хLj@tgGc⊩7Zt0:οn/AJ$cm,&+P}Ln- &T ;uΧ[(A5X2s0=SÀ}4&ǣc 8Lʆ [vT[c@aD }t50qC<>I,bVck.\=%1,=. 0pCupo0HWo`n"-Vf #w GLf#a. [ak\5sU=d1N/ijbGFX؈CþV .)tfJN> Q0ޮy\Rb?tly&`T6 [Aߍ\91V@ ;OOm^" $Iw6PV^9fvINؽIv K 0vp5/g-ǰX{!dZZ(ۭ}|.iWOV+:<]-Y݅ݞ:C' !pI&xT6:M6@4cjwOcӧVf)|7aHܥ)s$NlU C.mA Pe5p}%`&U*1D0d?u'|LBt1 B5%~8%l#V&/>$60ğ 8:0i70,Q=,RXmAwvRM!|LJ2ü+2.li^y _'omĨ.c9`@ ĦN6BgFd(i ,x2nE sYqs}lhݛ~]OdF= \aϞ8-d ˇvmde'#&-ym62𧮹vćY%%#(|L(+hRwXMAd pUu']ǽ5ir2 ɗU Ezt|KI Y7:$9nd. /1 [9 wtVOJ Mtv $49_&Faڠ3p=-M!|'reB>6{$8X#p d +.y(nM*,(Lm莶R5<.JlAHA3aBAy,ӀڅwH"nb&:f@r8A.Qji`i~c`d״.$/۬KL3J2œno ,2}3qF=uNCAzDj1R4eFɬ&āG@{Xj*_p޹\uw ‹5K ܢe,$[\񥳄$cF'U$^'ڮ \R/-#o^pX[@p^.[%70,ilp<7C1{7W[ \R Xmj\T.4">nH ةlW*J qm?7&X zc0f-t%]K<6d-Z.ͳnYWe'TJJijAqoCF6iR!ἲI|6Nc ]2YRE#q$#J ܋OC* #jÍRhYK`bc@e`&țҒ%utSbM&Tr\;'A@`$`\Ukve՟Dg%pDpѐ"N03/ogiQEY \R#2Ll#gcna=S/':|Ia|b؊%%{끉7A)"|Ә)nWY#dpL&i"1I\Yx,'3q|p YZ9l8\ S(l sUs-HvA/2 .T0@Api ѽ9doJxpўv6W\ EVPb 'PTbHs FjxOv'2ECKԂPş-MϺLwőRcW3ziLOBTؗ-a}  \Ҭݚ sj3ϥ1G] K BQ~DKrbtb x`)ӡ$۝;v8y3 */g M9%uqVpXAbV/3>"kOX*F0].H@niSbS ;SE@ƽ\—ȌSadpc `N\YϢo)g5R$u'!c(C h nj ެ 7Oh7CU_Cܽ W7WjG:`Q83?%fk,D"!HZ@2 R;M[IFE+^,8@Q o^Z,>=[N[P+$$X>ҞD].)v`RgeG3ȊǬ{3~|;4%u~Du5fK:x  P*R;W ~hƴ쐓CKw: ~]X4GD:v <ԭs9Y h3|ʨ`{46&ƄKJXl3S2 HªGi{"=+N<:xW'^`pp#G ytHp}ΩwM`WPl.7u ᰨd@CrZE:4]{MN''+T Y~".AP?t˂:X( Bnj 5*:/!98[Xޑ2"'=I[`0_ #7<;4A: =4t0봿k \tzcBA8TKlc @{]  _3&:o5G0v—g]䑢q/80ͣ8KalcK ?%S*,Հ.NX3J ai@wӉ=c"X Kr`fQ02ڸu`ݘ{/YꂊHlZ`ﱑ 42#%m. M?5IOړD5NL\Z .LK:b@*9 X ( АֽOfjvսVձ8)FP2pah E24]&YϼuPy7} -&iWRsCwbm7%!=Idѩ!!xc?VHE+[@KiI+bVvߺe]%n~Ys(8X/plB aj.~ZE~`~F86qaΡ B)`+*rWK mE¡k ġQ]ݘvUbYXL I5 ̖aY(t0iU,\_ҥO9\d?`A*j17`M'VI3n5 ^ |ue"Oo"ᢅ.$J|kz,Ƃ+nnk uz,<-Jo h HGtQR kG1[ LQ7hѽRrM2>^QFcNvvެWKθQÕu.*n.5r3Ō1k]YPUJTz-5YHVzBHGM>R3u`e(65]d vCEKE [ 110A kTs:-zN .h3/yP:1IJѥԬxS ^e^f"1 Gcޱ5-E1ThZ䄫 (pICd|i ePOe >d"q@ I?n}؋6ZY6+']xt+OD1,`߬6)y6A>tu>ÛtWtmfkԇȻeaPyC\HvKNi'[&PSeH),}_8ǸY54ʼnc.(Y,v}tLj3^GJ2MDŽeYE:Z5 Pؕ0͢^ĐS"yp薈u ֨ X//Rgh\GӍgݿ{mPEsu{DSP-Gh*׭;U~sT#?⟚G/mRΐvٔ۫(Dm_Z+*Z T"€Qxv4uHJXhnM̩41$2Gr3S~5vE_YT"5X$zn#((0=NR,E=$|KEZ,5ԍ.TQFA촖M[u 5z5"rVHVrd]'?[l(Z+A U8ْCnqPPY;<o(^Խ*f++ \u>4 mdAgRl\fC3MI~~L{njNJp.޳{=h&<I_SH>%2%71A kʃX1j2&y4 Acm' Ġ+=\7u ,qK|6-Ԕc~sciBdHSV!&h_jv<{8 ?R#b޿Aute#4 $!A{Օr4"v]HILa w3TpKMM-X7wY0e7ac(e>2w.(-aGSGo6I0$]uQW7u;cM1Q!ӕ{ M%zW9,^s# )!X7U^ w iSY&OQIA=΀}O"Q,DY!ο\$済v{F 4nc;V碼aD)ϋ4t$fX0Z K<<vRyt]:yZF !JjQtH7(!n^ys**ܼt̓WEP$WO=1βPGJ ,V9S>V5^5uҞ˓ )$Uj=O*_ͳà5qV)9YM58>i`yQ6eT5uU~B'pG7]eM 8SQ35u)TfHT&p{S O2߯vїS`Han(U" auzB0pQju3ۻ S]FGn9Y4ty>ݜ3vaHS RQ2"Һ:-{,XLH&b} =[݃߄%.of5#;<֣ܩ}S- B׉L#cuU]N/|֮udVRAXrYI #گyܺ{2r$F)jЇw HoYy`!K6O Jaumi^@'Hݫ%^4f2۬㫅=x _e6t@5klJV~4QPݷt7ʱkЗ(螲8dYC-:,'M YI{@:l̺J/7ɀWdW'˼lϹ)5uyۀ'݃:h Y=zh cIf4%VI{+^b.jncV L,_]p T[lth㪇@4(@Y+μ#BVЊjX9DHu$"qsFo2̔3r {̦Āl7xDW~9 -0X!ը ]'û6KN IbT]`SuuLDN*Q^% ƌ]f8@0Q -gyy5]|r j3{ю|Y0QdY>)<}C!ka+E_ƒǗ:oE#oI'o9bܲ58<ӄ\_:um+Á@ (-9R&M>*~/uoS}@.OՇs5<:kLvp[r_tuzXom RW'v1tF k@M0]:%qSW32,qKWTY 欆дCLYGZs FWv e)߬e|XU{\0jP@e!e+HZKp`$+^gH{^bz4a3n{@ȅ* f&;x,tx=F^.8̣\^4RtiietyCWño٢VB4U\rti TF/J б|BwKo%"Gѽd%yt t bZх1vRѽVdH[Y|qHUz@n乃괄mZ9ѽ]a +Jt|y/ 1P C3k"*׺qNduD^ͶK GQmxo~/:׆yl0CzKzF\B[Vt䀡\Ǽk'7deڕ- !E70c}6\S~/ @X7J6Ms*Z<צ) @%$Fۅwнzu:ܰW 6᝷gM N|x=lx9"(6]w P52譲]KZ)6j}+=UNR_%5uo{zĿ^G{utd= 53܆k$H(U)w4o:y27w@P-CF廪r B ߵ3Ӎ8Qxޣ{ey̸l y1'HO.9pI=x;<O %@ĺy,p vH"싒rՂg.pRix惔{eik 1#czëMe8)gT@Cyd讨@<3/dƉSSG'! Gfx;o݃tG$Eye=Em,:vz7qT| ~ {m3w~nBazcMRpl:n=KOW@p^Ka. >y+Ur[Adx}6o iXzFAEAӞp7xqȗ.O\Q)wӼ|4"er}K`82{9z!gk$J(ƹdKR)ANF%nݸW %˜ "hzPsIg׺A0fW^6x qcyPR%Gs7^kMXL"};Eo9J@-{>+Vgއѽ6i= wSBm|Aԫ]J‘ܦx.|.zZ<dlAS ~Q2\ѽ6m3dy]TQy;$ڣ[7A5s?{m<8{$D*V᝗*0c,IpWWMO0zJ~gn~ت%iEXa{]i'/cTH8 ׽_.o9ш ëao9Z]%hw=I){34zE8䆁үm7Uȯ8GnxJ@.[`—,?}cOvM{!e,̮Nq/u\Y9"AnkjIhWdH>7Q(OgwPxxzqK=jp M!l^OXWb1zToJ@r6¼Ϋ]U,ŵ aYF@EU@g RiCRwzjf7݅/?;y_@#y^ݤ!P C5T>r5#C-Gqe]WD峼g\l^L[=x:8O yID`kձj'kbdbޞ%Js~ # ˨T< !LƝ8oΔup%&].{s%>+Xt}sjCޕEo 3lGr2h<$8G~u$ZMgXUBLذk+]fLޡ0#:RmkFG7wx )t+Hv B5S8/U ȥrA~Fy4Y$ '܅>zrs&aUι<|_%x5T jeXV+Z+{Zg6 S9VBͭ }}JrWxRoTᡛ+- ~_e_ZM7᪛kJM)6WqMPKyVSY(== $'pip/_vendor/chardet/chardistribution.pyYmsH_-8q^ RJHL"$V3~ \jTlfz~e4[tۼum_}-G jr$}ۣ$Kϳ}2ұuIkr S>Z.mRK_LEHt&[B+Ǟ |4}FN(l-\, bV2tUcM"U/.^"WL5ϣ*%¯Nr:YPW!/t/Q('tBء&B?>?LAh 0T0sl5-9OpaM!OW<4]wE8QK޴t+vJ7!Ns?x[_ MyLזjFFX5 yU #tV.ň% ÛS^XĦre9듺$}; q9hEA3%3f]ߣZ>:Z4fINK8Uhz޹zmo4{zKvjt_WZv^& 413+9 `IWh[z*:ܫAϥU͎`b/z?2ŒA_C }eHK !],o!^% fѻVΪIyJbdۇ'&㣣aL,f#(~cqG Xm;N8nuh'|o^nfi^{v%M;f%>=\Px)X>Hmy"WxHQşJ}ǵ:V"c;^C g*vu]լ޻N}{MoZ9;7~n=Ի/.cT]ռeyW ɲ$je~ gөDk{ 㷙ˁ1S P0IBemeZ:ՊR+ņق"g.E2I(5I8J#Wۛ 3*ھg=2g\^J@B؞3C}at(]BDy:!>zt>'QR4 T;k1#BcKXO,r8z1EYnViTmhOTq.܊633j'@ pH9гGP^ Gwi6Sdh\973V @4lO6JP m͆" 7@Al2̈́|pވ$.(܅(gl_Yx(kL\2*-OirQ!sj'.+z^1x@D@:;,4"jYϥJ-#FZ' ׼]vRM;6JKgƹV@njr&Ҋbax~ٚ~5ʮulu /= D)  _tap+]z'x*KX/F0jv>a/ph!e&E~)K/Vljx[f4n@_̦NPȂdY],nnEeϲ&>yxp52A8`Ϲ""STNj)6@[f<ʶMA[X VD.'P|:#edIsyOHy*6+gQ&+7 J#ӜiE Q|jbrQ2r S1Qېe{qM):=JxIۙs\di9ޖpn5TepUh&ו7 Ļk}'VՂ ]2^,W˟]n87}{4`]w3>YΓQ:Մ e4(W7NNs(RJ~]͑FF5Gw2#˸̣C%,jue`6e-OkS2]'d:MH-Ok'3Z[l;KF|IhS z>R*{<ԗ؇zc.^g V0;|* R4$1/d)_4BԪ}!q@4\ы ې-`t(xW y.? *%Cb3"jfbc뵯< +{_}coa]DFy/h{4Y@, ) b6Ƶ)1oS.iI˗9D(iWh里^A >z(]G&dsg o"vgчXIާT>_MwV?&(nOwo)PKyVSw)pip/_vendor/chardet/charsetgroupprober.pyUmkF_1swu;g/Fr]JP}g%YR*gg}]]U?a4><&ߠq D5L@O vK04؇%"ڡ$1Zr% ; S3{Y aW1v ]]Cgt+̜ԯfbJi! 3aZ-1`LQ2ϟ|NjF"F Q1LƐ dU!geH85|/aJԔ4^ P( 3s6)8" r:ww6dO^g[uYǟsD:AHR2La7= %Fm+`!/|.N _[w6/wKJ)/C`f&DW> Gŋc?.taڝ-;r6q/JB 2.L A†"9@NFd\TC:͗BJAG m;}g%-jkOrb)|#t KONXR@ΥrhDZmCS/SLZq[j<0mzdFfUˎG44T|~ӠZԴrG\Pޏ=&ޕ8-p^tq~]"R5C0KXS64-+s,] 2ݓϮ=9:33?C"~%bB|ɶxa6_u9Nk x24/AsFk?`x%:4fDA|WE>6 ^ĎzSJryҝG1y3ԸGMǦ9+5uXo0Y~}ԕ; M#Ux9LM&"E(Y(a&_FJy)%|+(GȼtF1ߒgۜ~Vjl!Vjxt' .}4 5?>h)(OEG-h,}/.oԟ~/٬?]|} YH$7I$QiOelpx|N?[nng7 <*71Dx2Jm_FG4] I_aO*@ߒ IYD<_dZӢ߻C|$CEq[1N3mMiwtn89O2+4jޞ/!5P^ggg~ \d|'t#^=#nbpp!L>{8^"$וm" [\rC~Oc% 8nơ/jOq_v PYlbpf=eƷQq> bN9ѨzL1PV$"ηX+'7]\q¿\ҏ* ;+1RY뽽[|뵻w56V[7Bd]@ݶXn!:GjxyCBqV SP,4:!{+T#VMl&0/jt8hF BnnO(#SCc1_)xT^^n UV'(S㖼F"όC޶צ,lofzF)眡QGis`x0IN ~`\GE"=7\Np^9,ǩoKpCvx\m_ 0lʀqOpյ[/Q뮞y+\ӉLT Y;69Ȕ>s%cȘs$,ͽL]DU"t='8w/XX}Ԯo[ 5sx SGF)XW D#0B_}{QH_;A?zިn@ F΀wnD4d)d"yEq LɄʖ? fbhsE+2_*OCYzN2\=u(0qԢpw ~-S@_+M.u6>I9j4).JOF#5:]݊x&s: Uш^ [uԓ8vI$y&!}(+<3KL;e$%.0k$o&1@(.zUӶVs2&~41"vp XiEL)| QdQx&)#g7&b,+47$hu@zƲ~+.{$9vm:h$)9k UB?nU <1xL4eR-Ixf $45A& M NҫxiUydؖ%=^)``}1z}Zyc+9C;פiĘgC_OR.\qT&)IDex|ݸHY>ZV;P [ 5NqZ~[k(^纚Ccg5z,Uppڇ["h`QX#4.w{”pJOiY-Y%^e*xYv˜jNйp^Мs`h&ؚq.2O# 'UqUuGA19pcEQugi/oĆ#^rRil)-/!(ksَo8v^Oܳ_϶Zr[Ӫoz}`2xvjpkQKn. s?k|Gft2o؜vxR;r񗑹)誉7~v S Z!s}Zf;ړ67圿PKyVSB1h?"pip/_vendor/chardet/cp949prober.pyTMo8W 8ڸ6 ةAQdeH~%NZI8f8Cç&h8K0Gt!rinV(&)'a?,fK)lA:V D>W.93qgS0EDʜ쵩@L m{j8s> y ~H |$rP֦O:: 4 X.v:鎙@aC+#Ğ,vDûP[0nKwB2=LO )62 n㧥tr<7nQ{Ok#u2]OII%17y6 l FNY{˼cK;>p}U`쾞1+v~iϑ5WC?9}hM;9@97\c)w'zÿj4ٺ(hԵbUT]i]^]bLmK~ƻf/PKyVSD}pip/_vendor/chardet/enums.pyMo0 *VH+5@.!A$S0vw@(~?Ṹp\!@l73J^oU!=)r]R\,ZvbF1!< K"6lȼAnͬӣ @Ѥ}Ze._);v~̂Qp 7q29o O7/"`[ 4Y|,5,&W2E?FoS4vVJJ%W%8Fn~x <fY74Ʊʽ f4s2+d5VE2[)  ZxɜrmC:W_nI.ۛU M- $ Vk4Sˋ =U V2Wͦ*@mǻ~ijJyFT!Q{Cz`xI؊䈞&зM>]]㭕vPKyVS=5n pip/_vendor/chardet/escprober.pyVmoFί%RBJͥJoNiUY=U.]r~8ܵ<;3//syS{Cwp7 ?%,3W\2F63OSHlNt7< SE2ӀAS4:b[$f5e Pje# F`֦S#HP$#I7O4Y闷] [ءR7T4RPU울/H>`Ә.!/|g L tJˑfrfO)bp獽ţ~-nh |@XRll"@qBILhOu^#WSi k9) vM=LXeRHE 4]xVC;.T# ?ɉRjA@#(RuNjc]']7z},p\"Vh͔FUrIS gґA!}mXI`O&IbGsRxX G<%vaTh*u7eta0 '{wL pzj0l-hx ji jkhX3MҺt ^T^&QR'Fy4H U̖)TQKYMgG{ (]Z$D"~%ö40t;w/ 8c -[MRv|?3|ps\!QS+>S!5[8]a>2WZTm#E`ڝJ!Sk^bv+5*sW53C'26:23mHQv߮d~#:y +4;%\u`w%%C'vkF'( S]qYϹ>1xƫ׫Ob.zdʳ:7ɯ&AR3R9U /fBӔM="฾?ϥr2#F7:7[7z=-9q`ƙ=y,ē”쾐h0@}>uʨ :=?PKyVS `)pip/_vendor/chardet/escsm.pymSH)jp׺ȢT ֖DRj_7$ ] Zt{fzWyՅvM8oא{:XBO+hx# /qDS`b[6\;|7 < &web*i2)s}WM=CSvSI 狈] EsjIZCY;.}h9O#ν'ay0p*%KI g?q`g0f` 2<&"'ȶ4sG8u ďrxٽqp)]p6бMmSÔ*S4#\x~36ک|l-z #Sp"?攄898*Rff:]c ffN#/|k z7wo[]bъ뷌dOqVJ:Mqy/Zn0ࢧCu}jܴ:o~hf )S2$J!'fr$a;r그>Xry[^~9J,M +e\5::amWnT]mpʧs7+'.WI8[lk˓.qN#Nux]t^W$]IWu%ҕx]t%^W&]וIWuU"]Ki-bdZmjFNN!醼nH!3Ig:t&nF:I:$$Hg:t:]Fc84k LǚQB9횖!&2x l4j(ыbl7,.Vg.JjQJ\rH꣚4#?>\F}p }ܥ*BF ~nv?M:Cx;~չ4߿ БǟaY Y^%LRt+oz48ƀ 3Αnب75秫Vk6=97(K<'j=-i _8P/*#[Xt^Y[X9[TY[X9[TY[X9[TY[X9[TY[X9[TY[X9[TY[X9[TY۪¯er.h)"c{YWK!ޫ'l8Q\p@4{Pw:ݓ# .JZco؏.i"CaFw#I/PEz L}Ir^%ZAzl?Lzo{o|ʜwF|B:k>AwV~Q&^_C {u\ v'@1 Y ]ض@F3ny{"ߎHx?|,V1_c)fxIP~|xb@tˆڸ D\;EϒmK,> s3lLs{ַkOIPKyVS 7*"pip/_vendor/chardet/eucjpprober.pyVoHO1jTt N&_FUTEbثE[ljE }f/.N?t۽ kw^n:\4b)$朄&5^RFeIa#Ԃ ܚm8XY(J9Ͷ +f W+Kvo &kh |fVj1US р4R5]"dvd?L3u*-4d~A`7U1LsaRzaJ~buK#zmv=\#qX}yI3+s.C_i$hⷧ~?Lxwswh2' JQn]~?KYO7Y,5Z=^UPKyVSAya4 pip/_vendor/chardet/euckrfreq.pykGH/FjʨK+Ok0 h8i{_<Jf]$TeFFK[W?Iy|ONnwq<8ݕuˋçñnʛsԳ>guwy;ûr 9"?\]I<;oZ?Rown$9nwyY{]w׻㯻Nnןr]|SOg_NIo͡<|pe)3R{woSysqU| \\?}է?\O^ǻs7>ܿɏovW׻rq]>z#Ͽ0RMj_(w7r8~y"coJSG̍fU^ʧݻOI!ǖӗ/ʽ'?;?ؔt~Mm> -d< _sƽg^ ^7_y#:I/PV\F1_7y6Se/S 9#'fgXKspw;{uf[c?^.._m8G_7JXRwn@< yU4ɿoK/w>Lܓ/Ջ=g_WIi;>_=?>W IsϧG宜~N3zt_==yN:C>b8cOK[i]jG+ceie7莋R$),8֚b9'_IzV>L&XdJqP0 s]sIenf_PòvD˵B[X&**Y "{]J:]}-dΖWLW=D4,GSZTAkXbu# K`iIHC9zRh̡M݈ u63+aw0 L$A`,8ĊglneTJil3&XpZ( [QG9"ڲ1.sr|'X=b6Dt86ki:&,V&M +[^uuO b:@bfgDZ y!cV0sA &L<\C^WoM'TO8F 6@ڢRlR~hC@"%&Ue bPleEV"` S cp(Hh "\DK8# ~do*0(VZ,*.se Pա;BNeёjm knYnN-WWqTYJUݲreԗ!\nWq3B2A"@7֦ )$!~t3o꨾Cl#ƣa0qa Ci28pNW>c`wCph8$5H>n1o𙊻{g6و]Q6~TA_njmY7Fd- :Z/ >󝄳+|L5rݯR`2ћXjoHe=V}& *:Pn~kjf^`Č?t v;' U\d2spNJ:ag-Gua03,n@nص'l̹"!i"lǗOI"4g3wdN:g%IUӺj%>/- X_XNAe@:A!p LkQFMu۪m 30#+(US Y`ٲHeN"a%+,y*FnO?eg x4teh;`Z7h_Pؒ\0oUtbևرqw2ӦИ[*u+}PE"3877&  `0"\Bs`>H(˻UV 1>#Gl@IXMjt6F29 :`@u5(HUu = .IQ/?$K&9:?RlpB2̧amTi Ϙ+a4lA 6#aŋ2|UW}PoT5fa#H cg`l`"$ ", Lˑkw5)U>oE"=b-Ov6/' la)@V5ԣ<ɝ,tB$(ʺ)edZ =f2"AlR4o-eL!n' k3+a+3FLigTe"9"(j"+XQ8ͬ| }x j!2S׃//?@ݿ.i'F՗q|@2:+9*"a=E,4FfdI$N bu&!xh!"B<*0 = mY'N) iQc.Xpabr>fiӶe=hJ> n\,#ABBf4lK cŦŊ)ج=QO. F4d9A\L3'#9.,RQ\pVڥ"2U9r*qMnA_ƢΟ`[KS`HYQ-#d>@eSŕ-xU ]#F,s ##;'fhr b6;VXQDhrqMmgdhMFMP׶}4DA고 0C#ڇtG2\|2p$82Ha8Tdg'ۈ1`A=B>h-Lj@};xρ]PŸN\=9`5@Șc(;Q9.5h:'>e%hdcng+(X{q;&V"x;M ȱ "$'?m1& vg^k#>ʡQDnJZχiǮ\CO"iG2'1LT 5妄#Lm8Q뙽fcQِyjf&Jg:͋e$" VfX_D0*3Ffdv z>ڶ<_DWpZ/[!p,Ldg {TDs9%Ku'GOG8Q ’DBDp>9KѨO2q-!8rNH3g*[y`'fUۘfZETB\QcTm΃Hqp܉,2ow|ah"*=m Ve`LxM(J=QDUq JK;2܄ʇ9$JG3aE;Ox&{10kf0XF;GuT;=_dNfqʔpno3\&um((M%Nv`o~:N X8*`Bݝ_~}/E*@/xj0w SZP˾nps|s B j37PKyVSI3:{ pip/_vendor/chardet/euctwfreq.pyݎ&ŵ*RBgeD-5[4<'.LM1b~D$ EW}]ۇgۧO?/?>c/}wwë}|sŻ7ﶗq7׭Oܿ;p?=nnŘqgw޾|5Ûo_<<ο|{ώϏzX?~1Ŀ3}={{WM>zx5׿l۟^ͫ<Ƌb}s_qoO>'Ϟ=_~Jǧs|[=xx 𧏟}qǓ~_'O_n|l{}}g_=/?`۾WC~+݋o%mLm~yw=|毾ov}NH?n R\^|rm 뇇ÇOO=ʞ//ǟѨwGFwo^XԴ LJoڶ$=qӋ7= P(ɋǘqw|}履~Տ#7ƳSԜh|42*7u??}ub%L7Jz}@,fc6U{z?M 0^|zߐ5z{xZ/۾ ke ;nnܗ勧=?<~}IoCc?#2?jɇ~O̿=?gϯ? n6&nS-6O12:n{I{|{\r8v6=.N|oJ/-~+Ŗǯ=z^_)n=a9z--uZm$&We%On%&3ӻM`5mͧ§K3?⮒m:wxc'ƌk\عޖ+Xvse=5Dx7FծGgǻj2s[Gx񷔝3ks7&6-Fj;oxb֓H1XW,8+2#>=\136%~t=vde x.`otV+QJϒQM,q;31Rj[exZ.6>6dz}?clu{gH$Z Pz7Ë2lg$BbBxŌ{fų;H@uϥ2)mIM,/[{J,v(3kv^lHaWۘzbsM GH ƭc$<[ˆ-i܄, Q;y>jwfr0)Of>yb X");gnsjz{RWlw!̱jVq`SbckĻ3pfsqJ;* e[{Nqq.-Vxκ SXuN1\94xȹ_R"` PDdc2 CO3+nOU-9 !U-`5* #F(L٥YpL?HkX [oeik ~Dy= %\%JT:DRD;=6ETP/*IR'!NYG=D-a0B#b=1Av݊7l؏ AŴn84/)TZ+ _xDž1|!3ɖkTuWc9TXhh3ȅi8NxG~1 6b"`!N=!Iͤ$,&?bă<;^%6a\$Dr KVOfRk4?Ďi<-ctXr | 0|~QNf=Kxw>xڔ AkX*q@> )t D#, wedq ׀ZX"`S )z؆nDE*áX2b0Q;kAМ'"`)p0|{2p; %/3- YDl-~PPd1IJbA.BbJX"ǁX%nH݄ b6XG {ܕ/|{1t&aXaQRh5+!6BMKA]|M&;V||BNNfTmō#A޻)"B`B]AA+ʪ؊/v,if?-JωkR`dN4!fBNb`BHL'HGlz)((95c'AI/}P'{Iw\F(̈6vxo$ HJ6SYb(y@1 >Ɗŏ_ЃhbZ웩/n,E,]_{ w*Ȅ9na)U#2e{ ܄I<KHrKYYӔ-z0HxL1^:L!/RK' Sꏻb3 &>0͞`uaS\%G1ք'x-&z$!cL"JSLNK5? /prcpr iA(]&Z*)L0d srTЃ1F/n=ot$W,H D 20rFPUH:&%'KlIᒋgfY8*  4`Ffܨ*T}C˂R(}_a੬UUVo20(BT!SY Snb=>#`A WI7!u6L$D:n 5/[FV8y J2`CI+:bu@¡JZO몎M79M דLRdhRlxHǸmcͫn]; -C&a8s|{6A sԯjw8YV5p,ax;c%J复̠tETHJTN,EgB˪92W <>c5 Y6#n$5ϫ>0I> q$-U$i7CUqZ_3\H3~%\ AMuP2, _:{?$JByyNEub`K͘J ؀۴Bu-3dS4t(:aLT&y".+һiɪe `Ekr,Ng j$̳P(Y7b^ÿ2P $klEseR7bsctC8lA)k+#˘wltofulxZ!3ŐM Rtfl93 N&K3 F*U^b-*R;%BO ځ8"T!`7f݌e=t) դ K}"娌Hߐ2m K>-#OHe'*@Z koM4rTXc%XcIO< vCznVc(C&D\ƅ؄L$X6n2yN1Dߖ!&/yYB[)Jjvi:A{t2]K zC|_BMeLIO9Fa5=V g8Y}+eYe> /iFd}r UNf|0Hjtbڂ3e5k6yE4*C,& 4yD XiºOֵn uKTy ^vÜtjbbl&8\YL Ʈhk,_Vjx$c,EUgw+ȿͭ<mơa+ X8p,FHeΔܳ*K  E Vo7,"|R$ǻXaĽq"ƌo` 6;;A#&cQҁ) YjyyZVnQn2An(W lIHFvӐϟU:ʿ/(.p_v,AH_ d7D:ƿ5]҄qCQGKk`8,#j1|uo|Z,GYX"LjMA>IY,u+MĊ/2p5[r|PRf;J 80X6M W{zRԛ'QVx,3E*ǩ[)%tB\;KP$D]t\k|hxYS|wT-pm*RQM.@C:qf] ObvڎyONJ(XpDt|E)cyي vz16L,4Z@Ğj1A6IqMydnn0cP `2b8Bԕ)>H9NgK! #ꤻ< &U2|3ړ՗ A"X~B?E 4$f!Urv'be)a D,{iN b](Bzn./,%بFd_@ԧ"U0;s\ m*wY -x+V)DdM圤JɎ6mQeYx'=X}Ŧ"QS1Oa05p,i*b6 pmjd$ten3d731 RM*vDHUstV蕎AMHz-y%>A$ w(?AGγWӦK"TAf6 Lg2d4Dnhl3PMe IC.hBMkfxhI(Sd0E6YX ⮁!жBdx?&R-"dK 7YW\6 U!%MÆ$U&[VDƗqjNo~ܮAXT:p7x"QӊF\ȋ3`btu@)=AeEDPX`f>9,FX{I_q(-K.#?aI:ɦpVk2\HM-[vۤXS +YGRO;-^vhgsb!r |R=^MT͢@ȣʔlpM#FE}, - Y(9LQtgٌGf42t-w{  4 e4Dv'DIt0E@jn Rp\Nl]| ^)%f05iذ RR3肌y-МB#(G &N4tƢ%E?l}e?k. v!E; ϊ\j2&KTzZHv)0V$&x݋1 mlIں'EvXAX g2SȲs L)']JH8 OKhX'e]0j @SYm0j3p_30x) 2KzIy"kC}3~ڴiye]AD!+ø79tAMYpI/%DyPnafQM MAȒQI8/ftK)n=*I)vg<4DfW*H8vlE$`Ds)v5B$8마*vP Kx7Hٌ'z͜(#++%Fߺ^50RJޘ1%nu@]ȟ“t'QH\.[hsְIXcj.Ȯu}&[ڵTiGT~޷C]ix )BcY+`ɶ6v Kz~_65HH26URiHqӁfC*4:SkNa5QP&CM4epLUW*W (dIRu0  B%91Rj| 4Q)ObYnsĮXy^t[,Xf V0;YY2vUkjn j:ܖդd^TSN !EIO]M!咊fB6Ŗv, 'Xa92R(6d mn+](ZkM|5MB}6V؃(Z ilEXbO/!!iI4"_9)t;W_tm08bxnPt[D=!s}bFzk"%B䡝2ݡ5l)["LJx:H(?]mG"fzmYMҭe6--&kUYhCq+U @cKS4"6H~_ XedI$fEl`OƐn)M)ƙ-UNypFY)wI y0&:`9qZ2ѵ: n$Aö%9nvF~zH.bbb_ Uw:ѻMPzWeVD* ^.t[X{,l!.iwD{KIB:իt[E=j7S͌ P:PmK$ne 2nIH[C SNK`y&R#P6s=XO,ut/kX2q^n<`Zn:D'.v a8,VYE|.{|H5wBIc,30ʺ6`G5-ıa=ցi`eCw W+97 ]da%ss@P Ȓ÷^piQ/.S<)Ob&ymny{#>gR-/ǁDPmɒQr;mWuȂ#Jʖlh- NӗX`x %b,WWӣnK]f\N{ HIpF:J@ѷ.bLd ̮Db;Ym>\l-Ox[F pe3 #6 F¿I{7,KL?J9Y$㒄D49&(M}=Tmб'/kve+ v loiJͨ~=sE(3uCxŖ;s6pHV{b7HIC|Y2@'{4v;c[Yca.O4_Ӝ N[3a<>r vMGH= zdJ,]JīnU0j^4P,<](J~.5:ྴ/ty!Bh[K%zӷn8jM\Vbn7|^X;y#Rk itZZcCiD854 ? oT"?fɽeghyiHyᇤl-/1ٞ;i;hs I:I:7rbM/wX̭O<>c1>?jY;} }w&N&*6γ1/6ɑϷ,S&T)K.n@ vAҜ^va|$_@F qV':14< Z2)S>& S*v켆)*Eۗ}[sEn jbE!J/ļV1mK 3գς#Ϯ2Mv`":$~ r-KO@A3YM!7Bzǐn7y:DnujM:ܱN2fՊ8>Kbii - :u l< ^ݐ]o{*`@.K*%!B쑤ѷ Ij>z[R\)/$26xdL #eH=SRvQu_uHVV}"wWkԘj7h"p]Ecl~VCͪgٲ[w-G^閳zmv?=dw1L>X<#á\duIwuLePg?i %U<%D1lk)N9,&1hiELx rEAn$-s39$I혋Ŝ ޝHMO&a2$:zZ`mg8SoĶf`m]u!{JZKȊVZ@x,7I:{%(RX/DOG/)`(OW\[$·tȼ"D}|.Tt[=#X0A6=upJQ%Dc$EzSms>.uiۺ豼f+ )ۣ!=I< TG@ӞzD3@%KI@ *P\Qf}.鶫GO<.'c`QAW4m_|rxԟJ}k]egTynht ˦s<@r/*I:ܣ5D\1c^k2Lb]?xwZ v57b=376]L$ckEqAUvI/RM hyImӦ> ޯ{nĒbIHi$[z. "1r=DyBO.8-G $GY殘2XV+~$A]kP4ԣe9")5~&%k04ž7W=tT;m&s[Q&6™f'  -J)u"nqZ,*Q6{4{r=s*@/~ma+<ÈQ7T`%Λd e1B ?8B%Zy' iGY$)͕kW' p7e;\0bvPtjNc{%7Z H]\U0)@DšuaXǛ4S1댱:I#mÃ+cڙ][Ƭ+3@D`,Ǟand~to-$д5c8,ݲ<0X @S,g[un-ߏkXnaC6S흵|Dl[%;秇 R״xi,,[6y\eq٘&KrHjb`YY*Y4m4IF4uݣ@UT0I '|( )Kaލz茬aBa=s "cA $[O/.SC49|͸y)pnj19Wã6Ӽp(l$!9OF${ YsG<ݫJH5< \ƌ=Q<ы?Xxp F[ញHWf Jw @Gl>tL)P'HŲ2n@xDDq7*XIIfB"۫D݂@=rf(ȶvw n4G7#؆ O`T518%H<̞kUNt8,W ?R[A*ATB/ 9,ZkC[]*];Gf!&t <r=0hgIpʚ_-[fymVl~aK&xvؙI2~nndb~PwWy B, g=/M^;3'F:Uȟj?l+4dݺbm} Lb()Zg[eeY\cHna&NGgi/*<ĉ|=]rz ;[>Q5vl!1lDR$2)yGgk^T+ԛmH1y_F119E~z- $-@i%Ǻ>"7 7aW8pu#I Jgo~}sPKyVSA8"pip/_vendor/chardet/euctwprober.pyT]oH|W /ۋVZ@_FQ`akfg6 -OUS]t}ChJQ4gѿtjSeYZ(L$TJn(E,hKGZzG~eeJdr8P?eRQ2BEQi /viKcw?@LscԲѺ1 WY㩭NtKϟBh:Zcپhov?MNs6VtGP~kX [7.喙=Pm*J&˙t{zIc/L& U:C;j϶pGKRzb̫)cvLQ԰Ѐ~DyC>k.Kfo!&i#2hӌn=ahT9+jy|- /.4yy@-F3ɢT kdGQҨ i8[PHp8\|A@3-n̼d{!۷:Tm+c)Kl Eflɜ]ze`9ϷO.CF>9ۥq)z1">4>#nrk uֱ/Y̯M*e! jHR2q3GhN{0XFDsT;<犟Wd5=ƭVs^`n\  FNY:U~7WtQuiWZ$& ˅;?K}J4_qEbtS(c}e5୻zSUx"7PKyVSEc'P!pip/_vendor/chardet/gb2312freq.pymoeUvW, ] $Cn+ (:@ E/T.+LlӭʯyV"5>9k9s̹ӧzUgOz|ןso_n^ݖ^_?ۛWWw*׏>{{xO~~ۻݏw~|xuC~_2p~;GG]37>*< o<-yCy~p}s%ݽ},ÇoRʟ|ss%3|,wwo޾_wڏǿeb,~)ޖ7slczwOwo~|ǟ~y:a֏?=CfZt>no^_޼~p]o;y~"勻 \uOO{?eYxs?2w6q'?-m[}0 [̪p]~yˌ}eyw_<䫗1N՜槟cKVu]&gO<ٗ^~x/^/~^o<o||o~R^\_lmnǫۇr9W7r o#m͏eMl'wQ~;O.Z󦫷v#xqc|z[$]Zǵ/o_/WA僋 O[-_/w+l ׿V.黟ʛ3QcY>O~P6޿p/Ԟ2__8޿ly!C' <{} tjxs}Ç?~9qL?6a(}](#>}ρ}8B[V5v6?˫7MpM =<_~ͳϞ|^|o_>?||_<OYξ-J/OyEzYq^1e۸g?Dz׾k-oVYc{5kmY//2BlqYƞp[r9mg>m?cd|~:3J?Fˀ9rlhR9w޳\t[' ';˚i_e%c9//JWR=ZкdeG%ӹyceKFk5F2a߲GVێ-\3šy޲W2L#{1~=Liσk^eٯlh2’=- #E/g>pF=r(nGqYٺ槲2 Ζgfc9M3̖}-ʡY7f0 ,fMa唷1W֝j,WϏYN;3M|&[j ,&YOUue|>i9#/O!Tz`duپ873mc 9.?aẂ52a<0F8<1ˉq`)Kˀ0T0r8w a_iƭbBWqi3KmTQCbU٤Z9"fV3#ċ ;b&HIsB^ (c9@5Io8y53VZ.fIV]Y +9v@c2U 6)0SPBXʂIY4ňEh Hzny80?O@F0HxI\jLCd< 3f68^:/7pz@B0˓BiXCղcؕQe&* ÞX1Kyp=F( 6ˀH`ZMRNo&R8!G8ڊSd*2=N5pA56c-` /N d)lX7ı71ba\}` ibB'2P-cPA77FVܶ|'5I8X %uk]tJ`|,FS֔Sl8V%"0C#Y.ee'D{c1|n:/^v]lÄV6$BeQ@0R:'vhl+8y<7rafdPG.d/*&c! b\7+ 50! a`wŨrB AJ{.Sк7؀&BI>0e  G11gBHvGJZW 5~/&ChCvXvWr!r!{b5e*t6eYA-`92|blBW5֑o"MN|&N_B`>}# h|Rsp.p&4& L.1g;NsG8`rG y&#.7Iڪ0OɡB2vnAaxC:`}Cd Y=yO; ]𐌻=0-}ka#]+Nφq0 e9-g /"BBd z'BC* +u+h75@;ʼn#w}7dtzUv4Uaa62LP\rqHއPPCA8ʃIiwal.E27ؙR!Mr-% Ć+no7/{ؖդ9Ɋ#Ģ VbH<~YI'* vg=N le#}_[%֕3%8<`tG#`7>VEֹa;H IA 7"L?dU@HDrҚ{H4fV.F$@t6JLD*&0R!d|ugP=Zb ] ċ4s&+&(l"yX^v˝#׃(?;n,Ŧ%^>z@#qo2EGIXUpMJ04iFG~JH9tNlЩu {=4HXAPFfbߞ`QI@F͠@r 2ALf2Tt848chɻK fagzNozVv"aAU)Թdg/:B6މoqTYbBf=V=%3E#x9:$|d_Vn*~eYk2Lffygi8*MT}-rB+ `rsJB|u(9#J0]kˆ<(1L|5SSqkDB6Պ!@Gjs y3m E 4A.7 & @@z*o^4z,: E5!T8;V\:uBE=4+ =$Pρ5` lLb3r9(aU o{WHbl vP;cXq(UUe,(\R&Y& A&dR!\7S1+>n FSSϦ,Ch47B *TeQ^b6qVojtL:ۂAcb̢ʭzaOiEh3ˢS 2tI'@1{v4qaHXY` >ԉ\b%?u@#ǻJ" bb"R?O{E(h 6јu$OGF4Nk{fM33/+4}rW@Oᨭ-6 4xV &,B!TI0Lp r(44iC V:(얨lqJv.eivnvko3{صHN -J=xo2ٕz 4PJz1XxϨۯ {[Ag(vP8X mRiV5d%$, }y,FClULi6 8I A؉JJ|&y6|#dƽ0$=&S-63**!jPV!l=w:v[?B 6j9/:9ao{"u7)F?/ LXPe95K 7;9UwK궢A/dd5X]U N/6Նml!>Vj—E1Z `OC,ձٶYe0#,Ԧ\V73Vt_56C>#Ŝp73jX%DPP@͞GHZ M;ޏR`wZFeKnhrQΙk

^i0ẑ6G0,'ŴAW+&_^Aavf++^Jz;; D 80QJh\1mBCş.Y"@ 叶߫C$>C iI+^q̶E:oH@!%& (CN< J\QTg^S,^stS:le+L,eCL!v7!QHMvM cZd1YE&vbdmաK63GhB`&06jFvґb*|hnyV鴐JaVҳԧV\<K&iH>D6yk`3׃[" cl̹Z-X26\[Fؙz!b_ t-6j; vU1d+B*o*re5Eo-^DD.=*>RU]6d&XST8KC5Bz(f _%;-e(/ɤpmE1m,^QF!#Hky4.s=i w ]&kRHkUJ-$̷TxE#/zTބz `>e^+$IgB=} ܅%{qevnB3v۳(p͂%ξ,)ayNM5HȟUcCMӫ^ǰ;@#@zP:YCxYr?fUyd^mK!$gRVU#۬;ȣ8! PAx^$6Jsj$ټeYF%Vpa4‡!ԨXq&g2q7ɡ$Yߋ0Lz*ݼ3 l2ݥ, ^GS,}Q;&gFV`b VIi7b޾˵a^Ul#R!,ʝ.sۼb_*,U#j`,s.,vpA$ Hq[ ]kZ5ŔfH vL겆 4yj8ꂒߪ] ʨ7cxWUlLd^7SʁkCV7j;҈J }o! MC^W`'X4T.[)vsIgc[<~j_ޮ'Rmn/UEEw/Q, S!zT +@qN[GB⡰6a$ ,z (<@Z;y5xe!xe{ћ^.SwQF>qX i.~lǖ-|8 NM\Um5t,IMfTݯ YfEtf<'0U*iӡ)ŗW<:`%l;`4X |Cgn,{{2UDujS(/Ƙd.1;Pjڼz΢]s삱 oX:mٶr=vNZ jIgJIh@b7e/ PnkæyH}^S6զ/6JD d`ԩVJ`5Wɘ1VX, < SjCݼ^u^_[;NQj>CzQ $yb%TY\fy0Aw} *T"JJm4R<٦зnJ( ǴWRKbAI V,`6PkithMfDkWsa1 k\YaRaə?CզLhK1䲨C9 ==|/hJ5[t3VJfe1 GsS $=;b|xCncA? kIa.Zp4'wx/ DX#Z1UJZ>,{<j4ߞI[%Aоpz+qw0`>)'3id1g4]̦GL>Y\4p2g/r1^*xb9c"ȶ_3TFwr6dA6=[1]gQ>tP$e2ƶk؛N]Mh"ևzxDFaMIQ6:[kV Q֞dS]uz’]& >swaL{T;ܢ>˗h9Fs^ct lGA˥Y.[UqpZ/ڴ+Ns1#BWj__ڱ_,y}bS.(u}e5]zB Mxt|PKyVS )Ӳ6#pip/_vendor/chardet/hebrewprober.py[s۸?XJkkUqDs챝f2y3$#H+_gwh'wy=ӞM.x.Ƨg3ڃu0HISuF%V]ITYh 7B֔*6ʼPT4U7֪cMhba4"Vg^1kxPI:/Re7"ϜPPL ]lHWeYc?M^Hg0qb<.?/8mQ.M^e&ԅT3) u5M/d(mՊEsb1sפҟI&cz1<9}tIx+x7*5 sةFX%]@!ΤFU̪ X>_}S'qts3} kiR\ CBg{_>:Q#u=~ݨ7WgCx1^Ť.%*2&:I `0B?92XiDjlٜŖF?duw};ƾ}8}4쫗'X&di}&%-a񋓓'J}œ~wY/eUO!=G!tn*8[@ol~[9qnlvJ醔ϳ?!3WznOSZ-LJY":Vʹ0kKevtR.[}Bt!gT |GnU+bCS^ ~?#+ν$x\$jh +:,oZ[Y~W~3CYɛIr胊rd=Է er#sfؕx2|:9&c*؜%M*<T|.BP)dx<8sJ6_LlDcH^ R_&ؕpљD ?8DTx~Ak\IåeK3p#3YGNX-ԔY$kj(Q_>UP%YV13c^5$Ηx7 R6RdǼtO-O i_btZBJ+2J5ʹ'/Pߩ4XJlzC%"$4kVoony,Cj1>rB-UZά% na&Qf5o('U0%jS=Haa+AFlDy@0T#٭q%BUŠ?^䩑מ5Qxumu|hop8T>S$B2ܝesbcQ伀\X6W΋hsi]~vl>9ry3UHKN oykI ®Rqhf]@8 /ŵ(#1~D-ju~wA8R'ŒN9Z RFP uZ=/XSBk#e69F"#G;6zHk[5Hl8QԖZqr;+@/2QإzPh%=je)8FcNp4!sȥkVI+IW}gUPq &Pe(jRt/ a%rp C} v!SKxbhC8-A`43SB$ru%inc:@Gpőwp/\)"B &/ ajk7ؚgԾ mΚ U7 :V+( @U`C;@VP %#]ғ%dڗBfr56g#DN#dV r>CHw oywm S 6RxC"RQIΑCi>(Hb@-x$f݌"T=CsW# 7eEN\wl>;%SqQ"T|[RJ_*-?M2AW,A=G% փx܊p] z%u8Cgk9(a* cT(R#NRf6RIjNzE< @d l{;Qd.oOYmPD5kmo$gDUO G5-$ 5w*Ilvp<r3r}!H쿩y47wJ˭/hHj45nӵPQɭ`:PK h*mFMŤBf,z[+m2ޑO'EoF߅&` 5 G|XdU]h2m\,RQcyDb*%lrȔΠbح0Ն4cQ$_z^!6SWYh$AUZɰ>, E*yP3&@UB>2+^նѫ=B9T'PiM'b'_F:j4?\\O˳KyK&X:0p:;^ɳ•ៃw#e6x=K %Z02= Zk\$ DCݹ|09ѧkGɑ&6O?%H҅OEۻ䔄~mC22͂}˫g!H'Ʒ 77g'Kz4z7>]֊e[̸dX8jSgS<޻t/*|&(OzUaL Ryfg%5g M kɃm}K߭fey6#I H:/I9oX[7 bPK&]"(zR@M,AL @=w.MSmCu,5M:knhC=ls9ZUߣ%ҢVuZC!冭Ċ7Qp<>3ͺ~H "9v'Xt}K,ϞdM\Puguxˣa |u(;PG #nE _A[,xQT C\T&)w'.ԕJS.Lgr\6@>'pq]x?]и#UX鞆˨ PΈ}T?B3U/uf 4Ծu0MZȻWyQKinSxH9/ˊ֓x>I7CrtJLʊ׭PlP^hkivXc BC挻 ܊~\8a4`]fTtvO{~gWf#)o۶ ޿N|ACWl71rTh 3ݍw'Ĭv<:!~ͤP~xE,0w_>N^3TJ=I}_ν  wKK3t~hRkqB\'r1zV]?8zޓTBE| z }1wRjj:5d/HNW.wfsl+dYv'I& u4bSjVW|{vwvz7) Ԙ iL= :P~+wS~vchUR1y&ޗ$M탺4l5Md7wXjpT2cjy_;awz%|̠9.!j4oEýAphĂ9j3+l= TqMXrS ˂qZ͔n?hcXo~Csbou|~Ca|FG tҟ}&x"J+yvƔ7te1,`u=-oiCR/Oi. ܒ&PKyVS9a0dpip/_vendor/chardet/jisfreq.py_ǑS$ ,`-MED]cdC,iI "[VcZݜO?;Y\/` T˛YU'#NO>/yo?'?_onrswu[{]n˟*?7WOSycw7O7wP*O2f7Waoû}ȁw7WOoˏ4?^ꁱ}KT~֚9į䷏_'y7_w7y|V}wTpY<ߕ˛pqTCO|Ǐso/__?杖?\]?|ۛ77w걼ǟ>S_q#)_ߕwj爗%7WO;:~7j<|NߟmӰ?9`>V9Bˋ˳o\˗Ͼyos_sH7K-TWwOs?=?gg_?s_xWW߾,w^~_?{Yw߾zy)?N1kɷOW77x|\77r[݇57}BWn~*wOo7D?nLп4]m.O9W'~gp3JU~WGEʧr# ǟro>&FܾOpϗ}Nrn_޽lOo~]]$Mջa_}4msA&1CϹ_Ygr|>Ʊj~jaG~?~GW#'GZ5L5`d8Xi~oyÚIǾw#n);;㷳_~m.]2yq?g_^|_|/r`ꁹN,۷2^C#sUi&RiNz[E~K;JdfT|A9iys 47mۡ9;ya9n~TF42WLڎ\X<Ѹȯ!בS3m 5k-8;S󑼯%y_||N3|ҚȟzƭNP5ysMkvkܯHbuʆ]\\\ȢǢG.h17tA3i431R5݌nF7=n9tw' Aa6pz=Apø=͂AϠg3\ңңңdsFF\\27777777.ƲN^+׍ =;=;=;====iBaQcuv9y8c^Ky3rX ĵ 6[ Jsc1"׸^/-cX9 ؕ7XV.׃-ٮ9 lo wg`jczӺ1FeG 'lĺ533+I\0ǀ,L"ןн16Bvs__2`m }ڻaliCM^xF_8N<̅;Gt<ݷ3:QD0l B5ˆ Yl:P&/t=b;=:%l/%+M<P1"0~FHn!keLmL(v VQYz8ko8`ah8`Y&f~:yk6 8`c'nqccukhq@fCOCװ z =58 (u#h4K$09 &,1@טƜgC@[-e.q1c<x>d<.\L-d(P @1b08q֓zVz 0`$H#F`Բ׭'/il V`$H#F7|gC׭7 ]WErf4Ru6AxS۞uxbYVkb(PĨe#73 $H' 6i(VX"\0FiͶ;#yI~'@ƚ =df'P7fLzT ML~Fi`z&/+ 4V#l Fi`F.6zVfFi`F X40Hk{f@OJl50H# dõH#lFZ6% 4Jl50H# 40H#40bKXi` *zF`40H# :\7xI+tұ'5pCjH@MjJS|NxeA]wK:0v0H#eõH#t0Hn ^J+tI#t0H#XlF:`, /`V:Ft0H#|SXV:X`cO:`F:`F:`~ ^J+% q.zip[м=7"&RH#3ZV:X`2q4 IC02qaF nm # 02# 02# 02xゕVXؓF`dFA=H02#=.dVX# 02# 02#{\x+ 2' #zF`dF`dM^ VXؓF`d손`C " BiF: ^ = 02#L02#hD4N3hd V&Xؓ F&`d F&`!.z F%!gCO2'iQ F&`d F&`d F%f V&X`ebO&!Ά`d F&`d F&K&XAB̆ؓ F&`dl`d F&K&XAh('`d F&`d F< ^2+L#L02#H'u^7,+ {# ,0# ,0#9dVXYTl FK4o0ob y9dVXYؓF'Y`dFY`dF%VXY`eaOY`dl FY`dFYKXY`e=ARΆ`dFY`dFY`ds% ,ž,0# ,0#)떼$?8MTFiͤgbeCye8=FOtz:=k9=NGcp< z=AϠg3YYYVGGGG%x[ٸVGGGG%iΎӳӳӳӳssssspAAAAAK,wsssssssssqEEEEEub== 2dGhZ9#guhEd:tGdѺ$N"Hs™ O-;DKg7%:N =-䋊EV= _a!60p Hvd <9zTw/ n1>NZ 1%3/qϢ2$kFqxLʌH.+IBә'"3c !l!4Q4n8*jkA`3Q*èP.s@qse y7gh%2\f4,w@iH 2Upp݂X#8Rnkqp^F2@*t$14N\bN,XQGXsCEӸKCa6Tv_V胎Dlh= K iB$W07Tr '7N}Ͱ0\*tH&w'|y9y97EёW#zv` 1c]]^+ۼΐnq:4yGA@eŠ.։\tߚq#[ &9Fn*t =,=ȯz8đq;yBnWvpF4BNFэPKC++ znNp2Ď55B$ uFfՕp;UN"dyGq}'ZTC٦otj"dl&EӕN! %hJ 0nԐ7)AIw%R}qrJh42m+ǼvBP. -\a=1:xah%[pj  - arXF2eTDh3;v1l[V ̑Y-Ju 8W࢐q%fFq'cY25_7-Vw -7C ½-7Ʌ+l?opbs~ y E-'͘Cnf|0a79`Ivj7 l!/R!r8@!١q8@4ج.!Yg'd D,5\̑k2>=9K"| I-c qu␐QcVVFnxqXl!Us!)DhNH:BA42Fl"5~NK U;pYZNAvQS7!8c\KQMX1Q/X~ Q1T{&%M s͑m-71RN+IBJA> ]ޝucVq-\bQ= ›6V =$2e 8;u(F֯ Ji\x !,uAŊvd~MzWUp!k P6h bRE956T Y{"VŇhAJAS|)G7yIED2)$T I:E&/qY,RFocP02[:HKZd*0Nz}I~ƒU @0728I%P8QS1 Yv[]9aZܦt+LD / c+ V¤3h UH7!8u8%I.d_( łFPP+(<7w+OO22Pp\E:l\6)z Y ~?!Rjϋ7]\fPSX(] YRRz3&ԡ:en1uξpؕHLlWL {nF*PxOm'|'rCt%WSD0M!ψg  22:FeJ+|ˍvԽ~ M,Y'i]JGX2*d1.#JuNzNTf@VJ)RDKc ukصhKT-`I*si@(Z3Uk}L>oQS65/#RlT>$ɚo݈-"4*JySզLu̱zcC'8faKՂ 2ycKJ:u%D 䧸] l1geN#T`V:u&CiLr2rDE&P8xGH~ٔAjaP̡CbD:C)E9o8D>ԽHer~0fAeHYXe*#Sf/u&0WN-$ZFxU㸔HQ<q v*uNݫ VD0Q9H6)6dnu;W6E{ݨ{-DiQ|&ZvE0&D?<r^ ue&_NYY2R4w;#g [H_; ϸ{*/R]R$7%U{ gL)S R_ ѕ2T*K]G ~_ꝴw=mPd+]x"!Y`;㆐-@G4$R} &vԽgQt%LWȪ $“*uH!ߋKe_)}h0:-vU{ CVdME3¿Rɔ~WO.*gd Y%&PZoFQ3Tq^~l)^62@W(*WR^yTPj4X(Q^j:8sl+"= rmRR4  ȯ)S&"eIK{W$PCzX#B'k \!*iǹ{-ʿq֖H"v C PufUG_N8`iש{u%)Lp*K黢*aB0rR :b$4([w{*HkDTĶўի@ Bwu5XQ/ՕIe,DG<\wWǮM"3b@EoxګM5z Z.F% ;qDH\?AӸr}Dm:>,DW_;ɶJHh0FRI<*=+Uī`)(ګ{\^MQbrl ^KzJSav F8Hb*p$վ'BI٩ӵ$XqM8^2RW,k *=!v\uAM*S@~%@E7To@7ĭ8**}^qPKyVSy%jNLpip/_vendor/chardet/jpcntx.py\mSHί8J/{󲳱 1p8:DָXIm[oOeLč Rde>dVJٳgձObKg;mu[Z5RTxo^ݦEߴbʺs`]O몯TrܗhS?+n^ܗlت;oZE ,.eї qhFOUzG5ժG{B~jpm'.ˮl? f}h?j}Vwb_( {7_5[RպiQkmٖeN<61/jі+EmZYTG}hS/r}uዳ7U)Iŋ.[ͺίyYw(:qu+ԉ "N5wy=DzHFnk5u=%X+ky0ZBTvt>BM)6]ܬ#5j+~>~yZ?^^]NUVgHݽBUE?*/^?:~?9>;'P\^^yux).\^_qUPV攒jȻFirQE*vJB2D\ln[^ՅnH<ŽBȺjo@??D.UV&ROd4m$~h^7}}ƉLc)ě+ud?q|3I-B$Uъצj5ZM((ibR-[壵ժ7e=*ۦ}>9R#j-bwg7+%cr/ML}KIͷT}Ԝr ܌$HƎdNY}YlL [[ eۮ#7ɍƠ/h C2[[;N51IvܼQk Jim9i^9tt c1Gr7Cq!YJ1FIPb5Ia^t런"Mٟv/Fac-a8K`RLO~27U,)4J$ӒS T?G2}L ?aBU'K-:Iissy4"~#SImC04'uL+Dz28+q>CK䄏t@ɸFIGG`L"H< .XC$( ^UE0QQg5y?fm0(Y'4]=sӅMNqT}ۆ;=4JY$Bd<A@t%Q#-D\QI<!RLےc+bmB%̉Y>Ǿ%PmesS zɈB(#sy] )P= >ز!_&^E29Q;tG6iG)<'{@I­]w({B)%%$C,Foȯ3q)lǿ},6'A#];2QP( dLo1Me`Ror&0{14ny/̂3Ǔ|zx8!y% +#$uҘ:gPN"mR9ydJ\anDTKzfJGa _|E#xiX<{%>M J? fERdU5xt8x'q,@$.}FIOaL tCCMD~8Ottd)5 (e8!cKFIs($2\"Rtpv=t8Τ7Nӯ*@"k|ǍK#TXHy3-9$ʆCVFci \O)bSn Q)A3.:@I2JqҰ3`x~K+ḋIPsA  $~h/y-s"ANJ[1`T8 !Q óؑ~xF&T2̣,">4fQ8<~qw´ ]`Lԟ&c,.(;"`xyJ1 B=<1">Shie s[tWAL#5Jfrx127åEE]izF|4M08B}](OgOF-Xp(qq;jjxt,#ƌ=[drɩB qq[&sta+{=+% |R^z¯$]pFhquBR Xv`TAGFeJ9;צ.ikqv qx3)nyqz~\ŠsΓ sWkdC?p%F?̐F%dD;yx-4QPƣញSxĠ&p!ؘ#, g\b96|]t/+snu~n{[9zȾ4߼x9<~5~yy|suVƱ9ιgTَ9(b6ꪟvrtob=k˵t:1늻u9}.˅c}gyYn&MlP&g=Z=5B<='C}g!F1o6ʬmG+V kw4DU Oh]^=1@Z q؛盶wlK$f;5fRfmU -J∍P mꇺy+O ~>vϴlIK-~Qr޷ǪtF\&>`\zʤZvSF^Ph FQ/%zc^,kH$, z"Y׷~ufT ,Ca-M[0ɔ nɔX~X\DfnJG)J0O5+;37fw N4$$5FòW}0L bm^b6^GIe*@K|4yV֯40~hP]$1~g4JOw>SENv:wŇRmK/*Ѭ⾪o(\k$F^um>lM49/? {Oxo]Tmƙ"#l-{yJHձ}RǶyɓQB%S44-{'V=w-c*dC(OD9dM["ɻw:xz?m0oʺܮf/A65M3lhVjp82_6;j**{z+6C~,u? =o)'Ӧ)rhe{ù`c`s0ԑ0>M/#ee^xz5In'Íb݉1##ށH,c5.4zyzr=S}eOEj{< 6en?3iLRT r1=p(_˪u>Vy8~؍Zoߜ?zO?9 ɐw(O?k<&MzL0"a1[+J cٞrYNҩ4^-BIUG xXbΔcIo~r{cp'ǟr }ggERSi56g< ϝPKFtVt˜)pip/_vendor/chardet/langbulgarianmodel.py]zuHI(9<ݳ` ,+RE12$F"@DQ>jeE6kնdE=~>I }nJf[UgΩ{{֗WNkݱ;646V6_{y;['66O7+g67GqÃGCO GG֗ W7Z?ZZ=+8rp|}[V_}nେo:pG8zґc7xqa]^h4n4k4 Z 9C?`MeYE?Z ;_֬4󬓾_`W.EI/=`6`v8dk͆uV%7ؕNd0ȷX'lL~`vݿa02&dYNo` }hA?b4Ǭ6觬s\ _>B`v_3moؠ[6uj _4}l z?cXfZ2uf# fW(M㟰 471V 1 ։X'3N.?0ڽQt|!,Iޘy.Fh탼w1FbS 8EހS y o 4EހS xp"o)"o) Q7p[ԍ֓ԍƅ]LRwK@'hHSԍt7R'u;݊^/dunTuT7R &d a"Su[n u;1Lw1u|#uT7(FBn7OR7Bn?턺񑥨n7n7唜/S7n)R7:F'- xAEu[[u6)Tznuꖨn-S>nKPYVx!Cީۧ uWGu[ -3Ld[FN 'ہabE1Zc"S]P ]JNT7Z{Qmnƀ;n1Eu˼nĔn1NL֥bJvSq!mn4.S<SZTw~LOݖa*f@bJcnFuSPvb Vv[ce0SgS.Sn0R -g--3Lw9m i)x2-)e^0PEu nQLBun|&h! nvCMnF^u;&}PO`1[F30LWB /;_u-FD)lXVJ&+ u+a)w)- &gS:1LdBN '1Gۅa un uW0WݠyM)S>e2:ځޭ&[SVgIu n \-%u;X-Za*kKEu[ 7IrBdg@-[aDununQL邺+r/Ŕ>*!YìeύNT0٥1?u;Y-@u;)+OY218DSʨ[SVegSzT.=.vE-Q`ٯ0&y\01'2ʨؖ u[T~IuȨWS7P'Y-ےuɁ}l\ Ɂfq :ab)em1Lz}n10_=}Q|՝ObJ&2궨nbL uŁfEݖRf̀zg8)}&ӏ)uݔ;0ɧn m)-'u&dOV`yN ^gu;Y0na3tAh\f7ea2}b^ n;?~bXT7`[rdn7?YŔ3+{^wubJ'궨n-E t+L&S&W vSvLLɞy/)gu#}aRŔ^%e[rcJ ubJ uǔ-EŁ1ޒ#na"n%T7>gL13VwS:xN  u_tRd璉noꮼ&ċ6+L]uuuM k{uV&@gԍ˛6oOR0qu˨pM_bJ a;T7ZOq#|,䏇W-8Yab)Ѻ@tRJu[08в۲8вDF>@@F@gavZ xfρ,[DF턺x궨nKLi+Yu[#Uh) uB1Lu3L,-1LZ-ntrERwnYn'-3L|R-3L,-ےSSw-92-n;#| u;n_l&Ptc+ku˘ sE?fJ,)Va)\h Boa8܀axJ)0&ӧppFldNn=]Sn궈nљ[vEtNʧn&~ASr/P)\6{)ͺ-Kj2sc暹tQn sDes%Dlihbnpqe;4!%-9ݵޅ}E|>E !7ѝbE hێ| /{ީ[/nu[n1=-EuKT%QLu t}Zvךk&s[ )-qD!e+CJo-KRdP7jnΧn_"-~ `-/.xYH)KdY/u{Kj2@KGu/NN|u򊜐$֗Ș۲:mَ㓹-}2^]E殼_fn_Rn~S!l-oȑ, %YQ7+}2۫_bT7w~e WRSwr%sGݢ%eV%p?B佔NVunM Zun%5u{1L*4Pt~78P궨XF')Fk/kLU 0VSnSO)- L,|;EoQd4V7z}I_I'nXn)Wbu{JOt[RJ薥>EO6MN]k=8dڇV~}Iuf?Zt=7- )eK\"=%'u;)~Iu6[DERJ>5s=]ѽ+/ccF)消tP7ٚkwS.4wU[bhenǰf睹EsWf |ꖹ%92p;h)kꮩZ3mF)H ( uaUwMN%b%>g-q;_m(eg:yǂl#J0aת+-=Z^";ąabKnԍ>{Jt| /V5u_]2-:;_uWn@I2[v+qQ7L궨n%taXn[b熺R@˨nn tz@PO +e0if]SPEu&ݔjQݲD1ea,qh]LuΞn 'ŁO|{nۢ]l&&"Ꞻabno/ȑQ) uZvSTOӧ/Ԫ9n &&@'s  u.2L'a w߻pu<ܺo._TGh3\CƑƆ['Wl]oǖzNt78~K~x-ǖ ãtl&LApIIIIb&^9Tz1a^~ʩJՋoz?fU; ` p?7U{J&ky } + Wt=@^{7s@ɝ&qw֧$h1wo@ܺqv MYd }s0h?@>G _sɷA`q=6vL0.g1F{L~L>&l&,&}309o_[`Fk&t`\bw;,pQ`q1wA$U*wu1m8~r',&} 0Iշ% of&H>* ?[MW?7?7wڿwڏr6X_uK㊺hVK&JqAd8 &B_͸n2;T|LiBL:и~t@u>7BLлU\R*K 8C" SgTF\b2.H~PU> 3DCɋ~R' izPuᆼPu{H9B9O6A(.o$߁o"#~nns=RY:~,j5ـ? _:1ТlX-62:6]Iilo~/?TMקo|@?P`MS9]4rzHhAՊi*UC+*|hP}@OUH@i> >͇V#UާЊ$4ZTyC+*|huP}PBS9͇V)C+TNJh*Њ%4AT塩4th&#G*;L#`UtFd, V`zuNhGs"pN,e֢#񍎮 Bq5uPڷ+uc!y;Hn?(Id.{@0BI3V$(%xդx2cgt %IG#K#X %zF36J,bH1Tr!K%BJx&h2wSw3ws3}wEd@wks=gBGNߣFg!I)l֤L֤4M6kR&5,$5,$5)eɚtɚt֤L֤L֤L֤L֤L֤L֤L֤L֤L֤L֤L֤L֤ÐL֤Ðl֤DN6kR*'5)͚5)͚BɚfMJd&dMz{&kR2'5iɚLs&o2Y^ ɚZʚdM:dMJd&i2YR4I! HYNC2Y2I L֤dL&kj&a2YӶM5i$5mHPʚSɚ3IL֤K6kR%5u6Y-٬I5kuE=+klU}0p EW< C/PKFtV?]݄%pip/_vendor/chardet/langgreekmodel.pyku律$=sQI@e.!%v&f6#!ps s싼KwkO8]⍭OS}Õ܁ѽZJYZw}x_߹7X?{8Fo><[/ GSQ/zꍎ WzKKK{* 7_}T_sq#'sG:N:|\\D5fʞޱѿ5ڢX5oX%VwX7bQu=`}NdO3~>Yֺ?gsu3g2 _+i򋬓v,:aˬ& _e}|fdvoNXo dSN·>Xu>Xkv29V?b}l=ɬ_O.Sv5s|v՟Y:`2/̂ݫ5j6XoJƳCvqru5[8Py},Z^B0$N9 0Hrs%X8)CNqYNa0!8 9a)CNq0:IqrÐS0!/1aX\.ICMqXa,a28c3va,20{ÐaѺ ?Z?,K@Nqr.q ~]9>50qXz" ~L;?la @'eai-%q8ÌFsXKCÂ9l00`ø8 yQ0z.;a#K@N}:9l02\~87:0:#W榄Y6pv~X8<0/scQ/!ӡu> ~،Â1ޮ^bgbzl?<5%ʙ%I6.sa&K̀>a+?lvp̼%26#r燅D.3/Q {29\>]v^?la#?l6pv^Vvt20Cr a\Y.pa]F~x[.fqc.py z  /9pXfn WK~F^}n9 y8,a!/Q}:Â8,%8 8][]/8lh]-?l9  6zp!ukj'!O1?\CfȳtB8 yprXÐ':'0OBs8,9ÐgP/! Ka|Dy }:Fa#Cm09~o!qa{0׭[+pج^bνp|% v7ߧC־?, ya3?l 8\6xw9 y \ Kq̼5~"%0 Ð'qؚ󇽘laapC.Ðwc^B06rz 8%rq@.}͐^B0Z8 9a^\?Vaa%5" FI8,s8^q8}laqq.p8,aȓa1Q^ FFs䇁|F)%5&vޅ.p~FÔfF28Lfϥ fqP.!`0,3u gKLtfi 3;l[caa[9,I0Z8$=,%3;^0ZOq028X88쪇zͮÎörO&=\0pl9~^pð.Z" ."=,`XH TKL;=\6ð`1lœv^hX-aa3; jOm;l0ft^TC8;;`7{ga4"l? ͚֤%)>./8L}y C4= y)lMp!y b;UK8 ; (7-QDz]:.F϶Zl٨n;8l3].t͞~7]0̆aa[1 y3~pa#;\bDv?L'N0ʹaa[1l{Vbn,+!`xY o3rfY2%"!qvvQ 0r&5k B/- aZ J]VqV Y E͕%h e$!Hs0̢tvaV 35{0\Zeax0Gư00ݒ( R OpLnXHa9̮aa[9\6*Y+";lv>l;,X+y,a>$!+!x]㰭^(?,%=ьaNr[z6f懅4KGkqv3 >YBsaa ?8/>qqV%܋8*eWKd?Ñap8ZOt|\zwv k5Hhj0.Ef6ڧ3P)?l!a/~u88l+!OzX([8 9a|{LO>f3ӕaZw!KUu=ڻyKqЉ{Nu*:GN>|go;ٹDCOV+;c܃`gdN_VyIӲ}-גrM\r-7rCͤr+)2A]@Oyz5$TBOр'0^2PC:ONAzu?asadO뵡BOŋSbT+Xzx1Tq.$@1 ֙xĀ{ 6L-&!v,޴&}TY˛qCyL";yDGt+D><yD߅8H`1LJL2<&b >5&bu&b鎟u4E49:? D>G)D>GџC"j˼ !fAQ@mnhAZe0{^cG<QmW}VQOz;!FuGEU<8(O" ~m/!ѿ}bMK)hQ0 }wAǢGnjn<1 ?hjjtoU557ȗAKi|=D"A"?BjQ@M(їi5  Q@ԟ:H/@F#1긁 ^@S$/^Ko^O诒$Hod_e3Լݼh?Pz5y˖l-׳dѥ4'#}Ĵ^ydI;9O%gH6Hոhd_Pi r+8aCN_ҟc+RC*aH$D>vᣕʩjˣBꍎ Wz7Fot/> ا~ΦsQWG{nuIѰ3\_w.tז$Wڹayף?3b7Zlw6:j`[w ty_n;GX^wuw#}uWwFˑ   ?  <|.|>|!EbRrJjZzFfVvNn^~AaQq'o//ï¯on_C?ھ#?~t'-[8ujNu:^p{nu]qRVBb4dmz26G=y#HFަi>dm y#HEN"R|i>T?@rm])e^Uȴ)gsm5&vG}Cҧ=ib%ivGq#nw펌.l7>71";~X؎H!شPKEtVc&pip/_vendor/chardet/langhebrewmodel.pyٗy@$jL&X!0 id&ag|N^~_Fݯ?t(ԼH,UC#_9R:_;ґڵfm}th9z7ڌGwۉۣhTƵI[DfvV?Vp??% &/՞lOڹz/mv .myܓgϟS[jDbjKG6dm٧05w̪z>^>ICw'κzv&ZgS:g#E&:g2;zV&ɬdrȎ䧬u6&l?cdCuU&ª@T a_dĎNeGLΒ;g&ZN^e35֘:k7(eꛬ1-6/YoΛv0MLf|͆=֚uv0U!뤝Q&>>f3gWXv&d?ΐfFؔ5ڬ=Fgllk*}?-b6p3k?-r2[2[xfDf.h]ZfCِb6 fq fv*> uE|vq&vgKcfM6pn-<&zfl+p9̖GQ#ù}v`mgg lT^qMLg>#Iט9/-}v>\f>;?A.g{fOTl왘g7lFF7.*Ϟgygg } , ϳV}gj\uMFYr<)úll홭~$㚛E1ۯ̞xf{fN*᳉32k̦<Hf?\yfSٞh<"XU<;g_llggvSµ~ul ,X`~=ˌ0Xamȳ,am!Oc̆m1m3a8u!gzԙ^9뎰-鳻>DC;v`k@;^{UlQ vﵭ!xm+h[z%A[bfweVځ$Bœ $˃viOC{2ae6j;6ԪA*YAhKpㆇ0mBvr6F{ ړ ;@.N m!OC4jІAm3eڐ5hC6T 0~fG硭&9mȳ9m'vim0%.vY$жr~yh 68mZx#evmyI<ՄyhA{Juk|a_0_:j–o5h H64hC֠N4hC֠N4hg6AԢv> ]`\c6v+mv h#V6#ЮFў| i`SHeev>Aom'HyC6.h65VFՅѶz iȍ5#!K VV#KHGVj*=,Kmpچ5v!˲vdY!MkGxAj".cH'Z!/;`;mCDvvRxh z?Em^C<`:v|Lb__n2{h 4iۥ# _ ZcȴѺZ6z.I]B\tuy8=NW+0tNϩ |8]B z1V/F??kbZzZZVXg@l0M&bBl3 vG2d壸+ D^@=Wt"(D^W!)dnt30qS0p3d"QDqꦻ mf"&cCAsCs')ĤVw=-)nǡ%UВr:xZRMW} M5hI-D{ZRJWwZRI3ѾM:(/ bRIGigJ(TUW9hI%ǭ!Ф' 9ĤNk]V.BKJiax $PG4DK/ &tm4ӄ*pCJ "?"?._OOA!&5$TVݤM )+LIAm5=t >('H} zp}R&uUgHL*j"QM2II0%iHbRRKMIEjRSSE\HWPCbRSC}YΧ*:q9)C*IRg~{F$'e6h"Y ~%]I~dyYqe*U翦L35̶^fj5IG|ξr>žyKFtMz:~ r4FhNxx.bRGxx==;)'7TɲN\s{tK"hFkcӻ:A I0 򬅼$ez/iv#IGߤU*E~Fr! $diUK ɲ:R,2,:䟒,ąGH: *DuT3奉b~N܆F6+ibP Ux$Uz`+i)4Z:4Ơ p1uuh dj)`4) DQ~J7SWJmӧ2V( Nt </HN'$~IM]#Yqq(MCBiqF'_IvJw`H%ş,klj@rZ*&!ɲƆ~_ ԤoDSf& a4tib1NPZ rP: $.@/*1B0ߗQ%aRTHLR_'UAJ|&ԷHMBo(7c^n;0*=3L= >=R=>^*KCۏH )cӛRq (~Bb;BpYNrV:gd5fӾ[ˍϞ?yLmv?;d}1nF[ʜz'o z7÷#Ó'?[[I_+"jc<6oM:ڍr0Ug8dp??mÄh0}>EV4ֿډޕhP.~!~1~)~9v;ǟğƟ%~PKFtV&QaL)pip/_vendor/chardet/langhungarianmodel.pymU,@v'Yr$%HV ci̲;s`r 8!|BB|0/vv^[S53&rM߮k5N|Dhg0yqg≎ll?=}??uVo?~z177? _/vƳxp˜l\߽}qm R'[N_pzksۗ.}bTF#ƃFXQtt3UϢO&Fϐ͓ 3ϐ~?CzbHl{DH,i¶#ߦ1zsDqn[6L=KF~Umc9[mH$*A; d?p 6 , błX V,ĚfAXkĆbAYkĊ@lX+Ċ@ V, bŁ@Y+ĊaAXq V,ĊbAY+ !N)C8b@lxC8b$b@ !1S (VM(Jb~ՄXWG쭈q1Xq qQ1b)Cx X5bhx8@ELV"XE\uEjWĊq bޚ0,YXG9Ċ1K =E<kB"=WMXZ=H*ļGoMHY"V*bĵ5Q pE\qHEb#̚h K@\:b˶|mu"5ZAl$ a2#A;e| h[|h2{70l]{Oed;3V64穕,Zl܌07ɤj&%ٱv1f~j k,'dܕa&zF[!r_? j +"ܴ8XcKZt$R ݥ0emx3 $0l.?ǰܿKbu_#D.GxZbgx$`7{!m6[j&88a_?v *Y ]>BY1c \MѸ4XEc@ fv}ag"U~h꽉0GCD&6>6GG_ð 9'ħ:M870N_ǰ;[_hNe\xBx""cتi)&>0¨@3m5 #.cr Iܱo c4~D<Z"aF  8w0jsѣoa4L~'?Ĩ N6FS^"6V8q|cjD#n^ǰ>x(=EH/|az&?h 4>;=FaހP= 7L`nȁnB~bCFA?0uNa? ,C\lj\ o?[Wj|i}w˃2޼ӻvwfo~mGۣީaEc~WF{^)&2׊HPKEtV`̖ (V'pip/_vendor/chardet/langrussianmodel.py]kGy_1AIld{vfv(,PЅH8fVʖ(l0ϓ3%z[ouu)zn[|o_Μ9ުÍ#o?ҸqhΓGrГ[W֮}N55njdƍFp' ;,xqh#)O~YhxOK8=|F$g5^yc" LťzU- I|,|E-&b Xܝ X|7,G~K [\oph{J ~_|;?{}]g?p ៊c@s(FbgQ/yb~>6 bTqQ5[>7*vEjgsȷ~7ep[]};*2x^})8H[/w(.aSx!Kč[9q 7.7{H x8GGػD܀#n]"n%\"n%\"n% D܀"nLqq.7qMa(7`q%%l7Q"nl7PQJ0 1pčK x8\"n#7λD`qcqcq6[ sr4#):;# (:ۡ;pqzCvnPpqqc{ED]M܊]oXG*5k=GsX%: ) )4 6ɔCqSqyU n?ōԡ'@܎q܀#q7"q;q/j[qc器kVA*nUq7>p\-i1T8Jp{wnpkPwۡqTCq5L_,؋A nc#nN p( =[ya{JCX%v* Ae>jv mCqVּ-ywv=A%uCpNIh@̞Tm%\mC1ۀZp ۀYG1;37С8`JK ۀK ۀ8U >ۀMNu6)Y&n?$D܎kcA܎9e-jvLX-}1o2pp"D7Ngw{މ۱rvX^A^;q[q;Iqnr@#7Qp&C;wA%^čӛōfu#nP~B4U Y"nq/w$9'nvx*{wu(n nU&T^VY۳M:w69n1SɜeAx{[=%uv%Af a% ގz;yk$`Jc$oOπ`N 7+w$9!L&%z&F2Y3*#q qI&={d݁ L )/Fg2 8z&p;V(?w$9'n\C,%!/`5|1ۡk\&nwYg$HsNA,j7Ncnw(q#犽PA8HHܑ&q;ġGJdk0`*0CqY%Ar(nG8 n7w3`"n⮏=ć* bQ~*8Hܑ眸w $nC)npIw 1p7)\ǚevMP-=PK$HB@#n_8YM@k n&q5P  qv,I񡑸#q9qр8ǍCkCq{Aܺ]=w$& Sqjq(jv( 8w5L%~bJWua"qW7޽: AJ x$A"qӟG8YMy9Y=kwJ|qA:'F8:I?kъ[;*y!n;*q, x\JjС67WI-vURM܉|)w~HD~VI\%wA~7o:t8;X=[m\|jekNokNcum][im4vV66.5vzvgXZ[{խ l \ݸsuegmsⰛm::fͭUnit?Vv}jOW\Q' :z#s?9~ngϞ{scuQjʯ˕pR 4f/bLUmnpDŽ;dKܕ" th KE^ޔGZ*T2f xb']5Oࢧf zjVಧ=MpS>5MK/ՋOK+Ջ,Ջߤz Bjl . m[< -?_\_jmxwĞJ@Yʊ eEGʊ(+z'@U<̋]In-q+ V*@fTZsj m#@<-y聿c{7=:Pޣ(*3k8bG*i X TR`l/N ѹ~T/vs=V|"Bu@U{fus?Lq`VP 0U,%T=`TUT~ ST~ ST~vQj@UPjU3@UQS||x| |$e`VR(Uu2AݬvqkuAU)hVu7BVR8%]3YMk ۄ'>uC'BY]PYų5*xv6j z:ŮhVDi \mUUKJ:ٗ{uQUUQO3gXƨz {Qu{ߌ'F3xQ/k~>h(Ad&Qro/3d%QRbnYy K6_%&@9$JMzIٳ(=./MFUmd%Q3'o9Lhʨ^O\%+ŽKtEU|QU[oI-$J[lIQג(u*[-`zvZ/l$J`tp2hYm`{6_1KDCzI7:;/I[3(t&Jg$83FCM-1^8< ?gTp7*î`Pɧ„FyD _3 +x~ˠdFQőb{$:)p;n83inQ|GaR ݿon8CK?HtmIIzrKt'Q]f"%K{A)te.Q$]@wyH,=`( w9D㇓tw =}>^J̞gT0>FI~(_J%weo7%~QTHRrPp!|%i$>Ũ* _5QY)|aw"5)MWV%Blau#o+F 7Vp63SnMuqE7{L@-[Yx@%G;88qivNoTNvogycr} R[_ٸtuRvsWV6Ncygsysk|yʱ?f|Y| 1x){:t'.d~$/es|ɶį]If?0"dZK~a,ż0Y,I&.<9圇d;:!خoȱRI&.A pK*p8lna{K KTgOb]ISFk%Mws28"]¿1He;6َ섭mɩ, e$OO`}nO׀\9jKYɤ, Ul.<&>gTr(*!naG>U2d|acQ'rP'UrPG& SgdtuAGdtNNCY-s3+f,O0OQrτ<ί (9 rp/:E(H:ag;VNmod[ ;V NƱm%rd+٦J)+mJJdr+٦J)#mJHd+٦tJ)mJFdr+٦TJ)mJDd+٦4J) mJBdr+٦J)mJ@d;#&SN)<)'۔Xv*z9r drd+٦lG9٦lOM6V,)9c%KJɒ2vDRJHX+Yd$,@+Yj%UR+Jv%R.VjMu<u"=m8XCPH7}ш Z4b(҈H Eb(C1H EA E9o"?d&""S\go7DCSDN<uIG$Rս'Xk':;y < qbP*4ϳT BP%5y*9 D5JB9&APN(A!h AP% y*93T@ HE{RAݢlb v+IL"lPawߐ L:Ur[̍NUR" Ey,DZ|Qb6)ꀉ$oOQ7[G|uD7[G|uD7[G,)JG=#͎=#͎HI4 wE -]n`v.[J"`vKJTu4;Ec)NWoV^A镋'o]f 4 {c>醝 5}gZ8*&!1yHCbcCO~K.1$ 62ZC M {C A6dZAvdڼAd:Adxl6ty6"m`kd-5D ]"~Y6lOk]FH)mad~"FH-mad~"FHGc60ZF |?lm$۠ƑlE22"z: seD=o,#mnceDko.#W.ˈh\Fyc=(meDTi2"<':vFĎ*cgJOK#liEҾ<2GhkJxtvx3>}\6<Ӄ+uz<'}N9=N1^9N}Nw%h}'%~Jŏے!~ܖ|ďն%_J/9tSPK-~FbA?g3N_JRǕ^Sˈj_s%~r%o?gG3ď?z)]?|NďaSs*~/Ww0I,~J]]ďn ;9c3jo$we|VzFn,~ߊ?LPgM?kDBŏ(nňpS }v9I=\6'9DY=z4GZ+~4~ڶzfsrVϚ?Ə+xNǷ?ƏUmJn+IBOz\?|h^P'~oUD3O?1oY^i<MX=qJڜmĿVĿ曻%??/z.g_s%~S$Z׊ܫ%p=mm7j7l.K+6"~Gj⯠_:Y=DJ \O_ŏi.DUh?wr!~WY=emj)$*3VWZ=’ \4'/ՃնZ=RVev k/Xϙ/-*V ϕ_RqΐlYV|_ZyۈՃR̾\i%"mSR9~)s#jpJ3_r-RPrjV+9GHS/z\])~G\i~#, X.SSTuN)),UҲ sz8ŏnr?ge=l.9 *~i'w9wSǷ''w 5)Hۺ?׵z+~|+z8SCXm+VҤ%pN4j?/Jv9M>G{`?GR_JU$ٜ]ﰬ/,~$~WpoWjs]ϙԳ&?cWCjEEy6'cٜďx Z=swя//>FsV.Vg#~W緸]qu~KjHm$"u%?GoI?S?gH=~NK߳g%/ܭV0,_j8R񣽧m_!~%{rW vzsJ9rz\uhC|i.K*ϗ0Ǖ#Mꑦs{lN9̅KJO%~򰜐Y ͉9?W9_1ٜ/=lNG8fsrJ=sN4~78]9}P;} ~ ~r= KǷ,~Gv1b[R}>՞$W; Kv,~]ϼ!9=dWokw~9s;?pON%|[&snjNo!~.߷/%~a4tkszv9=G7~?W⯚#UҕzCVVݓsޮozY= UAJJ=%Y,~爟SmK 6p{!~j+,"/zGJ[9mJ=[F-Wz[~+xV-K[G[=zоZ=Jzo7?pqƿ$fsV9ſ/=UMW=asy[W#<z6wz|oX=mժyks6gX|z.-N{.ʌհSjB&~ K7wJi?xtN.xά_q#Jz8<~D6緘p59Ӣt~eذ?9G(~Wh_0x1(#*6VOMWuC?%p9Ja^䈟SRQ+QqN.3񣽧tN9k?CO]3,L}ZwtS p]4HVP񣛚kwD\\iඬ6sSV+~V񣹟oqM5_\tax}ˁgjxSI?=Ef"~ )ܭsWg-SEiqN:wϝr)8'oIv緸lN-U}hT4StËU*O>p }) ~Yv!;:rzsm[UI/ks?WSZ_j8"~Gv$~9̥\koDI]vkm.^g)C3Ooo &&͟D%ɜbj7oYP}M(~K;;Sxܺ3n͆`vg0 F{x2 G`4fh<f3je^LGgc;|pd?x`6K M7$dg~QLta2MU7/ؿz@7>vnѿg9J'Eܸh$aGpyyyyyy1g6d+Ax oYI{Z4(|!&|>x8hhjYF ҈~ς4-H#:cAY ҈>jAQv1bڕn.cS]҂v #5לx]65 EЮ6G,H߷E,HѮ= {,z8[P9ޟlt iK\)K%q ^(=mAH޲XPE'e/[ O,h-hKI@- ] P',hJS ϫTن,ٸn1Oܠ, 3 xdnAO'-㉓_}xK %wE)/-Hy,B$CI@cP7]T:0=*nw(Lfӆ} u^udwz܏Jk> } ˮ}F5B]K%>*8 Q6^2T(-5"=2t^h$=y}*N, *NPezI[t=J %t=Pjqa䄥{[FOX Q?2҈QW?h:=m:^:9hu]=@nAdQQ:#xDM> OQQ:UxRҩ0 $TŸFDC+q7ab Gzp/$_0$ƸFD7ȓf02q3Tibc #W7Hf n-tțiDt/OC ֩jӄ9:f#nY M=pn"`}-%:Ҏ XFB-iˀu ѡ5Wq櫀Um5*6Z _ܕ9mUOZD?o..M*&-] m*|pCz C[=:`dҚV?ͅP6]:H\{HjE4@UĸP `d \놅sVDb瑖D- ǀŽ `\.EZuVQb~ TGX=Qb?lđ4K@u5Kpq_֫պ`FƐ `} ~>Ћ[yX]0)6fʤbZ_,2y)d'^8dH˦BR_dt]0z#;&y#DYdj/ؿqKv aFZ+W ;_/X#W'5&Jn;_|} `'Z X:z*|ooKoí (-}GFn-p̅,,}PH}3o^_Sh|{ăP׆K?igo>g0}op[Hz٤? ]{_nX:]>Y_[p48O^nluD0Fp#v?>?zvTRIH%2T*PI>%SMI4ƔR*K)*RRH) vQ*F)NQCI %'tP2AIWz^W/qV/gV\~URTH>T/?S/5S/+fRo!Qo&Qo Po*UdSC})SlIbE.Gg(~P\{Y=9UϤzԣvvPKFtV1v'pip/_vendor/chardet/langturkishmodel.pyU߿br)`ˎX2ҎV;ȱ q^;;ّ/zޮS3ZlOߪ|[<:ӭ득ލ3;NmdkvkK7ӝуkfɵѴ5?Z;K_<]vl%gZOƳK_Lhnս]:zntcoK>s=O^DEGp[D,O9Gr):}9BqѮ觋.s;[9"Nh.;Q+NE>CqfC1fu[9gδ9;HOGwaG[5qp3 ;o-E-g[vF{[8gP ~->3磊@1/9iwh1/;-Bq/:Wq%pqOͿꄋ1ꄋr`173).[λ3yɱλ3ű|d}ysq_s>Яk8YR'!øYC CkXC YP Ckۓ01176N^߇7֬'hհú:0!$[Гa'u$(rOWs.MǰFjXŪp7 a %)1\ݔXX KV"W`S[KVh Ka\jIa'_5,pr5IUʭa #YI6(uaݹ:7G P5߼laIxIx[Ò:].x0$:hZVaع7ؓV`XDaÔa C5\`X 0߼klB QF=t ;W\H rf)֌ +Har5ܻ!)W0$4p B_'q?Еn4/v.f{7Ű %^9% tc Z0;ewPx:0-n[t3@0v#p4ba͞{Dհ`XưVb? a+0OBUǰV an` n:B aX 01 Äu5В$(x? [ ;]~hnwûN{a0 'հFց0"aD R'tUˏSߓa{:0Zy 7Y4,0\ Sxe+(hp&<g[j.Vv%Sxƭ+µP2 >7 ;s9]Z8 {WIH:7)|_iD@al K:q3_?`ؿXMR&10^okX 7Z&c 06T a1,)XM}J, 4LaOd}N\&ᭆ' °RP31 aD{p-W]:gj65p:aaU] K0 $0Uu c5 aX! 2 1wVjXuUuIP üBLBH F1oJVy#Nn'հŰZ bjJauI>pGR( fJO{Áռw^lhjP ǻ 0QKtag`˷6ø@ %:\5l İN%KtbXB!6 Ʀ^ bXDKtoj8Զ>SaR^jO2SaZ.o9opa8ȕP rXyrX 0t%BVx_WB-kq%(rs+ܱgK P[J0R¿d s/I6zXa^f%0ezs8;wÍsDaٖ"͆ywwPBrxs05䰿/f%0k{J`C{X0UM]H_BT:\ y57} Z(_Bؗ/!-ؗl&)Zao_BCk0Ek=ܬ/QI6a.0eD M38ԾrX5azc_›êAc=,Du 0(Y#dK8ܬ=,%6ʛ{sXArX`K%=_.2=pu[b3%pIZ-\%^z03za7i]9FӃp&5kͦxFk6zy}t:}hvgӑ:{d;xp~ ;ldZ[i&{izy. ]^=tg._;}~/=k9ϿT>dz1Y <,<^ pg1p7ý<_ ݪ$\ z)I^Ȓ$,z1M׋jr=XӘ 4f1[Mn4fqN5YW|KטYʗF,Ko/d)_d)_ͷEA7ٟt|n?KA7SISCtCI=LA7OP(f݌NSQ }nFt1ɢ+fDq͈᎛S͈ގ]7#n;nFtv܌/܌~nFg)ft3 *mFK?ڵjPMi z@е zi2G^J K3$4C@Ϫ#@/ͣ=X|yY4vwQzl#sr#3#s#plU#p:<$:<$lC2Kt󑹛bHx>2S Gh)#[7y6sUUa3X y>t=:mym֤'a3}}6cRGa/}}~ɼx6W*<6mM [͒^yβc6KBG2an&um˔M$P4%f_44?p?)#$s8K.7Eh! O8eH_3P2?尝IRGs$ɀ8l 4a$}/m>ۙTC/VZ~G$Yi1cYb'2)7,oXgN4s Q_rN"_qGok9Tt_ᰝCMy VkSHmVt>͐Q;(0ICi3.GGm,CJ_8lt_9H5 g:U Jc1HcR~i~}h}Ͽ4?ccXDf ّir}J8mBQ+Gj#Los2rGmn4tq4;JѰGŹ9tgcEec ;HQGU4?}DC4eGO8},U:$U?htmvM>HS~ɯl!'<:ۺ<޻;zld ˣمh!ef{ۣҿ?mލ;遮ܙXG=lO;F9vq?Scq:?>ޟgFSpV=:ch=<>ofv9pwhvc{O=3~.>򕫟׮^qs{Kw8̿:7oߝ?k^w>|-(?PKyVS#pip/_vendor/chardet/latin1prober.pyXmoH_1*_HK($$͝tVQTYY`K"ߌy س3okvR*@ۇ^{7-i%Xx` £ 〿17weLbFɧhhPc .FM(9NėOIӦ7M5y,xz L "aȂ6}@+< Mah+M?xS(Ef6Ѭ$Qlu9%W|)۸ÅO"V=on7| YF}K½%%ȰDԁPG )*5|afrʅjV5<~>N||u⫟W'| k F; ᮈ*;<wM|{wM|]wC|7)߇G|7ٸ&5I|B|~Zqw/b)EG|1_Nq_bt]D"E|>M|_cb^ 3'K#>#2 !=t_Bu'K#Ngef@L3?䳶44 3 fO:>iu2:!B#UH7~Z]A~ˎ^j/D~D.*ǿLq["5ѣz-j5녿%TvԷϣE?-*w .H|V,oq#x8 :N9d4'\TX.@R2.T$U[/9XEpM> çBۙ`۽i>~g(3U /Qs֙g vP2|ݥ f+wvħ<S&ZU TaFOA;49,јg34 j͝Rn>i=F?{7)bRx.gNv}dƱ/&9ێ>+Kվ5r;;WmF7|)jnv#Ho$ϰ<LN`9f"Z񤄭Zw#(ŪD$j-[d~ȎP>7~N[֏ '^G_L%A[B&AvlTH_B72XJ.$W몶ׇgt\APKyVS6uMU &pip/_vendor/chardet/mbcharsetprober.pyVoF_1:P9 T%TSd{ 3hΕր ؤVݙ7WWc {OwIo`{U"w4b)SOL"'CıOs)^Y' VNPȆ40mC)=r6IEd*a?fq^RJ&xTy7b!Cbm2T5ҼE8&&pFE ~'DOFS/X'B֬,f-~X{ Zh4MTαXh_ofDE;V4"% 2MiU(=Je,Wzi Ki.by$ ƞ%B߁MdO YMnml8Vm؄T= $5j ! P1| ~GO҂)M8J&`K_dRwL_7E}pz "q<#d]rCyzqD˥;VIRlz5n{}nhoӃZz>V% yɺփJƳkI헂ƾ4w9)9Os)y^\TiWB}_hE4\}z_&ͱ2C^Rk}6so. a)1zn8 ?+.*^'()3qNjDUB^_~WlI,:5j7I4U8^)%|;93@]Т:`2{C?sފI_B -͇x+lv` YT=y6s8NR :/?ӃHǺ|J//I~>f Ke<*]<|;[XmY ?w9'ۛ2?1ٷ[XlR-(,yxڿPKyVSr&pip/_vendor/chardet/mbcsgroupprober.pyU]8}W\i^@VjwH2%hB&qcG31*u#!%{|9P0Oi2x@,Ah43b-蔓 )-x"lQO6ڪJ8W¥ι!A ;#J$ ,>Mj'3SZ;류\y6Av7-IIZ慧ImoDyPȵ[zGБnoJ>)F,B&IAg)VK̽b{*uA Sdx*)爩6Td*~VM4ш+nSXHP< WݜFC#4ԠyrO\ {:M@u|/l$<4&MI{V ˳B6,=f9O؋tt,A6Ę0J4A|CE?Ox>1 g(Eq" Q8-8Qb/-$@P' i?^eJasqRb6y>$ӓۙN2 w -;_>mZ,! HT;99c٨Gߘbj7εY.;9!븮UkfCZz9{~x<`3u!v99/1n܊MqCYnLȂܽ\ ž!|*kr{QL?)Z_qi JgCӸ$hY9 Yٖ>uB=4D,o/??uf 4PQaچ_.ـPR{<\'cy 9Bv'›UKc[#<!5Fz$ފDl (p6Y'`U)s8h{2>@7b!'?/~0|}'srn\2Z-ix'0O''hĎχ] 'G!^uFM,je [Zr#qCYpzC(;e7=lsz[lu]߻ U~dΌy~Tc}z_B.[NokmN `cgYءFb 5] EgK"Jeν9e8k9:Q|x]#EnjZRO j=О;m L!r7׳j~C]5. 5)M5)MoԲURۂVۂV;Q;Qߥ]J~|T{S{KG[ߤpfJ|"5"5|[ͷV?U?Us5 >Wg?z lV rCJ?kF~*cmoTuM*t2]7--%IU!Hm^ gW 80/[^`]+ "kwTcjq3?ɞ3}BQln1ٍ\9SYbwuB.z^obyT#)y#5N_qjۂ U.%FYjؕ&{c%,K 8uT:$ "H-J ̇4J|$&$ K)zB0YʋB)=zA H)#y$PS|reMCnrӒt+7A~ŗ?Ő)q_/O-iSȢנEGIzũzh>SZ2ySWN\w>)ȹ韷/$g=Rj_ҩ:^eDP~m4Bm60gh^Ya&R"Q )/ l^d")J")P88P88P88PJ0f$8VBqQMX=+ P88P8b%$b%z-JHJ(Jh;+ ȟf\6IH"!CHn\;=5e4d|ѓ^BԒzqXmZffյ6Kq-c:%AzuTUi V0XR!OS(;G±C q(;G±C q(đCpH9C mRr[PbtOB`(>P|,0$±o(<4 X`xPđf7y\_MClZO0SUO_n.@;Etn7Cg{V:-rhU@+_tS7ΫK{f`12@R֢ie6FR۲7XL3V*epj~?QOP󱗡peHy ^đpeHy ^đpeHy ^&L2"<ݟbA&gSY53J.g&} Sec_?l{xEF؈ v`7\5ĢXt88X[w r&/5xádE.9j9N~'+D8DFy%J_d»[GvدTRA~zz^W68^WeR|$±8^ KH{)k/#p$W8VAG*HXI c$qU_icKc^07-nTw˹[K?EJ[sޔ Bdc#ܨLB˰)GшL717W֌aYAboV*Y7 ~qCvf0sG<[Yo@Ԯ!{R̦qUt3lzl nQmult(%'1P8 GE RL,˧S;-Hř0?Ԓ̧|2?#A_x_'&z +‘02߼05jF[\ 1x<.@Hʪao"~z nd0m삚ۼ굒TQL=|j^uv^M)^5y>ZjZk~ӥ7%ֵJO?Z]ykuLuћZw[kjq ۵k枷ijI!{VZStLzt]y MLmszUm]Y65{u󫚫_oW7M(c+c4={c滿+PKyVSrs&pip/_vendor/chardet/sbcharsetprober.pyXmoHί()ZAXɞ&sY!&AG ewrќeZiLiGt4]U]OUC7]o@^m;wh*g4Y2*+#}F<¥i_ q-]2M>RRI\sݲW&)s%A?`:5js5Aj-UQŞ&X|UFzjsh6[QJnQ[ڐF,d%hg쫅W8!zp =rP|E?4Ԥ%=nR1x?WHs/J >Scʹ~_i##܀yOir!W94S0s`J}cSx1 =ܐ,\\5SE+!>vWbm~Ѕ^K%a+\mG׆JoH \*J]'b~@(dH!{or?N=xۣQ{0y Zp"ւC0Rn7PQ^7y2w{=Sw86=G^goq:zm e,sqf.CJ“(01 i2c*\dz ,b{sWs ӛg!oUYul ^B0{!+Tu6m6/ZϭOt>dnOJeP4:Bg ,pWZJg J:83,V>!;HɅT:b,'uG |)_TƸfY*YZj9Q^>Lj< 8hHpVO9EUYυ!`91qCsG0'btgLW#pÐ 1J27Ǿ{_>'7=Nw&#{|?߂ռLoZ}_ļޤIg:7/1k#k^Vb:q8B&uCSeuMJ9I\dR'k︥NF칵}|LJ +I(GS@؀?Nk !h} ?3Od2; `э7$EhsHZ:NJds!xQZj$?ޛ,dkaE#*|H}0ƙ{Wё+=򸛞M}ZV`3ga!rlsfX0'. SB {co&n2ԛ(B]L1Her0(QLǔrmU2R@!] { ]+/@TO?p8pސ!a`POf-aZI9}'oB2"f{_Vid{[{o9]-1Sֿb9P .vFºS2\?6]bs$N2,*3RuxhqhfyrոyMrيӗ/[7]{(%X菧?t|J_H_aX6& 5Wo5(TDSH^:9芒)$Y!hKg8L׹-Ѻ'vg]fP!l.>+TM3NkeiǕ>xd8pB.>EZ0OaTG pL"Pߓ[gywh$NoRDZjG3f.` T=p)"{‡j=STc08Ԋcv;HS%mPzjLMZV;E fYi|?F‹Z=㼠ڪkB;=E۬f11|rfUPKyVSUZ&pip/_vendor/chardet/sbcsgroupprober.pyW]o8}ﯸRJ-u3>MKT (UjĀ#1~PSxZKs|}5gKt}^녎^^\`ʩD$,8 McFD,8 Lin(ↇF* !ZX#0b|c9hw!slfi"BfL4>\ԗ*gG4ZeXLM UZWt{sSA5<+㚫HZ21JRWW?CE AP<vULt)_R orGbmZzby%6b{?i8p2.nNILYB>[hM,4)|`/d#oxbJ5\ A++{+߯J{t<4`x>ݵ6` ۼJeyB%&iTh*eЋlBA"t#Q nA'ݿE5+.2d5mE~Kj{ׁóǞY$L\[f KSmJ~R@1߰4,vTkCnJufWӌiY2Ja_Fn|h䫯4J ]*4d$Mwf-ED||*-c|Ca71 BSxaoR!~#CYS,BбU6zxNCQ)3LEyfkZ^JBOԽ!qns?3aV{-ClTTQn}dRiH\$kh?0F`k&KdRgH;Yg>JKfq١p":} qqn2M;3wН;3gӉo9%bVKFBɐG:j0"WdFR)!$!M)t(Eդp-p~ǗTMXQWeUhr%rQ-Pk־l\?g/( ũ49b]XiQ &'='sgg: p> ^6il4m 9 7e?yI?RX\J/r/L).ϘM' ;N8[<bso>[_7KĽEoKvKqtq1QϽJntp@ZrIr/qt`[l2;L!D "^ +)&x]8{<9}R %ٛYL&Fu5 O\o?X끕E%uuFw %wފaHNA^vNCZns]BR/'$|mǾwO=OJ:*^e1X U-J2SkxdzzHP*I]oCRTb|nR`|x}ի_I1!7__146'R,yDgZ0ݹ>~ {|[šKᨊɞ(n1#"yt8 U˵4PKyVSNjƃw 0(pip/_vendor/chardet/universaldetector.pyZms_1q69T],i%;̾3KR;=3tv~^YG ]b.ܜނ( %]plߜY~w~{0`vʮOo -DŤLS rC[Ro+@w=2Z?Ɇ~)-3a=ܰ.d!*^5Ad{w.L0{G ~Pcg V٨7Mnxa#ŋ4r9TAgh)\$xkK. W"8PcPl$5DŽ[``:88#8MpR 愈Gy.֫PQ] \Ș SF Ԗrp7elG&\IO``5V V@19F<|O#j$f^ * !Bz79\cąIh+,mY$wđ_6-ăAT'?<Л bx=.,K;&fPt)ioXుgqg<^&U^xicPs7\B DLLK$\+yAhɖ$<{gPQƚq>JG5vTv\_V .#7 ۣ}>J[\ W'>}Ө|ۃύףc8?⻃]^׊~͛W?ԛV( ֫VVPEd CcëCs} ߗ1)1C>q79S>chTܝPwxLaֻ"Y^5H}4D?})h6rO,v#c<*GLL Ҍ9U`Ij'a]+YOr<ܜPj%k5Z0WqGS.ҨʘE6LYḯ̬b<)kZ>82ƄT `Mչ_-3">ۺ'< u=fGOgOx.G^IV-pUX<;S$f IZᩯ[[:uJUI:lN> u-'P(J]gR|Z@W;ҔЪ{ݖpZ-_ 1 ~he8-]m"— YOdj$~ mHK4?~c:_Go,{BV-pUoe'.\>)֣:%)WŮt7*Lz&;cڸztV>Un) WXމ±v|,ZiٌS!`hD$e< ;/8kiR8q3-pg)!J&>}g^$`Nl<,w|ͫgjw$.̦8BХ> :=u4p-!|qkcF푐\-XjeS󒇛W"62zבa5Й<$%oA#W&ڦw{S2 HM 5=t'/̚+ 6)l)b *15!E߼gߜe БjF6^#'}hƿȫwL8|7yh3X!)noWJjV=H_%=~O:|~V)t9Ɋ|!q"5 ڴrֹnf=ї#3{!}4Xi7m2L`Fta*5dXlƷ? gȞyuid"9ױCZ4/{P~ a ˮܑT~#B3q2|cM*lJU E &W.r^^|#ʣէC)bRu` d}(8^;U6v۲=me۰7g bIo +K> |cwIѧ vH+”KQ;#ܜ4GZ)Ex%T#E1B*zpgtZ{e>p kuwwRt1M5:рעV}~EqPKyVSlpip/_vendor/chardet/version.pyM=k@D5E*C H@Ҋ-D>8rf^Zk>4) iXQ%ȴ"s3.$sF0x#W3-Ջpm[V7 g8cNԪO'R<+H0Gn֦n]{;wtGk?^^wnw0PKyVS2#pip/_vendor/chardet/cli/__init__.pyPKyVSpW %pip/_vendor/chardet/cli/chardetect.pyVMo8W \vm ЋvQ C -Q6H* )rb̛G.n+-8~/,h%@hPF@WiVHpt*u%Fy5"ˢy 喛`VcҋF C_n`'Uwvuq?JjYޱ//ػ(XcSgWKnYbLRuPX-ȏEݻވV7E^"TTVKDJDH' oqtX1~۽vdNJ66nWั\gmY[ s\r)zG |'> QUL[_*䂮F*ax+O F_#fX+kr@h7%FZGPO[dpP E3Hָ}'%)3BN9,g,>4qfa zyK2ՈI(XtW~8ùwtY-Dl -սXhv.Խ6qn=V}]TlgzV4 6-^ͶZ^%K(~s݁Tj=W >z(7'6T4+e3Ly|;]1Hg^1^Un&>_][tybO si) <+cexK_qO<#8ffcWJn}0u_ܾBj5|1V;KӰ3E38.&ͮIifsyܹ}K$+E]pO$A4{W]n'Qx8|Kspuڃ7 ^ h^&\5/~Ž++qI.=Z7Xʇ$ cUU2YW<(H"z)LPKyVS(pip/_vendor/chardet/metadata/__init__.pyPKEtVzgCK)pip/_vendor/chardet/metadata/languages.py\S? |w8q7cqTji4W \J6`xaxxCK 4O3?%͌fAHurQNt:䄾VFUz7; |ICR$ UYOę2J2!FT%&-&( @RBL(Ѷ.ãmC jKRR:%1$aDȡewPRHbNjihNqCgiy.q+iIM[Ha2x%{Tvs,搎.%(; 8ADމBOV$:R9q1) aӽoVfk2Xc}훛754$xV=.Íb2Ո dԫʂ/[Vb6ZnxJMËoGLQ*NN8 r;<9YQ2A2]6~Ѽz(\lmr2k/ Ű @e rXᦚKH!P֮vU#J&BeT(ˤ+c/EA&#Ev4P "fFY8LBB}EHaޚ|&ƑRyUO\~ie~,#w?vK*1 *L/~H XgJJQ/r zpW dXp77m)g9 D )<_KUU5O .eVH'Q/ b2{,ٲW4r, w aEy)%9ko^$5)ObNSKʃb-6 %ga快)C{Ky@cdZFe|,Iۖ} #uEH͛h@t5߰z\7h"?ܹlHappppppppPxVx^xQxYxUx]xS »‡¯#HhXxq8V/(,Ra~g1iq Onc-룀>pÒl8,|jkS0£ !|k6x~(\hbǸCi"3_3MFk_E|NZwjF-~};A|S_n ck |m153k5ߙG#ËGG-_][_2O̧'Op:I9e7/K9cΚseyռf^7͛-yǼk3y }Wov( ~:X$:c _7]| g;8'!8?ca!( Oz{qiWX.P!(؞> O#p7d8eWSR*럢ynҴ5ܰYM~Ƕܦ-'& ~ O hfA懰.}_M6,7yڲt GO%'ѐUuk[ָ]p®888c[MIn)]4-ܬHಧLZn|e'σVlWE v-;!Ԗ]lnڋbo2c>B7aL]uv&+uKcQ1 >įU:+x:Q7bznI CZzя>> :ﳁ$mӏI\lK &h9fmbU77eJmW*_6W#W3jqGD&ююmhcD' $/D;E &%$mhvhvhD!,vhWvh׈vhDADEDCDGD{@D{DD{BD[ 3=' $+&%F,~%#$;L#${dq%1'${d!S$;AI ɞ%I=GS${d/ED$;C$;0e>CI}hx*xB%unbUtcU6Ep%7R3_ST~`0p'1v}"W=YAoi=R(VG/uV(v~mv1&^0z$okñ*qB',4Dam6nPKyVSR٭ pip/_vendor/colorama/ansi.pyV8+T5C-$Iq̐0&Kc7X#{%ߖT{n"~$|8U %S@R!L'~0[dRZm[I] dU p(F *#`\1H B|8MeZܐDxwϾbF\յ AF4P, ^m[`jVY1M-B]lU"x P;H% [JPbبtZ{gYS"-l zMii?اܔqUG}QJ%oeO@J Cƙ Öt]uETD\ K)R=1%DC|}gX<'KEȩbڃN7R#8QșN4ы)dTbv>Swꨵ(E[) %t;@m@麌^p{ŞΙ#Z]?3〔j)xrbw ƵD5"fug/t:2Cp&~xVS/&)]:2Y%Ov=hkDzLS<τcJ@'ٺGؘnez[WEn^ (ze9R ^i W{U1^W1F4 ^5yk ^&/xM^~\ i4u :Mp&KNKuHXhbe"=ᴌ3Swı,1AԶ1cȶejx}P˗CPKyVSq )#pip/_vendor/colorama/ansitowin32.pyZms۸_?J%ijRYNvMmS3E0z]ui=_v..)O6i\IThLh*#!G#rr}Jӈf( X,؀tvy=#0bpT+EdDct?OZn"xh~NfF9%KV6ovST])BЊ<KЗLȝ9&<F\; I3 B)۔& K=3 d_z=/D(Q,X*~30^#Jd qJhbIlȂFRc& *yI9 %=wHΜElI% R57c$q3=g aJܜH`uKLj xRRB`${ #b*W$k 'D؆Pyu#߷Rc+_0fC@Dž ;K&qa4q1e2KcbFڅc79.yzƫRD‚Fv#Οgof=X# SPF :TEh<nɟHc|@7c)D_FwxfEftSxvS#}K%6agi2Gb87ba=Cqq& 63Qs4H~o*f`X+:V-9(Z 5Ĝ? lP0!sh Ui V>Ѕ vR 2$ɤ4, 5Hn vTĉ%"d@uځFSw5X '^?:w{wt}8|Q26sgLz9΃w{cS%!w FHuk=pKp\ X &*KtSi^5DrV^j;X2ի[OV< UQ&% ^pffkĐYe#vG rE0t,0BaWFΗa}ʣVm1gǤ˞c-浐hO,G>[`*Vt*$3#A0DU%,Fs@<6Ztɍ AH8`M({@RU˛ϙ(֢oDUZ8QÅt^9WpF1ܪYY7yarp2r}Ej⑾ŝ~С{ruQ!NO>M.:bb2 ,ԁ=ѯfKlvٮ^w]\|m﻽2rˬKf`0_'!]ڷg7@vO,z&J WfXU4HN,x%JN,%%ΞN8-%f+A2$V'3RY<|7=#ޭI闹MJM.nR5ntrM5[Ii׹M MnRUnG䕹IwiqSSڼ|TO-)DkM %V#75'ߖunjH-!1~6;mk5Mleϫw=Cskϊlұr"i᭍yS0@WiwM޺7 ׾-~5 uyt9σP|˙3=ߣF 8ގUm=KړT,pCԪA j8F{ǥ *i_T7@seG pBR'嘤)bZG$OG@=H jjj`A(2%*qB* #Q#0o`Qy]n#]+fW-{a |u.T &xLO_EhUk~."39@h)\zJTm\ӸSXz7BYМu_Ďɳ?۷P mXJE<+-L1!>v1@QovmuI!Gd&`U|bl.CZi1$eО sҩ&.R s< S܇9Es 70^ڛ0Hܩ)>ޠPvg|yh @]1WAlh;F?5RX<ë[\spT᪓E; oP< ֋ j6B к2$)M.x-#ӆuP`s^P6Ê%by` ?l5ZiyL/PKyVSoEr{"pip/_vendor/colorama/initialise.pyTM0Wæ R$eWpTͤ58v5v(ΗҍT՞ߛyԱ&?$3&##)+* 4"|]Ppiˣ"_nNI7o;uJHܨhA}s(&W ބD):;1D0>21ʱBfÄNjp Rf3պBy;NCmAW/HipKnbV]A2na۵=P@Avv`2:'SY~Gve5'ܱ*=qspYA0{LA [FI unٌM[gVbwm鞗%<9LJ~q]N#de<5zACܙ#?<:t{అ h '!ݼ:qÑaVCuzyA4A['43z;ti,d{$v"VsvNt׿OnQpt.1T j"K[Օ&rΤy^&ޠ ]ڈtM&I{;e~5'N>J7m7BSlCPKyVS Ppip/_vendor/colorama/win32.pyW[oF~%5t]yxpf8_3 \I[iQd3mɈnv,~|H`GO.lrܒQeHr h[|=hg%^gk%B58$*0dh|?sH>&+VaZsy^&xpЩB p>nIS! Aׇԅl2@B<1UpDZU XRH.m)`{Ie"ʔSiEae#^gqZ.7Y!mw5eP.U]t3 f>!^5oD]"5|]\zY_:p(v'I!7&#;/]`~UJ"{}N1Z}iw^7·n Y.: ]i4 gDybٰ̒g\Og]АJ_x(MBY9\yΨ#3IHpkj|3ԆoZA]457zFڍB]eu3}IwwxHB[XW]eͩн4ʸZLOM5fa|S*$$"9fА0/}BJR*8}K8,+rJ0KcNjۺ5׹y};a_jrd9=7X6«-9 LԹMeƋ3krxkY/f% ۈY:v|7H)%h?ǰ7!'g}(:(\k $WS^|,8B xLeFebl29HNj{!ЫFHD1>zO-8oZQfYeC+4[[ iT/-EDtC4ɔQK&Av[Gٟ[jK}VnCK2d >=$*hm[)n3/$7Qa=bWhhZc`ӝ0 "f@Z,+؋ih29'{?陏WdX8SZԲ}. \RhjoC{3ȷ^=75.eZX83+s7^Y2 J&1SQN&w Ge`+bWc}AֆYFH\E+SK vilSS֗Da瞴Qo qډcqLRUƇsE]^C˒is٨c"z R6UZwbR[`nwEZ&a);h64=$$IRD2ΒMm-Ys`̩|[]j@FK5\[Mܮªo>I ]\K1fD͏-sg`U*[ ҤCptj7b'?[fa-ɇ8Rju_d"$>Z[$0u2"<vG-t([);*Tm<%Z5czlnNun?Ggs&TӜs `Ns'1%O%ly˟U9ѦjK~m쇴Ѿ8<юcy-9ɮa 9ޜwGw igs=6ItWWWUWW7IVU3;r]e7F#3mVDkq-Ch2ӢNє8/g}TE*7V"ʮWM n4]ET<=>yu~68>{ugoGh7ʅNgfUө˲ute>UT뽽Z S?^.N8_N*YDG(n^LīHa Q sfŬ||%(y-3#Зi^5 u)4MlUdȴ̝$Y^`è3L̹O׫,o mlr\uTߗ6^G-uPd,o[:?OeVЇzg|L-`yv@JByU64)}POQ3/EʤO߀^\X:HIԣ# #MTde4!v0-Q080UU=9L'.ahʼI^e }楜c/-WQVkXy*{onIJF1"iV-|31%pR]?[2v~U"?⛗r/8%k 4rr֤US^,Նvuv,(tQY@ Xd#\(4AU ..t]VR3? |˺1d|_ Kj9A(|-hdO*f±Yxw0!ه V!^lVl:&r.@Uv%3EB"+48jR);qr؁|+h{4ML t"z7*'`4SQ W7/ŧOkD #"c,,GȇG,Ʈ44&Ѡ2lDR&FEO4P0 /Og͢DR(j} OGW-@M$hZl~i׵u g_|HDweNJ "JXiua^U_ḲB !3" x&KetgUx0*D9r򀯫k. q/ 볷/Wg"jnOZ!2 Au6P~s & bW_UP"\вb>((84ɣwfM1 ̓/`")}$f}Y.}HÛP?XD<a$DD;` 7(Rai*Y}hUp CG_99~v~Q@c#A Cojۖ۴fk\x f"OkyߦMx6g+FHj`pA=sJ=Nnt?fe5 ~kd",Z>F5 L9HKu!㓀(W@eq lUZwh^&\p? (/`Z7&aIR "Hor|bzճ~xsoOߜ<"(H)ALh:( 4O3+h YvBɏ怞Ђ XyCKnyf3U\Ej_,5x' r9D $E99 +7Ȣ4;gW~A<ED:X+h \mc4?zGq]p_m!j~owہ^|+qi] sV,ua=h<֓FD[`ڂ"C6=U ~,ܠg/(}JvfU$Q'xv)¾ !w [bw7( / ~`ŪeR'Ud/LƔ4f: %>njwi4\- u [vscTHC͌ʭΧ21 ^ceR_=|Soy\nI 8L~H$^?Z{B} Ӭj?"0t*hQ TO]_󌀽4Bg~[w[W tG;]wYUd'`D`ATU!DP63*O8F3d$RcDY 0낙P?K#e<GԲ߁_^(3q E(,#o HD]V75^Cmq* LV [NAa$,|4 Xeyj) D̐lI bdeUJBa*aaޥR 1aAkpBQWٲl' m90Y$gsB/u]&>.qـݜ' !ئ^vx: %!t9P h0hYmv.XKd)~O؝<̊ú㶇5c:iR.>D<)~<`0ZXϚ 64}+ƌ:-fq`"YP_q2ٲ*^e an$K `Cc1 nzR%J`͋y$Z':f]n q$$2P8"- }Bsd𮬧]m/>!qZ@$Q!SJk.W@2t)v"nS-iHM;ia WYN 9 s@?ahB$ sHk uci{R‡LE~T]0^_(Iٲkk% ,"EQ#[(co:Z$0FKeϖ/Z<pIgR=Z[+P[~jyTGMSKM]UP'؝k/{F؍ 2{<{fūvGC3HII;3ĀX*|y UV=,Wrc ʘ `B[ 1 n`>Lyiw:A (40:ͶȆ`!ɖjE7ũ!8?/0~ԖʚtѬ #tҷ!)ܶ+lYN. a4Ʋ6Nbm)h*zЭ]\)!|+<հabhw?0G<6c]iP#`&ŠQ Ǻ͟&6zl5\dnm_Ae~@&G'XI'xpob4P2&M 5`$X}`@y49/!bs?t5/(CJ UpZ{#-w8vd"/͵X(< 6 ؋|G MH(Ó&!/d-4H[@5jkcct↻ޮ {ʜw L+ 9'm9`OՄjd'PtyfH~ڠʸ.ɫ0};VJO.$!-}-$$/!$؁;$O%/o"v^! 6'8Xؿ:fZ-MU)dJcpy4~\p^}20>@)i~AYe7G.2 Nst 0v hc\5U_>i+6E"Q<:|rcwU0b``在D H= 6<υ qЬ.>_CR?5Ё 0Aܵ`-[ %yg̛]FzJV3R5\mQmr"l媊S qb*p}{(n70mIpu06Je22L@HtѻlZP >`lr L4 ?=$yV7\A,šL0VSH4l옦=L \YK/]3m@F=7 `SOk..iC]=3~,1Fb4/,RPGǃ/?F?C'W*/v7i ^<K[HԞЃW.^YyDˏH@t`}mנe#VYw"TiBܡq^ҙݩ{H,WuNN90 Cw`8C۔n%e[0*x+O0&z7oβVyXCﴢR?>ۖST9,ی)kC[?A5v-[5E0xԂYZy;JM ų-=qDHPuGD_l3`UZwIB(UL}#oQUaRR[P087%*a-19#۬ơ]p"[ ַx #n4h Y0z&gb[F`̅U\g@wYzo+0#GWRuY<F2u@9U4âq~8 V '%u<V 09]Ўm+@#d5AIeLlZRف f=߈'uݎl<|Ddf:Ն/#w;#eēXE+ CRuxhTHSg5z_-b|dϱ3qH ewf5GKËTO,0/]c\DŽZx?rYLOQqSA/W lR'#h"*K1ULrfsa)kk -0C+ѝ I"N/ںk)S_VnBgE×lky'iQ,3.eMd?P7颼Kͭ؈ ^o,GR naM,kM*ݩDV1ƫH%BILe0u4]e*0p/_1 dG`KzjRS)/<G^1Gzu3g#Skŧڳ6pv6l`N )Ci\zL}l׿'6/&NJ}"퐎= 0N]옑߿WNT "qxcLGOxH}*H_8Պh\?|:W&rw$樄C0tV^e 묈jq"Z ۛ8T{f3RM7P(ov%_?қUUmru&G9:f6"O5ӒruS ^PB50r;M4qP\yj_%Gq [s tun5f7# oB8ަ5U#U._9v,^|19ps=lɔIIƤ8noPG:i>+Gu`{(r?%}* Ak-QQPu5b9&o !W{ !ڨb[xݡDuUvjj2;Wn3o%PG@B#].Xhϯ8:Zө<}g 1֘E5PrhlOl,#KQ&dmUsʗCb M8gQ˟hq oTl[pdt{}kě$uAJZ<)ˣZI ǗWmZŶ9Y>:ɶnCH,9/(^η`1h֯\?͘?;8eF+ {$KP·LFKY~C[19*Uwn$jw+rz|xΰBs^ɦYmL/j>5iY|I=ttTL NͳĢV0x!@3/tv+ӼVI%نҁNb %ݑHo0JIMk5hO?qa"w3pr6on0 )5Xٓ[PV. 3YaFIVKr mpd3^Tԣ cQl L#ڛ\zQjHmu/tT7)ྡ5rG!lĂ[k\Fۑ6Q\?'x0sF<uaa?6-7v?Wh8[^&$x zѲXM?E4Mgzdl Kf@c'OBg*DxQr)Tc5=ULh ׸o߿T颷~zXPa}Ԋk&"lL|P= /Ǯ%W#r~?5,n^΍ X|Uu۾QՒu}qJ.(uzym~:g*ӛ`P+.tqT[ S7ؼ۬T!'W*Fh#ti, :ҮΕ%z1Oj447)p;|;ܲ\sD9v3:/"$hVi#߱8jtj c8aCnUf"ٮGWR wU47SܿoN,=i&i^,GnRLӾ٬n>j w "5'V6@ˑIˣt⸷f>6w" ̞w$YiONNޛF"+ԄMˆmΑ;=g|eҎA[Lw 5[/95=VFeE͙VR0f,SG㶳W%۱D cD=fUeO8)5|̧-óm%eU5Nި"0z ӟWnxɜ0bD'zg;9JvyYX^WA6kw&>.˨j̾@[Ϟ+BZ _۽6>@.):Bd;E&Ȥ-C}>Kbޯ֥|nGVp=N1s5t{Of}ɈᑥUZis@ʊJŶͅߜT%7um>f `ͽ@}&k2FRC($]77XqWS Ƣ:([D/Kd>}~ŗߦW/W'_ez廽"SuPn+zE_|.c;*E tz5J Ბ Q$n:9̲VOʽ5ǒn6"),㣁:ȁ$P6_kD2&Ǽ+Q03)׿q6oe 3˰om;~"۷{-H|0v<"1e6EmT3DpCQ١XgyC@%=\Jy"4V.p7Pj3w[gFJww9*`{>d YJd'YA .N]znjWcnr@EvaQNGzPKo4.gTZ:s澈9mi۝nN*;>D^ln7anNWrx_=n JO+'~~M*Z$o+I[ЯZ7K醱i |5Mº^h\ԔO~o-\Hr)r3Q [vktgƌ+F]. #kbcj(غ/U{}moʝ+ߤzaP쒃UO0~k+q=S]w.Z_-kGfYW7ܨX!@f#!^E7}Z PKyVS@c/spip/_vendor/distlib/database.py}ksɑwhoi3XNxf@a _>hώ[="UYUYYQ6oe}umgzۖW}6z6ξ/?>\n}Vd6zQeSO{!/~6ٳ7?{?yv=z{6dj]{Omfߴb66Mg YU-@>,:Ww[݋*//Ewm}+@+E~_5%';Us|^v8\qA]i碓- F>m+Ϻ5g}_w5*Vgm۴JCz^&٫O?ξ}髳Iÿw30o#V)[I6/`ٺm֢J̀Ffr[Q,faw쮅7_;x.Z|؎frN-/7$ zh!C{<&Ϯ^f-PP愄&Z)Km4cT`g?}{bh x$W=yhKU5=xj8}]ٳ7繿7=;{IC~y}3;qJS,0yUt]6{2j.~|Lc!+OSQ*k^.la@[%.lԉj)( H \L +n6kj-60i8;B_ϸW} O+QCky ZpXﲒ2{cSp;d`bAЎ%Xd8k3c+ŊZpKjȯϵYձCs#, ]mXku?@ʢ^Џc}| wb +άYD$ Fv]ΉjA1''s^m@iL#̓A"1V⪘o3BJEFf7 )2#2o:XCs-Qn4{b^.KHC9dq la~h70n]vweU$hnu kam-qs' +Thӥx[%R+2L-'-p݉h`};2?Qa{, 7ˊ8Od6[3hryTQt@smEFtQ(춨6IϟZ14RFD'aǞ;HG,}@R0ԸD0[xږZ*bs[ $<~Y-)`v<#w"0ihu>+*TM_y|N,=^l$WS~\\EMo`g[wcD0(`Xِo_`J'ٲQߖmS= ?Aimr>I!,)zSig~DL(1@p8ܑyriRQb ($dAک^;)ruߢ`>!x|4gN ELZW4T5q|0jx IUh#Oʾ[]m8(iMYNamV6ig +{hŧ7%3ס0`EƤy8;3%]Ƥ-_o1)la Lt H]Ոi8N%竌KAw! _7_wAu kh(ϑp@RθhjnHJpd9't[jv$"` O4fW z{˯9H̯ɳ8:1ovg_LϣIt<{\[ɵL l/Q^@7 5@m5 Jmq['/ Ϣ]V04N4Nl( Hs?WwAjBgraG XxW%D׎С:T( MIXmya>1Tm0" >_#IBi: zǖA' !\o]&<qtEOuK5%)P9ݬ  ,=&Ʃ3B\$h0-c/p!.G=12 `(EۗMUx PpG:Qk}~g5 KMHl5$d %Rck{8#m'o>K( #jUBI)4H8yX{rKwmfhP*B2n8&(ȅd ˬ{Eg%+6]b. |%;"b'^K?$fαjK//FHzձc `C-q`KZ0#YG;%0o.d>q(vZ釗 Jƕk`e?R}$ĺו[ ։&XP 7T0 ERVD]_c/JP#4C[BjF?rd e`$_LD_{Ʌy-ZX S $cNubqT`V$mZ Ч2i:7=?89=(&M:Վe[# )xJk (-7(_U7&UC xƪLd)rQk`H!2Ä%SAW(TvnO1.Mgǐqc=| ;jk3ObQ5<25Q<b\DyL>Z5 VbMX$]mH1-7wOZ@S9Hdc Gj9Ï?)85D{)_^!0ੌbbUYv)4'٫_ǹ HP\ttLx{IbS+p=fKs_ 2X. 0D(nT':OrvmnO'2 oH# o_y0@D+uUP`M3d;њ7q}m/J͸]R.JXNg-y=A򢛗e$I# :&$Yp|&$/#P_GF po,V.P@W7x|AIX 2D8q~86MEr jG؏g:'&3/ύ+;D34MY':E;9zDrzSEYa+a+ Ku~=eL\8l]!zΩGt`RS)0/;4ROn8 RD{D Z*lŗJ"0'D9*~tQ HO&K;Tmw'%z*c* wֱPE|,@AJ$)v8Ekf 5\uxy߬g֕{@F7Z{>ڱf-i/sڝe$|7Y,FFQ1jye y!diO D"pa,!QY 2 b[8TF[BRs / Tw G`PBy+ f[P/za;CӉO˦B`r\ɯ81Flyq6mb;_ёm@ zSsib*Yd]]^tw.*pʽJU~KՀ.U޹(eh~p.LI!e% YmwQm*QН)%|8;Kӷ/8VZu-KF1>+F'dL7YY6&͂p(LkJ%sh.u 3xev LHJj_I,OY?>xdF4$gqEDLI{&]+/}RYGRf%A!Q=V [e?..Fj!dyIn [˹1q9CM=XޖLcK>sa\6z-wƄ$/vѝk,\G/j @]`kuS^ie,RQVuHu)`/ӌ̄ 5xm "$tmns*Vku,(-n!+p|Uq) a哦d0 Lvtjǭs,s6oECzӳɉvR}o)n(5@10 1509|)+v~u_U$B˵EkwҤ,E#Bp|SE;]ĀTQ 6lvR1/>5 8! OMbf}VXOc+ Vsb cB7 !)ؓ;hRkZ<.Qd۠c%}ﴰBLvldP4;C+ҕ|utF+d9ח#<+PY6ZԹ dFAԬbe"De9S >W)r~K/DXxxѼs^!éRMJ]ge3TY J8rpia&% X2v-l cQt&VE'd"قVg#i'9ͮqO' eD$EqDXJx!N~һH=`1v8k #ߑ0:E,r[!sV$|Z^"_(DҚy5Y'zMs:9gbJ+F4-T&+% [Z /;;$'e@ljx.tBBAM 3ʲ7esCv5HGhή6\6f/jf(!h Ф(߹# 4= UTʾ,\88:Xjq>hFNE;wR,LeV~-qfI1q:({ނ׺MXM;Q7 fDWsAoX ч-pߘU,**g9i4f<8D̳{i9+=4 v;E3@fc} uaT2W:*/ɟ~ 8Ѿ=ڵyS8O<D~7D6KcgVKeI%KVxuMJk0vg8Mu0VFёpv-GfdI6xn*G;tK6GO,mLs5 |Xֽ0Z,dXDgr*F!'rXUSvGF½U}OQstH0Ljm#<_hF7&;"`0llUސQM+0i~qq$+ ]`V2ޟc#  /]~1#V3LG#!N:M<\p28N|x,GYf+?nn@u&J/$Ͼ"\ iJaD&%8?8~2iqRѽ]WvH!.E՘Xl*_i ")R"gt)5%vp~MMu,*CICٝqv`ܭ/^6K(wݚ֦]EsdM+\Ԭ wӾ0kA &:^CMRoЉY9*ݢq~su߯>MY6SБEc"P0JA|5Q !^^ 3 `F qq?;·~rDJ/ւj FVN ,% a'dMF⛠VdV$_b8?՚'^kuK@3X}P0Uh(~/|^S ysU*kKЛa[YJqP[$KZĆwKU"O) {}bHAƥ؞Ȥ'uўE*M hjQ"fW;r%vq_rO0{h&&/ke"vKc҉-Yś}j%I)!TН|j؋L*zkcEEDc*51'/U;ۦHW٠g*UsCa<ξ}3E۩D-Y.70~m"QsCC}&oBu;)Z; y$d(\^G6~$Dwg+#kIV|89x9b|"܁ߤ,"y$C' 9dRLO=L: ''M< 9/=uFy9=3cqc-<67kzLXf}D(TexH{E/rj dy~Y0Ml;̜({ K 4ō nl_qIk8wTQ `DmR+vKďm cYPS"wYbɣGZ~䋌.Ee|C Hb^ &3FcXIywKh >I~AwwX/nmx%AZ9SQN$Q(O89=2†tID*`X\F~`^&Ka`7@EhiƺЫmQwK3* fo4 w'5#]B2k""pHfݔky/MO&e%?Kr ;r(^?M;2ep a"?e(L-8$$sT Yd}Mߵ:w*d}fv]V⼗Ac*n:p<.ל fhi96XGfX3r_Y!O@ngU\ YfЀˊe'{CbqEv.QCҫW1- H,5 =岜"@HE5rT[wv(zaG9kZz ~KE >Ok^ vjÓKzkQK 72Ղk[s[M|3Bv%r>:IF87x)و3zhb,V_gڥY/njǙ͈<6):/rlOeCɡZj+I鱎_-SdVT$VA. >1•. !ܞHo?w *%rneFAlѯwǮD h ] ݸyH=Aб=aƨ9$ym".I MŻVVRpb?+C;1*cΘiݣ !]t$b]}Ezcv|ke{Y(E bʹ9w"JA7ХG[%'6EaxI}yd|q?QqlOab{k[/|cgb. +*qckJd7Ͷni;$TVu pDK>(6ynU^ћgef1*Ok|E&NtRYUQԓ6;uݝT, ?2ICf] e Fn'G-/!R!4 45faD+$3EԬѿ =AX#HAۆ ]yZ hVWt1x%%`Uf1: !ig̿+X!AcC:j*'Hh_]yTz\-$B=kLOVKf&/LI o6G+@PKyVS2HQpip/_vendor/distlib/index.py\y6?^CZ>l/q:cفǨRI*U]>HJQ;Xd[%H8\$e YYUj>'%^̪-^d,j ] ^oB)6VR|[6E p#1TUjt_%N`Rx囫H".R쇗o_?㻷?~C,Ub<[yـ]mtUD{-w:V3kQ2&-?8Yi^)U*Svšg&rOj  8d_'宊k~?uTA͊5Y*8owb.x:L]{` r-x"e\._$dm8jBjj#0^b$"|-j_$[g254S5qAd4$Z{1 pz#_`ίMEBL搙 1u8tw`Cp/~ka6dYc=]*&nfd u۵@<ctIe'bk,( Iu"L7TCT ^n:JM9H f"E}rw FYIA&tDAQtxdN}T8Gjh+$@0K% 3$\/8PKpdҲїDxB_M24 k r=zko<3˞e^T nrIjdvb1e*NXY\wBIݨZ0Ӗca&&mKvi@G-vZ{'=}zx~4!pfLd\LUVMkىrT9~97`[.9AKӽ6]M6'//ÿҚTeX30w4Ea=e;;!FμCgY[<]o#ײxkL _$=Oޝ}Q$ 4f&&mID5]JWk-NȹOPнV-3jLL~lJʂX5eƝKm۝\#_.tiG X+ İEx$R8ŬnFtzTDLaY &Ƅ&H⃖ѴFLuD&FnjԒM::HԽOeq㰛&iZ.f䨜 kF:m[E4 N& (V AvYȘp{42 'tx5Do)d}9SƋBBB> (C: LB}(0gAHBV$x9p2&M) ^.]Yb/P@]K54,$\?qIAKk6h47lW Hb6P\ =eYupJȩ 8T5ځ># ?-Y%hQ3\fߪf`kFSHC̦xS34$DjT f"Q "CEX_M<]Z5y e-:(Y.`%gG2k8X%ᵙ>$LT}1c1Vb3%Vc譱tL靱M~~ &pXuU1sPp82WTh'ehd܎tVq8SI-W5c7F:FDlơ@+ jg5ّCΉ˽b3[M9RIl8GىWTXNx$r]vi;M}N)Ox|^K[wF9`/FcZg{MXB7;.qb t;v`yѹzqm`2\UJ|&(򀵹Ն`n^=u55cN=L<#3 V `p'_Ѻt_)bsMմ) !H[dFЛ$WR=VD0"%)^iC dmuм \0"s EЏ.V~ޣ ivn92hhVD ILMꅆmzl bkܘy3r\dO:O .ߠok۰0wirfW2L? dP=^`\8\C]3@۹ ٸI-L&r)+("6fn0l{͢hRO~y~W4j*VAV.D]MIɛj񰑳xn`]~Цcu?i ^%*32eܼ`F%i{X;XhuHAZlZn%wcCi%e aL'-ڀ5-vwIoipM;:~0Z7y ꭋ(`):7,[VLWrI:ʽ ]Cw@:U lXF !c': jZx󺱁v{I*$`cZ XA]Ӛ4~ 6̙0Ja|Pg'á>n Ÿcb6 RZ_­y,iԱ%1G ݺEjMφkL j0}ok'A]^'Lܟm-"ւC 6 g,H[c&U HA[X {HPZ/E\%¡ŧ\4EW_pt׻!_r+E w\<>14T}+DNg.M4*,ڤvU(-cU,,2Ii6 0vhA':|YtXڸɛ>.m:Imfi(1wri6N;o60d%~%[lrB=]O']eŸvM[TjcC[!s#QK'ҶknN[^rI58D$|VlP̀n(a{H뒌I RNn~y]"&"A{QƊi}0(@emtʶ nm6]Wøq .PcdPv=l#PpQ :+Ƚ˱j@HǙ(SeG0f7sCG";}:{fa00>I0Czޱd:ѣ߲ -ăIӧ ė4CMeގ4jmpɯɶp JGൺ*dGKLȟ**;&V^ƾ6@sfeSv[+%I KСGAJ_Ȩ=Hyq=L񍱡P,aîȧmZD7q:"rcm28D{u hx9&f'L2mFԳ&!¯C;%>m%sĚsLw21 \œipIj6MjTmM&mMu_8߼f>C_~ b @ݩ`Im U;ENyi?QTuYNLp-,^,vؕAE΍l#gfi/qR*3U8;HFTÊPÇn$%})B\G_{[cz}un-C-V:C˅.0N5,\d>?c$%uZva;s*W.=Oz6ܳ;=*8phfwg)F&9w0n085%3gnEhNn25_[aW5W|`:W=K΅y`ݱ w=)Y2@Ӆx5=[q1z&=_dk:")|u~6}s܂6恇l,*\xNy[:p%j@m d *DPKyVSIo "3pip/_vendor/distlib/locators.py}kwFw D/-1(t<'xl'{7  0(i}UP);=gqnnw: N?< eIm7G'G'ruDOFSoy삗?M žYQgiДAswuY/EsTYu-Ҥ|Ȫ gۦo*Y Y|^>7Mi^x^}%rGGzSVM%-re k?PWr U_Z}ڔuf4E5nr#*Kp7lӪ*I*YIP0۬2+z7 6I_VGz_uR0\CoUh[Q83-8"Y7^m?oY=O6iWdEVTի/4y , ԙz4ٛf֋ :R7cJ<~x-}Lf%3nNL=M6M_gM0TMVyJJ%ғ?OY:T0/UU6WI ֛UL*_f LĔnZaps˜Mjˬϫ (5MA`jvf߸9,co2zV2X b-}N<~Ԇ">n֫kN}^A_=߾>?Bi6neQ-h*ՊpLmz]Yd#+GͶ*]v@ 盘*M5nI .신\6KU{<VK`3 Xl+_ xX;=ȅ//kR*L 1FYm.>pznM) 8QF|UY8bGsnuИCwpM^weu$+`R2˵i*l2TYXUq16ݶ~GTxӛ06*Ymh\r"3Wa2^l|Ull.6Aj o >,\nq0plqu]BⰖn8jL[&*m}?i­f!1~<`g]/2z.88o5aVDy1/+\nh]5fUP>]Β %l2/l?ykǺv1voM• 'Vyh@vM:[7"%.N7-jj#34Гv1ȑي* "]- 1L".@e4.hIT 6dM;H.Vu &]%(p cſ/QVQ9CC ^qISˑ$ ,úVZAŸCQ7I/Agy~ yf]^\RMFWR6̭IXf`MIw: yv6KX':& T-#@Npr1-2ɋ!RImRp7L_Eb*JpPBۂ-rqJ%jƓb!(%Ed?z$M dĪqeOx>EuRa: \2W3p (K"$1IgaWeyCh.k(m"cE)ZA4Ob zE}| )t\/ W2«>`9cH!`]|Y`\xJ?P\@hY5Ӏh Z '5nr.(T mFE-޶ƱQsa)++FFA89%E) 238J(UR(ub8QXR9ͩ!ƞJ@lU3h'G^߈+$M?/72#І~Jq)vdqz S?bUY :p 7PHbQ6flivhHc!4Fܥt@wS@L>&رnhʋmf`kf’D#c:h& 2AѨX'Xdk1@'g9N=C8*P\WLArl42diT̬7mn=.ԦW;jrE?s}Noi 1@U[$ۊqF#goa Y%HAl0x Q͇p{vV b~uVDj H3 [iR6=W+T E HPzIS/@,'.gTACC*,]1!"@i'M,c",Ĵjd;JvE%N? ᘒ=^:\ۜL6 uj4dZ @Ie->z>j`fdڬJ𼬥UF8*~, ר'\OK+ 3.KM-2h$j%E%ͿȍixQiq.ZM^FM҆u ΜS6g_h1%xrB!ElS˨=R-$XGi04dFYYC"2q5@ gcɱ@ۈu.>UfTJ4f,"y |, UID&$H9Ep[/fdѸVMӤM4:PR zoƺ@|eNd- |7 Lj/Ж1 q[ߙCUjrHҹyྀ] ҙ[K~;ܭΧ8@B[<M^` 8dUZ'r0MIV [ǽH,S{CL+X޶{?C/ ORi^,ʖ4rZBFCY`!"w`8Y20=jYW8uƩ3>罬%5J<bOkbh@EjG{(y[3pDpŚn @m,<;I]wBA^W<5i boHooxBlpyz L衮yp=%P n"$roCp4ә qvE^Ͻ_ELt{y>y=E ƳBޙE:Z #P-uT臊œH`X_m+V!}< r&띘{Iv'/'{:e79.Sb`dsm)p5QPFp0[?…IEpcuWmr1 !:R b0TkQ*piz'ԣɿگ]iS+c8Uiij% FIV>z*m]k{BaF}w-[ʵi@Elfv5/%uz8JwNݒxdGÓS*uXE:U^QwT?SFT;GTg sYqITL1^)#Xё2A+ʈ#Ͻx3`7-!'\y:g he$x#9YhLvHe#{=Ob T^r4Oq0OITlsU$ܚTL`["~mI+ITqeQQhB;/#4nby%2x+t}WX0%[/^ ̃j3WHq. 0Jkz uOz^Ȭ'aUa=4XG]vʴ/ fC$V+z5A4,>G(}!?_~}! ¬4YH)v s9lBG1%4Hn`ty^M;1slnE?Y tk:aɶ&sv%(Fm >LG/ǘ&*Ȝ R!:0v&Fᵎ s@zvY@>$? Iߗy2CtYT=ΎrT[CS[IZ]9Sv}/?'@8+37 V@ z?zۯo .HMM=6TQP>yP>uKt1^#x Z1@4EdN3J" ).ɳ!6}p:Q5'h]_izfH82[^lL}Ϧ}6xviN?dv˟ҏF?l 9)w7W"mT ҩ>/`$^(3̀rJΥ*qNfΩyC!:e.Ǜʖei|R =qJEP]۰+;59,b}dAI$|,: ,RA]V !>Me&1%D7́}?QHv[}u0fb1%жijS(k҉w-\.3W+ʺܗfЀH"K3/CI@Q VwYhw>L:g∭oSkPQOasE:~Wz$ |@LF3ԺL^Gg{W7K%?CoI:4>({:]/&@ǘ˫1n]e̋u.AUau%?c'cאzZxBaշkQ9>ܕ dE_4sBbmV1{5"+jk$`TOT od+Vf[u_eO0@z4wϰ Չ):{O_:)?T 5e:+&eDŽA/>ěCGout:bݣn6`$Lz %̕=K:==V4gzM-]_uZ; zH*'׷F+]3|k:D4_taޢjF=*(2ժ+o:@B1Q^aư_nN~V!&Le uY1wa0BM@ގ6 BΠɂ:R75< 0_hN$*`L3/]i'[yB(b؞(Nc`Nx9C#nrWt+!*W;7;'U07sMѻddwf`DN6y=6}̣ z%JKZ}-ҙLPs}Uy-Zy_~2/&QԽf2i{L!#fh˒m,< :u (t%TG6t7k4@kw'^)yk1KY1nۀ1y-x:f9$ Y-Zhv?x@;c'Fi2Ћn:w$|Jf4tt!"g3;a(RO{g2Iq?/ >v2.[;5}-& iyϾ,ͦ&I$PJ'wY^a^G<΅t B&l,^lWZ.NYe4\VْNH'=æ˂@T8Umu1{`̙~4TځqqUPV(yeռ݋uWA?o]۴ NRף_=~z@pU׾&t7{:>DiVa,-xF9P u{·ow@6A]yG#ۄ*yc(P~fQ1/;ep#6E$xs/a (j`x$bs+w3Cx.l%uO.ĺ xIA>q%}`ͪJ߆؝qCi6vmc97'foߣ *ߩ(\Hxylu_ߡi ]32~]1 2kfzUJtR6w7Eɕ(h)&s̞ݘn'?uw^у'.X cNCM5S:ƍ!m/o^o[S+M{w-M!_^H=3<=h.WnH=L䯴ՖQS~+ L UaeoPo77n 'tztqrB໴@FzuNy 6h)?i*xTE߹l$0[V k=w ~4}Mrf?6:(DqÛqI!k vl[Bl\XMqS"k>ř&tG\8*j51 o&XV74`Vm0$AE 1; >7֛,Lrp:`v> 40JEHVȒt\֒ RLџ\\Y2)X >Rدx5  në+9TXw适ʹ9 $^w58VDt >o:lc IMbI8<ޒoNgqOf뭜tx$w0\#T`sƚBG yȖgb NKz-۾F;y mI2;9.g+ |3Ӷk8{GoM@՘\ۥn_Dj+LAfW;W +YQC)@~ʚHwlȭg:adY*UCAAmSyl'P kٱS9?QBIVS/TlCMv'BJ+̩td:1LMw8q@C̘ U@n`s9i2Xq0mye`CO51+\J_c]1ͬz1~SaVhyԶXWY!2N(9 6TSdTM閉u4y4*#޽tK)9z+A̵5ҝ)'@TK@P(QB@_F>19sGL芐1f5{ofZM3,O}V|l`# te]ir3 <&[4>w~z#Cj\&|H~g)[dL۔i)/)pf&5SLbxW <{XU Gdʩ =YGZcNkxq& Ao k2Ç O<]a Ӄc䊔1Vl#߶'ie-u'c(*Oª)8"qT[HV5u3`VɃz48g|oZ>|P JUVv 2W*,sl3ihHw@@[Ҵ19A h$ 0)Ѓ@vƺ,_Pz 3[)*){I@G|^v '=6U~SzQPFg^ oϋ =B.ߓ Y~ 1h{3 Y鸼%^S ᢗ}zR):"<}rpZ, K,1Ϳ7dmvN566LXF#۠كਯ"'(%@RPSSJujtD}8ObZfy![*4vPKyVSx5gl9pip/_vendor/distlib/manifest.pyis6~o+13dgi|;P$$ -k?~{HmLLû/@;l;bl6fe1 v;@,y2=޷/FKv."cbZœHDê هÓC/X??^_K7x d9_\X1,MdĔMKd,d1ʓIv^&wa kX!hFR \=d0z)-s1IBoXƳEXDs5` irnZ\,w]L#Dp$˰02dY3E 4 Ϯ0K\`^k,/>;7pCvͦ"MŊlVe'ao.'40puڽΜ!>pak}y\gO/d zWr7NS@eZe)P"3g1@Aa &StmA5h*r{H^{}=p6E:jf| ;.?= ~:<8:E& 7 l*{ xTx(ۖjO$-hؑ/S, "Xrq]' &dZAVV0A`@1N p%O`.'"Y3<$9EE0^y 8cv (|x) 9oswDƧ`>ڄN$uL zA@d0b&(p,P\opD0:%}\h S$Y9I=;ү+#*(J@ ~(n؈}hpXBeQ|@㗵NMsHrK\=|8a;M5+\//zR,q:\/%Y\j槵;rƀb hsAc=D@+iJ4VD!-jTN$1=&'h~+dЄX Aav֢|ҁDKik|r 6^gb_SڙL]YK5޼;ީQ%9ѫvD[UƱvR m &E`lepG!壌Ƈ "pr'^P4"\P^U@-qf[*t Yq4ɯ_[LrblOZ L9&HLTRNWHĸюUa*7űs4:7V_ sэFCo ϸi*yc>)ga ,tPS?,fO,ڵ=CXz$iR$%b5JW3: Rt<ñP[T.Ol 1rjTuDҰBӖ{8O;Z:\5,q\; uJ BzqMwSko0=%$FSNLC@]m@i<9S31 Gs1Qy&$~9. qVY]'T!QMr.4FwD$T͖@Ёu%`rťd@ 5|̋b9~<g+hϥ(w^,m,{9fOUqdrM\L@"WΤ A~ZS~V9aլKtm0HI<$\ŸL: ܂Ss/f "Jx p0R6 )|% dW)o:(Ck9dIh4XS +[[`V *&a<#Y zgx %B5P$%T|6 dNRTz0e<5}$Y1wwlvdu:RӰ=Q/rS6C&T :F=,N%EKZTAԦF45!?]4;Mh}/c,0}Mmhmg{uaKW A^'T_\|URhqE-|6uSo*4|-T" քXuԳɣ~u+X~Ӣ5 { D˨R)eL2/5(jţ(zO v65ا2gp $Ag喌"0yv/K" 9B N XqFy\6 >Gr;TBjH% =_m<ҝlv'AZ`BnQi"U[k1}8/7J_(dyBOAqS4C~rq~M1eX'4ΧB}9G nor;hZg@ʱ,u bMcZ \zڌW]TgZ۔u3gB=jKG':2nw*uh`x]=cҎWӕ}lMnu+/77ף} YR8&=ćɬ' oF&5-jz0 2["V, }P?&2S\pl2,d.ދ)$]SKG-:uסPP#hF~̒,L(rxzfr/B9age@*<+ԕ r//D\ 8YcD6DIhC;,TTYlaۑc&Pα=}r-U IݚIwNXw4!0b+(@S13׊FjVsp)at_s_5Tz$hTplt@C6iJnmѡr 5" &&<4*XnRV2TGl]P:`⼖`X7iLf{/I.Dt T%a PzL5R[v9DU ,shzE~9 YȤ{1Hsjw~ ݗDZD+/OYu[G2} Â'a Ó EtN(mK&o@oتBukS˸a@OEHz9m;wkc8z:NszVcu3SӪ"(Kn8^o]nH  *^o_,UvsJIvxzwvo|MҮ݇C|(`cjBT# $Qj]RRoT)6G:|5!ŏ"z'o/@r v#;rT][(dk_Bx!Y iCaL:pibUC!3RGWNy^)tVݸyV) .n+:Ky0;+wu;]P`(\u鶉U.C4DґdtDwoE}]icEzP]k ( :5hTٚb%5Qk *xFU֧J//7R7ୱ؅yٌMTة( YSXK-@&eU0`nTx8]"[ ;l[5j,KmvnЩ5ah}}tU5A|c8F#/~}&o t4]rys]PjhڞrWPKyVSy}pip/_vendor/distlib/markers.pyX{o6_MQHj@׹X 4.#6[H*C,fDxo19~zL Yr֬ƕqN֐MJ{c\pAw~Mw`BI̖ӝJAQ[يkp0ߴFF1V3acɛl{C(ɛ'|< ETix,\IHM4yqEŦFJ傜.Nɏ-'8x) ?fAB tSh7ղl+mUFZo%7ynʂ Q@UP6LQ&-Nwn0ݖ[FZXa2"X$oki6"^7R"uxR,<]TԀI(Z+Y)J&6+k*ls?dNەsǁcCp"V+ZU,Tabru|a Ŭ|b|\>{}.]_8"zUq8VLg_!A7ͅ6T,)Dt[%[Zik\| 3+ohb ZھcHyhbD;=ygLIol Ae$V(*5YҪ@LgVϷ\1~7UQT))nF!.C鞼`M` ٽ!1O_M`2:!JX}Tc CC 08cdH4T=b5D~!h/ #l7;[qgVO(G˕I>u`o)t ‹OJ)Kw' {8b=Kh }1;~q=0c*:K=(+l' _(⋐wR)\V3D>,?a{)B`ʌmUr(llCC% ;Z->ʺ^eMz`m6^=.M>e\@ uk)P $({eVL(N1k.EH\MՃp8 \@lͰ7|٣L٧KxauV{KuBx?3ޅ}K 'O]ξE khz Я?;_ȾpTPUQ¼$e;IV6 %FKhNS'h+s6X:xV6ARׇ'G_=y}t|9+jA_&pIpQVE2N.YŢ:H< x}M76pԬ P%=H@b'uXUVPwZUe2DiU,ռn~Vy6kVBn6_4ASBIGVe,Ok`\ \ 3yc_o"'K\(aUr~*o7MgjiIdH7%[by t-%vղpo:>8|:>{kᛣ \fW>h5VzÁ }$BBbB.ߔ v;X )a~⒥PCP3 | ~E @a FQI/T]ObN(|_/ ZXt'~q0}E#DHCXFx&Op#?rk=gʈ7<eweXrF qc ¹F$ =^f3V /|0@ê$=z_eV{ʛ,ef׫o݈`V-<]>~6'm?K60fi$PglrDu=O]U9}&ӏ$Wfrƪ<]hЦ Go,ɗsxg7I$9$+"1kÉfNaKW6 `ml nLX%B 㠜͖h)%Yƕ6A1˗)çs(R@&gp6T[Q/PDh18@Oy@9>Qx#Kc3 &F`ț z9zyþyk|1Y g"k \i;&\;9Ƕݪ0 Yz_?ۃͳ-o*ϓ@w'r`H<zv=29]5E-"0(CE,wZxZizuZ)*#h928@7 2K$%sWݬ;dwޥ]_`& X' =7; Jvy=gu#'7΢vB5B*e V}J.{؞_%u̝toP ğ"5DuVItElb[t6d;e!17#GY':=y{˂ ln' aYSs&O0MnK=0 dZn~u2d,p`_Iՠ XؤKb% 4q7,".ƌSv@c 9>BYN@;ɝ5YI-/_dM$'k#iH-D#9Yo.. AnjA눢O&dMbrIN\ߒr "8u2їPZ9+"*G:NϨ Ӻ궆jJw4[ '|Bkj,MlrC'X pBE3.ŅF9,R|p\uw\ll$A`M8^ aed^FlԯkbA/M Yh[\%7l|pb4/`FԄ0bPUy͊@D?-vh)˼ 7U20ޕ@_IR*6(`{ Kp ta`rHjq9Ka"L72yZ Ւ.lbe&V),{Ť70{ѠAfĬ3P~M[ 'W$P=G2 8/a+j7N0L&Wkīi'Xeu$e7OlQ?mXOtvMHpB'iHy2chslƛ]*'#ҾƧ m$qn4MEPڳV! O1Q eD ڪ]GZ9kZ'o߼$3{ X2_dDFM:@jG@;B1߿+ϱ8x9= ?=31 g81kWs9aUC]n#rppa#P $E#msgv{Sܾx^BŲPVK%b䋫XYrZЀ3Jpwq/ZTwApixn 0Z2.)Q9aVDŽ^&p\/#eH%pS1_UIl)gD~T( T܁2I"gXrM],l`̠VN "U%QlQJ*kq0ʛhVaJ`$a 8 |`\=RsJ3YðX=lgL,6ޱj jCJytCGȚ8j_ {b1l^()U3FK0X)92Y1-T MW%8˗5LƱvd`MyĐS=AsN" 4zeW&#QERxzj`s"NM3]VHl`S.XVq\4ôxL #s/LQG-NPu[ǰ%jűѾwQ3eBKYއ #ʂ VMug3@GӫQS& EBe.5de9#i`[i5v[  \ R)1Axr#E@:.\oSNHӘXb5ֆn+!= lx-1',(Pjuv-'xpq{=H8` ZZf-?_ J*r-UNR7  1H9{ \&<~`BoΜ!ܕ!'YXg(Zs ozLMmʁd5E;n`8%a<šglּ'UWKPv2E2mX<{ YD8ttZCy=m,t݋*b<˚&nn(7ͭK.l0aPv?%zAm_ģdq{RQuCI|'q``n-<[ODԇwm'3B>>i:=`lEd"?P|#rm]%I$"H?MhkǪI@RI}`fp2HDJ>V~[ƶ3 %7I')sH09/UC`)gtlQAcs;c优u `آ@]gXltCC-jo jGEF׀fg EC }BD tsD[ V+[*Z `9w3=F<"V??ʬseo?r[O-{z+-E;i A|@H*~]kh:! ̞5fdyZ%׷IuYؔǦ=B0ȋZX|J= `ׇ]tUd{#n1~_qB; 0e^  hvo`XBEX' ehc >D:oUD9oq/pZN2ٕF!4~P݉iN4J؜]1ZZd$5ҮǡD D6$̇)bN̎-`)L>:L&aJcy '볾*nlBpN&V VDp{U^yd5m|P#U*BAL 4V0Py6`ಬ<롛4OoXs $9ؖ_(xg6CFp^{Q{˜eag@q(HY~V neV^j F*Sv.<7 pӘͮ$*.u;-GS՘<c >CQ~A@ᨳ~6Cpzvq 8/nR`9Y ?;Ԝ"/޽ܛRIE"g椵3\֜ʂl1?Qp4MzרKK/I~Hߘ] `8O)~C /<m Q_zVҮbWgOf؊_[ۣ^C'r#+ɾ)IE8ZTSaR '|h/.wt %hi+?Qi[1:.,`&M褵U`?he S7:&'^ۜ1Ű6Z垞j&.36k|g ;aJMf.(ik[hN2@dX ;{.0YY)LgdEmbu@?Ja eWi8Qu)=` d$_b[CS3 e :3B~(Ag"#o']Qi1jrPLw+\mPYe&vuLS@g: eH ps~04Od`-)jkZe߳_յL>̪ ,XRomQ;hCOS5,Zqzsά8ٿ)1)Un-=b/) FqD\I{ 5ߋYx\pVVlDLܚ;c'0u\-@GBUlEYYɽ>ҙ BBiJ)a~,2&4pv[+%˓(>CyzT:Y*؀aQ[` w&?1 YC$Mc)[ 󑛒pnp_EO)D 5m-Z7Lyx A@;u|,l" EJ^@#Y<ׁox<(hb`g2Ϗ ,sIQ8Kyc0ҹT8jz|dN“Bxf7[O]k~U0tUtw%as #>^5ՉIrn `V}ԅH?d+E3~d&tu}Ա }Q$ҫgL7>,z?/O" OG,_|8Cs(V<~Nİ=8|>}'; YR@@/T -=+-e1Z b˰f$e퇔H= 2?҇~hc<ԈxB:_fyz]]MDjblw~1+n2Iapx.ntkvNa¤.\2C:8obpUkF:/1MX,ka,w>PhYȠdU,,\m+2eYa碜D99IbDFg0UIYuVF-Z)%/&FE\E~ue8֗`4h)}ynxNd@Z-J.{gG: "hzιx`Q܃Һ9+E%-UT+Jxoq7o;2]]]vWN[7pR8 (d5f;T"ީ[WiX!8а*/){) mJ?&ґCZmen_a?ƌV Dt;U׏x-Fd{Tg2rx]$U2B|#`XtHXW!6эX8j& jë@ϙLfucP1x/ Q7S:%IFx*nGP1|T r}5 !tQW0fY+q Fk~ N7-zV.5L5̩Յ~*Bod[-WxpyjE~1NX5Ƽw,KcmoGk^( 2LXp|"@nnV{D4}TVüBzLϟE^&V ˳e ^e5r5 ,Hw-]"}6z8$'n֛4o8[ }jYg`X ҩekx-OMN x?$VНc/"7{+Lڧ&m3=M`QqL €JuR96yoo@+_nzߛD;Uyf|u@zAIB^=g'NG#萝E7q-O/::&3uѺ;1iŮR8"[:w,iu Pu_bd_XӃDcǎ^%tjpf{w$u Nkg h=0wl1+6o:[tXI3y+iUJLb@:BSh׭dGPZ{!)xZP;?xEiLeOiN+S{nK ["eǩx*Iba+eh`gq) F S^hxshd6\"XAdDHob^c?,xye|8EߴJ[{7$V`s#)]HK";^ށ8"HOiA:wB8~ptEیb+=1ZNje]cV=!X擡!C?xXz_֎'o`QBnƨW"kS 1sT3KHJ4l5Fy<~$lH{zXz Y$Y/bぱw"x"_ݟh:[1=ោ)U b~j!,QɲZrVۻ`+XwVΗ:V=7 ~3h8Q`*GS^۳:Le~d:seca#֘apP#Of)u/0?K\^נSom g|gbЈM 0fRsaO+1, za'UjǭK 7L̫.n_*Z@;A}6VJo\;0ZWs>;m̈́(~3#W|X2_XI7*̐8+q֐nSm8Y.BQsͽ 5'M'?tۍmvL6rR%XˊI=2b![|ڛgpQ>מ2cM̷o GzN?oj?F{c/VdJLˁvC6s2AN c+ֳʛMYk# S:*xΌ>Mb$i< L\8lƮ0{DB%17 [ :K9 qY_%TUf6 Ն2F֋JdeEuPKyVSՀ D* pip/_vendor/distlib/resources.pyZ{۸ߟq|0m/iRHC6)B[ͬ, {gzxw5- gԘ]|{E*şh<EyvW}wy/\\wtwEnM}+^<嵄gET|y]ա.׶b/e޾yͫ~S޼|tMUYlI,K0E$E35Tb]_߲bBoPTG7RK~|6*VHUgra-J4 LKH*JQۊ:Ɋ+1c/h:׮1нgQ|OGqջ" >ca]w"gH.JBj-hGȗ xVNHl3c55$z̤='aS2uA8|?̙.d^_Ɓ(V&.y2BGEM*cMS'HQ`3mؙR%0.g 3+Al϶IK^@=Ef+>!Q%͈X)H&F-<9U٨kJǽVu`;IَW)x^ebT4SaXŖsO7Rvv•?8R`Zuq ԙN,+:@ b-gX&he)"gQZDN!A0w#"SCtAʠc]V諞)$UMMp`3O"=QH 6EHBa[Q'*;sWιkh@M>9y%|+QW֘Wu(HV \+Zȹȍ36_M+ΊML3;c|Z\ Z@{n:g7=^_A:3s`|iJ@h*X8[^EV#z@d,aEYv4WGvP=sU8+k34U y`+D;\׀JD3/I nr%"1*i%N /nq 3;0.1 <[Aa7j(&[U{ 9q!۬XLg_>]VOBI]mgzLAJrv{HU8ShCv6ҬAm}GydaYeiL!MWML|E\f} Uh_neY⡆ i^&qy\O ߂Ea9G6[v_SiSYu&D% L(>l!l{PB Fy~9IH6a` n CIj&x9 1l8pW.` $@ ,YhI5~j%-m Jbi>iԚ5ғx&n ;\TwYV܋4tBf6wXymP$: <="ۅ=^Gn+3Wܸb7.ó@`0P`B7뽀RFWZpuLNT @c.a K Fa MN eQFQ h23)ѿazB۳x bٜ].}Q40~`_!6 ZnJwla an Z`iD7WGql!H-..yUU= ґkGαat|\jj!p5gX@kv~[d(c?YZOjh 3²x͝㣆Xa6~AcOC$r2m: !p&$Yq_ڨAؽ ]>y4 _CuzaZ$7ucGzdѴWK)(wSGSӭQDCNxz5 ]A$~UUEOQqb4 ZhȥThB+6ܝ7hfAcn{_TO{SC0+FT 63'S (O31,:,ϓnw37r X./ 8S]gu;K1hҵR 4֓/$vʕ_n 8n..2WcWS(@dƮa# ߋ5DxC!ѝW%n7,SSßZu}Ah<ɳ~~&b<؊=|tF D̼ GF1kȞ?̠WW'Z@&W݇x:37 .#φmg-A|c[KusMˋ\fK"?<TG*$njXe6YT`8)̮,$5:iI1KwyX'?b4S۸΂"ڻ&p03} {o/쩭{"69@k00LQML<:э~;U9wR4 9[z#XJ7ͪL10iU7~z eÛ0?)r3}7^ 묩 &?4>%Ez zC-Q!KN'f2b{}nnBܕyϏF-tJ%-U{$?2GUbGwdjKztf!woH.2݂uk87tKs0>iI,zo50PKyVSE8Epip/_vendor/distlib/scripts.pyg*ejV҄ݤ s"J39 aDbV~-s¨w7[y^ǟ1L$r/h7%HGc>=ĒKy{s;\MߞL圔 g< F >}`էWo'׷۳70zQ ݐNQt}-*L`#RQCvkg\=Q`ˌGbaL knz-_RaөHN}62`$zoiuʓۼoyy:A Xpà;A 9ݡ@ B蹯8(q_Җ -M"%W l7g3,L}% LNʀ$xJ }6wV/3mwsBx{37̓^cD4{Kh=[yMut 9-%('ax"nGiFJf'4#R[ER1 4dy\`~@#g 4٘.ۊ ]$zGt2Np\s*_\0fH2Bw7fDp 8naK+V^!mh׍8 1S`_<`ow9H`ވ\f zElr¹|@zk0&SW7gyn:΄GLwXg;C|6\.cac}j0hJ5[w-#F*& | (mdЂc`^^18@z^@\m}ϘOsoT|݈.N7OV0 n;s8& 7ȼӍXnL M"O0{ XMоڃ//ꝆAή@Bu-;YhUʄJSzO:@|^mR+p^'`Wel>D 2oRsk?[фh !qKR=)갲Wm0]h{ei_ Sd jztzVJef D2*u0t1zcA̓ZuH?i  ͗cs?cc%:V^8'Uj`)0hܓI r9~o(,0՛ƂRÖ?'ã-gyA!d*0"aZ ;ΩGm?|Ij`*~<@b`rH %EAތxvOmfqZ{5) {yuYן{a)icdF80UZdII*ZPڮ MûF=Oa=; ( P*~m@ ~@U,sL"_:oV0lܢ?pᚢPՉgkĨ+67DWt)XP;{fB$-vrrzcw7_L.Dhh N(΄DM$;nf` Rv .̱rE *B_{#_Nrm&K;j]y;/dŪ@c,RBb䀍ث7o.o^w֤N% atѰP-"R^3[Ax8h 5`R@FQ]6 ovF!U.y v7'R(2<8<vxdQ1"b~@%{uz==`ggo͟O^ 껧Gg<ޜ^4yٳM[Iih[) $賭dxNuBA1 G2U+@ 䅂0f,x[ؘ*ͱA%AqV~ 8ƹG$<}wt)BR@I$&UkWtV3 F׷w4}{6Q$Om!2\CEa ooQ,t?>UhkESEĉwV.X97ޕF[3- sdmj)eDG J6TT2ljNp~%h4v+pYJC6D`t6P `ћzZ[hkoeN:dД%߿QTC2/a\`̳惲6nGōzm ?񀙴LlȢB*P ̧†5͈kil_-4p͗yޏˈκh;%f7qlz$.!iuJ7hU͐fBX=*):Zp9v"eOhf_2j0@L óO_X&+`*zrj eʑuّZjЭ~94*na֤b*ϰKK= 1P谴bvVL[L(XUY6Vݕdݘk7Tr^|eKqձ&I[ʇXu/iI‹w_Sg:|=v.H`_u]WU~tq50G@jO-VyHx]!uG=FךJz@^ ZBF I:w/I};tfeeZ̜:ٙ dUP#댘 hvP`kl\&c.R";kPq RI_!xAʡ=/SOh@?„`ȽT\`V{#%xƝ"(C~q:'x˼1kT)9qRWO *0/ΰO aDUmIl#jώ0 VBn!OSyoD! $ޠLF/, hK#%ΥGOi,RF^'_0ӡ4'N#dsK -azy=tDuJalbW ]L؀k^I]8va~(ΰe GV.`ƳY+ )wKȱhA>|j?-g,<0R޸☝d/NeQU֯@ցd:#ݍ %> :Lg-]0ֽ]s͚)" yf߱p5*Lƾr 3uխj|T$d@xRg키{T\̶ ZX9ܣ"TݝRu--ۆ׃'5c`<AڲV*4.9֡'~h;>zIg4xjt[k D"'MS4VdG"AJmE$DaYSX9-sF,^ih1Ifc;Î\gw;nơp5_+H[F~Ow6uFӳqת"iV:(gV{ݎ^IG|\?h;) Sfy+wmNNqGm! J asOSX4j4g;+UUְcs&Ԭ{:14pǍNqs'yGk8}DaI+׺coGC Pom\~]$OMa/;c_woDaMZy.@Y)&!0l.ty:_cmtLq y~#^{& g\Hٴ\R4ȫ rf ;Ṅ~ ~GB\hp԰ӍXHRhuhf4O"=Mb^O":[ vv4~(X[}.ǘ]P]j1m!Z"8mem.D?6Vx_U~8guenA$RS:1{IZm ;N‰~j3AY6W+'r+M1w8h+)^9"'UuEUDl]]iϜʡRLEDwjM[-9%;\?G^E60iOmâ玀"' ͡7N.՜PKyVSmHpip/_vendor/distlib/util.py}k{ܶw V8QIUEuj[z%9in]Jb%$W\߹@p%%sI] `0X[Y v}]^5A3^m|j* ʋ6rQL&+jinogn5ARLLJ{xxJ6ULi:W.r< :"mMzٹ~Rp˼&igOZ?kW%4ӷ_kh2h^Ep<|à; ?iݸErg`NTi-/^7ob|Zu`6y Z25`F/E>e90R3&&.&/'G ^}ƂW˴+O=R"氬ZZS?gA莻Z#ct_K Oze1*=Oyҵ9rZ@shpl{>Hqd2?KDPM#y hTԊv3X7RU(b`YI8T{W :yxܠn K_7Z`UpKPB\\"⃵}$j)ÿ)W4O`ħ?$nPņvt\٦m8ZBOL?xE";ogzRIjd`^ueujH^B9XX%yf̪=~Z⁼|Ww@<'?{Zy I1ZBa % 5 q)<[yu3od> e[Iv# إp+4i|ӕ^X_b>7REx}?AAf?FVԨ& 6Gz =2"CsGU{(#t9\9, Ku} C%QkJW 0 F_;q|&΄F&Ɵ5m ioiBFOE'5Y-..!C!ҷdW) _bE1$gb4+l! ,,Sdj錂]L5]:]ޤR4=i+Mp +1:8x^# !ڨbT}؈PѸ2롞X:#.@hԔQ&YJCqQ}OipBkdUH`~v84=w6Ճc# :C%!ӻth0FW߿;owcXmIJڜ56 "MIj1J 40<ȓE1Y6GB!qۺv(YVoǁ:' 9M۬IF>i Ϫ C`?tƕxdns{poJ_p$5NԦEZijH4R\BJ!PHmXkH Q)maNA"y3Tzإ1("KF>R*|7]3/Ҝ-H_f-GO !trk(NGLTV6A-mbr(]e"CBפߏ&0FW|1 ̸i(&sDb`v3(H/B^$ao0:tNYdx)3.JT- S= L[0#򌭦7_հLcX Ŋapy2Fٖv%O#KjTTzE?:y 1kpIM ҹópN诖E萔 jHIguhE`Ub15= ?#Y1=Tkd;* /ɤ0q+&a`107}HP ZM~&/ ?&n |ހHX&I,xa=6oia^RTxY5AЌۺ ݇$ЫBQgTI{T1CAwݨ65zQ'/:mD@d[o+蝖0{P~W@Xf7jj0؎(U"+Qa{T^Y%Yǰ?Y%fZv~y'5FɳצȨl4kPsZ]LCۊ?'b:KD<6*EfJ򃏮ų)~ ,ͧPO[>W3@#A<&WTИNYU^>४c#Hp !ɹc˲֣o0FR W+eNY^|Wdb;vy%c Mf rJ(}$y(P>sި>}t1( w88!DbWpcDD S]5+;pF9p6ɬw)PW,~Jh"ąq^IWa \WckDh7^YQ@ѿy;t8.-!ua$N[%A<բ}Nfy>_u)ɖư%7)bXm_PBY,pQ.PO@)i # D JDPح*R;1pu f#2z)ˌc=X){ITh0l`Ё>wN#Tȭtа*B&=\`afյb&v ~rj ڏY2ٚe%b$Ф؇H* = OXeҠd9jNW=Y怚<oǪ`fJVi*|V Q=4HNZ˥NbM EP_ ү~NI9O M0?i MBVgf'`Xt'q#CQgW(#A 'YHBD|Fv1,';6__YN]<+5=3ͰRS;ܡ ܓ4OU*\IEه ֞0ʎ_kgR HBBw\>t9ŭXL3xlgz+>lFaZHS%=w)1!4V_lЖ=E=\o.~:s͎ nRzHnXW}Z0WaCDB>xRT8ƂiΧ̭l͛7`>D_L)jyX%S6wf51Ly6\5_k_b{-g?hvMyyYӠYz =T^'SꔠϲSחF?RS pwқ{?댩΃kؘ Kk rvP{%[^pX艙4㮖4(U-c<cYyRo&~g|fOB8ɐ-7yooZ-77 ?5)塅a3GcbyXtYV,i4MQxHθ$M;zSv8>~oezXFv_K l%!)H8Mr%lފ;y%64(H):_q"$H%A S4Eˆ\C!:{M^ꂑ #FaIT@-=~$DlYzc4'x^ZT>>d?b߿No>Y_ ^8825FblgԦ|urz~"a!o=u\v;0D!쥜m+ q +0)1DS dp$nq{*M YHJɞ :Ƅ %}3qф()rQZ>"UfffڤF˾-+}֤ZAՠ(i^ֻ=8:FB*1@Ba88NSSX$x%NX=#yT܄UƗD9Df #ڑtfl{Ӻ0lЍ! tI|% 4 MHϩU oppX qA:Y1-o,bwm|}<1ӉJ"cA@@3Q$ 2x7tӭC.!=H`CJmU7'qS7Ue pfVRe+Sg$r(/K``Oָ[S(('?DҴ`|`"IK*pn9( G#0o}:< 됋s=]/Qҡikۘ0ڭVz:YuPW,27SYp ^m?/@d6GAl{b'׼\s~ j sV(Da<ַm Dk ц9EK\VNjd mɚ~nMV$mU9*RPntszdqAE)C;{ Q_l^1phIٕ5gZ2&"b(@1mX>1r٨nph`5i{Y+8Vu:xp?;wK6=F'=>{gs|><w7XL`orT>:_Nn/"BBBZԱb>C 8Ű/(}_K(svQ$3? Ț`”S-,/cxq&uvK- )AJuR>EQIw `g/S %0ʈ,.]ZA0#F!xE`zZaGH[vS,i|\j!0H6?Cqt/m-a> ~mV)lZYѤ1 g'bb-s^_Z+QԿ=={&qGsny{{ןt+{DGa{Îhu%.@@Ͽ ehCoQs/Jy#uS变W 0tsSBPU#}V5޸iOrtʘsTD)I$|ih*x= wpis P&,@]cg{;h>d| ŏ괇<3sw/$; ߅֟aCRuӌ DF(Fa֘ЯGLSt0_ø\mԋsLdsorKqtc]-< nū9oOu]v +DaFal +kn ʆٰ@ڂi|ɉp#V} <m U 4PEi`:e~P^WԜ2ݹDk :=?6&^^4V]ͳ"zضS9*iNCi`&@)X-*%940if7=#'Ù7lKWZR!.=RX2 99W6cK'k8-{1 +1)~#xaٚ(HGPv9d}QR+_4= cRT&3`vC~ȢMЮ]n}2˴ 'mjfm4("лǷ XNo1I,2N(= 4W㳉چL|d.̰5٠,;ͪd4d?~7inc6 QT{&fUuD2[,1RFXfS`*ӃovȨ%ɨvP` {4D7eeM9AGWz 5گt-ݑI)EOyaRk]ÞUCaB>p &\;hR ŀǧ^th#QPBi礪B-(o sFSNKIzUb19l,qk=j;HFBk=.k-)]>uIZav;"=֖ͬ08HV=Չ*-c$f{~+cWWT$݈eI켬a;zT2??YG+'*eLS=*!a#][ Hnr#ZGXNƊ4q81 _҇3 %`Ft=* M }BQv;% $AQ2>fifHwyS 6V*Iފ,Tiy*ӁWV$,޾XJȜ+n4T*WȯNt=Izr MN̊jXmQ}cV7\A#&pyw=EP/q'*?+__ax mԵF(hᷗ]~4TW#PAgXS<)oHKc]'m/:@jcpfFK#)/ܙ$XG;``Pfd +Z"{s8hKM^4)&EY[B&UNܡAc n\N@+}fU5/L8ܓu.C mO{P׺Jl1ɰms"jY 7d%D~j5"C3Gu|]e( ěu={Fk>ii*7˼H֝S$ PIOHz3sko;GG>|גgh6e]+oh&AP7Iۭg﷞@o.*ŁgdhxlV;ҩ~Ej' ??t=Ҡm cԺ]-){4Zi?&ЋL 0VӶn@Rn.9e%N7#3՚A>7* G?' S9o^W_b#e*Ӄ6~6 Oj |R>07: :M߭m| 7`i8j:*O)*5Ot SKtq;wd;Tp6뇀fIzg&AϮ1^vE_9N^+#eOaJBm5^35z~!C(}-ΠkNi*:M\Pm=#m4Sg 4i6IrHEʳ$8نðxǝXڢ:5zN<,(.l]Q9'wW?S\1"<9;MĺJ_ў[a >03\+++DgTIvャ|}/N|>HUYsIǜ`\܎ .o SDVoPcpZ]+f(`<;Q&xryRU\K#U ;yjܕȞZFJ>U|ƪ)3ݔ0z|+yOD=ڦ=k]N. [UO]R) x+9y (Mgn\l&frqnжF<->,r"ĩQwZ-LW]!8ոJ?Ѿt;ǠD{tjar A*y"#rՍ 4D4BlwߍaXn^~"62#k`?>F3ZKܪ1seB5~!cb xr۬TEn& Br^tBĤu4IeCo` ч%b&I# 'PN SRu;-\"su|HR`+WJ7 43O>-FSɌ)KEOO"G`o?Vto6?dttM"WY1n^Z˹{?.>e/Ot+BasiEcJ (ďi2`>7ަ2NVWhRa}p_.B9Gj< ZqW}ȉJۓI hN{8c:%"4HԌ\55pQ?O0ҩ4[7(1XxHvNVW41V\]\.Ax;ަakJǢN.RW6k[:ӡD]JOGZY&We)S*Tx)whON/="rT*gEg8wIC2g*+,~܏9v20[{a L'AаoX0SE:K1`K1V]`+LBӠ >!Ӣs駾W36d!S>+~]0;%1 Hrr4cw ntט}RW|M ~w/! C҂"SȮהمDT ߩ%G@ 0hEC%jdR%&EN(,e05[ÃCuM+hʴA7ktĆNe;N6=aEiii07oz/{":X4osI~ PI}Lf=_.fG0tV`'1+ " ^bQrq1nLbG=bwzmKViGM+.TT7>ڋ*.\߸$Tj7[<Ӛ!&Ltyr1.CS@ecDoHZPQF̴Qp@wVS2P4ί/m@,rT)ZP6fƓD@ZP}=$G>D#fTW"A%@ "DE' xr;|9օg @&OZ I=cV/iOvY|Ny.\'\o3n·gι3漯oGd@CGO޺;{<B KƥsXK.R>X[ q$Gz<(;{{Jyc|_Qʫ>U3XfC ד(js!ciǠ#RJ~].$*cn7:N|6aqnI^zHiBMpZu3"o-0ACqu>v9A"Nnd %:-ʮrD9ݼLi*|^QAt@g|?uF!jgiG/ohW.,z%B-C 8>=~&3| 2KQ=p{{6ᢁ&'MLŸ`#kx w~k! H\&NEGI]}D*r2Ԟ "S~HP1hǒbKԵ+A$`-}sOB[k0_}Y~H euz()tEUaAs/9JK)nU i,⤼m󫫫b-uloIFu:伎\QقnpI*:-4̡FjEwƅt7%r"-j%W#+>( ɦ/R6$fe&{ξ;;+#LBZ>i0fRTIUoA;[]_J)H32UCb *DE.QóKt$ n=֍[Zҙl'KV$2"MPwEj>ԾܹqKI>StNڵUЌ:2HuQ`YRÓk`/T-)>mƳSɳj5/qŽB;=dd؝|aR)9Cby.mw_,:mY dlr@*x$բ kuzٶˮs9 UP\#jzrܗIp{|Z`^Cǵ(4٘Y߄k:Y1 Tq|FbS5W_wU= kA_TPBR:l[ 3D+8XKڛc@N{ @3RuTB+͊$G\wv% L8wkVh:Й39%+ы7f0\`hxq (ˠ HfNx#tn{XU{QH,Ģݵ݋ԏndQ/tԆ1w1.W?[ .Z-fԶw>o" |=YwFY38pkU'u<ԼʱR&K/n$Lń66@Rг,0N3?r,y/6`>N*&=u&zjԱ١F.d^./^~ǢuNp+& v)st|@&,68t0vVc ݙOǟ6 &?;iYׯ8EoW 9nT\5 qfz8hI*r 5`ّDф%F(T^b)JW@f_u!}ٯebb A(D⓪Ĭ8mkvI2Q_8[q~T&(&qb\aq"R11,934O&(WϿ";cHxNS , HoLyBOx*SެŜ;+N$jnwR-iOL_eO\SX1 zZND*gFP|f!@Ef`\D̃ 8t++p. IąPoSp>HrwF\H}xsnjz1Gۇ_\>ןիO3 @Nw@-ud%h3|n/i*``#끝yDM(r?tDϦ$pv _k>xfuA -R*@9CTLG^Ѿ ufJk[:cPẀ4ƼqKho_xRSaʱiW?klPKyVS2~[pip/_vendor/distlib/version.py> d>db K:}6]nh~U0 +6UtV\g_$(M[$ٔ|̀ $pˏ=RiVf1SiՒQ£8.%zRE( gi~)`,gk.[vMPi1UkWFǭ@bW]MۛlM7*.\1ǯثca8>joWRrXV@kpg)IFƚtM oijlj4mIa:ȥmIʍ/y6@ )Ū˘ ӡ> a]NC\:YFfQ`װXhB0.1<#ZWΣ\)qhȎu30"7'_?-t +hDODmyO,H6LD\j.uiliNK"( Ȁn!!km$&K.vMk!3m=1'ͮj k0?h@}|ɢBTHf A̢Ѱ;"[- B GoZ'B*$Z7wNdq+ޖY_Wd/+DYDiTXV}֍޴<fmd '!¦@\Pq- Wʟ-r%zÐ7,=`G2JU" Z!zs!FC"_ҿWK_}.L֜^W"\CH%a= kjlmf9I(/L.e֬uʲQ,lRBR<&eWb:]lֳIHiDA1c5m0ej3 hve,OW>hi5Dc\hE#@\'F/5yk sUg9;9;:M?yI9Q#Xaھ 9ǾımvzfN+LA. #^cDG{s^O&:,rհTn (iP*Z4BnQw׷NYQC$yd!ŶF$Ze`M[Rzq N'91K!mqs*(T;>M9Jo\A%Qٲ+A@'j IlP?K]Wް?qm]$|$blIZmQ[g4& Bu/t6 E.!XPA+#,UUDbd;꫱\mC<*ER69<_QX#Mw0.Ax/XwPMWl]E[CxSrؼ'ÞEKVs"k^bдR}rZڽկңمHZ-RQH\q?_VPݔO2y0&KHg(ԗ ٜS&RTio{E*.!}ï4c TFaMmC`OkA;*/o°䋦+i@jQ-V V\H)Lj6EuCO] @}6Lvj휠D+]pP,+yA++c?|U7=S߲Jw [<3k)]ǰ,mjֳ߼=Az5O,\BE eX2뇑7/CAeVϻׁ3cQ=c <؊MNw +OK8'6$, '`^ ږReN$ā@.z+2p%yThE\DVI%zM.[y;SRV/yLP#T} O uYP#`Qga$7em<{ _Zf&}c0~&cv0}9O{ty9%UrS^Epgm>&Ln-@Жp1NnCu%[wߜvr֦~kVGj>砮 * :ߋL&R{L\Swz@.HVVǕh2S0W0[2>aKB: U:UT-N@₡&0س% |5+Q5gԩ:@iP0̊c_*^]}ET8/Tkme|LS|5B²hQfG==l9°EMīmi r8T u3%#L)j;]᧲vB 8:ǘ A ix8nY;l=`;`JC$4ܟ\6wSBCscbHI#\Iy Bu-AxqmĦ :&Qđ@j΄)BMi)L+?2>6-a2d]z](IjxǯKﵜV+ua2-/H7z e"4EZ*+Vxg3LRUƫB.qy{: qʇsfpP]/"el7NbW$iy8NÅޞ/5 }JVZP=cs٧ELIƛ쿪m$dXb?p+K 6uߗ^ 38, P]2aBf<Q" ٹ|&hIbGWp>|Ô2㪾{.[MwǘHRAHX EQ)R6 ui.{x[,9T>pߓXYa~Ok;<!X^o[72vZ@PЇܗ^:m@suX68"8ͼyTv4 Ԁz@̆`z vJ9o[@\C[ٚ?E^w.u -D 2QiݠPl6Juį]$v|>-V½):xUXA -o(3>(q_l(漲kgyS >C/J')S޼h?Rx rĠ&h(0ŽŲje]|#UE`-p^ȏo^ '4{ (MnMQgSF'ו^Q-O+uR}ɥj1+Z8KӰ c|^XWq3۟=1X-eFi7d q%IW)b;؂djD@TQUٱrtY؞i Ȓ"!WUZBkqZklߘ}+V5}nn=o뎥 ZhkzyVbzq6n5|[cEE40 B=Z0vF+ʓIwQj%_^!T@G\7smy. Pwg;z[]QF(s]8a a8k9's4Nۄ 2sш%ic4Ns/+}Ꙧ+p ~ )o@X%lh.^EycdWR.չZj<;@֕; 'B~.X9O }CL])T0v~?CVu?A!1I"NߠQNTPiTBYвR4fqBG#|=#~krP* c͸Jp[!SuAMXFNWQ">A.tܖgX22F)Q;A׶v١Oxtn`{?Y[;? r*8q()h޴dV0X>wtuvZ>_}j(O9H: 7Z\~K}E<+Q8<`%Ic-¯rjs붭P6F'fbC`ܑW($xg*߭.H66[7'Uo Zm^zVH;gppW.k:B$h,a)y܄Kj.K3""#K~TXYgm##̑E5/ϭIzo_7yiIf?PKyVS )pip/_vendor/distlib/wheel.py}kw8w v{rI%2cӏgInNg0ٌ%(o= AJv=wxc @P oM[Tӝֶ~ϫ㟏]a [G|qxos  !!`p*_D J+c<$ʋ Vw%^F!j? J_p!,!\^# O~~hs߯|7?7P|H/D/`_;y^GGQ>ɀpw}G7ƏY.}Y"'ngdZh{فK]@m9D,.K5WcmK( J1-[ؿ#_kxdBn=o@zZ6pj\̺Np@pbܾݺV'<]M [/I 6#u?ZboeiNy6YTߧٌ9D 4 Yr1@khRjV:⎮vXúąye_ '$:G3⾞Uk ] JA=e$뱸QDUcQ;rFqaoLM{9 \(U=c֣,r<7\06Gos*-لoʎ({ G%A==fT 5u~MK#J h$WKP\3O+fЮ>'*'NyXxD~в[m2ЍN D 0N2ip-f;%]xKlO_ziBIh)oV8]5%V!P Ԁ$aSb>_w=GH7"9l#:P[f^)yxUd$cXsU& ~:70mLT7l<5|جcҕk,q Y ;]]3+RG6ĵHb n[Rp/C+VsI&ORZuػ7ރId^[xT@19gzRn'~5)@ž ]epIfp\#.T?aɐMyd&ʸ|pdCec :&l1!=|нa(X'!.F xY$v<4Jxu5ږAkΎT\>wq3x$>)X54ﴘnk|bRbRP:{PWQWr2}i "ƨ._nX'Mn7,ʐ`mnEJ'hIm#\ Z)2BmR^h²@ [71ɗP_.4Vٿ_6+\Ba̅OfLS " Z݋$d@@m6J@u,6l{S/\EתS\6cuϭ|@lX{S1^FAK)>D_!4hA$MS)"`4ua _yH5pS+ʊ=g+UBXm ":(,ˢƳ%҂]H]w&?Oʒ\3j\{#&Ǡ48#Xӧdw#L"N6"OFL&[p\VwǭŲR: 5F镤"m 4/<*c NgkŞ7J}AԺ 8DqH&W !a+AG2 l6/"d`B6P׀hAZ r g+$T)`pwAuu5sHrQ wdGсƅ$-P4TٴY"[0vlB+%Q 0fqq yA}SP4nQwz-Ɔ-z֦Yâ6:U~$,B{t'dԐёz>6Tqpl]$# {*+ΕQўKvDYbA.YOE4Hq ,0'2=E~\ [za7Q / uCBwX<sh-GA-bFVvfʽ_t4 v<C-%nB{pdqXJ$^>܌HNA]ؑ҉ŋ| ?ݔngr՛0# [XPpp91!KCU$ bVO|1E(o()Gč @N"3Kb?{X'=k)EY4l^NnVn "D ,󼋌%,pJ>lg ߊwE3ʍ>!/ۊ@n6WtY^0r6ò\i[6Eɒ_#6[&(͓}(`JN.rosK4AMF7b*ǀFlA<1X* 28 L.%úP}^Fݟ @Lc2"[䐷Kg!cmBQ0nJպ|7jXt;aTf,iw" `I6tT7\w:۶2){W)l KW^H* ܛ Vدǚ|\w5vPl ܁aOKܶUmsTE\QYIa@V"8,yfz2 RZB\j 6>ysի3r16⻷FjzE +‚d;W$1;v`{6EʧhrJGyͮr4sRp +HѺ.|iMhE}(wSK}jӹS~Omth}ev/wUe't6 /a9V2-j%}oRŘX5F$X tE[-J&K%ƒ޽LJsx_r"lY#,mڥbp lrU:ʵ2[%G5g"Vx}hUcĘZ,>ky841Hamfz]x%mh.I`&GҖ`B?8"Ays1UK4[v2A![_Wu6~ܫ*U:b[]->JՆ""ǕƩBnRClm\Dְ(7IS%8~Oz:0;ӄ@X_!|rY /5[ SqA;3+3UrMPnK mE 0xmuUWPp1#QU?&8 CɝUlAx` uQx,ާefV9W,o<QoCS}`z5CT0j1@)C-h ~ӑo&jY;( $ǕPr紘JJ(INӠ1hDtd$6`&pq!8T1Yˈk}maK4!:˰ d38& GX ~HSci,0[#C=ti5~\< x+ZT5}$P}6*KZ՝BBϟ%Rwm= [ tHkF9iK=ώt?nFnb`=7.e Z]mhjlYg%hG&NviGi&V8JP"GSVСA 62ǀ.K.k<~n/Kˬᕤ1 ~n8Y*؂ڡ]mētbbh/1( 0KzRZz%r屲cUbXSQ-q/$N)\n-U#O9YHL}XDuKlwb<<8mTO; w<ԜMs3ArI-//qTAp" ;yRR.N|Xco{=}XBei%7v7ՏuF6Z7k4#qgCώs^ֆwk$0|)3P@b3Tӡލ<]A^ 5ĄO KH ^v;QR35QhZp_7@3~wfXz$v꼠3`#]ٖ?[ 5;FGCk֚^RA_[hIQQXρ2%)@C59N/GtI(j\J)Р?=G*q( KdmtzFJhr A4 SQ=%gݓ'ay]g=3/ C{E<F]IF/Vs+0Su?>-1Ĉ/B/hq|=&'7zT, lSo{/_]ڗH1QF GeqWQ 5>>V^J[-l+(b_ր`sO{Ϧvx@|_XРXhXKJx|mLs/eZ2y|0$PqJT7#TOQWac=t[iHs!B+CD.vdo"'Fv#lMɬ\mb gXud*#6i;H)O:d;by9@W!rX@ny.ǻSY,PAA8ːkÖ0iÎځ}Cm31z?TnQjYUeXRL؇ Q:pZmO` *tO]whWX) CUڟjm8hGX%H\^ «PbAU<^g#B2Y'=K&grCħ$ചvhm 텻=26E2 \ǐL&(U;9DiQ Hf2s|ixuziZɺvF:BD|2 {Q~7qm9\S'=T=:e:DOWryG[%h;2cqȷ*ßJP5i=WAٷ _F5O0Z%iq Ey $¬4 )vFtV㔂zZ5(ov[;`)8TrDygOPu2ҞyYH1@fX9$`hdL˖]'Eg+ tBٓv'([3,3Jr`U7/@̋odհc`23Еݽ'FOw5}V0R Ocءz@l+ү%0~lbsaЖnC_;YBw Uu;wnӺRb6(I3u!Tޣ\Z. cnh[H"Pp&-cEJt`O8ڷ:7C=@p{ 0 }Z{amN}jًԈ:I׺FDZ/.6ާL 2/1(;05jO%l}ԢNw51х&WG]^ΔbҠ7OIqq輁g!'ԄK%ƹv$UuAv]꭛ܚbHJmgJ% :Sͨ"juՂEZ?r5ix7#ֆt'T^2sցoNS}f_BξM>KϠ߿m{޾xO2HJ=lrt[ϑmL8!Sl}\f#(9^9 e~@2 /;` hL1Q440_y` 2٤BYWj,p02úV=g5!0wʸFI8VʍHUgGtj5Gyl^aY]IGǣHv拡Y-glR_^=aM2CƳ/e )jL.2gE*cDK\~#Vv"hWt3ѽPc8Ԩf"[%)feJ1fI_2 ep8F'"3/AŬK`n-ڊnWDЋ>CN9u64*"^2V_t"g΃ i4ʘcK&l-ݖdgef+"x=*sN&a?*j)F :"LGҝ:Wʊ ]Zu T6v6![!3zT(r24əa Lh%nM2]_#R$vpg̨Ye^V*\^XPwQVigybs[([s'ǣ SרaC<+!s6bKS`l`_Ȧo$ʂz݂cUGHxuO3hą|`uC;)D%+DOHGށylW^þwI 7谊J$wqWeq|}n\#oswJJMf JTVO։"IO9r>qGAc =>lXڌgVU'C 8A]<(.AkBoOWRwģ*6y@0lnQ8C6NW֎UigC֐ﮜ|O*>#\>vCZt5rΚ |\P`#Td:7W$@?Lth)ݐъVJ2+/+duyP-䭑% Pk͗hXd!ro`ad u^2Ωq]e w7Tw nm+^ےn4TN3w,ݩW& Ąt1ta !f 눅do>s B!wLg2O|B/Ă~=>߿|yFI[<#ES+q]"x@iS edBCA0E>h^33}8Hwo\48y}V`> T-ځiƞPX HG ꎅ,uT6L,Lfx w 2zCiaW{{ ۽G'}Swmlr߽JniFL1/ˉщt||4J5 ֝c1 vThkzONMY13~?_-}cF)*lm=ɳWѻdf2kb}Ŏ~X}eRTLm)ϼG1 xbFK*C,Kty-n Zr99"rfy#Em[QAK[G?PKyVS#g)pip/_vendor/distlib/_backport/__init__.py%N1 DQ= (xS=.͌<G+S:ö.8A-Kͣ2`'}hHnJҎY*rU}X.yyzƂ\~}XYKξ*j/VXGn|(x_6 A{Ja[6LnV囊)n;J!PKyVS/e%pip/_vendor/distlib/_backport/misc.py}Sn0 +-19AҡIO 2hS$C'QtM==>SX[2;h^|6eSX٦sp$W3Gop_lk*Aʚ"vuZowTz7O]L>6LXGj@JVU+4H-Gc틀`L"O$]=7ͪZ_NNON͋juk3spmC{]}ioښWUW6&?[k޽o޽ad2E̦+W81\@&f:6yamvVMzrO?bεU9m iimW1Pfg l|T~jc'tFdMI[9(,f˕}E,ּ_uUOLﶮˊKuLN!]Y V"(/?ەn垀9u v7{prW}ErnQojDo>jmYvZfryr*1DU%茷t#t`Ʈ-VQi+0 &379XjctĦT &+`(}GAx083 AvIf󽝑t{jXvhVUWi5t~ֿ r)՚UV D0q7%%C'oshqH noh?T !p7pݘ@ǓMSfnڹ)lmϟ~'g "A.l N}\7a Xြ <}tcm+"^}n,'7\"&0r.bu0D9Em\2RY*W'f03cնR\:\׍=z>ءq/MF̬ꆂ\RL@QR0$UXx_Dn63BjfX~ F Cd\=jH1-7D<%x3gS4-lZC Y)cU0%+PUX7MفM keC 7YnlAS.Mg,_~7 F.g=ނY7F>#kX|5+z9!UQd[: Ȍ@Ґc2o;6౺)NGs?eR7&T9vtMeG܀Yml̎Q8NCBT)/qpV1j|"QH`IyvJ:4WbTGEJPo"߾jƚߜs)Tы̝V9>$(Smڼxc0|f(Qk70(-Z%FE% XNB9¡6{%.H5csbJ2wp@?ĦHRa&ȭ&YL@é#!lΖC|-=M A렿Eݚn`>OȒ&`#Z/wL5/} 'k+.S:B H뺄Fm, x$ꦁ99슼i)` j`iB -$XeuVnv Hgr.(z#:]4`Y!C_o”K=I%AOA@bRYEp XQbg!,lYٲ҃@Qenq o\U%Ȋeq^AVv*e%WʄH )zZc+p,ڞ& GlvLK!N3q PHxDŽ&Puee7@7ڈ8I!lsgAƌl0嫘@4JbF#ưu6Y+ѶlA'JP;f<<ǧ*H7sW`)RKe5sA*!Hx59Zd( X@)Pخ52+s<}ZyGrmU;ѵ.]c3$%Ey%,lk %K38tz5< U (BU%uX_.PEVmPhWahR: L\"DbnY6qo|fXk*Px{z@\k w=7[Q:ڟS>@KE2s(]8@oܓh~2XVt֧gB0GwHrAhYv$X v zh`4br2 as%dlX:пS5G}A%H@ά8 ;HVSk8-dC.4&s=+U; LNb0`0P^_tlN͏b SW:#aoQDS62e,+8G5XL1TJ Vdk] `ۡ<|K6O.9zx? lGpc<̚I^,XX(*vuK nG{Ƈzkw+ ex+הc>yL̐\cZ,?$4)wʊsŷDzכ.2\]GdNBgq k؟4Xu&AUčQX`OAuy9t|vwF.iE6ҤIXppvFfs1,QB p  6Ja\8,Dpzu;|ݓt'lkfEzrM]".مI/y:.eyH/AkWXAq`. hQJ nE؁?笟 ,"!JHU&`5Nम6O5 ź2q4TXL)\E(+8fMU&eI{Z4`^ov^nqCqԨpi:p.A!/-gȯ >Y˱=&6h͎Z'*ȂQy)`qoX\7aO I~¦jW)X?^a۝nD@)8nuTB5I%9K]u{ %p{-@^c?CO$3߸C..OxytR. z-ܼc d -!I6.8aLPK 8e'FaQw%|[8L>vkTEH%v=nʅ?ʵ*T*)+घ ۦLل~DL SSac DL/w8^aȩڐ4|`0}]}k-g@x'(YLsKq 8 1|_t5=u;nO)/̲IrZ7TZb[U`E<>nYt ס ! xՊk?/WҞO/|<:ѨIB}HrÓLDte9:lI?SȠௐ\K|ɊN-N8zM Q>v?Qc3_&)KmjdN@(q({! )Ad=6|X)\dv~#Ƕlm' #0 Acg8hdsgnd%2`6'3qU6`dQAY-<`*F|DG(3R^*͛i$ gsYR+l(}?|yGy zxMP!|5 #0VY=B4ce΂ lmv{y9PO67q3WwX3TLtܓks)'k6DqwDkB X26RЗaF?'<.})?/=("bbrN;0YP.o Uj b;OAjhB <[@wgzC'kh=y9յ6X3Ā_`% d+U-poMNfY!Uq=ѧX91;+(B1T#(q"Mp->`2__YethIej*tm.IB n9Ip+=2Rz G+@d?*4t[Ki*yWS#`м<LtƮp= @@V@H ER!CW"M˻fo`ޙE{/׌fxo%F^76z).;}Oճhx>Bh,`j[~9:=_1ŘY}EizpF ؁Zd ui2DZxF)/{3\C)9;zRNqr7}"WU ySq Ά)`o퀔+»2 3(flBdf3%_+(|;A+7G/ʆZu8Zx^a?R%Ec?J96ʕ'L]׼dnNas$! c|Vwk >hD  vHMyV={L}Y(kkIǓ ag4ʻD,'(Iްx| 8qBµ5Oh>a[n=\\9-b+nWs`.Dﻏݬ?9/9ɛ" 5 ߀fa¦UV1>/^\%PĔ͖ar w"` eb2W t.4 Nev3axc%Eei0 lz"'MFo*FDxo?(gkJ/_85q}y/najDr_dHhF$lDl10B-ސ_q: ޠN=(9E>1p 0$Om?}>lAkeMCE'yժzS`ZOEAs9(-Q2M>a8 /9$EGސSW& t_&APÍFs)'B !77;D+:Ig6t`^-2_ Lʰ&="d9[{j5/(D>r}P=<j,/|VLC_uڈfHxb$1u;ty0KWNg? $f <&`/akТnMꈗxP@{U8QW*e1 o-j~Su%Nt; 6l!hJ"jơᠷ֌\bфkVrJ$֔N&LgF*t>, :-ԫy4+|ՈAD9ޘ=[/}2O9h?+,ۭ$ \94f{a $ J[yՖMf2a@*ld|x6{*Ǟ , z)gۯ.OG'( ǐɇy S62^`u`hN૽=74k_'ڔ'ڤ/ DŽz CO>cQ{g PA~KOdzTyfs=@[? Ceq`+c=د=%jLT"@ yriR77t4t\mH?߁pd#T i5 IP:gplMԬ s56!i!?Xfm6 Yánw#Z&e ML¬_.DP|jzsZdw??2t3V?:Gv)!z⋰u0DN1QC]$@Lm|x9 ֈXoyJH`Ie=zpy"8tf' ɛerXe?,ÂӴ, "#pu@FY;V{|{0Lrыwn4nGj#;82S+S(J#a~%tW[$sx{ף{W[v{q-нHBaaiw XӧǞ4?.;޳6.mthꠇR_K4!"rXޫ#Q܁NDuLpX)0JТ@Rfa$u F„H2D;X*d\mŖhS@5Ѷ$m=,cw\<g;?x6P$Nɹhq%@u `(9տ2\e"H3h(c,[D _.Âs1 3ޣ͢F!gqXP~ȶD'ۧ=yeTC> W)@}c U6C-m&^kL>䉇ρg>TW+VTPP'fH!{B1\|82!)Es]Qf_;2QX41h,'@{! @ZKʭQG#[i!E;44WJj3W/Fl(za ,A/ѫ Q@LT=C= hȳa'\RZP4P"8cZO/''\nol>{*H#֮%(+"9/F9LF>,z fB#eAk #i> todv3r2]SgG n%9Zݛ5{_z>J᧡J x%*ECc Z7F#|*rY4dSpo<Yp\S0HuL[QN^=o٠D;5vemO<3:Si ;{|(<&fw_?6,K7o/Ey `SWpmVEo};vS|UZ,kNߪ#r iKm4ΕWWMPpyZG*=fzg${UC: ̺-y `W)S8kʚ#qh T%h̲ +IZi`1ԓF Y-`kdn`[Dh\òW`Usj)i*}29əX;$exͿ5@!VI7&̹*Ԅan'{m :pIT^:)(_9󔴔4Uvt4V!{]Kj C5=l\tGl r[Q_~Z& !3U(.=eHMky95BZ(y^ nVD&fRSѬl:& t[(Y<]G% T42qz#E ^CA5/D^DIJwWF1Vj0}Kɪ+ߓx<7_ &Ӷ% srp*(Z} w&.W2_‘9n+Dߌc?7 l^buy|\ Ipa]DZȌ^5@~CGь)/&1G*0{9KH_Vuy[h`>WZwޙ e'pmNJ{t#"wҟe; I خ`ipеQm:4\PY'M|׾\Bmhz4!|xX+X :Q\C(lldW47``LT;rgT̆{" 0Si 搁?B0#SEȉ*&-?] X[,hPV(#w;>+NbK4",[EN%]c1ޱ,DOH8E|ق"Pʻ0,K e)D$< >h1{$Ty om4ۙ@q I!ݜ栧C Az5AXb]0kbm(*i!bx0/6-|oHEf#&FL%Qa9(I^`.FB&ї(jQ43LҡuCt3B[޺oJA\5@ Uxh ~&#U|1-0XjB]/H}MGF IL-u4_'0 <,v42D^o5EkxDMab$t}} ]^ hTL0 =mp\=jmXLYXO^g < $0U t(Y԰>c8Y-Nlʝ@g-Ihef/mw\ Xvݒ@gw*PMƌ$獛k^<@٨j*ϙUw.\Ƀ_u3Doiz;/4凮_`L+Ќ}w?LCHDӘ}n3{^~wFaY*4 ~HG)ũۨ Y$<3=Ji<,G$Gm7YN 1kk̽wq%HA 5?:=VU:`|e,ysj|P]OXf]y <*%~<\DkүPW5GTb}R"}Wa=z 9c$PZo.'.SdMs[Ao<{jcS{fQ" {*">;@ru{z0y>l|YY$ O{$pXxNQMZ,Bc6*r}`O~(Z̬C*0:Vj:;3:jCa S !RCqޤ1W'?7c\ryhD0\5`^O&J_?PKyVSDBCbYi(pip/_vendor/distlib/_backport/tarfile.pyݽ}{68?W>9Yk9/M}$N꧎v6馹*%272CRqw⍔nc`0 f6UqqΓvc<_쪊{ntejYVq2Ns_3{0M~zyDʨHʤLMR\eYe]%E2fEUɴEEDQ~Mb*8IQUfi6(X<^_V7q`ie>IcM:ɪJ 4߭l\uz^4M9`!\(qF7iu/+=tXv2_N+U$)(`dHeߏizb9U?xYN aQ #(y{ ˛k8+"`ܨ+s8#TzMxd{[\_@xNLFQW g1cwU /U@J_1\V0Qvq-ӗߎ^:?=8}»C90^BK8P 2({~{_q< q/ rY-d4khQYO3ZN,Gʼ)*A" $^\UsRhTQ?Β)x'o"Ԉ O5u 鞱]DO>~=oàj'e1xE~9N:M>~;Gg)rit˦Ms9 *͇2gm4ZIQdQI]XNt1dE{llT< `7Ӎ$YTaQc(4Ki~S"*z2؉8-q@Lp#Ru-"G݃J8?*j7U"`9Nm`'G:7SyHo98ǀڛ" 2KOJ?MT=5'ub 6g:ZsBjZω>t.%._Hj bB;DFe:R\qɏr6 7|'MTm-Ag*  u^W~ Lj{םNludKeaƓ*)6>Pp7Pid wEOAɧr ^ƫ]$Nގ`9zN"_)y8,DlIcIw+e>"//fōÓW?N^#Ýzym I|l ]V@U^nW `?yXW>cK|~z}Ǭ|ux{~qp6)sO.~ētak5VR5nv؋×o/u߾6l' #vrDu_.$m}t#skMKHX($RZj#%H8CZmB^Rho2lچoH>kR{M"k"FQYN"YD J9:<~Atqu*L'i;:e:ŷ3DHpŗaƷմL*GFwh-M#p'+GUkĂK)H}F4*h/q#ՙ]8M@6hh #u>Ms<_&4doi|tŝ|Ra͊m!K;vIK*‚\ "`A`_\]/0-.`X=H\xhF)dcы(vLtI7·%_ɒ t@W~] C2o^h<"TL -`xP!`dQsݴULZ&h1꼪Y"2+Jֈ6 ֈ<@D< 2(i FD5@|MmbRzXy%n'yNs~x̬CI7^?K*2ߖJ&9Z份oy\#e6&ic\ϺYG >w:y['Ѧ].T}b_I"&I})GYC`Hec-aq|Χ]%KU `\xՂi -x xxT&HA0ޜzxlZ,>) UQ8TT]|k.di_ӲaH뽠(G+_ؤ^/'b()|{i$/?>_bz^ s± Wu:i20ޓy\CDRXv.!B}B!(P5WIp*8V W@,5<`-34C)/5*;蕲.s 'h`(?^ ^N&sX|(3i=O?%T5{IzQݚ`MH!CvzG]5m MF2%jwM~vMu;I`Ƞe@%h68̑>lOQs|-vQ<N]uE mQKk#tt[=Q޾;÷ѿsT/ԏ'z }4nѳ_:&NkarZ^ .52gnCTŸ{Ic3XZݎ)qf;q3 ^-]c}a5m;| ;kaqƇG{p RH nՉr֩swGsʆ!н2k<"QL[8y ;2Ms@໲T|s#&sهŐx0Nvp*?k@k8Q:Fu6xї$ evr}ŋ×/wώ.`owS9:ʕU6O~/<zuw3:wu, 8[ؽ{L퇵|>nTƷ%\/ %to)K@7NĽ>!jZd1ћUǁޑ{JułF*6b.%e_uf_*vydW Ls)roϒMRVoEf@nYaJfhD}/ةA7t8fVr[|\NixWƘLA>GX %7q1-W`WHfYtEP(º/˫no3iw_o⇯g@BȔg}pw-ըTBxYᬾaGJNtVNRVϠ὘^tǪqmԙ'Q9k& ZȈ&|H!}44  9OA/>=_^e_ݏ~7VJc`Iha~%)CDm|zp3r)^0 K?[Pt X"9“RVNڄh NX4ML *$8;!n[{J2i !;u J=n1ww{{jaYìXg$!xFI  O!$u9gx Zmz{-=b葍՗9 iZm n遀)э6Z#$0]a=>˫>BI^U2VkhoZ$&uG o+g|np~ S-dm=Qzf@51@ 2Sz%Md;QWzokDm->]I0*yQtSahrv˥Pd);^b_}pyoVgPh/Kp|OeB"l҆%ׯA[NjĘ>&]:^%e3tg+_|#cY*wk;gƸ(Ns!jrx) A\B=4cEG]C !r`>pf 9ͪ5Xd[t=E-`= 1Unְ}:$;)G<\L.m ,eA:XG`M9x9HޕI9j-Uj]~cXys&=}4'vC c\:ͺLc@<ĔXj?6"v/.^ze|G B@0b\\VN`9lUi<Іu.̚gGy'ź0nNoMwKfT _plF/=)t{?WƴxƆ*?OI|e[给%"4n_ f8Rݞ}[TVH_pOmcLMV@# i@vջ# S (ޢM2* z8hs};hSܬ7b|5.i,- m~vͭ(뼬GD;9Or9[sYJ<W8Ky!Rzj8je٠e2~ ֽgVP̷͌ЂjafmeOyC`%9uqtɁ% GD\iLBS= E4KL(}$Vo!{ssH;V_9$L.Sѡm-5NXE[<ɵbvLU:ZFC}*l7Knx0,`;y޼NS,Y+'Ot ;DiY#iLiHZˑ 6F_-5D3G )b^ߨk+|x!J򲤃h"oF<[]uA[c#T!eBWP !uR={ abHd0r=؈E∲PU0Rsʍ|UD.9 ^Qء/i.ɹ-1,nͪ(ud} v RY_W4Mޥ >+|/; ! btsSW5E/]ME 58@QP呸7ԕeGfmZ8UgSmCP3S]#UYQGQ)EvtLRxR~7.L?5v rzBiT`~m .@/8,(V]f_3Tx!Y:`,0z޷0yOý2W):3!2і?7b4l)0OIJ׮Y Rֹg8rO4T ;)kϞ_'6mF÷,^o)1JH~]·/ɹ2"Ev `Eu"zɚ}weTg@1XlfKR)$#*D*q f94@ O2 FbIU2 `S. ʤċ`e1;l=%}8I`ie\0tf*, (l ]H9a+uCux̲W6Rgz[H9MV!S NCrER AɣJK$2rZA.G`\5zBl9ɌXn4h'ÛSq I ΙSuő۝Jp Ή΄ӗ IqmJw7RZ؛ft.%Y Y^|$WS2@8̽mOEϏ=V8Q3gn~jۏ4׏!Lh,od6.9 |T /c}#0:B?2[ EC9<6E"!DHdUoKRk|A\&ӢzȲ4ޤKfuoDʓC„J\ ywri.6!SX2+T_FEjEIJcya'^, 2ဧeJ {'IJyT&(%ڜ-:x%$/mV{m9;|y| &6= z]; ۃ5vPpth/Y KVAo3Oi73'ɞ55U^›yFTip'W)tV ~<̡IŢl@.bOjՖ^mtB֚czgw,/!VUCmUJn)p4d,grgÔBמ$Mݕ-ٵgwmqGl@VD#k$R^=m ayEj`(>"SNmλ ' )-tz0-C1K8:"}}g=i}k7j:xmd-7n+ڛL~WП6gJkS!s,/i3/o ^ώ,eC@ ;nz?8F ˬbķ ' `Og2#PeUJzӽ'-4e*$ M$0PJXc_Ug=hbպ y)3 w9e)U}7|Rި'*UW)Oӱd>pSevܪZ:v<~u[~ၪ=|G8GVЀ{ t;6Cآ [6KESL֑c )+(@ ୨C˘xqp^#JOdHRuȾǾD]~{+l L¼c?J<ՠ$ T( ٹçOK9G^\a':Tc+ oR hїXT8:R["lIu\H$dWJ֒sS(;|Ӈ{IuD7k֪%<vD FiN\uruUe2!&ؖG!#PHgQڬշtm╊]eKSEWٸ4վEX]6Hެ7-y^2W5b1̫-Sg6!),LTFђ7B_MSt]Yξm5GU=JKhBד$7oN._6M1? Ø|0bʹ7ԉ6GС5 I:[&;1MQ'JB>X'vL5DF6f+e"vɴE@Ϗ%L[VAȥ69'S%Ba1Ηc>,K{E[Dh(utHhFe|f#LaXyZa^o0_!tl/wؚyGY]us eZ&c1_RpB5ĽÄfr, 8*OIÆ^ܪ(iE[ҕ<\@9@=uY--ȅU ŏOT8tkO0hwW= mlZ Yjg-g BMrRc4a([}pRMyCGqyzbI+IQQqZ"fWInD#.m޳Ӑy!& +h7v74SyH]F2}ޜ+x󼾃vu\߼sOf-0Bk o/^n?+%SL81IlʲHko#Y\_#Qc&qt7h.2rAcSqP&;.:NN ϯ_YK @#{&R{"t5SqBM *0ʓ#y]*:,6*R/ ުߊB\1)Ώ΢ΓGO"ho Xoyso1Rhu!t_-KҨ3`hI1#p)w`4a7Jx/`}dz7ЗXMRuKiV|Iȓ/'3,8 Й0u"ܸ@3۰v#Q`ta"X3ds&GzIbi.%i e=vF^KO*[9ˠpP٥8{»` vd^sWR}WYų)Ίx9TQ"`ӹFc.6o,(#؏݇HbQlWne}qRbaw*v`@)*0~lWj GvVRāvI9Fc؂ԍJ{F1Ie"dO՜@;ߠLݤ'^Ȃ1*ȣG/e-zmE{:mUHoܘe>*7Oǫ2oa ">Tʈf:WD7 VKPe^#^;.}jL'@ngv|DF1$k4 EuB8tn6\k`!SUjxy7;ڤ=xAKc; yt$rKl>;};KX /OϨ'V:ߗ;yT8k8M>6H@W}P+qŮ"n?6L[ #x V 15KJ 5V='Z+7 m(*ai3_Њ*J4tN 1yvM;l8}T}k%_=?vx>~aooc_0_mC1l!5a!cWFʽ۸͝2hߏnIQ.pU~I9]8b:]\F\]>w2m9[.Ed4݉Y}?~Q$|i]2kE^ )G͸0ĆGj(W[H 0[߈hMJg@G}Yc4G,u#q_0lȏZ1w}tKSX ݄A֩Bi^OfV4zBŨPv K-uͯt;v[L͡/^S;4TVv˜:tЕ}K[un`R 6 #8e0eg% ΢RbxcEfӌ*2u9+{aE0V/|&ʫLn9z<|L;۸ *qQ΀IgY^$# QE@t`fDTԶ,Γ ^(1IwcDgg 1 0bWaN{wGl Ooe\*xSj b;S :TIQpՇ*١z1)]=JxhW):q*E@q tAc.ckW}%CķoeX/2p&M@h@Ң<â3֮<1<ҁ}hM-H4@E7z JwIy)gr7A 4D[ #e @m[tɂH]v08rC&*a; %L*5 Ќ斱Vo2AJFC=\lv|ׄ'MI`2tWGϸn$вp]Wq679s=oҍ10$SmUO sɳq\1_?;ӿS- ~wKad8&vR_u-3-ZUipR$-嘣hwo mrh3U8TAؙUD'yn2&e Hѷ&Ck\@tE5~Etao4Nh3,"?KǏE>(jzL(Ԭ!PN:@.D1hs;WDrmprZ^yOBgD83n@HSݚiKϒy~CF2maTbX)*Y%nujS+8//߫sվ:@Z,t%J%",l&&eS %E/k;kS tB7>[βT5:*ʧoOF/~kxZIN%2C!R^0!h^cmt3hVO"D9"Psq`ޘ6.t .vWT!SG]ag%ggO^p@?Ŭ Y5,+Sx*tVbR|Q86AZL q*GP?.oQ7-2 'P 9Pgk67[M[mP} b쇮_[+uo3F,(5j4qr0܄=g2g,1 5IKTzC&0xchx4s`8c;5eSݎ4CX!3͉l[+b"KWY '{';e\f2EY/h{_6O }7Ep,}WlHsw_>c&.ݭ*R[_#B+lqӛW[D殖zғ^}ҨZҾyXy:{-_ qDH |~|9A|گi%q?Sud+[@ l8D-%?/*YM^k/ ?Wx\||]4 {%p6&|'.@k'_/K>i7 [b7 Cc,EjGǃrM(KָD=6N TGb9TѬa$rZc1A#'FbѢDXOp53[ӕ<9Pڃ9F׎݇=|o ^?/M&@# /Hknn(ܳ0'ZasM iT*RCjUOᅀiTgQUڌs(9Z ,dUք!AL]|;?4C>]׀dwFhRŲo1(;r3Ұl:YvJAt'I6KKp9qc-,ڿS`͙8 Ify;pFu8o査EiSQ/1rzu6mߘQ(lMN^xw@cot`-۟[O JLysgssZN*+W_sCe'S+9"qD.nO~ܔ :O(JJmfpPyVL1A6 x>K 8,yy:5*ߞuR #QV~Tr7fl#B Vn 0#r%8(bOrsfT"ucnys iǚ$,S/R΂ sDaPeFCD ϏtrkТm c)黶Cnj_l ;+\ GHŹgy9 X AKDfsizykuJÙS"V|0o)9qMg+*$LrbV@wm邗VJUq!_e:סᨬlM=EaBbHŻY y0.⨒\U1\K2a^rZjZ|1ӕ5K˷PjɢYqpϒWCXy]u9 QjՈכ41{ 'Vl_o1+F+oVYW1G!65Uv=Ctzu OGOpƪ 6kS4IYnzs岺o Yn8jp>::?;|n؁^ x7aNȈ99^2v|gFW[xbq(o@>!Qt@N~I "q TV+ M[c̈ztd_vaC|d(+kIo2J$??~jePIf/Bdi ;-~mk1 >ںgޱ>;nCмfuQ/[ Ao#ػZNk/;Ni0xfF!3^DXls*<&;G>SfU R_ͮ-nNXJv3Eq~q5K5lT:̊QI-3+f>Ǜ~?^۫06~ ;ed6@_ݰ?dr#Ief!76CoVu-߬=P熒h}NR-p,}^e]J2EN^ g+0#"A[1>S}jؗS~䜢:%nuvHيIL>{ XUAcYnϋ:I+ _[@lHsR IEi4TadikH!S^ӧ L'+6œ+|] xM־0.fKʓYGb sYU#J'|' -ЯS}5$C ?d>b`l瀛@gmM=ngY˕8SYYϦh d}<hO}*Ωy?d;De*N; ToNOɺ$_W }B-]rS ɶxgX-ln1^˷:[.&2"nv>%PwKoծh˔~%{gӉ 378֮M@h8WvSұUS{&N:.Ph7EB8O P'U4c h4CIx͖nIl\;];jwef,l)1߭`iHd.jj};!(> SL$E8!j:Ȓiƺ`cHZǛh= &jf ё~mj jo;5߭٣^?^M'N,{-&4ro=kkg4o\ USDI" N}XdKO &Ԏ? 0M/JqAT $.% hG]@G;&ۦe=5cB!|MMRYF 5*"SsM^5->JAw< *p`L C+mk2Qe9[')Qλ)l<\L߁7§|@7 NGSe";rcZi*hqu[x+ajmTXfY=F|*J3Թ8`2M;층#yRm(KUB ˊ<& B*77!Osn̺TWi x'ӢC ~ ݛC>Q) M)A lv-ɁcB1xDg ն\Ͳkffo+;LG͘˻@[ϳF6|'wrymE7 ʪ6>&iXB }5Ú׍?;nmMGx@zwxR{gDY(r W($n+X:B-"鵤ߌr+xVFPe{~ LPko1vx trqsDP0{6|矄#i,_i x2dEo xzňE)e1weܠ2cUhu<̬H*8X5 +@i NH7E\7OXjT6>V7|,kbZ*`v7s ȿam1% k1¥jG-^=iq>IrQt6m,vH*1HzNz0}F\5U5,D@}}u[+e))OJŨ%{@y9*O3-PĆR;%]kQ$ Q0}gu꺋[t"2D nt -RE/J$&Cy2o | 8tG~~fvyiIvbZdJ}l/0sX?tN\F93߆@ɭk iU Cl|],tm3N <\^XpzvԿe:ۍ@3K;.#z99E gVO̼ P${3{, &˯C`i \AS2']+?m|Л(.0^K :-X'qGڷWٶ!QrPda枆ڑL!OF35"07 +5o:PvodaTL}=e56uΟdɣlzŰ/v|C[:8apU2#939xt\u\b"f:`6VT ]~Q'Kޜ)nVMb%-k`0 I3*#ܴ{cGLr~{|p0ۈZQ4F+UvROST: gtm_mGv˹.yuWel:l_ǟU*Ѹr2<Be+3/c=7.%߭h[hUNm#k2-K6R '|j8e!@<u}vHъd: vo`Դ]˷{Mdn{Kw+u,F09חQS:pC!Ќ^, .o o>x 6HX(JbXWia'&o矴Dz Rh`oALad;o#1`Mps t"ذ!NA4V{ yk-rgS:`R+" n0F Ic4SuL'79hVEO[36e;X\U.f':\WĘoSb+_l,0fF\CN5vji{L%kssg/Q0dmS۲eΧM`2y"Z$5!5nHsrQGi\x`o؇ˠ{Z*xHC@]S"k=52jDeW$".gv-c(<^fc֒hI7+~WqQOF90~ rk.4v4uU%D?Lt1)MsNqk:g-kll-/⁢\)ˆrFzQ7nKe FOE#+x#J?jXDWWUL#3Wˊt;c6 .z@_PKyVSoD pip/_vendor/html5lib/__init__.py}T;o0+̒ Y@6 d( N=R_E"{9g/`I݁vp؀{WHH7b-[Y /f{GWVS/væaMg#1Zt TF$}p'Jkn@T Fj`BledDk"䅑C4hu4HTTL}ى(1fQyEݐY1CHEKDnFԷ|ؗCG πۊ߁p oȒjݶw=Fm4I5f@,.T(eX;-tdmu.lE]'Tsװ؟)Fu*VCyn (bBߤw0NR+ʲ >X,˥rFΠ[hF3ZRIV(w8Oe^KY'.G/3ipswAw+K}/NTvX<ƍK3<;N=AD/nQiR*|xUnQed7}q?ȥ즀tB PQ;)a'EgPKyVSʧ--XA!pip/_vendor/html5lib/_ihatexml.py[ks䶱_%O(ٛY*$$hFRik|h4 Ipιqw_/qCS][yX-u+nAl?9l.bF6uJy^_ V_I{ڙL&'/]7O^;?:Dĉ-'w)")p*!=ȩ碗{) FD$' '/\éHs=̽XC虧d89,%?~w$5MOJ4/?K=_`,C)Յ\ʱPZO9Gzn \ $ʄ$xF=A<&Nb9q"(3$dhރiNÞ0Ծ!Zab)![XkN4GOM^QLLw1QGv"=*Ԩp42QŨE&$XBIn98-cg)lfZ8̊0 f2Hr+\'`X:7aXSNrHoOclNC*JnIaadZ8)02Z0S3ͬ Y3"sfXPz2B,k0#2h"boP"q虯BLnBXFdIj9XѠlc;[r#kQ4ƜQf}W/Գzj|zt÷Oow ler!r=Bz0BYrZW(;Z?x8zTlmQ'JjK2+={%q|@, 6虁f,)5g %z[gY .hX,7 f0=`\ }<0za+uNꏃ) zД40 if4)Y2ä^x*O{ Ssr" ۤJs3DFI̤d4)Y`@9 lH6gVGbh,.z,7hd9 YNYLAN6VEiS`4"s oRybB+'Ss{djn%'Ssj^<`.eTl.,\X xU)ujԊߤ%MS|YWFv)i)3<ŸCaO,ĺt=V*cr+6&JRcUF X gc0lIC-=ccW<1ظ{xx86>FmGskd*#_8IDSI#!d$Y=I&zrғ[d Keh^/Pf"M<b՚SMs?/\] h,>Jzcf|:K}u)L/ L/ɞ3V L߮~kxl׳_ap[qwb'S5eJD3En$9[twVrSywTN/o7?ҵ٬zvFw=s^vñh<%g@no6@wÕ+mݡyߋ3eɎ MKbmfgzVbinlf%Ơ;lV੪گ7hhR-֧%p)Ttr!.Jo=s[Gr\lͺRvUNJZ^ϸ2~tئѷmK`&(vT]/zwBN_x! &nr;uE9GtO]o©z>;-jիC} 7ʶ4Hk8 t}|qu1| {ܙCe ^-֊FCncF/n'虁Gx&pl˲p#' u\؆(8E.Δr")R?("?M&7T"׊T%6$2ZX'껻2SK`M->i髋?^]]]]}?WW?]=֑BwW]|z ?>hJ2EN&@C(ytETQsС~g*5W0--U:U(CT"IZ1Z B-hUArr2 Q&D@U8hK5}(Ja0-4hP_!0 kh~¤FE͕p%UKR " EԘf`As F#O,_3* ~dJjXeP),Wf/F1rgÔ12)c Y֢M-dQ]ԧJZSE2ϕG~ +9ᾲ1@U8(! p<qǓ085d)8@* rSTck^-P5^d퇠*j:]5ܮ:D0 vhDŔh4ŰQ C%1dc0"v%eT jèORݐw]u#/:؄3k`UP@K4̺*h %@K@-@2]APOP !uIڂ@рD@ Pi6TA =;w5GTݖ*U+rCИ* J ڡ̔BwU(I&`w}X*,DEŰ5U e%+V6 N]:sj~6~Ku_ӳa4[LnrP6E[ 6cKi+9NI*4]ry$_!ş)B>}*؀ a/I>.!I>*:̘>mg^Vp$@\c{"xrv6Q)]{<۬WyVV|0dP;1Du{- (VgS+?ƗØd1;둺tQou4u-O|20߈a})S!N=UpXΟȔbu֘ȰoGKULdG760e9/=}GDڠn,}sT}xs0T3Fp̈́NswU5}p.C`Ka:t2S@⤇~j缒f>2tv,bHAKgSeenJ&¶#/Ư)y\8s:֌i}#&[: ؚ#Jovk7Qߚu퟊ZOx IxSQƝ#|'/8l gHnEYvo=] 6>58~G YOR9I-6o]l&~ߩT'")`} ;NX\&eZѧJBmivPKyVS6=a~$pip/_vendor/html5lib/_inputstream.py=ioǒ+:1H-X^XB,Bah83C{VUPc@.|ŦES7%NY*fѬӦS}MR%y6dMOӤeV[[ Q$xzóy^N]= 5^7R}꺘i3)4mmg8e\oj}ͫs]&'-xB~YUGYq94b~|Q k,$kۗeU W^DeOb^@BQ?o>HR зb ,X}YQ.TTYljL @|-shW4 PCs.[5 ̙f!&JT,VMY˨w^ѿG)5MߗLNNvGS2ҿ_ }W}ݶ۞궧n{۞n{^궗n{^׺n{ێtۑn;mǺDSv&mvU51Ͳl!9g\52R5MVl1XU*IcdHes7nJp<5p ` M:M"KҔ/TBP|-& Ċ4p*XQ*zW #Ƕ8%k^.!\ n]_YMVľ"Iy8)}ڜFv .Of)?#`㊶xZ?$YMgba"O28߽#2W-xz <5;N%bB nY>)WU1FKq+5\` |3I!mr \x%_E$C7 ys9{Ia\h}<.?+GF[[;y~ $$cOp`B y+ hٓzCOLȗ$|DZ7恻7:)oP.,ҮY#ֶ;[On=9g \zc8 ȵ9hj%{mT.n5J쾾5K(y+w<˄?800u0c˟08ْ'``'##6ٯ*z]Z`Xn4'@¡e#CSYw fh,oxQAZ ذ>d(KK) cFɢkaXc-=D#ߡݍV@{`U裡8=J*S=~8ʄ}RcTAX*Rs\ZNπpdQyrP:ft2I2%mx-T<9B=ϯy trTm5#- ރ9)"b"q^b|dڨ70a2]ЍY0Rd8'LAEMZl۱!LC$KGu`޲yxoN"Ԣ`yvJh:r'P:b 0?FbAuqRS8R<.=͜F諷[qܽU&L ?cqqd*z>f=x>EC1`%~b`iheTS^Q գO ix9b`c.(DȰQ6P)7d6ӵCŸ\(E1x/l'ƶ9ʘ 2c湈ps4Xzm W6Icgkowe}[8rK '!V$x25,[=ͩ:guS~W=Zk0fz߂.gl!Μy/68Dp&R@f+_\agf)0@8(M79<جwPTZ -d93'86K DzWaBTE!*3C[1*A{,*F[dnj?ɅJU&)7r8hm cA2řk`'pFldSuCB"jRBI:֏2uިY&Hvf;C!a8ig`F;"$5?FyDl[m[R7w)!-yh C<#g%g,˼:~ lbХOD\U>#\#\`r0wsSJ9a Knx2{(S1"jMQ~d'ة&w,()=zN&)Hrsoy7ehcws7<}`MT__Mfy&\r^6" n)~5XeKާĉW0F JS11mI#]Cn?J+"Qܖ9x02-tc @hK뤰b|j٫5`;0Yd ߲%$oѬe7E`3ӊUuH3GZa GYQV ;K>>+x%b0%re@(2x T舂1y~ڃ,Z_/>z!2EطJRnp2z v/@ǠNY[XV(˥O 2!MY¸֑3;ZM![i# dZ ̿(cwXDT ;I|.jذrL򹓺U$lH(&Go %ԡ K{ )wΛUsTߓYND$Hv0\m_{ܼݒ*=v;a|'Ȑ +Lҫ'oȃLqͼIb%_$6+zг=Z@=JF gQ o9Z ,oڊ+m1u a>3 <cS1 %Z0,KU\ZW!"h@lIEpBF6\vKxМ 8ZEIY:22|9PXF.;AnB#:,6VOB];yy^L1nY,OSg;sCK ,(t7DPYfUӼLI6%ͅ&d_u~[(1 2!!LK諿AEL&lGĒ-*Y' M7Sn* z e/2%2Ú }ԩZ%)6*AkE7 L"r͊Jښ ?s`liB3A}~EEb{Xe6{/`hZ#ht?8vݕcu4 ASG!ct9E+e[V-,YEW5xWWWc<,+і-Q";XWd>2,шU!}kNVJՎطnPF-',;4c>GL0I?Fc5Qc6x2;kbϸ}u:1x]o+|oq) vjB{c~+ZroW H:\\^OjE}N|ﲂ3R: .ڀk@V!~qu UhDu-'xQku,AڠN'JUT9w y?ޛI1(}D>#B(?~ ӰAD#*h}aWri<Fۉ7jSd/Ro8 ^U^= \ ]ƴP¦N4)wԽOQPP`^쨮;Cl22vVvX`{9L@pM/&`"TbTzH)`j poVgLfKjWn%$|lma6vѡ=t <2qU-k1TIK탩n.˄1n}hXھZf Ni -]IkΨ~O \h)!k5v#yk*j-am%r(v㢚2o+ d?JPH@mQ`rt1o8r{嫓$(-]aɫC'sgދSOpvООA@aY?H^i ru8s\Tz&1DHMFF@$ 1Ԥܮ1kŒ^y[5?L"*^:\;L=1!T)KKy-XeWu>mk+浒ZnnBx!u(!=(l WY~exwq8S3a&Nn0XY&P9ψ}T10$gAt|p6z,jx= qkh h.*1ob .&*鮘x]?Td7P*%:J2CaJԈJ@G~Όt(ByɠP/?q !ʾi 8wfElMQT. /)Y$w@E\x#'FJX+zG\{U^h{Ve8WC( Dx.`SUuT5S6Ô 772d=@70V3VcHY:|,p\zW<\d Iچ&E% 5+J\,K͌-[`&4X=md8]m 1L:ZFb:$(?R54`+W,9g ӵ|QҪ6PWX;`F[r^$w. #]oCGL'C"ʠ׋yQeb>cn:5UlDE aֽ>4XoX qZ; a=O*oF|+#仲LyfU^W[#L0Bq u]IKk N K_ƚTACK}iid)Utw9uǺXҧp AJ@ E>tH~PTf8\=6h; S\`Oo=J8\CַQO1)?WT6V_r/?۽@W eBn0E "&dm$O= @t2dWM!Ż[D.4ad4zkk97ѫ$Duc ˟4vˋ 3"0xj~l'΅S{ڵ]zAŒbJKd gi\EY-W{LNw_ɢLq/TiwXppҨ ߩ/`켛"~ { Nw÷"^M/;&hW#aq#|a/8Th07W9@:Y;pR"NER#؟'~Y` <@4O#ȧg*{,YmH ٚaL]Hm6Lм'~mgVp?h8Ƶքd ;]0Bԑq3QjDj!^D}QCEJ~5irAXjYD%srzCR9xPۮh)ҍ#Lu2̴P -㼤ݬsa I?1|(2ີ;KAG,7>Mϥ xU-~oG^'D{ k{mNz,Jy D_y2wV7W9|vS¢f߄φւ0Z-6|ykڶI-Ԛ-$([ )C/%eemhL%m 2:y| 8{OoS^?L'dvFԥq0=`Xf% >bԪVr31-xRn⣒oHp+_  }ZG:@~`.̚IUm:4: b\H{" ?zm+,3Xtc{!ðs/lC}&Ԣ>h@#X5׬O6=hHptkF~г>e_͑m/(m:߰MC<{zȮ9ok#&ϗ.Uw<F "ŃF9Ԏo1]JBƣ(8jXqDTї@PFnO@Ǜ|ÐJ wnqutOë13^ B2$_ʥF@!턬 4eD{`^F4w~4z50f=Czۄ6sQљjO8)߉b'gIQSA ٭yO0u첁$#HaW*%)yO;܇_U,);FQ͟v'PP?xJ0|,!Tm-aP!_ PKyVS5mO,"pip/_vendor/html5lib/_tokenizer.py]{s6?gjT'#[{ljif48w$$H m=$Dbr:,y,љ B-gGiT᧋ׯ^qQ߳hT>?z"G^G(}r|Dzwxt(ހgPDQQ寲=2l1Q>~ܛ@;BښK-ϺG;,1'1|ī|EGC#<˿''6ƄI E9Hlei H  1$<5`s |!CDP{Z(hF$L:˿!>d{zGo\髯nuòaCDnik PO|Hwf/K#@yq!% }Fm} F2s7PʎHSwEm^@7C o}jȊĐIP>u0tm(PSZO@*c aät 0,mPPaqsL-uL{ (/YA@|/1mJFr? HI 6/Џ~"CHg>44ky4@)qл:V?8UфnL)kĽe`j MD D`mם 0|J-$~GRs=.`쟚 =R{t>PDpzzL1$hӑ/+):`Jꝡ cǴrw!#l6"EntV"'1aM648P=G؟- K6sˀu{?4*,8}8*W)OiT9l>nB(tD&1lL (cB&"?>[Di3}Z -P}-5(@@eξO&v7TQlw1Fg ;Q0@+̓xcs xͧ74MToE)G5h}}]SZE5h}}CSZ?D5O"Z'7ϛz|z}sڔiT,u}sޔyM1hWYHgaݣ&˷sqߛ 3Do5hЙjbO9aXE DnFwoJ 7^ ;?{}Gdpe}Ezwԃ8E˗2Rϩ!Ggx-Fd">O L0Y>8,,r 0r"_al.9Fڮe4KU} h,rSԤ+ 1 :;jG|+Yy)0 ()8Q"`c"!":Ǡ|8 _j|8~%+ /3fҳ8vYbI4@Q|K'cf@YӲa/]3"|(S?GpS4l>,HXIOk71-ʶ3HDx#BÑ0Y |{<-> *CE( 抡TNr`"c!lw~œ{-P'ABl`"ͨH`2^ qo~Qz3?P1:(x<]Dsk#E2_dHCr-a%CJc۽m߀h"v{JQFJ$pv3lDsx\ q,_J&F g~N/cKqFaLȺ JÉήo]˼0ad}TdXmO!/xja™ Nm%dS}.8%L1$Ql#+N6Iy<-up+Cd%pNsk iE{o@+xR {T(g^yS_Qg :uzI#_t^zWXA*!|&EBYO=d8r< R`*I̸WWUJ^iM"_/S54E+2D W+|S<{vU'X>K~^R{y0(B|yR - .+ C)La1ҥ"L/@GieQ 瓴@"|OBជpS GHs|^+YNυY9; 4*@V$}ź< wta+iC`c@:(na:$R]\0f=xa?QRip=dP\ZFᆄ.<1h+e_",MHqOmMuX 0̳rStGG){.v7OO+_C!yR)}-19j)D3ٖ{Gv@78{֔a03Sgbj rQR)#IMA`YM$cF11R{ ŝ"[2ln`. F>ʱt)U#)#L Xv/j܃1,LToBe+lRG8yΏt?ʈ` qDq${˹mL7"y]<<ۈV.| Df&-_%Ѭpў/v#a0?0_:QqÌ\LfxWNnNdfPק|IJ_ q{L2AFx^b@aHzc%5'Cr<!'&6tƜHҤCLiJ7^*2tgN~b0`p c$ʆF!/=h nIR -ٓ(5 YUD|ǒq߰EA=?eKj|Ӑ< %P1|;wic8^"eMƮ?pl= x\4Q?|ˀ(Ę|78`/)e{~" YnϚ/hFWֺh>TF5b6 U׋qK F'ep؈f;!amc}Q[RTqWt\ƻBwGJYz!asv'2X|䵨b%wϛ s3\w҅%SQLل}·p۸?E:c^1-Xj\nrT"͟?fIST$EHKh-|,*TZ. BE~(tX0ϝyUb4BT^JﭸGhey0hO9"6 o yk- IDv2&v@G%r?w-,YL]`N LdYuz@\i;I&%9H knGe6ՙ]ݱj)taN.ªe9ԅUk LD\`5h?,ߜlR˛nq>n8-yq̙4y{$$ٽDRM_"{edK+gcinidr c[YvdrGlsm悁x(L?ۏ[1O~VgkLiCS$eOƦ;_B 7o;iE%w"%$kN/b)?=%Gx(Bj;nn4V+q-$uӚFA%,)\M_.Y -uU!S;orTMaG2jCX8aفKi Ҏ`g,L@MZ֏ju|5)gNNW۞K#]R-jaڃI 5UmU~DC2% Ӯx֏Bk}߬R,())|YzH2$ c6Vʳ WhJ͗o6 :wa_x#|U"O9$wj9"ߟrcYgQ N~q'_Rd|qNLȞ3&m;Oh&;㍢e(EEv*'ĶXeY Q'O 68v%-ÈO]x{^?kӷ5Ӟ0Qcy&l,F@Z !&,ƽ 5O H΍4+/F_JSc_W4fvv3M_ c\R;OƋ)^7 ߆ =NwJ,±ܤninm)2ջ9[V#ru:RUvJ\gז6GKh?s,!)*F*yRC 7(uG Dm+v'_?,䘸-I7-b/^OzSݷ ,]Ǜ{,XeNaQgљ >y~ % &%y5Ŏyi4g1-w#V,eL6 ~.ãcpAcPcW[RTn)5loN)["`B \8ŠLb\"Dbuj1+}RKֈ-.{wC3L LT8޾4~?=?}|A?ǂfL~$"n1DQsŒ8oFQ%55j|Vx͂2^PPí5FxƸI˽]E &Bf7\i%&n~gqo»h`,q]|oƆLwbUZ|Ѫriz9ƕ!V% T6%/gNg:c EކwU\궨.{PqG.A&n,޶PTew] s*KvToo9*^Ƥvph'>eBoRժujuc]T69M1U.)uO~ׯ nnsϯ]4  BSŧZUî7̀M7.n^ħܛpE8(zT{bɜiW'}۴)>C;SI( \$Ѓ#_ɽ83CRq2_4b>ލ>u2x|hi1CLj=U/3_ze3`Db-j^7mC0&ͬ4>ՙwM+V6`_ۆO74%㍭l[mkĖ.j[W۔v!-H ęty-VXk]nw1G.i#pLUk/Uzu.uPAIV%%&`R ~>R wJQˋrFkҧ(|d s\r"G2ưd=6WZ:»!߳3!t'W$T~3q3U=FExgW=G .KԴo DUmܞa|QP p= d)mroM&ԣ]>Dw׸Ľq=, ??⫋R=&⋂؄⒊VS?^֦PI4ׁ[PKyVSӉICpip/_vendor/html5lib/_utils.pyW{o6_⠠)ʣŰ5vV) TZc62)Tw, "~~htYJ4.F!%cTBXYa`IvSrӰUyUK< 7#TQ"I"kXY y^WZ+wu(E\J'F7D۔ݿς@,kup9O^|ͥ |ɪ¦2ឯj'H 3u 4eE<܅1o]0%يk:}"V4w.0إv2~lI %yڂ U=wKPcȡ#S򗓓#|f A2V],,6`W uQ66+af&7Pֲ2$gĦclxbb,[!!٠Ge ހ!/NI@\A5ˉBs <[1) ֔eXvf{ ?eE4=>@)0x0lQ)2O 4ɌG}̤KAr~vU֣m+kEum/IJ_ c*~vl#34nZ+^Mؗ X \V0(=K|n:SL`JZ)BaB Y4rM6|-/|¥R,Ls|| Oj܅GiV-c*G’w4bm.pH%=D#iD{Ѥ)EDU{$[ʠyQrG*Kޅ//I!lS5Mp|"bLl]"/HQ&#'Z,td*& Ƥ[2sERv4w2B2T ÔoÿP̷2uV `7utf%Vİ>.:Fg}ݪ=kysIn& \e&)X%7{3pE7U&'3k4 = 1H#>9Zts[/F$]ou[ X=rxZLhà8N27Ǐe螇ku#^q#!6m]Ǥw\\Éz>hS E%?BH^Qz:Ed;^uFJz~r5ߧprO[W~bp"fx}+p1)'JO='i T9g@FAHدȧް[knlRA5Vn8 Eh/P˸ɎycקFa^G}rt"MiZoװv{Ů@2Mcx5*OLZkĶz x JHƁ--tԃ^{~9\fZyn.(iZ>"8chmiT%/kHQ If?lmE[<2=#`Cw{[{G Fm[c*ߓ+A0|*)]V+7kGiZDhIVŻs|үb⪝mKInc`y~PKyVS[2&@HF!pip/_vendor/html5lib/constants.py}w9+8cxXY%ꖏ庺jkI2KL*}<}Iׯ,F@ E{(2 7iVyeV~ (yL"? 3/=jE%G޿.MB?'e+/|U¼SDSVy/ 7A6*VիFɍGiFI5ȞAj?Ͳ/((sS =YJTygIxGIx&ETKˏWٽx8\zmm~0*H*r\ϕry"v|/QMPEw5[IћbHiS$?OggE=%%,Տ%F o ܁iZyV!N Q%0iF쉩6(PJg?Wu/ L;jnKNEošȼ`_;X%j:i[$Mu $;94y2F0?UOpZE OJ5_n{\YE-RSVutnUgrUĹ e;kkխ"~]yˮR7o/zAz8i Z6Yy[](+}VVBb hyvte;8ڔw;_&^zO?;tզ5{-} ~XV}U 0Oi޳5zָFjB Uu3Z"YgfVt5f\60e]Ό9(7q{NtQl2&ΆGJiRS/Z!@uG gU?Y'uX=er]V'=m /+[5z h*m.8{d7S۱ߞi9ÃJ_;zOyl_+hz.)dlVK::h??բ\GWvmKqUV{F5m,?6T5֠{WmV_C1*p-)o{[ε.KVzOMɵG_1_! Z{5Z뾂{=X-_,`\oM&3mYdLX&H$GW4fm_u6tπzUsKPnFGqgѦ__oUd+j2 1t]n(0:4lpyqebVi\u}6HjyhUhM'#vRC y_u.A6ngQߪF3Y?uiҀ=Lp%zH, jUBҿwԼXkLi݅jQk4ͷ|s{{v"͖ 7w=o*?o/2]`؆NM]Tx?Y1}"I c-O7j6ڨ/aӿU=(V=lbe=A{U4w7QN]a(4$؃_9TF{)ҽn1E{DYm1˹ IPDE=>{HEC:{{rm ׻ evSŠtpk!)jOGMWgՀjUG;0߇%/ hr#h~Ne?~-/sQ(N:z7أsjP3RpOc~G2K>'`>,GhOf{%\==YDaf"ZuZ16~9=d/˫p~?ރ뜪ڇtEbDYh-Þrlg+ V]{<%M%$Ine鲅_=W^Tۑ9w9ӝի:_CGj+r(޳W*+Y(=Ka1VIsGl2nOߚgЩ){ &d }!=CZDq{7@# na^[~{ٍ cN=vϣ[5rusˬ}1<ԩe XufR?;mze^|yA)sAQWc6ÕgoRj7LtҀW@T,C y3q4ZVhQ,bLA>W֭~wK͓MaLDE,b(wA e j(`s/YjG6-"n[ٔVRYXh%ȧ)7,\htGw˼ R/2/+[/ Т6ޕN+̢;MT~[48Lʮ:]E 3/yMtlO/hm n0_>4:R7+ < ~#V o߂Ȱ)$XnLꧡȯ5̪-D/ZBW׳cTsb3Ӏ]4(r q ܮVWWTV8)~~a_#nB/ެTJFjAc6%* ѯ"z&m)o`? OB?_V?EPf[[mxX^3@-l S {Z226-̶-<ۨ%iZU]ߘIN~[niQuXT'OCMT95Ipj~WZD~ekZ*Tni"\ʄzm h! ]WTU?l^c4D7Qx;O:ߥw+k篔S;R5S kh[hmj\$~sday\Ezʬ:NjǦڽt9IՁط_M y=|gilo(,Ty{َ҆"UO͏jG[o?(| gT咽yVϹ*kF&= 71RYx}*5m6ۋ-R14AQKm/4JyEc8k:֩2իQPK\=KofV˙TVMXO^ UI^5^_{^OUTx4zP |~wzi]pplu Nzw/z8˓zoO>*wbu.LU EEcU77ir84M6Mz?oʍrIOU.O.zg>jہ& g[!9jH<㪿×ҷO_^6}y緯.~일|yiOvF Քј{R voùhڧZ/O>645 }44py؍X L10Yq-r0,hj}g+jԠX0Ȧ&6 7á~M6ii1CFF3E6A6$CHx{kDDI 6s\Wr xDI5Se(`/uq3wsTT%ӞehzfxU\ơE14J()s`'!fX"^`ѨN]#"-&$PD,?ޥl YZ8pS;:"YfeHcRpAgfƩBY>*P$~K2Ks#yPUl١kR*Eʼ1sSӿ@O?1`\F :8_16C{jM2 !A؂6(V,1?t`J*;up¶q!%VB]O$N'&Β2V1􉃴8_J}\[H:w569ɲt_S\¢w)>Kv.et;Z -.l*\$U;=4߇YO@]~:tE\.DR8#r+dMdӄ6$7fz* oO'ed33\y$@&6Yi?1x(,w$V)_dH(YTh+ph~n4 ;9f~>o6S*$M+V3^a6_?_l@:(H XWBZF2/59d$Aox-c1c_NM!C x6@M aC#X^±6#tY='Ig2v/B`n*ت"i1Z#K k3P۴2zsͲ1WW @30c&J?dgI"¶r'U'Y!ޔ}c|hs^Fb%4@Bg0κPbά<`iK^j#tJ95x_Gu8 jPʚ _kQ}[^Vp؟:"ҟ:# qn۟C:5XsV\8g CHb}nf6/b'1ŶF'tl0cM"esz*V=6QEj4z rfщvzmZ^TkL!ZĐ/-9]fQu>q%+\0QG=l>1L}Ia ."/vq -/>myi_Iv#erXN)zmSL,"J =\G™AUXƻwGL"0A=(@R,Q`: &cv̩ p (|N^sht_v-58xxK+oBRNp0V[l Z:Wgb̬bo%Y;5\Ix'WRcq .\z*KlTȿzQFG:vE7ϬlC.ܞxgQww+,4Pj'<"M&vpp`>AgCFӼI@a u"SFJ[޷QvO+RE4>;$\g]dhХpD:llՑm2J28%j'-рNM@ (72ٲP9B c_Bwc&NJkXw*ۨ(C! +v{; @t5Y}Q6֔aMZE֟yU:6ePRZUm{0P*E[ }`5ܮ -vՖ9Y`,uh暼jw.ghAYq;$09_FߟqNű{TlS8>h8~o+`u"~z2#:3>3k܎0ٞw,a!Pq:Yc:s)\! :@eI8>0Q ԞU#>R)gLfT?jfT#n} UK4Ļy%i 7! P@k=lx=m"+xBPV^`M7i&I@7أ}= W*c,=FCt0{8<{lB:0ń(Bs9"FS?2nfyL%| @oaʶyes`}8Ȧ>}5Jfg|*idDgE8FXQu咃-#*";V>B!WV|."=QV{bm;/v'tFq8-;h:BR5;%0bPZu(<+T!NSӯ0qd0]F޵`c>u3 ,i֕T '%De C dwL57ӟEŨZvVN*sGqS$RU)W5|م3@`5gv lM`U3T'4@g T1mYVOmaʬF;qr}~u\ ',C? {weIBp7?k6!:P}L] 3]iokFm^Z .1Bl_I}H8Ζ9s1^qVa,dκ&#tDLƂ Mee qMΔ]%pc κ`NX08U #s3C'F)2YJc$YYʈ3Y:EGQcYƑMBe`AXA$"Ym$Dih8oQ: 5C pueJXv ]_},NHTnZq{c,'rij|l|e9d} * 8VmdK}O#1߇9|3 ܉2l93?6S/  -ewz m 33HC67RƦOò[a1ąhgtG?4b,䏝FXď4Oˌ?4~:`) TO/~ OfƍR̎rۈs🥋Ӧfi+><{y <7[/2n2x?#huGp 9d\rz0԰=${yV:P#Szó&';AE~@'P WO`< cOHkކ 4PPQ>80Evc(=DHYAMSld Ps -,bWX[PZZP BqEoOO7~R#ڋ> mhF8:(ѱhr04cKé[N΄=ؙx_>g8i L}? N4z>w|EͮhY;x(ǰX7F<F$&$& Ҿ!5b-!5b!vg7l;&a[Wg7uJdzY1pvޚoqrbЙ|.eکۘ0 ׉Bo>v Ufܒ P&6FZpX܎T#OJg0H zBoQ=*5Yr)^w &7"+ƌިpW>~y@=# cs'ևDKX! pGXel#;<`Q{(aC܄O;wR-*O3sRctGi7fܘwMGHAeqZpbZE vQHC"p&B*ݤi"L?rPHKYQ&nA1pf :˔P;  ^HTx( g&01ƃFWL39AbznE}hI_%G O),d2κ_ @]hB<{!!x8Bg0!YNj1z;k߆ !!i˅r,.1$((o5w=B{ fZ )cjtLP(*/0Hyȫsq"`d~qUC D e~"b&L/?CP$\58m{(8{'sc0"ᵈ)[5GP񬢂"sdR_#Db!0WqKh9g8%\}ݢb3h./u`HĘ3lf@>Qx״HDp KU&m']pg&`k&B T7A xb& @ *ل-| 6.cIڮ}u5< abC<ȝϹ (vy`X܂Μ`c{B!%tQX &@L9#С@PFZNC 9Tˑ@CoTc'T!+ط>ͧ%F=o\\/;-1D6z>-\H\Zo{,ZO֛G$DM#\{Z ck+QT"&!HˊjD `&}IJҶ ΍,RmJ⑎؅c(hr{&P0fUc0v HYv6ˮ|e5}puK>p;Mik 37k),v8 md. zV>=:ƱvLm duDH6E0[& K%x#T"W"l4{EoQ< Eg^y(F|g+/!UÁC+nj{h.Qw%La4`*B, |07WaӎmPU@JJEL[CȾJӫSX4(WAem>t`]yt_݋\^oVHトcE:`ch}Bu-8H̶I@Dd4#/D ;D-!(Y > >tFaQ n9IQSc }4C]ʜMIl=xct\M @XԚIDCD+8%NFI^%1ʣ0'/y~xU\nkVf!8Px6J) m)ѶuY:;f ÑUdmv 1cl bĈW~C,^ £J8bgz&*&wh2ZZ,ћb+ۄl'qv쐚PcH5 vwf!8͘"3aLxMBH^*E@JldA7[ ▐f0ڸ1&~ ,҇?PXiUPѠ|Xt`psv ˯A`/^%|UBr J/bSK-$Dz|lVr1t|s|8?e5/U1G B&ΒSeG-PÔ@Ǖ&q!!-_Po;qq(*2լ;G8p{XdEJ5Q=`3ZypݧAFr[{쒐v9??o!`8L i:pr0NևK7Yy%3 bU.&6SZ;+&T/+58d8jY KP2mw7-WAnX5'q z7l@N# ^CzL`8VBJՇMAκPzmiDΓ6 s!PHBg]0I#@Q{WzuX"JB}4W$jl_>\WI:$b㪨ch1z0u u'9iD|I%HQAV\N|sRj@d}Q-q?'!`қ#aKىC h<֔>!ݫ₫`I(8bIye273l:`b_݈۠D/ :B=pyoa8ټCy`BiK!%Mq!ܯ&Lfs>gTBfCp}I35cf5DbHN8-LwUIP;X[4sE YIAiE7l)EDtaԁ 2Ò S]2C &ҕŵV;NKܧa^ą'BpP{}.~bE+ ߩ%o[&B)5&&Qs+Z %Z-x1%J?ExD,l 5,mXqkHOUP$`0+7.j 魎nLX(- YG#:68EI"Jz_xdh5pr-sL~ԡ_;آ5?A0ąQ !  6xl,YxW{wrw3%HSD p }ˊ>̍,.&,`vۉ w(;ppRA#UYbͮc\װͺhYѐ`FYc[W3zη#]űκ`\F)`>8CFxtڅ:#DքO:& Äa3P\-?LG#8o mj*!EMf ǽnK !#}cV07N$_Ayy`V:[`^M`DF:HYSU>).R@ "D;&p]B9Ptoye*HLȘh jѭLO<1~t,h%b(?d"O"7f@OnyA#Brj+NJ6!ÍlcBe74JƸv2MfOJ6S\+}Za)%N!x; t)R)(dC υ0L)ֹO[C@yGsL`^}' b]7T o&f6r5PbC,jr FRs9[jcF* kK<9bج l>$4D. zy0aT!;~bn-Ԉtf'W,SB jGh3_4%=4 b(7NTdXzpz7d0}OpzF+`0\49r:$| rr=_ڥk/_R`GiXGvN=RHg0]6 <](2|CD!9ڏ (4aTP1u|_p1 78t_SCȆY84R+Y(''xX Q9|뻢_t=cծo[8CM~VC̫R w(&`l;TBc)EL0 / 0tNmxs]sJ%4Qn(JWݝA_S DS$jKs8qDXƈ'pb"P[RwHMЂ_nϧ 9CQ1lbIz3)dy,N;bcYi?nkwSI6rƲx '&1c}R,+7Q"x~٨ky9ݬyɃ w(4/.XeFТ$ a=lGM`_ު3%D?ºVxxnlm@.3¿3*W8)˜`G8O.ݓ*BE!Z*"o?xw,=@8eߘ;XFw(@\q'յ4k`*̑ΜP%.(i=%He\i3ttʋ[ J@aϭ`(6-ҜxhY`+*`[ űo+AؒM!oD4EeN iVC:t(v %VF0 xS"ۄ_"#\;(:ۀ)nADB>Qg!UtzZhnSw)NwI U Uې!i_RRF\g](\ɸYsDhZ]4i[[pKh/ gи|ɘR'fy(0,ˎe5sX8Eq޸>4Gm'>!g5|Lp3<:BIi˹D7:&/(1`  ̆Tc0}[×Xh`:1M) >f;cTo3|Gr4ؑZ7r@$"QH .$PI=zs9w8G#>aa*>ćt3E~(GC$Fm;:Tw/ dB^SQP^U*(9C$a)^!oB!XaA IVu FGt <,V'ʎZj` ;hp%Z~\rխ8Em#H S "q[{  9kj"'6 @tȘ;>y`r'j?vq,z(񎾬h],*9\TFi$zgN`ޓ :  `FhR[3 Dl0jncN=i=~}M{ЍӶ$V{e`~頕1`F~e:jaN󴕟ɌRV~&;6턠 |jGF~Ѓv\p^&>>MBAz+~(a)|郅Q:Kp4n[Iz.)Ȥ|P>}x6ϣ_oy,/2: ]8y o(в=zEtW Uof罛^oZ(ge (6~ы4[~3Nyܦ?o/}X5^N»HT0k~m-Ǐ?zQUY+0Ey_=e0p.J6e TjӴ*'<Íմ]_PKyVSD#pip/_vendor/html5lib/html5parser.py}k6w FS{ٕWv۱?ʞ}Tqr$HԒJ*I=Q< 4Fhtϋ|պ ggQ\Ee+r~YrIuL9KqZޛU]l2"llIxe9$=^ڮ0&YUY$^dO0+|3R>MG$۸!Dg2ͳo")W񔼼xJOG$jE?Rx12MUJE$%٢~0U\,zEU8,^}Q-7YEE\љ'YU7ezBT2%k]V$yU9mR ~=?!Vx}$(ɫ/Sn;wތ#|<ӑ:>yI&Ţ<h}狪Z=xl&'Xc rW$OϟUr eX<߿׷ޝo>]\+! lF9:u6m/|} D >,#! QBDɟ|L+|nbɽ'%ݺބ OrhTO21%CY{$k>n8C޳*^j}LZ%>YУI8yاC! +j %T8)Hl! cϣ*/ELzsJaþ$cKZ=8}39C6t;5H1ι|TPl(L dt# #t_(K,EBg%[Hz K⸆E5cNE01BEj~͝_C1JFl,HIb [a]ѭ:jF cᵲ!Hڰ`\2NbΙ"}SXyFw50|\5S뤸,уh&KJqHy9U\qQK+&(6{ X6ułj@%#(Q@233'xA b?xZ=>Rroђ`")NJmSWn lBAV+0{:8'?ӭwS r]\o$]@*sb.QjKh _ӭ)5Wq1t4al*KU /V>U0A*`{U+db`4@i@Aya" dY]㒱#*~6c=|fU ' p3B/іbN@Q:~dy_}C xf4tu9}y[|(ô^paU?E@qFT(Pjf-i<|:`gpć0&ŊѨp]noqVty}e>?F+4RafVi2E0K;MUHC і/3vYҍv})y >@y d^yp:*~gQ+sYSfRe1;^)ϧd]+0*I@rߪL$z,sVb`[Tj[{Q.YY :g]WVˡgNǏ>K1z]͍ eC{->6)myc,9TIgyT ZS,_PU qb- S x{-x{K rxVhLSiл0`C15:iTO@2ϸO$-nt-f"[%8iB\s䥇>[ 'P :j1h1 Nrގ-;)E[2C=?P ^ U *փP(7ATm=Z7t:};5(=d;5J:}UԤ*/hWW4M6~H.X/ӼD»ށs{j=^f&%Y*|mzQVx'1`+S%CqlM覍??3wu1_U[DߔJќճ~zZQ8~v.^Hbc¢bHXaB&͆5 ICmO1J~ڰ$s_{K Ժˢ{Z  O!i:fG}ʁ<|Am,*sngp~NIN'H8 $z,eooQŽC3tWTBDCep0 0~q}Cm:>GըuF ?#6Xڪ>b5ZkCa5`kJ#Aqi:q[gt#$A?Ô~]oKD!2/tч)N' tsٙW23ov1CZA5rnPT$']j޾]}7+{@ i&<ϫ2OO,"_x<]/ 4hJ=j[qJ'О,Ps5tFd>+g7e<qa/tfx&~n|8Qkj Pdz2 Yy"_3r_n V<>xXź)510/aT1#Ej 8dkhvu TӤq*|l=!҆a` ooAB3Ԩ;}iD>?y󿟼 n IrbYklx`xMkphyO\'Y `0 ~EoNQ( p>ؖri=f&KioD O*./p XC泒;͊T`J`< UQtI{QϐȣbYSrS?qZ|ŀv &J0J< pDϢZ(B9>9)>eϿUV1^RH!~w|k] XkֺZaγ3tŧ mW:N'/ `㮓xqN5 C%czQZQsO- p+T>2~yyXvH `L p >1; F,yE8WPUnvvVyEɑ.9Baw =?yIGϭ؃Cd@v_бHJ73蜮+2Z# C)G]^&3T@[eCu[T I ރ4߀H5nL΂ Ž~x} ʯHQ$&n rB =~CIpTBZXP~[ZQ"C͛ <]rSZbSJ|gT݊p)s`I-2*WSxCqk1 4)4.9-Q ʱmF#ZAHW.@"Cb"aЇ/?liQdaKJ `ۂUT̅WV3'V!hM}>QdDѡM/V J)CUUl[![@,H~Jq5AӑPf G`IXgW\|-aQ p︭0KtSR8`:jI( ĮN!7ӽ):tJ ;neF@"FM%nX/9$W J*O閮l/nهm7H߼~=;NNNVj`&q'R47rgĔW+կ Μ-54,Awt_r'W跍Ƣm[RbA؋[PiۉֱLu S߰[_LFs՚*,]EanO V?V_uѯtNBy1p[C-(VL']n}%hpQnmeԲ@bR1:+h\e%%)JH02P$*7ʬc+9ⲎԙrΈlSz q<(tI wtx(zw<|уM~*.9Ld2"hH^$+S@)"i0&oǽX4إ}tGCO&9N^s 6_՗T}|Yv cEU;/ L #1 L2* G4`{~/|ؓFHir r9Ya5BX|%$=*n`p՗ i%.޿1EJ3Wpbd?M?=|ttT)`Ҕy P- P @`&ݤrElqPjԜW@r ԉ7Oݹ.NfE<9v@ƪأJٯ"Aا.jGI莦ׇ"?=Qk@D< &\&nyEB-_i]Z1WbA#Jo' sbܓ"GP#7U JE8cEJΗ:@\=z>ě[j&u1FfFUO{p=m|;؁0v!eT4CF4f-F}]掹HgtvpM/j ʁ#욆űqZ]1:wIz\ŋ6{uI;7HsV~Vc0/Rqs Ȃe Vvw1߈`Z1o n`op8f?fB!|X=nlj|c,h+t. Aҁ^k犛OVxF2F8c40i}{,If*pr[S4{F&\cNߘw&ʆ'jgL֠Nԥr820ȭ q5s(Tw[s tV32WiL>=5=lPl g;]kcrSQ,T`98Ϭ=c]g{g*G5`[4-*ut %b~ĦXh~늝!_tM A* qx 2q[ 9qh61x_WRWyW) Nq[v.%Dn͔0OgEeYLy% !I0gCtBQDΝYoc?R ƍ¨MdƏZz͸\Lmɋri[ъ2:aMR}5%74 7hd*j]z8IR!q_m]΢{|+,A)X*D^DڔQQu/wVUGj׳ۺVnЏ6jOԆۚG-33ZpAq/UMbpa&H=%qaYʔk!C0{_qSgyD E"a. Nd%mĦoUvχ7vVO(p0K%:vT Rй X-{VOr$D]ӝ/JB_SV >8!)Y+C59Wh =(RWtz1guY8P!K"HQ MfأܥT$=!p&ئ3&Rn2PM)\'i$豭!VƬOyKkKFޗ>g`n\AQ z^͢`P8rr3mO:էPKs5]aT ,ZP=2,*yXPgEp[j$z/{!qT_nnjx'|[wt5CЂqN/1|xl[Þ%ˤ mĞ͉7QKΠ^Kk#T t>rgUcr BXg-W|5KBػlW[;4FqY`*#HbP1:<],xNcNLu(P۱hZ?m%N!PB6s6`#סJ|8PD  8`sXQH|.>U1$kHT؟ǞTXDX'4{\Uuuc3?l8 Xr4Zv>%:&+۸4M K6KiqM#\6!e:GOҐ@YǓb"gBdAp;⣴S_)x3_]EԄS$fK*WGϸ2/#A.];"sAS{BS1|w^?WJ( 9WȆJ8>"r*$m`bm F4)1'pBӮ=|zI ḉ3Lp7ӥ'Uƺ!q!Oܮ;1(3l྾-$V>2:Е >:<ydYYibQ澅UocU}ц;*~H?U徒n*.N7ĀQNS&@0oʼnmDh;Ex\J2Φ6z1aQrx nj2 3A%3g鵘[ALӦ1bg;B k `j1myCʐ1$`fCqj 3çf͏C77y'3s:F~rCt U.IvSBݧЯrZ\y3OM>,p2sSr\ێ(BӔjU\$T<uT-33}+4 ;ooRCNqtg J4םk'PM.7zNg)8zqg$uv}9^LmoiBg|nV%%b6k/?3 Z^j9~ ZAv˻CԶ1DL 1ޤ'(?a]߅юE0Nid"|@ͬ6 lV@&A䊙\ZGݭ]XZwVꗐ1wW w6{wnKu]bO[H؇T7LE<'_91xAA-|~ w\fb >z6Q.~گ;Qwkw.։ov9蚈Zk4TuֻggfpE>-=}?wJr^ ܫe( se1–Wꋶ9F`qiyEm\{M{{Cm̡‰,WwKJ]4^?ذ_avҲo(bE,˓͈zz?q7`}>0 Δf))$ .?`1/1+rRfE'=AP,?|ߔoxFVt1K&88H4>'QFc>'Zk&6\U!J! YJt7<# J{%nbVRbI^zBSS|?iX!PT^mmJM~'3jmPxiQ h5srAʪ^Чevͫ8r-6qVo/X15׹`}ZxUY4i]-`6\w|0$/D!p$#t axbH0:!z Axn8-._!~l-Z隬I>Q]04˘َPt~(jGH8&vw'8ͮc"iJ̀e51|#i*v>[Xt{c3FXl=ݽu2x?>^`hC$Yd EbEIӗuZʻ ,Hz A{ 8:.0ds}\ȿLMu M[յ5nʫotM6)W)He*=Fus2p/H 1K^W$5pd>'s H`~?~QO S.1`ԔY?4b#d~յ{۷: vW2G=QgG#b~s„gG  ٕoq/G3Ϫ j|P.M} ΖK黱$5aگ>64|q 5n3Ը0 ˂@%B ]߁sAJjk\ByCJ)!-/n-y@JW4>..i)Ǿ\bDW4:R!0ȊLVFx(^Xm*aW6C㚵DȟK<()(!Sn\q#pO|=c{ Vgֺeq;8֗pOR<H7eH-9H徣U9IH ?3oo,bYJ{xvo6 cXvkvڌ}* mpXcf0Ku7=6П֩Z]w̝3'7Q2M3r9vg z 1 C$^@-bٓo#4nڮ#uH axN'R4ΘŔ(NyTK0'㥖W/uCYUdz܁A-URB+|o F5DK*.^pt'*i5c^k.*w @Cz;n)"H#WO-c]M5%["s̗nC4[s㶛ND{[H' H?oγٔ"6o(82Q(u'H%(L[A 8Kfdkp8sXIP ':iz$'Дo+ɪB:'t^'pM*frvEdGx9/?w 0 cVUn\Ֆ Lo ؇ϸl+Chqv VhprpqVOcHӧX.?Դnr\;6fc$4S;qHwI9{j5 QKT:_OS8pekk?}$0CI6w%"Sbs;#l$i#XR=1P8|ZlpM H/,(RHq9G .Tb~E!,xL_աB%?†"f:J:+u޻:8B.Bxq]{K=e\, Eh4$ uڎzE={gg/&7wܪbӶvjn}[ܼ@7E߮Z{|m}f٘odƄcnq6UC\[*ntz-?\Ԅ}ejJYʖ4NE!hggeW%T|d6"5p!$P-\jQdV E˴#xj[ EG)HG:ߡǗ??y% ȃw\H-&d[Y=d  %DqG£4YW#mt&vgOh"OHmc:&c݋}w{)&$u؟|n'/$m6BnJ4fAQ ?D NUFh~u'9ve^Y #gZ,2<*d4\R+n}d3+>PXoG+Aj9.\"5D̟XEIuaeoV3LM,0'w 5vC. 93|>T5R#hDSx+H'fD5_jk%u=u"%G Gz3o|]k9z):N0)SѼ{&1eH_5J|xkKQRE! bCr:U!W:rfNl"iTՖ&#W$^رz`1%6.2poέĢ1n{]sz7UB>IVv2" sQgŕ<ȌZ4*H$ʹx:PaP ue1n`rZaN-Z otiOҔ;/{ FeLRpUęLy&%1JE1>I;kc @Yn:keG=UsNy w8n IV͍BSd۵Cv=,h醼y׏=4X!v{;1ugB?^ʌK8҄beRv.NܓX(D;ʷmGa{<&˩$uoN>̦۫\[A?ؾmۏ1YyNDϳ-/(Ϣ|f⒒ ςb?i׸Q5@3J $n0!\*;J+pvӜ.ږkZ}qwl{9]1yhvCV/>$s}G;Q[kCt1Kn:QZ(*л;`K-4ƚ ?#1t[U݋xc0ؙ|䚎EkL(M?KB4 xAڞZEO?֯Ŭhy(L; ]==4L@|C~!ܤ !T oZE7q=vS:xۘZ}̽v{I7V:tf)iCl`>TJNNWx][XewBVZL&.ٵtf.3`D}*qs^Oٻ!e%)VC?m7gjGaϮob7eK;bpYym93Y$k2I h:_V¼'ðk> _Ivׄ(h^рt@cnw !&>=0@YdX5Sw-o0·˾/c5 qg{e$UwWcn_kU(cI7lle#0H]mQ>aƝ &j %6A(ܬ7-Q'DJ.ڠlX~g{iitBvLӷGz07*,vC0kOQyk, rN+B:h(EJ3);8KNMoS9yDv.X0%ERW=` {\IEwK4ZfM YLTQV=ы5$F*[d&=ho칏Wj HO#Lb3yMxe*gQ<UzTɦ+sE$T&dۓ*٧ݘ,4c ͜mu9ڍ)0]r-Õ0iJle8e8pg^Kj=Xqg;f "6oߑ{$ >T<!}E\5<#ɮ9cɴueYM}= yfkGO2DUJyL2iH P{sGeڸZ{ҖYքjU>Z7KqEyX~.sf(Eak'Y i(OD<(ͨ3' THzA@*(qBO0H%g ]ck  ={xe۸mQ3(s/<ןyi5~mΦ1A>q^xVcQ!FQ^c˒ʈW?yOZXBx_jG֚ysU߭u{ r)T9iNi\ Uv*]r$8_S9mu {{6no]1M2"%@M>S>{ZlEbo7rOTh3 ]„ŏҧKOƋEׯ#rPUHUzϹ5 MYM𽙯j,CE?x?ځk2d[is`]K-r8Ua*T>!srI0?Teb"Oug.jJ+I9KOfAvv&RoukUw-%C<\зnЩTwGp҆c&!D񄱝BJGy:2 qfs*v'83^Z46o+&{kW֕!vjxz< m#N۠܆fM6#_(f0GzxgoUmI~XߐIz+޿Ldm;Jwbn؊"7'T\3ؚԏ-DWM甾akw<<>U*PީZ{;N*F?X24^StG=ea?1Y@"rb7Nc6H6t Nv%zi4aV1L- 7#lB9*8XkqPTp(7?nWNd5]C H G_ˤ@"7r,PSNܴK`ZoɈ=ЭHﭓ Ң8QZwCu0o >^5e]#2zfmBT\ By eoZ:v[v4X J웄 h\tp &<>[,],h87S wPuֲ*Q@"~rͶM~ آ:wןnK^fnQYf+LbgklM5KjhO {ϱڝ\ZTif Ŗdw۞尰;͚d"o,Ȼ-b_ &$iޓyiUG$u.)b\Tl[R%qJ}CNW|pN[hb^b@a _fQL<0>my1K_]E>vM.ߛboOrhzY @P9gQHD/؉iî84t aKaBU([%'(BgT$LLP߯c>7xC҂<$ i좑a&"T#$F~ 22!A:߶7VЍT|FX8OIq&IWit!2%"ITKϟ?O2-<0ռ/@;l:0Z]_W(v0fsbd0YͶa3p>JjU9`|Hh#lЅs粍&XӸIۋeT)m 1 xyfF0c2hnLDl>Zltmif'șPU32Y`H)Af!9!MFec֖bƯz<:q&"dqL#&EgK:cA/ rŃ+T`Y]*V M?:|M!u<9)ցebkY!e?6h]h`@x`s̬~Q@@ MQڄdHrlUdzAPAUXYn"OHC{Q j;gú=Qy`Ĩ )oPqdG20LZ\eA4Oޅ :c0`lٕ'$C8HVP*ttn]7WLNbc% x.z(bpL.Nj1Fe~|a { ;qI+:Api5ƢңR09*`B>kDP2XXIp(^K6iYS^A(g[NGXN*gfKe9c9t)?c "(fLc&cu&? '92ae:ֽ0 -d^'vj%,8( !l%NW2x! [CAS PuJ"%).X=6t G\T7 q\-ɵ&İ9lԲF& Ag¿5ƶYVؠ[C]xlPeH qe.%KZU5;$p1uZ;EG8 Eyi@t/ng*(7_SnWٵU[j;}ޚqPAe)FTY Q?JB4hB, X]4tWhu:JQFo:-eb7uk]3+0"Z*d*l+?yM-r2#_+srP=!˵:=z0 ]!GQpPLXP`+YC,`ŵs7Y̔xPBt"WϿz$/5m-QDVbyݾS2 ??#4D ,n( D^G'?PM~k0 .$.#LsT/7bY՘H'cQƱ[ tg(m2 r7u6ہ~[6'7Z ^ŕ#fY̹-;w_Yo[ s :1ea)JxkMpuT⢩{.]Ie%UOtvtۡ95hzzDI::z!.囪UIx3xgс>kV*rrviS Fp[ݫ_`kjýٺ4;vsQr)e}%чb"4Ǒh8Ock_q8;:g^O+ru8̍0$7:dzrO9}Pw9tnyqP/'Ǧ-XQ] զ v#]ޘspVCΞ-;!G57>L./˫{IvygC"΁g=SdG*44'6w1*^3v],mRh~y"zkU_l?PKyVS]TZm&pip/_vendor/html5lib/_trie/__init__.py5A 0 }E,_x)S;(utUcIt;(ڜPl^1ŴU{"zlI6ۙT*{ǐ?\(>57PKyVS灦#pip/_vendor/html5lib/_trie/_base.pySKk0W MRXC ҐCn!j%3#'kJ{%˛>Bu1k;R'̮@mP{y3lw-Jk,Da% o-6!byKuqDV74wu*9ULcM,5RMZ1BFjO"c]5Ɓ F+ٯÉ)k\XUmqhmHDkC* q5J4zÐjƧp7s8U0eH_rP݄m˿ǘ[2Ae\?=K#6'dԣ'wE63L=^$Gֻ rëqʤRQ;/pw0_Vԅ܈T&O5?}WiYh;E^79ԁT1uE|nPKyVSKOn pip/_vendor/html5lib/_trie/py.pyUn0+>Q :E{ɷ hi/JI%wEN@Q,pf9[gO eۇޡ@κ񻀆ɓ5j۠)6RtԕMc]<<^;ƲyÌ_Rc&q_,jŴem [иcٙւQEcu[W/".X4s։ݽ+{#wyviJI7_g؈Ma#\:K&D.f ')jk\G%&rL܌XL0;-:#!b9v8ó& /; X&fpݮ:kJT̞rIjwɖxDV%rsrY(_1=$b]FU >meF JFpe"BFrd^voGIb/Ĝ=ȜHRSV3^<Fl dLoZpߔk t#0-srSy#3 #fۘ)ֳ\ ȶ>ef[Ú[)W>*uY/V>GbG o7r4vWbnZFCȃSA gW3^U1/<5_ P ^j{Raz5\M-=PW][wXQ9j[x?=ܞDT^f w7B9oP%-/PKyVS"ĕ$pip/_vendor/html5lib/filters/base.pyeA 0 ~ >k!G*U,CL1toՎB[_UR5k^`+K^f&6*>s.$ ON͙d}Qi'P%POrc7Vq3<6w2oV~?ѷFJ_u_PKyVS? 3pip/_vendor/html5lib/filters/inject_meta_charset.pyVr0:Ì&I'2bԀDВԓɿW+d 6&NݷV*X5DE*d^F)KēB)UQ.*kK cq<ϋs5 ݬهs 1j\47~, eR.@8 (АSU]0e O!W.zQGЌ;n^qΫs3Lq,v7!͓`F ; [Sz;kA[|8NFKSTJ6>:JU-=(aPt[9\R3HKbd|z=:C ocl 1ճY>23(q ʜ~r}ΞfX|[!׌(C,#xS+D.W  ) t hNk9 73SB0{V mElҲ'n&'.Iq𞂻= ^Nm5Bֽobxx @_nSSǻ o0]2Z1\"PwpU]4~ͰȿOUqϼ9CJ:BjX ygؙtX:sa:&I伟ףӭNk*~y^d!!ǝv֌&G.J<K@bКWy}ဦ&mnV2R#uن٦xD9}W87`rl:? 6xIO{CScͽPKyVSdl;$pip/_vendor/html5lib/filters/lint.pyVj0S ~@.FBanJpUjK$F}G(Zn"Kt2 㬲8QJ[FŸC*JΡ"Q)ƹyn s(etdkKdv[bڃnي%ҚPM4sxV"]X oxkZ&}~(!,$i#tEz~/ɲPyҀZ+ .ٜ6?9h. >2%> ZI?Ō"q̳9Ug%(TFG66DNϾ0UIt*`;we<;Wk`#SO&m OlVb)KNgt6zFֺPMi?f=Xbih5>\92U>Ӱ n9+=uoWcDTE 'ҽg$t6HY=E]ԅ8دh"uwM L uk/"2YY/mCqm<,Rn-Xj^O+.?!xJ]kz3RO0F=Rhn9Έ3QM gydu S<)\$)[zP;f _6jZ\BscaASĨ;}OR4/W˷""+m(`+0OPKyVSu*\),pip/_vendor/html5lib/filters/optionaltags.pyYK6`ր"IC(CC40h^ DwߙdQeKwCHc87xUɖ\.Y^n*vV.sD~\U)Uy\f<7&+1c^-[l6K^Y~g1ɬ.+fj%䚙"b]c}v9{wJJls Ft&[6xk[nX,Re!v\:F)Pq2 q0r[.C3A)Z̙ޝ fB:wlrm?Yߢ@v[37t9$Sn 7<*^ 2'nw,|?TbPD<$T! }sL) 3G˶v#9Wnw'фFqƖEҕblb Yl%*sk v\ɫW^˟20rm:Z[o1 cQ5c9S(q=zAxeOt =㛵* 0Zn-](79*q%%X#b؋LwZj>];Buyk >kW 79tp͈A f~ 8#i=F ֺdYDfm>Iɂf~1fl$U5AVJ.CF02C$TҜnPd<\!syq{׫o]YunZYu~xħSz>uOv(rɔڷ+FhI cP:9{cAPXaҸHT&ܚ[xBlL3` H(8+ Xy+Ms, EJy$Q#cAR",=bASi)>. 0N-0%ƃ=7$<>؋Y0R4[KX(=㊸f`]=H+4EřG"GpX<ze%x!a$nԃe^EXe"Acl8|l)[O={'<ʼnGEGn~=rsh̖>9tkH%c5!>(|^O\όccR~} D^r=zx}% }:\쩭X!S?TdaJ dO #IKqICD1:'l&,hApVj-)=(_98;g~]+prHH;/>IwJFk:plT͏3l:XIDkG 8Hz YJOmQT@cJY<=ğA Y39En"5HB(_ъ"(V<R^0aQ?--)gEd6AnPfqI(%?>)#d~nYqRE5CS3P6âvB3h=C(uoj-*3 QМZV~Ool`3Oz5, yi,VYnAX l0K[*[ea;, trvl}1R IۭutcmQ@1k0gi0V:T`TQERӵ  V.nxx˝Yjװ)gtQ[;; ؀͡K+x%\D7²% Lx f 8gkgS,}d ,+\xU‘J^S<)AOCdjk'av&;5JۋuI傰3yN)QBAA)@LrGs6ˡxc*]+E `YS]`| 5&O u'.mCBD#`]BL;DoLLgr6CX"i3 T)!iFN s'9vҝe +R81LLz V"LW)BOns89q.r]c+Ē1u;[5oݑ ^`E劲K LL!=2;S]NEy3m},҈;34Cs6ltC1e/!'lԖ Z8mnT_,.\%V&2DZfң'H2d%&ixH>"_i1DO9g0)`;mJhdL. iXJX`$u(fgz:&4)<NJZi͇l?eI1ߺ^4GDzx$.\Gdivl :|`)4 ,(x\aÂGfBa!ak%QW "QyS!w!54A-\U^.3-ڍUΌ`f~"HE>(L39xWcTTJ!]8X;zYVMFpX"Qv,(uxl!);\_9ԯv 8\QyϺZMG*wC B@IyƢ!,?К[JdP8~[! .sS@p*.wfdV%K~UBFeHeLEPn6onp}%jE6(,|;-8+3H_Lx|y̑{n" R)[Jt'np|O/kj,5u{q?p hD\IA&T}Ҫ8j-5ټl׌Ub›&#h}[Fµlf-9ozCE%5 [q^-ZÝB &yf} I+Uն{ 10גɼ%[ O hJmI=LdGD#9˞ǥB^6Id}6̬rJn6ֆɩ 5Iij)ØG4M?x 軐a+ ymIAZg4mtJH `.<gdϪlt`!|;J{xHWJ3w6d[dċć^^ e3h4@QsJ$V?qyjlpŒ\S ڬMOی Y,XLWǝDAΒ>@st^7?urNs|~G!mzNB;5|^|N+ka4U1v,Hf7`ĠT`aC%nDziz ԪlM M p]m~}yX5_e-]WKTJ(W;eItx}?xdkӌ˦>~Diso|m6l˱\;lhY H_f:xEY 8l `ڀwOhRCC2%'2[} v lQCT5)MI>n:o]mӶﺼ 7yAtWZ.,Õd.+ D^Ug/ub |d^ UeD$e|lŁ " 9U0ЉۍŪm{,,I}a 6ҏV~_._\.O0Z+{%>|xwpq9.{1ls=ad:1:bxN |C7-[{?^\Zu_޾ɇ^j_mmfu7/`-SiZӇ_^meg H7@T|As/'f2~^|D"e/.V4x~ZMEX?u.ؖ`ً k|os/(W_?}xΆɵIXJQmh$8r~*m S\ʸ+Vyr5·MRkO6]4\񹧭O׶oV5YbP'Q&okc-> +s3tZu!{ M7Zqwσksu[!;:L#dHZS4pdu.δAq0뉩27&S+ߣ PK¾լYcPxBLi.WM7rǼD4C#,NM97!аVT$bH /0v?LM0!`jЂA]mv} tb 3ʼn%:ZVUv|@o-z ytc{;2'dx}V`ܣ!'8b^2X|NѭӔIբ,00S$$A~L0fuHK,NR=ڨ\Ur2x}wZ}[. Ls-p^İaXG[UNqKn[W}^5?]^EKF.5Z]52o}q~t:qwxC3^]uud1XCl!w^AtO%JqgGr/c8(̍JO%<0H `J8K#&h<%]"4B&|9qVO'GWNo_^OA06N \gTuW+9 $H0bKuW A7TpW QſqRP ڝNR-֚8дJ"rz1 m[Weps0iR2Tq,$UU:plH&`.4O X98xGJ}0R5Y3A\*U7MnÓ=zXU.s?㩱h>~ HչyM֞WF_'Wg'=_f7׻~|uw7Q4\]G':^OO&Y̱ ݣ)\Ͱax:͓3x4khdzR&:LӁ>+MSRb ҂`utm,QS<>W|\^ u*g+IsVywݜ߸NC>+V@c:<:En DVf?T+:~:z_/ﯣyz^_VO0ݽH>s}??s(m344|b)9u r ?^þYTPKyVS`r*pip/_vendor/html5lib/filters/whitespace.pyMo0 6u@CZ,= CU,܏QFN;`:H>|I顮яd?AQzZ6ZIV(XXpB9 lv^hmZEG:- 흰!c=\彑:[Yrv7_ծ^}~EJRapSkpJEY[ο hqηF)18txGN R`O^X݂k$#ƺ1.ET}D%V$= [-uPuQvXCD>vƂ7Q'QCLq{v8.H8L|h[=a|Y+]Ik3S]n'U!܇qWz;}[sX[:~8v<:{3[ӎ覞g1| I*礒skxGf=O>KT14~E7m4vcvW/PKyVS9qZg-pip/_vendor/html5lib/treeadapters/__init__.pymQj1)l<" XhRBvw`̤IVlZfg8+U.`0‰:hGX{Yi!O%[0&DTjv Sܒe 9ݍ/_zgRHY sEJ򭤼lLՁLQz>Ɏ:_NVL`C\ARfS3i2#XQ =|9CgKxqC PKyVSR)T+pip/_vendor/html5lib/treeadapters/genshi.pyTk0~_qhl$!Qm9jKF}:N8-}X}J$ymjŒxYIe>iYԆ%9RP ʌ%7LB{^6LRȷ5-Y(*h !Z/CFv_,ǯyȤQ♩`]GAQY5Pw9܌=430D5j,ŋMkF1˔3`7R;w..7췁9bw%z{fq=UB|GDⳐdqMB w"Ҫb"[5 4oqCgEVOɅ -&o UfKzTVQ UҘ \(ȟ;&¾~M暽jw7Qip|~/`}k5Ė#-jb-ķXqPȩ}ܚ kcdS%BjۖD"C_+ 4`/QB%\{T!7({-/e5 $꧂wz:>i\3K~+5rf@ L~nFPKyVSiD(pip/_vendor/html5lib/treeadapters/sax.pyUM0W :CC!-`8Q#KFYJ{GAaHJmMEQwX Xy, *yNAei*,hrب܉cNEQ=A{o3aKӪ1>KڻHZ4V.Z<h[pX(S  fkE - T.I hz@+qp5'>7$"R1*zGNJI8K~Rr@) o 6,ݎN㼁a؂!zhHm~3r.(`a'>rM5$9I{t jb/@_/e˛=?6!i/NN޷԰ 0Q, C:ti8(^M6˹n^t?k0:9Q/Ll|K,]}ݏ[\JA]}|!gWե(쟢÷ +Jz]8~61Ֆ1b~n4M(V87Ipxv #8г6/z5cLP=); ,V\P?}*@zG͟t6 V4ͪ,#q,+N@tG\>A&2 <&JWHK,`x Ŷ 695;O9ȇt'3>\ÕݺO菃[fJI&/Iw4uTڨI3 r Ej$2?Ȝ/4LZ 89UNOV0i<߇nh :vze1D J,] }tOpTmO{kKlRs tC3?6Ta?{1Cfc~}ׯ_ke ]M]ju.>tK ܸ%,!R)Liul|uƶ(zߐ+m.OIL]赁}pLL5PN0 |wnA!~M82YFXҾԚLt;Ir[o%f>f0p|C3lf!Xs(h::ߩ }dvY_:O4TpXq%ߠrpdaRW>5xsZ_f'^o5>h8)pip/_vendor/html5lib/treebuilders/base.pyko 87P]qKZM$F{5%Z*Kn53CrRCQq]r^7Fm5m#nolUcӪjߧl!K-U=em-K4>["EPMLj.+RhDj+ճJl<2*qYkјQ[^ }v]lÛь7I'l5}L,JÄa׻V =CFm#UÖpc#Fo '8{A 9{jT%\[x1 Efn2e@dJGw1~rͦL͓)T3aj䠜Ci U$阬vm&EPKsf5FB8Wq gggeŵXUR5΃ lu* ip!`v ,90Fp#a:^x32F74RrBiքvzypf,hv$``jI;˶UC6vKol7$8qU+(v[I?>zKV㱰h c/[y%;O#T#g(` x4fYepf*m٦ղ.XU"(捬L cx@BpMy-T W|2^s^fw ,; $n6S@{q1GlTd==O{htjOdrS Oؔ`vrS<6zF~C-4ILB6t=i3dOQ#BM "qGVm3 :)x4 yplZųQMO~]pçZѢZs!SUZ'xGpM҆7dLcS," bCDeCB3?w{ղr6_#!@$KH>8N>&KwUSP/Pq˸ՂFz5Z?щw4ygU9\VתK)|~ymse6jڐLْ1eh;{_\ ]i{񼫇}-8FO|/w-aCfl*K>:]f72ɯTP:8zP7|ttX52-Mę Tmc(Zz>]fGFH8me^v\3٣Gݮ}Sfɓ'>PH}y`q;l0idi5%΂-,Xz0Mz)~dj- !v_bꕒ؟β?%ZzKIOC):`K)*ԙ+aaH(|Erv؏gGwrMIl*կ(7pZT(10ԍ3"ݾXԒ."J.+2ܮ}Gc 5Фߏ=oΒkkw 7Fj)9DOĄ @ܵ Yck\T;pqzYNP4iw"Ok8)-1[4еUӖkH<U jn+]`96b.!(R&VU I*{ioP+VO38x=#xSIm; Az0rT1?}&jH&\ "ye]Qm86@D_e{Q~18ƿ0ҍflM銯F ~{pD#^pk݁q駎 nYh]݅㰳 k6A hs&[O8 @>QWkQ;B0;aCRxai S&D0I,otBBKuQQb܏kBUY.KhS)ykoM i5Vn&uG:bCNns&`+SYSZ)f[Q;3*l.>\נmEQ[ %k#{m*Y^.IH!لQ\w"(fSv\?=ki-YԢzųxY asgDAfkG.KKcoo]z47l=:h`Њ8y|crHþ;x#QjQtFz7\۱ y@~^":7Ɨ6_uIs/ō^}o";ɇqp|9VxYV@tDi ӷleTi0/S+!1qXys<ܰaLsVnCe=X>r022ѡ^ǿΨ&# Vl W^Ќ–Ν`M0&x'O*D*e'Cz0cU<ZK_x+73{2YGq](Mt7Kw06O$nErUFf5R )8 :}!MI(kɳsA**RiqY '%ܶ |`6O"{,NIgS!$O<@5}8 :١گ\)V62a nIHte}N;ه3l33W1S륯E +$6|݆Vdv8ٔT1.F/MKK9Fu(Da6yJ֑ øL#^xpKlH8$Y+uh= G.=[0Pzm.5C4NQ1H2-Vr%H$;hO3Lf9d~aZ7s/ ֋J.ؒ@K>m THHi^/86Nʑ9t@a" )hH]r ` -z'LE%#C1by#oPD\Z*X\_䦘Ʒ '!-=SPKyVSP"(pip/_vendor/html5lib/treebuilders/dom.py]o6ݿUD^ˀ`)&VlI6HtU&Ij[̈Z2`qə4DWqF:Ei){ިVNjEN z(ȊsN[u::A݉ :2Q#- XČ$-`7q !&C%I+ #ob7ateڪPh6AhÄ.4DF7N )#`~ީ,HR022sT%ϫ߲`NN٤,S/TH@W7ĒF  1J¹Bl]{c:ɝI#P\{9)/QO`Du2 /2rOKػܥmŰ5&.6c&h]˧2(z,ȨAK%q1){P69)XIPi=;dym9N;jr"ŵ%ူKcAv:_,&h,23):&A֫ŶKW{y54PXd_j-jW,=vۢ [a\MIVY'##[YPL>5k~JIh;YA NffnkRUW%®z3ev< h%5k+m.!sn2p͛9CqFax.)E&ǚkGW~~>~!b"ıʴVybMSIi^/ƣrv{P-Nlt6+2C,*,{a!R#ǂ k̈́eK1( w7=_G.nk$[@X'gcHx*ap&QdI[ў:5bH[7Z@c ?5* w$2LuT /9Wy;L*Å߁},ZWS1kӏgʪ*885ޕىȝ7ǭ;hjwrvu!^;;Z7p % ˤizchJ92JhjWføA3Vx5i2q)a}?UL'PKyVSMЇR $2*pip/_vendor/html5lib/treebuilders/etree.pyn6}aD4A@ [H` h3FDij$4'ݗֲt7˦ڠ$Ynٶ!IM]5 GZ[F{|Ӽ*#-HR4Tdoǂ,b$e$;iJ(͖Kq#eV51͟4+FX5ԓ(߫DZ~)QgI@rS/Ҫ  Q 55HeyѾTٶ W8eUW?f3WICVF n aGxۗ/argYFhE]Cm^d / !%㏮73a(\<va/;hzf<uf9-02䦎oJ ~qu$/s$!%2&:C.no"=pDJhe0wx ,:ϻZsZ^"?!ۂL}f"x CX"aEnj7҈\\5*=IJ+Ҏqc  D/2Q?NSS&ⷶ;Up(a7c(PW!K!ш\nq$QQоVAѮI& |c!ϟJohNC2YV"L2GkGXEA X퐊n8dIw^8-n^5Sbh6%4AJa g!&PT;Xe=J$7`0-@yx(Z!wsp˪A}vB( ɥtἯR @p~1ݿ0 C +OsvFl;Khk햅~:d_j)[\Ɩw| & bbS%rTs̷*<e I@x(GaFgǪ*BX4*Hi;Ẇ[@UmTb:X Ϸ⹧WqWBDV}GLHV 2w(I_(xsJӥ]hg2Ocf˧{U >{ V c*=yL'Mg{~K{l]nN%wUճi5>yzAS^g߽{qy0֝)fl9S\T}*~0L' t,~WBPbaoIcotm+S8~e㬡;d 7۵ZΛSbbC.]v՟O73'e&EoҴrT:1‹DY+vWoӕv#̚ hZ#kƹgu'WF)`:t :u)Et:+4 ]!)ӹYbzΕ>GI'k 9z^P8Gi,=>ei& tYcwrK׷dӇc>rp_-L7* :aďߝ?;;7!1BxcPmHuGO^O8Z9Ni[ fo}`=#s;8 @ vDv4xTۚ|ݐe.&X>]I6(D~9koL:;nNl?<  @/5W_ l IOAoS-K;JOlZ5PD)]bc0: 5CvN(Pt8FU:URJ+j|*_U^l+1] gw\rKIv~oIC6H/_sβIJK#<}z늼`g[u`s%ބi-}ﯙc!~dcWg?~|+:wrՕGlZ旿^;$QSݭ:|K(U%n 7sSƬ\yoZs90VH!v668q6I.m'v`ͧ ѶX=1Z ێȉI8 2՜ʅ hG&y0Jz s`*˃~ĘBOK[d{snqƠrr6Y~Ȟ-D"!Ƭ]|OXۢrz79^BG=\~'|kH/:ÒϻvonV=3>YlY!ě/B/PKyVS9/pip/_vendor/html5lib/treebuilders/etree_lxml.pyks~BGT"3Cgfl_s4q]DBcT >5.$칖3b_]. ޕi3*+›ꬸ#&X]1-+Z"r ﳔQf.Ild=)WbjA졝ےl9yu0 *z5M9Lg$ix]nنtִ&ˊ{d#cr%WiXp`LMz(&l+'#?eI !Ve$Y &V%&k& E0Z2mA&)JN&A&ZbITsmPrNkV@L LV,WMT,I$t˼Y,4{xVsYR,γU4#i)H!k,=I8LGI+@ G0 ]&wMD`ɥ. CUS\5O)]WM8c; GҳmVep9YfN\L@&ϯ\W .77[p0=_gl2QV)ɗa@y"4rN8\jɕH ,+:C\Xl02ls `p@\5 ihXæLP}Y8L=I V .V#.ta txVG:)|$4- & Yu VJWM R![ʁeU m@idj돬hUAjgŪ~ *n(4GB/y )_+K t C@^HHԐ7[֔Ӻ:#p }8T.,>G d`P=DҬ(Uz`@W'@"լ*h~~}ާkY ۖw~܉='Ә׸_|v9!DVXsvDc>IȖJ}4Cyy瘣` ϔL$L!/fsY p P~ט}/+@[aaCrS99]{PsԞrp-HD.`.[ z# a"9 Q(M7ѴnNobA^E0aVR]IrÃ܉pgm|1"=*̖lB~#aEn5`j}Ir]b|SsZ7N:lSKK#Acu Ue 뛋X=vVf/?)u뭶1Gxܷ+߉&{N:zy8;av&m̖LH@-ʻ|ϋ 3\>S1`tFٳBaGD=vv0{|R1灇I~h`Zoְr3]X C avufRtR f),Rѩkκ]Wmc|DD܀#. o4C$SC5'_,::L9XC=bapG'2ZqQvGZ&!#nJLLX U=]mUybョIJVׅѯ&wof 3] .b\U /8Cbt6*EKu/;T=󞌪{١\Wm:%Fmg7&}Iɪ{{(3'uv0<:RYsv$zݭ16ysv^O)jۯsC7?q]}^f?i@o2<6ij$<S@K{l0 hF}S~z}?rT@(wΊɘjQ0sT.f𜱍|FzQhaT.:=܊j#7ϖ^,,wM$[i/͝!͈ :v%u^p58{,[>Z9ezufBzT~r1>w(JJ Hk96[`b狝9`gkZϲC _C-x'6;( j}^%ê͡էV*B`[+zY]Y |́x|hu2ٛXh*(?L8W7ՃD/cYC:`~S\~QgFE^s_Ҙ AV 󜥑,/L4\̣>ԼJ\伤l{W?"*?dn:s@wsi|zQJa9¾D{ +C7/NaL-\噼9%^" U! {Eţ [;Nnxg ~2<3rANEWS90| zq%u(2-̙=WSBZoW +؄CAd?MdT_pjL4D5rvlC2p`%X)3ħ7j%@rc*ˏǟ~yoWWW>~֐Rb`Ka刼۱`Kv2rXhT* 3Y/y]eLVf. b<\ F',mQ \+/um2Z @aоT 䢇4h^Ay?}5Z 0' εL3Jڅmy-֏-$Ni6DBiL![>_ѩ1!=*k=m36A0PKyVSvjW,pip/_vendor/html5lib/treewalkers/__init__.pyXߏ6 ~_8`c(ak5:\@g+Y,Ҭ>Ro;wI$#E~ab-% R9Bs]\Y8Tlv\ղ׆C#x; o<V3ˁ!Ñϝ; ?=evOU:i&yJuu'<DDnwЏX{rT lb`}|)S$p܋x0FK|w 1Lqx2VC50 mSl#bo)^w NF:Ҕy3QMa4v·vA`nM)Y euׯYY\—A t"\aPǔQG,gNk'x*޳\¸*pvwv&"cn`l G9RA8:B e0-Š{>;4z(udA"C4\ ?rP՛_{AhKO Y3mX i̤8BblY!:(}TTG|n8{B[dtc+6o2+Ru㙽@?!Vʺ'l~Nj9Хι">&OL WFטJ0 8F5@uMAV7(<ڄ1SN6\6HyUݱ,sX/)^~e˒-Rvv{hRq7Zq^b^2vݐ|(÷#FE} Dr޵} >|X7A?{9}D# qo;_,Oi/2Njb񫓋.~J^ }&݄wa¿=Yq_=qQjCmvL|N -oC;D 4]2ϭI7Cwدp-* %̲p=ir C |/礳*Mcצ;H՛?{yfxT'p훷;ʩՃbSf-*@6+,MԾ7Gn% &h+>KG·C@Xw!@֍ \ R442?ou<6~2/B<hҵX%K7éSP?%)XR G["^+>uo:MAj|sM NǧD(Cjd%C39yԠ58F1\P@h@+o3 *#S0\ngtO1QM>u{I⌨؛jm u/л)=a<00UOzJ>{O>NdLjyϷ7~H/BY"6I/P1eNrRjũH u2Vc 2߰6G0RyGy%u[37lӻc|u?1<-Y90{]XgvHPKyVS!-B4(pip/_vendor/html5lib/treewalkers/base.pyn6]_W",mNѸ iȒ!QI"sxHż*Kd>_|Nvfeƅ`s5Ɉ ӈc.XFVH6G LDma&"7 ݲ|GCmʣI̶ GDnhFCs 1.N?Lgx_rivbvO/ޙ!>ŧ?HO4aL ,ڹ<$Af>^M<-NcJiqVfCO՞VƓZhrvLOW3-/)Oj;C˜9,p.P i;q9'!OD7,!L O dCo.~ Btp= ^0{$]XǞ/# QHʣԐ:^ l-)0ϦRXKhϖ̧%d(8Jow*Y4ɲ4PN|*%hRDA*c;.6R5DkB rf5݀RMa62d1M*6P(䛏N⻨Hvv'gt5-ˀ""QƒWPaX=&d]\{@YR8/^@g !![T)O` |Y. 5dKmdCR&`[SXjBZ̮kCʁaMZQ2*B͢-i^Q@&4Jkiɇr8XJ"*?L) x F82/;#\jyUߡOk$`84PU$t]_h˪k Y2`_6(ˊ6&vہPO`]`"͂\FNK`Uy4bQc =],%z\~Wn9999!9݈m[̗DA>^R8uj uk8w \2ڇ/E.HW/GVuu=l<G x,4 ]i}+A@pZLA⃇Xe\&J1y>'Oog/O'r o0z)W-8~\Wd"-qC5{yаWA̒#"5| -"VwyYm>ԅWL:9pdVR ~Cr|$U\tGSzJUѧ)^SEX*3ZGeCm[3EjkK}ݰ l&`kH fed(3tŽeVa: mF$AZoiQ$7Iz5҈U ԿѰJ^vk%jP5rqn [㚙A4"Xb_ЯK`clpAa]3/ Y)Hb)w|#Oȓ)h]?~OKW7po?>Ck\&@ҐhS Ɇ(v(bG\g$f1OtA.#o C3ǔ2ijK~`)4mY6Ӣ`{ڧ)ƨF$pa8 SbKQ&Xsm-9 8mdON~]Γ뫫9e ]D/Co=X ѦSx?>~$DµiS_շRYEQr}8J2=?02踁{uN<#n^C;&:[ث(vҾoMƊy8b;H8T̚1AW{Tퟪn /H?<ܥ>e]C!|CDE7 xK briwGjQCDg iMJ9Nؘ PzVkl=Ixu9싯b&؞u j>%ay5i״%bJyER3 ⹞`)I#!q,֭^^7Wt--ls] nt.͉Jr)!w]UL%CW},!@=`/.` x0+3? !B snF]Wtװc U[apUhI)MSZLE I̥@uEg䥚JpCr񴜻)om3JViͽ-l߿q9TPak<:#a{:qD(nӑ𾝞1l@=[{5=0}oo%E4oFПoWx-?O$6DK7ogY3B39'R_f== 6f6+و/)"a,R(B`i>7dGJzi@E02[Gz)}NsX]>\^w-w9}./ٌ 8i \D}9]snM@rvynBm]wj(ym_Pz'Q<{:g2V~R8*xJXQl^.Be Wo;yGLtl|M={D RbITy2T@IN'PKyVSxQpip/_vendor/idna/__init__.pyuN0Ř総WZc+J[HMT8bOO,ԲDjբW};`#A$Ff7VPGyɍO`CEwI< 4rڔ7kplٯJnp^<]@ͪnc1LDѴN)E/ȒX6\`L)NgU5F{~zQ4Lk8q=wҳ(J202@.bm#.ABS->ʠ΀Ks*8'uؑR%Ko &KaI}⼪ՒĻNӨO-cM$F > Y@v$?6Mߚ8Q`#dWrp0qT{E ]D<9`7fPqY/ftJiU ׍&T`6MÂ&a!?usrz=g@J=[(BW]G6 VҀAQB=&{ͯ[aGo:2< PC\iPQ$ÊXyFFnvx]S:,EMrτLh ۄn}t(]Q b8(b$NwkFK41x!HJ@|0 TQp\Sa7d20}<`꧶M!MQUf7$bU tfhfLLӪs{nw k*OGb:p%fП O!]󒸁?Py'޳*vj>B+½_/\~<$~PKyVS-hpip/_vendor/idna/compat.py]10 ևI]\I۫p9~{hA-~jX, EZl&c +p%T-vp꼯ҙ;t~BUWIPB44}F(_ʕ -gDLT8A0q}qb7bltI=po@X  Yj~PKyVSTA: 2pip/_vendor/idna/core.pyko87  mn6--ӎYґRWnq=# g\tE$Ze)I4O洣g`a^>IsfNଳ@C%$J}.j JrN%r`INәF( fQ aL gIgJc:c4l,O3ՄMi.+"b Ɵ}.sX ~&AQ߼|w?*Xw!#ϩ`݇LD)'4>K`YLs6'Og1[ \)!dcz#l {Yṻf haqЃ& w+P6Is"hEQ&4XطJ<czP"P2H$H!E{jf[aϯl[ DDق 3-;%]@#_[nxq}ON8 8=dbպz&~8"h\(k$KHxM9 sƑWZ0s2hvAJ{ORi݃4W 8Z׆p 2Rn穾Iz-op5wۭj&ʡơ ]8$H]꣥2khy:/@Z/RFHĊGnC=Qn=)d0b啃xPQGz)h6+r(M׳+qǻSK-V,Us/Ic*ͼ C%?(>^}8u p堼nIq/oUrRZ( שl*/Rg $jkIUi]%y'o~xӭB|@דu6/DI4v&CֆW̶8l6\*˕R^p8qD$^-}6chzT7Lm^[2ďgҚS "F&!obܭBp+ 44VsUVIsjNk+oR-6tj#BG֞(YN-(UW.Q>AVl3zL^ؐ`,w# Gc5wahsq̺&Ɠ|(DGP 7j{X/7UN"sFFܚQ&\*0e[xU,YTMEIKMicˆs(2Zb*Ѽ,\H4/hf/A ~j%+:e#/Rnn\-=7Ԗw =g.sPKyVSv2o#npip/_vendor/idna/idnadata.py}K%7^/qMa~Ec0;!vyd!4f^U#i1< ?Oק]e|~YS_~i7|Y?ӿ }7?>闟7}i??|71//@L6Ck919\\AL O&C6Od |]d7 .;<왞,uLwtG}> nBOе]^ܞޏ|!LosY }j~Yn-Oɣ'K߄x*J͏b Ksw*?+ÜKW4 E#3ʉU3fB_L?K/<7CO'`:\؜_m;ܟh;K2UcLe[ nK`r<'lWT" bzqޖ|]NdE,>~֗Կejjjjj}#|\<F&fpkƭƭ7.:2!PW!r5q oh\e~7T/Uǭod~#Jod~3cTx dwNUcTsa̅Ǩ f;Xy*Q1~w;y|';d$sn,~#xMcTQu4do3W W+WW{\\\]~]̯g~=W3W W_߸ո[_ u%N# 'p 'p 'p 'p 'U2$N\eeH,&73of~3f73!g72d=c)/;0@0Y؁,@v ;2=/{fS_̦/%9Ys&kdINdIN֜ɚ3Nw2b~.w1]b~n!K N ^ *=P)=SBWPTKq a~,,97AulEEJPKFWAګ_xE_D%$%d(*iJ'S*h*S`fJJP`l)fuO ׺>/19JJJHJJ(JJhJ0%t% %L%,%+6ݥSy8Sy8Sy8Sy8Sy8Sy8Sy8Wyxzz>}l}`k`%n#7nL#Fx7Fz#^oz#{^od7덬Y#9"Gx߱waPXwn~"|P]7XI#+?ƊG6~$ƢG j sSbbncn>Z"67"Ȏvюp#;DcƸ #р؁3ܝqwwgܘwbgىpcgȜAL '&g +ȍrgmV{J&`yv`@y'1Dw"Nļyމw⒳睈y'1Dw"Nļyމw";Ny'bމ<䣈(t&&7pߌKK+R%byTITIX$^$,U/U** KKJJR%R%axTITIX$^$,U/U** KKJJR%R%axTITIX$^$,U/U** KKJJR%R%axTITIMLax- 66M}V`re2 \#Iv229!&|,r.4 dxJMos2̋$י2[|rᗻDb`VxFU(ؓWeѐWe yU>`ȼVVy,+&sCxᑍv jڰMlSٰSxur`2ƮE7! BaBo<<$<Hx2=0$Į#Xs$*Y7O\?dQL>OV  1Y!&bL!l}&9䑟>ypb 1 {[M&K|1&L -9%x;y'>NC]˅r-%}Ż[ A ;ȤU8ygCyݼpވCerY:9ovXo@w;)8"fFqug2w68¸ p9nrjP͛C^y#r792-m,x7o2`#7+2VODOC0xI>FmPO%;PD֋]K? HG@&_{dj -<#t\Jք~].ѿ؍Cb~.!>{;4{2cqݐ#Qgz\#3tǽpvx9tg+;7ϗ2d^K i<_v)1gBݥ5_lxH\㙘w)gd~lX%a&%r/ إ__~IpX%ay_r~IrX%ay]%aA /+%bwђh?~jE?؟_]ZO=KJ/~J/ +dP,\%]~ɡXӖ m-V~IX%beD_2)V~IXKr)V~%S2P _Eyma_bCc[ o*W__U Y_ o~sSߥv=wم>@Gnؽ=%zOoHuk?E>~A_zw։@]%y48~.5#%^^AoB7л2 [_ڥ?KNT駢[<>2 ^@7w} >t[vo&7߀_cu4߀ & R~& M77oo߀_F.;w߁ ]w?~(?!?C%mL?)'c _Ď-C'.xGM~w2%ss]VOЙ/đg%r#}zJD];~Hy]@AK;wRҔe_{9o:CWΜ8]]p]}_-$ws_| r0I}LqYP$CIJB.˗|"3!|^(zz nB+ }_/q9 O$'? Ygς?>g? Ygς?g}Kᫀ* _|Iq _|U᫂*|UU $9Wo7M7o o `g﬜/VȮУY~&} ȿ3q_pSBK 8p3"hk*C8a,[w!JrsUh ]nBF|a'W=N^ M4)/U݄UV*a^zU93!D43#7  v5fn$~pt7IfK :,&D4Hr*wf wFPv|N@J sշ4u_h3lBCi1KMbhǙإv؛ΛPд-3 b?OW>y ܄s8^Ԋ^gUڄD>$+iCOI0: 0ݥX8 zhP]J0Sj%.MΘOcCC׆ GBSwh`M(hhTUᬻР]Awx}D\'tRr^#vD||'s6Y| #M|vv)~K]-&w^.ZR@8xּ_2v) 0OyMMx_9CdM@iW ]Y1K vrOT Qrغ}J/1HRj 9H_^,KŐdG&..B>I^|߹ 񖦽c2Kk)kJ|i}EipM^c$Ijk[)6d4?npAૄE\;Sj + )Y#E=]UQ_n$}4O#F [vaD/Cp 'Wgn4,҇,?Nzu&v~V0_px`uNOlq%/0Wfl!<1`PKyVS,]['pip/_vendor/idna/intranges.py}Tn0 +l1V$e. L"@\6_gqiPu=Rzlc] b5ˋzMa=Yܰ?4O (\_ޫ72Ik\Z*Kn!8C* `ŖKpFb |EY%WՉmP$ 6QV%b%MucG!8 u>-R"ԀyME\]ȸ[X7zAuRx1ki R#^bѕ{0o# nrc0(.hdݢ-c"4[hg N_aw ;"Aoqn'ޱ G-'ڔg]dn2)n-7KQym'5x<33t̀x~@>ǿ;THn{jUQxOe)R /Hc7vĬ박#-8h0n9Vfq ?ܭLAgBcPj>BG5#|Pj>§|RPh#jS- ҂X *ZPՂD jZPG4HG}OuY)sO4z.}YJW\VK_u)=!UR>LJ7\ZJ5RZ<-:۞KߖRϥz~ߕR:)BJ=Rﯤk)FJ=RmmmmmUmUcV;|e|1TcjPՎCU;j4@;ʪvR (M{iOL4#|$Dh>G0fh3L& kOW) ԂH b-hAU -iA] R-ȴZP*A]Q|5uG]Q|5uG]Q|5uG]Q|5uGMRiRiRiRiRiRiRiҚo{\q9ڧڡڡO g)%-фN!ϴ_4E C)c!Mb)'R4s`a0RO3M_#`zCg,\">҈3U7|1){ǥ )wR}))wZ}){S9*:*7|\9(KW{؇]b.IW{Rue){W8ZJ1:(PCR ^bC7XwLߒb0="`zTi٣s0=!`zR2/2.O*O*....)4u U:+.C)5|RQj>JG(5|RQj>JG|W|5/Aw\.~5OA~\Fkނ6Lr_;[Iy |{| 3Hn >| 3Hn g? ϓ$7|4 !ϓϋ$'>e-kP5Ȩ-ڮܨ-j[{&)D^o~Frh:ɍ"h&ɍ"?^?Jrlȏ\; $7|4wPd&?h'%?$d%?&$?%d$?_KG %ύ!܊5$#? [1>V!܊>!? [1>IV I>bOg|5ɠ >V UI>*$kA|5yŠ >bP M1Or&$s|O&$G &9I5Mz2Hfbf555&H>y/z:w͓!5Cj͓!5Cj͓zw7ɽ/|iҚ'^{SKp 5oKy"{KΦp'ͨT]LLݫ.yQ[j#Qj{G|ȔS~.yC$7v^zG^=76 &0t&!דNS R=R{S{SoS7K=R`RAM$~L ^# My?FHn'HnCv`µ,6FRe OGws$>"5䞁ܘ2}$1e>:Lr㭑²܄w#0)F9 o#0)# L/(~DN֤G_PxC\I$GlRAߒ#6i|HΑP6ɿ3c$7̔`\I%VifB$l I;$ז")xI;=$ז")xg#5!w6\")xOPe,SYX&4;}$7jC!xgɍڐ{Fm=Gr6 Qrwܨ ;$7|"x( =~xO$;InDY>{sO\$I;$7|")x H $9;$7|FP$\!x$y>Cy3$\0!x'ɕ!c&CN26ց»:ŻKNdBdn%d2Y0A-)H}Ę"{w-Y_"b Ź(&68G@ ybĚ4 $x^&1ltFoד7jc"""o{wfyцePT15a8? i#1"p%dxS]&JtM[D{/?~y3uQEMUTPL'H=D5 > ;~r~r五/y{o uI_ ཭$SrՍ" m'C$Rs\#9:u$lBl#A-m2:%>#眪"> gI{?sɽ{R=TWFg^=:IErn{3{I{Ih/o0{r*'"T1WBqB rQ-X,Bٽ$7{kP{vɍ;Eٽ~ .BQ .Bp8pIm"\wfCY>wO]$9{M*B|JX,(*S!6_̦-BV[ ܒ5YI?AI>3-ådYå;Y^yw+q/ɽ #+{E?Hr"*ܫ~?ɽ!W}bd>JrOI=5{?Nr7H&="{?%1{?'S6ș}:Ir29fHnNra$wMra$o$9aIBrV/+ q9FM$CHI5=ϒ\1jz'cܿHr=xĨ?@r=0Ǩz`1Oc1=̼>Ƽ> S>y 0N' 3I^"1|&Cby>g>1a~@}fډ M5NLP?vX uNL]7z$ڦ~%uwLHm*WbL]7|b2F:)ãGm?nԆyr&Fr& ?SDcnܟcVܟc>ܟ?SVdrfc<r92'=1| -1n3E71v:> g9 !D ⎱!C ]coC zn*!@ =!> ]cC z:!;(FF!!r9yH87vCr&#ܱ<$wl717MC&#<$?i717OMC&#<$?i717OMC&#v#s<$?ib'=$?i#ƨLzH~T&<$?ir61*4I18Oʤ 76\YA摟:ФI@UyEr 5zHVAFkUyZmfm%V[YG}$7jC>!h YytOT&ٔGgInDy>AytON ɣ+$7|F:D>Feg'Hψgb}(!>##gHòψyK}v>8<۟]">#-h$H* 05$7Ho"9!9%9#9'9 9$9Cl{'?_Or4b$Ar$N!vS~Mr}:R욬$:b+*&+T1AUL-R2bCÚj>̩Є[Xߗg˞UEnqB~QE7)Ĝr)_KVI`}VSd"i)CLsjLoV^署署sR̜fv0ՌtVONyrj:KXk,vBc+]Q@W7L린nu{6/S{nN3=N43sʝzfzÜБcaБ`aN3znCəOΐpғ3'4fˡ3WԧKhT/{昽6=AmfNZ㩙cBjI#=sCO΍fǜ"*~r~^G98Uf@ޖd&a973k97|3sDaX9!KM{̩@Yf>gf3ef)13(3(3(3afxms47hd4/C5j҈a=v0]...L(LVaua"G!2e ,'8an/J{K<L9}O&ڟL$̜ 4GXN{2E9E`O&vODKIdb9\L&O&H M2y0sxUo)W/! XZ5z2DbQc=`rXcuG[\Hi3,Nݼ4{Lhl O&O[ OB%ɋƶ2Ave[=ؖ SYz`ڌ6hCh%9Q0]efkCmTaFa&w,qnh 4aRC'H NdN ++M&ͤ*]MNddVМ=&'&)<;E}ʾcf0DM_4Cm81IE8Qt2̮͢X{ND\]Df :x?Jթԁ 2b$2zx5Fr |2}i;j&cNdO6齠j %3 3 @A2mAbC N#1v%$>'h.1'2&f7HL^‰ 'z'2Y ڌ(efij͔8mUm-1T[m jxݚuxmW=Ҡng~f=6Tw'ԫպ5҉L[mn݂ ;68V:1'QIjhjyj6o1HZZ;ņFK}1+ LA#2f#2g>ˀF=r4h`-~pIװa2ob{O2#olf8Nulg$NF" fd#U {IcO~Ft`OAFtbTG?##@F*s`D臍&9ňiF~0"ssgD#B?Ĉ#B?SUF~a 3"þ&Έ F~n2"sF~F1q#O0"g h6l6a#YrWc/J3 w)MLWXU~Ѽro2o*{s˽ro*Y e qccW[,9ro9jֱܓ {r=Y3g6 la`{Q&- X`3KN5փ5l%XWXT6AQ~3um/#p1Kl`D5 (x wrT1fy1<q4X8fNzta|Ƀ.1SүU/#lA[\1s F. e㶥= %vL_c9#Z=:bRu*GHTH^`1F<sBQ(UK.272˝61"P{*GJ8+,gD~$4^XS [7[ˌpԭ.F}F8eaV#J#0uk##V#0uk3#KaVFF8qaV#@s xň90{v }5`J"E(ɗًP3@8'_ f.BON\Hss$xf 3by7qb(S<jqX :9Ɖ>y0H{wȕO2.@G' 7B.ghbYq=J뉣͞'ƞٜD3〝HX{f{v! G3CB,g6;=/byp=Dg`'DHCIxj/𘽀ƒ<>_+[HӘvrBcNNjL) ֘ XW/xx/xW~?'L ~bo~?'L ~k¼_W/xx/xW~?‰5k/il+^Rl%j Wl& W'3t$=%?z =[H(GV,!Ht1gz5&sM&䦆18s[vl+SH9NDGvR/b c<hH(.3lV,͉VNna9) 6&QQf2oNL'JÌ`jD:!D:DүnONC1׹d' ,_v"a]p{M?1PbjYSzbUÜ&4D-a}NOio*ތW\SD,broU{{Tc?ui[{3ݻoU?dw=/[Xc$ ~ Y{g@j-4PkZ ZhR@j#)Knurܓkc;[ oܳ.ػ|͜;Duךz\C¶x}Lpo/e^~wU!:vxb x{0CI]}o-[,>`*{~VܲǬerV'o6{=a޳ر+Y*$SEz',7aU$62` Z?,r{,-p R=pkdA+sx'|O;]틝 xrŀ7C Ҿ׌`VlflvL]#N zoez3["Й0HX(Hc8BUsƪ9y~"ybU}bL>Wc!ۢ\~|4X)j  Z[Kʔ{7U($`^CVߔPW~k!͞/i6HZi#i6HZk#ij¾=~+'-zBI '-nNs"}ٸ 6 qNa`- Cӽ?s nteAAiTa>eJg3{IldDˈc!_ /E iϗbKKUKB23?/OЦfhR<@d_ Ģw 5).jSȨAQbMIu?\?UH}Y!FRPBk +|йJ!`g o*\V~B`kK!\!; ~U B_*B_+B*+YvNY &Q$cѫ.YGY7XǡU,ͮcls{~Zr?aֲ|J,z9˽a,z^r/XvN=f7,Sb-Η^ps?ut9F+,7c{=loٜ8_:Kg caCb{Þ87#mqlo ;PqloŒG : 4loH 65'392PNa1Ӝ\YD%;Uzq\d$2'3҉j'`Wo2Rr:FR `#PqF0:HdL&JӃ~70 3 v0 v2 v1 v3 0 2 nd3gp #VFm@?}B?NF~v1"µ@^n};ψ;8ȈC;8̈~FaTW4l Yܐ6L3bRn $^d[ kA~i*rnި m\`EUKWN#^{ trWC.'sC9ɉD drd;޾.eTCJkez}9Y)׬L7'!ģPܺ2=nݜxDC͉GN=Ďܜx6kz=9(LmBqlsQ({È.xfD W<47f!us]̎ny 2byNJez$#T|s*x>ˈ9/Nq,sf'v`v#^?sܳp\/F.s0ap7F" ܃qESTOMsc?>7h;tN;#:NZ37}' ~.cǰ~bnF+bNy 쫘 u{*bGE# Dh1R=;)ް'hnTx47]b맹%J,4wx3x4wg$JFQb^*,4d9ÊJ#AK<:EUoz9ʚK(9LGgayG 6؈P<Ͻ*k4/.}^ؿTqhaI9ɶŧbi~=r5+PM5-{۹oc%j#UaχՂ qF~{M{S`OFYXqod;b@?x\qnqq~S,]g4F`͟>-gB5ۆ]\#Nzq 5?Ȍb dG[v13  vdD觛FN6qlqF΍@?̈F~2tnccӹs'#O#.:u#E䓩3@?]g~1tg,ƠuHv2=Xܐ=X\Mu #OSq_FSqQmӪSq_mSQ#qTnFD]W]W]JGѳ(g#PLFD9Oٶ(#±gc;ˆpl;h"T\w]*;G"wqm'wD86Npll*Ã>[ õ@tPkqEIwwqAIs 'btgD8PxqaH7⺐n3e!܃U!<$ByHׄtwts/Wt0f0N$\ZtH:\nq`7 -9tkf@ҍ p/n7 6{.\6 ;ƔS!/{kWX(ERX,nU֣6*,fEaumSX*>eۮv(,Noj=?g, ?+ ^A[aߟ/) ӞNA:8/9A0;g[h>?ԜAO+C6N3 <"!眤B !bJ!75=D>a7adžzsF"P48'LChX3nF̙-Kf662*%V[C_(2 Eflp/ Nl̆q#^?.vcT16F~\ƈ׏q#^?.ZcjxHZpc z OI?ʈHlIlĆ51eFAMbC$\d\6gEÚr7Ew]8ߑ~FFD+\T^e3@khrUQj u^]`UY?HL؋ ۷2#6;Ci.blHL4wdU!X-p$&:oHLl#vW NQ p T}3F5lFP2dBRVRۚK[1 4S ̙p#*N4՚bu`qge4K iUZ^BˋW^hxkhxodkxխ^o?]#^ko< MYV#oo-vjBFpCӚo3X \A"jgϪ˻saBձ }ߌT+}V|V(>WV7͟2F^Y+i\͈W֛z5*W5 y[V.҈WzY ^)2)kMxżKkY L ] ZySVu{,ᆲ (ݗ5բ*~F!^-k{X0>FzhWi睕0RĶT(+osX]M)+_2VUB'( x_1ճF> ([il46էeTIol`DD!>>Ɖb|}ŠUF7NTÇ72ϝq̈>ƉVFA4N}Z<`\ˉr"#mm`{[sq#[€ϡ~= ˂*[r&\D{L%N$ge.Qv"aKH8Ge'6ξ\n& }|G4W;`s7#>q;8i6Fr/#t'0tt#vZ\Dۣ7 ФkX'w~*1ݚ45NdZn܀+ ?'2TO;m }_wQ@7 uܰT/iij id^O+SӮboMSst]9u߉q]Ya" wBYħL_Bdb ^ ovR"ԏ2`@V5 =8Qx1Fp.Ҷ\m'HN2sb"m;3\mL8Qx9Fp.Ҷ\mHXGDmLb#8IveFp6eDmWdDgaF~1"sы~bDgQF~gDgIF~(0Ǚ[ӷ觯觯觯觯觯觯臮!vӷo#OfF-@?}0G,ΈF~B?͈F~2"`D #B?9̈O?#B?Go 彼ǵ@s2ay##8g?#QFp?wx#8D:k[!t̀Cpd7#8n3kv.Fps/k{2}N?}6߼\<>{3?7S`}FpHi;k߼qǖ [X X )vq/#8D$[/ώ܃jl0^eaHp'b#j6Qcqe$/M>箱ԠyNju`NFG38쵇p_@Azvpme}K9 9#v7󈁚G}y]f'_!C"9m9y#9=,r=g2#CXy0;yΝ`Fl]#9=R"[Q6>\#9{)rs[yAs2g*9DY;ga܉s NV8d;67q\ U}$)vϟd/ jm T ߶QA}v{?#?=K9?`[ 6/r& [玨gnŗ"v͋%9lConXЛܷ}~@1|6̃J 6RZ06zͻA?]#ƈocˆ#mey3ASov>誾2r6'@W:H1bjF`61?޴ZuZӌ{cS?*CMN0 Q4;[Xbja0nD4N^aד㌀S-`"B P )Ubˌ)ue.XPtC{`ӽ (慫P7,BggEȡZ 4x"GnF0iR#?@XbAqX0)"APs'x"uqy-XW-wbްvmbP&-ط`ymPM.Hi R8t=` ,5ϰ`qyC sE=чv),5G@?lX_VšF< %xX?/` hK@` h'o0ԋ^@9ی`9Ɓ6\gkϧ6sw|~FtXb[bg;{C,|3v%.',vg0lK<9TKe*,L,%bG&,,lhO,>xt%;i8mK<:B1xt%KlXQ)x|8k< <>ǡT2+|u|<>8XaH`gU = @K%ek1ymV3#1u mebϮ}vX@"C&?[z}y%)3dd]uz ~Łϯ1dr(_`k x/v3u!F8̀ x8ǀ_\`ir茶823~.'P=YOR@4dȏ:h!(hvijڭ!(j=/O{gLZAA AW4]֐><‰#P2C^cH~q\C'>NOC4T_=џF9!㜆>.hCǎo3 -*,(H@}<Ȁ=dW-{L `35H}u_QbKU'7`~Td2-- Ƿ1^Ъ xkjh.^F0V 3g++V-f_`J5[ _hWlp~M!=5s<͍)e`+/[~k6T}D=9.Ag(Я7296AMc'3_e= =ŬA7 &ׄX4A5* fu-u?|M=&xC=b(jՈFxǛʗt3RHt ºT S.†T s8/ ABlj!Q~ s1ߊ~ ^s//߂t uY? H5_ H}a! V%g_ 1C~ ZYb [MMuKC׈n<5FSר~T! ^(LoP ` )=nWqoU~q LDa T@ NR HW! ^Wj<&rc" ns'\5^Ț{&f Q Зgrv΍\VU@균/=Gaӣ2L31;-=s'sFCPc 1;S.-; bTLQdf P#yCd4I'%Af 3FQQ O( 2i4DuAfG]cLQd( 2Jį3EibmAbV0z{A_:j{)l7+֓ZyzJ)]=rTOAWRO^OROβl_ j)dj1Rġ)fJ sz9POM˛*c7U4nSOFSS-SbA?~z l?KP@)X@)~* nS5D/cחkrw]_.T5m2Uu]Abm/eXS6T6QT-l>WTOI^Q=%z 6/͋=mbmOۼX6/ڞzJSbmO?%f)KۼX6/S仼)&ԾߪosWko5Dݷ[aޚ5R-BXC45D XCQ@5Dc QOs!)q5D=%Σ%Q%Q5iC_4}}!S r2ԴJZQi"s.IMD^y.U?,-`/IJ(PdReJߔÛ*POV7QwT܊뮵keaYAWCJ"`Ă cC5} 9dL X"1A,دȯ<VX҇aH.~ըX@Zi)A:]_akUhIC\0S]tWTb˘IMf.~Ϻbi3o墾vuU,* /bhOA:Id켞9*VǿI ki)Bǻ)%YIQ0h"%׊m-\u R jEZO TZ+׈R4B7?4)Ab %S5HEpeyޜ񨥾V& X .0nW,%e lE SKH}6dZ܀`Aj5و^ X0ِ $MJowV`;y#F.rīuX-P[:C"s8.yaI@^KEr)٩9{ӝ򷕷\fFW<j_.=\?O<nr'2&Qkƾr̾"Eb?`y\_X^y.+W }M3͝e@aznFB E_brrrsmhev<^!Q>!1IH̆"9PtݕbLXR<>I 5cQպ^CF}'#x!du<f'@ wg@uQg yDZQ̀X"@Lo\fqA xsJ-D0bi2eȫ2߸ŀƀ(k#@P( { 1 g@dB 78 bD;[)rU) ˸be6?~˽koX`?#˽O,Q̿}_bܾr3,,w,!߳0@r2ܛOr,1bc{+O,܇5{{{ =>!,Sx\>Ro,d,d̗X(zx*˽_c,z~^ϫX?cwVoOIW5,l-˽?~(4x^?g;,z~u,_+{roX-}?}?YBcw4h-:ZܢŶ=qqqqqqPi*-:@EҢTZtJPi*-:@Eh(Yr:K?͇Ö(ࠡ*ra[\F&f:b !~*O3\eYFl?rwGP[DĈ?~:|p*w=13t1;Ɉ{t?#bDS: ~zav^.v]uaܷؿՅ~ׅ~ׅ~.1"..ý.FD?4&I\H. hTLu3R3L fLu򀀊NvMuZd@Jf_dY1⫄K$+SL臝a&uw6(:72tk̈́~صfB?eB?P2.)SP2չs#O'[d CT{'LuqTT-Luq.h\ש.u*uS]ϱuvNuq?ϭseF+s.w\<r-`|.> v&[b?ؤ-[.pdCb#؞y 6N @a4KCYst;+6Nwsgž]x˱_ۗtǍ+z>2-p7^lrU{F'QDQyMf2˟`P, /[𜪅p@|@obê9^;Uvs3ۯFsve_%*UVr_il32rY\#?ep9h{G@L=s׊@)*Wʉ}aQ6Y.nݸׁroh$ļ֗qKGݬ'de=~ bZ!%,Y_`-K\%3Z)+J,˾w^#<ϛ'r풋k5K^J䥌G.kɜWd~=~Wb}ѓ[DE\eODhEsXC&P.p9k@9!^\*Y e]v&GV~e*Lr~AeZwZBYu3EA]~x'@Oά'+捕k ʵCysÄys#ys)¼Q¼4a\ga\g8a\ga\g$a\ga\g4a\gFNn3%̏Ya~a~.a~. a~a~a~k6'̏-&̏a~ Dea~V w0?~w0?~0?~ݵ a~&ǫA`}PvUDXOT\{E:%夦Sr|n\x&𼻻'\ww]*7r~(,d9nky_;^ߐlIOmh1OihY(JeŝkV닸EFQ*9Z_r_x{V@g>m~;&c^ C31mɸǙOkq&yK('(yŵXEqqo&s5kJz9Hx5$IJ$pDhk{{MQ"FsMsC<xJ&Obbe@|@KmU߈&׷?/'v5Rk?0[@7l/6_+E{MÖVΓkws_H-e|S9R{E3.]OXa= a"a 3v0aSvæ =a' <$a"ӄ56J?~g7K#̏9'̏uyg޳C٩ϓKT?' yr0'W yr0' yr0'7X 59ȥn^:d{MԷ\59ȣFrȥ^y.}Gƙ<0r@{My!as-P`:'GE!yߣϢϢ%/Rs  0Saw ߣbuUvyTI1y*h]mF%Ǩ{Ik?Qahs9gfs=Q%Fa\wy^֗o:G=(}is4\/+eD?> UI?ԯoel][r-Lw;E2k"N5Bv5["m!CMm)c[o(l)ק5vKrX75.鉓 <a&,H%¬-z a!,U KXax}*{@Xa kJ\z+{O2M>;o9eYEүeaxCfO尬w8ٿ&{7Q', pSB+nfo8;?8FGu {\<@ǏxD pO\@Lė1] & DDD(DDhDDDHDDD(DDhDDDHDDLv*N@@7b^s!'CU CR CO CL CI CF CC cCdʅȎ% ";'DvHXq#<q#<q#G<q#u8H8ޑHDHLюDHLDHLıDHLġDHLđDHLāDHLqDHLī5DWCLK5DQCL4DKCLċ4DECL+4D?CL3D9CLk3D3CL 3D-CLī2D'CLK2D!CL1DCLċ1DCL+1DCL0D CLk0D[ǻ/DBB$Jo{56Lnh$"a?bE͈*eFT3 U@aS;ʈ%NF,^2b 6KX-d#`;Ǩv~A`.DRΕwuP+ !RoMH !H'7 < v b7gLqQKqQJqQ'JqQtJ)+M4tJ)+M4tJ)+M4tJ)+M4tZ)kM5tZ)kM5tZ)kM5tZ)kM5tF)Ml4tF)Ml4tF)Ml4tF)Ml^kDizDizDizDizn)")")"Di"E4"BH!M$()Dh"E4?HM&̍;bo+Ċib)q (ңĊh`E4?XM&VG+ĊhbE4?DM&QG(I$hE4?DMF^SG(ңI$h Ǖ+++++o5E94}E94}E94}E94}E94}E94FQ|C(1hh"(1hh"(1hh"(hh"*hh"*hh"*hh`r(&1&11UtE*I & &&iRE4IT$ Lbb&UI)¤ad0i2)Lv{ j2E5"L@M&ПE40SN)L(MH&S$J+DirE40SS\)M&WtJ+:NirE4S\)M蔦PtJS(:)Ni E4SB)M蔦PtJS(:)Ni E4SB)M蔦TtJS*:)ҔNiJE4SR)M蔦TtJS*:)ҔNiJE4SR)Mffffffffffffffff锕SVNYi:e锕SVNY))M&VVXYibe4TYiRfI&e֚YkRfI&e~JSkzf-\qV@MkM75ݴtZMkeߦ5QDF5Ml4QDF5Ml4QDF5Ml4QDF5Ml4QQDMSDMSDMSDMSDMSDMSDMSDMSDMSDMSDMS6UڞlڞlڞlڞlڞFi#E޴"oH7mț6R6UH8mlFʦJ)ҨiF4j#E"HFmH6V6UXGm6VQ+}Ɗ>jcEXGm6VQ+}Ɗ>jEDGm6QQ(M}&>jEDGm6QQ(M}&>j>j>j>j>j>j>j>j>j>j>j>jR[ mEFaQtXk (5>jZ(5>jZ(5>jZ*>jZ*>jZ*>jZ*>jZ6UQ*M}Ԧ>jSETGm6UQ*M}Ԧ>jSETGm6SQ)}f>j3ELGm6SQ)}f>j3ELGm6WQ+}>jsE\Gm6WQ+}>jsE\Gm裶PQ[(-}>j'ȝGkO qq:!?>'ĵG~_vx7ݩ^ T7oqaqqy?R~B> $W!B>&B>Mx(!H_8p9:wMZa44444444444444i4i4j6 Ԩ Ԩ Ԩ Ԩ Ԩ Ԩ x ķCp|||)L"i ׇH!=-@)"i H!~)"i H!) "i !*z)b !*{)(Џu{%7 hB ];1tL#-HMXMXMXMXMXMXMXMXMXMXMXMXOc7R8N|;H I I I I I I$$ $ $ $$$$$TTTTTT!T#$ c08p8 q5 FMQĨib41j5M&FMUĪib4jX5MU8@VM &UȪ d^`1I1Ia1I1Ia1I1Ia1IS5@0p@Δt?JV'*NL8RULyRLyRLyRLySLy8SLyXSLyxSb 'hR5 y F444j|'P&P&P&P&P&P&P%"z3E$-"glI=cHZ '"g8I=ÉHNDRp"pbpbpbNDEV"*zQԳ;ppD_>P1Vp{u@ҁJ"KapuXC1\ p-lpk p͛Z7#5nN@<9"~B{R{aѓ!'A䞼3oϤ𡨅aCQ Å E- Zpp@kJ@=y<9'zrXOpa<ѓw'DO׉=9<'zrXN+kɛkVq\~K[{DE)Ձopā<5ݺD^,^߫lSk6PNWVG8ⷎ@9:=mk;b:nbzOr@_6J79x=lÖ{2aVvV <.a#=$GÞtȏSa~ 0?~/0?~0?~o0?~0?~ⷴ0/~KXo a^&^}1nԹ~e{M?Yn;?K+1 X=(e?WdB\+Cˑ q!ˋ/ӄ2 B\>|9Nˆ/q0!.!)B\|^#n Y'EAB'3B\|>%G1!.>%!.>_p \|nYH99F\L%Dz9/lllllllHlHlHlHmyyyyb PmOH}B_R?Pʥ<(0B{_gUU wߙ9Feu}띙#?V{_qgf@ يvf QC)'jOhU>@Z'*{:;*m?+0m ʾjkՌjX5cRMռIռny=nWi R5RuΑٕs:Hu3LͱLͱLgje<=S陚Y$S3$S3$S3$S3$W3$W3$W3$Wg蹚'''':ClllcQrP\\.BeqPY\,.T* ŅBeqTY\,.U*KťReqTY\,.U*꺑R M/kzRozv+ 14RBMo fo@*P kěMUKJvZ7ߐ3$oH 1H Iҙ_jq݊oHΈp論~I ;`f߯r=C.:!}ĐϾfhor/ o wAP28wňB#CO !^dO?1xC3x!;o?.L0"wia=->‡@zZ |XO ia=->‡@&=% |XO}>‡@zZ |XO ia=->~o]@~` b\TDܖ^[A[J}ZP>~0~U>:țYKTūjעmxqg$&4:Fh"AG3' r8t W+9fWJWeEG r:| ǘх9 "ȑ|ٱ|8w!Xxm$kM\O]v&{ ~\+/Fox1:~ Nj)/6_'t Nj /^,T/f(\NRna0^6) $^^dW؜,`;\( ~N\2' %61޹pARˈc+5$y1-{JX4ǝt˥}erl4Pr@(PNzMEel@<,pA:A_z&aX?CȦB1G7W`TǦЙ@ 4V8Jt E+ 6VÓGBN##`' :5Ku AHSk]+)@^ICLbpc3 B?X m?s g(,ВܿAMAzpv .U, X_\9w 9л6:rx0A>4^<@tIhXTD'!\C YttV8jbMõ!OɭX:\xψGa:\:"V4$`p-AU_"zA ͦB꼠) _R cWp:|J[\Cq-niͧDx>Ф O'wOb8o(˱ur$ 3j:&c-n,Р=4®Q䱯t x 4MR[h8Dj'8Ej֢8FjtZY7i!~AZd(9Dq鱵utV::B$lnUgA;7 x})Xut::J:<6ijݮc BSbbE^GȰ)*::I=&6NP]S&a  OMn?FP'#F3jT;ny8;U3fv`,8jtHFUīyq<XO>2H}/2Jc>2Lb ڸOW5BkC3_qXS4T8>ktfu8@kt:s5> zd52]Gi iSwFIZrbSF7iZ8YktF/Rmoph$,~5Z_m<X%Kphr`Vh)cFwAX+|G=NxL,5Ó7YM:XyT06P=!! !؍7#K=8{[H7Y?Gn) 8.kֲ80kƕ >+y'1[AlA*AKGa<{F#f%ܳ5foN9J1% { DU;hwPӄ!fTg[` JV~ωp϶'#(D\yҸO4n0#8xc mM]#ߟj8ۚCxN|Ql4a!VZO٩:sT?kӔ+ T⯈6^S#nkrF,qFD@o+rn}un (9aF#@Y 5![/M"la8x L[\6S"ۺJ ,!!SK/0'uZ6u"lMam;<@l̓ȜNTnwՉ>Cs:m2aƋ˕+!RZ!=aZN*EN*W鴴Dya]!,0Aۜ>H,qam="F@zt lY/"OmQ?mF-6Y>b}m4fmzO3m[.h"p&(jXp&%7?E=>zlm4*o>Dvmv&gا4ƞUmAg.ߧvMO!OQb #I+4? x 7p<ەgsSDkcQ nxrsy_x}09u+7!dsD99AӲd-n'D},4Q7sX n0E N4? JO8u'.b}n{:8ug0&wQVé۾@SLwS}x![} u9p҂9-sn_d}\,U"UkܾI}g]~4c,͚nwQ۞_% X0$m\d6]CdWBή$9 iѮLs,AH' BҮ#cc\nSsxCtG~k/ hs0:ޝ[bpo«B޼"-J-`m#fogBr5\yKj*kx6>\v?k ?طw?i?zQqI?MϴϬϜբzETjQ"[DvU/TۍSb_L~1/TP,/%TP_BST>կ̣~}_ק~g~g~Sg~g~g~g~g~sg~g~)/TRR_ʝ /L,ꗘVNf_WW PKyVS'WX^pip/_vendor/msgpack/__init__.pyQo0)NKR*A=L5Nr:vsN)CA;ߝ/ИV{u7hI u}SMKG>XZHNc)dhH/w~~hEg߯޼Y\-W_d;n f#ug3ORhvhK[u 7@PƝSf3nM!wiOXNnJ gQ%f[*pe;ʀqP n`C*XȾFyѼt&^(E:"'Hw%$!Aފw‰ wOi?NH 6Jk~j4OwV/tcA8YK%qc𞌾셥;.rhQ8xtrk}?x?GSPKyVS1pip/_vendor/msgpack/_version.py+K-*SU0Q0Q0PKyVS;;ӭ9!pip/_vendor/msgpack/exceptions.pySN0+VJ^(ke5pʏ=k;S@"Ύw7b>l[T@bcMla,8#Hs`tI*?nwp =;gwhwL o9)`#l ̷05vRR;TU!>LĚy=M4U2~%S}dJr{Hg#34yV{eE-fʜ,F5~c<$\*e0.ՙvPkZzY&_0~'(BӻB~$EE֪ߖtҢ!59b#Zi}W_PKyVS<pip/_vendor/msgpack/ext.pyX{o6ߟb:imņ>[b֡(ڦmmT>H"-q;!#f8]㧝yp)ITY* ^\BV|&u;ʌI..6|y1NMps8g0àӉ$25$YջS\RMd/-ҀܣI!<=geɼi„w yrۃ.FP}ۭC9<@s5 eG(ț&Gy=%֯=Xĩ,rOa,*zr d \B„,]TRۃFr\"5rGSrc%Ea̹,DWÕ?@+/Z{/,:_捝15 叧Fu X&EH%,zÄO-O]-<;mQUId»P?'gY%׳gpr`KBoOT^P[RS0!+pLgi6%Ӝ3YdЃS,gǓX*0EYvd|kD$Iڊ6@MZ]-UրJU`(ֺ۫O:r® i(4#G&3ccI!v{?DpX& Kڴ9xGkysھ >":`'(ut40J_[; Mkkj gixӢ)ݖ<<,إV4.P04Rz4oW٬rgP̫ -qwW~X>V$%펋5ܬ*@Iwitgړ}?| Xc._}宵m5V굮&J3y+==ie Ag2PKyVSM$pip/_vendor/msgpack/fallback.py=ksF+&LB*CR-BoN~,] JH =37@PttF$N)%$&l>3am2!zPFccc&32sG3I0#\͆x=dg.Fdo:hA5AO߻`@ф+  4MI|`/tB(o5-^4&z 74.r͞-h)җ}]fQ+0e3L~"6y& ~K.dtأs:Z04MR |٭$ -@)kR|הM 묅R/xL,(2!ǜ{QQ?Á3usspyzvt|G0ˤȚ:ʵbm hft:  //hZ ꚨYUY4Lvm,ݡhFj8$}ѻVt1<b6m:\iP-5?gB.@Zԯ,пdTC v*4l8II7Dzlc1d1ɻEnP&y'I: QN@t m4l|@T{ ^^tx(??-x ?}{x{c7G ){014|V~s+lcd<}- F%*ZqcXusLtNd/奆MdsFEpa,jxWs$-#WWRO &$"1[y,XsJx`HSfr(! ѸʄiΓL:1/.Y/RDc2^/_BJ(%{ٻb@ T1RJD_݁r :{hIûdZs˨up0b{ȋ\^lg-q"Q$Nܜ6 w%[Z4 sB)0E߿;|mF}GHm'YPȩҼ8lM_ic]V<" f<N)I̟$DI8B ڍN@ @"-3)&InU`Y'X^v,c-JC ^e dS@"$VuRP- %qygb@[lBXmO 6 XGE/@3x aJKt=ן[:h/1bqC@wM%DHTQ̃2n=vm9IY4򌍨P0ʵgA{jonnzC(_u9HnD-U J\,VoVU7^V2eRV\){,Y#OׄR * 9NFSfӒ&R Cq'B@Ub]7ndQy+úF/nj]^ie5c_ DO)zp;rC@B1D72*;= kd<7yh5\TI c !}j27w` (56(d"["f.X- cW͖* -vFKDzXD 6EHƪzq3hEX %td.Qb*@"VB{t|_-czݳЀoƆI vВQ⃔3/vO CfC :^kgڒ`E2OU@"M!( =<+4  ekGb?H62ːQV뺭S+qd>4@]Uym@T%MzE7 qw~kF.tnZ0'~v쒟ll{'u+BNgۑj`dAtHѣl\(wNk]S: !DrWEbzOa~WxŚAwOF}_RM00'zHw 0,% 9]US䝯N05HUYׇ EšU/Ӟqވ:5eAw8wY:i##/㿧PIZ2e}R/񹬝+|Kg6CY?Zax;>xkE+[gIjo-lB$k!im$^V6F.g['umHm{Z4?R;Ky4w|A l-Z)!VgOmRgm2uWqrݮ!W_Q/`:=]G mJ|'GjlT 렊d#kOaY&X#1?_C,O9dDOdDJt]YLeTU먷j|:꯫ZPuUEvX79YRO]UoyWU=iM=Ihi.QFO ۮ': .5YUx og= .>vWz]Gx/5|^aW{^뽨# _έؓ_h﫚j2̬[+}ERF +涠# vJg=2v䗜R;ǝbcTrj4s:V>3M74k WwOR2Vkg`ia԰%Tc>$H<]kOMeɌbqc5ʽ* d͇I ?R/{LG " Ó{v2bdxU@') XME o9xNFxVO '9Մ V8S1B[0afb'3r\4I[MX'Jɿ#oGSPp,JٰhS#QY~gh=yEJ֞\%b`}dK"oE0pSr|,9ģ0#F!3M&һ1E֐DkT9%Gs4yZE,өrn8 1H0$?E,;WlY>Mh;úHqIrj&shI7L5R,5%k СDŞjUl m"JA۾*2E^'-bir\+WyReZyRޑ.cr7ߘS-|CYvY̞ 9J*ۚ-;!9eneFԻHA Wd?/M -td`TfB2HiJ.T+8[ RD=#c⢘rܵ-5AI$K|UŽ $SԒ5& ϥ3bv~"/zꐖb]ZQ(95{60|8dQ@@_qY%K{ dPЩɀb+%IYާ`sKAcܪpgx1gnx&eݬ'bp7lc"5B(%r;aHD B?,3 ~|5s$^ ~֪iXU!+S뱅|jvW"بjҘ\W9Gjn5n8%ܺ>ϧX2;#YKEj=vvwNr |ζTogbFǘL\eѵOeװ86欳,^e})ĈT(>h~#D3y:¿R߽eM\}\%XܯwR|.b\cR ׉K#:|i_'r/Dzȼ*"/[xVexwHːtZ꬙6S]LK*-(kLB][/t_v1y}1Ֆ@G@GG&zٷ9(K9|y|LG2#Xjxy&Z8(dַc@ iqViPoξ: c QmG9zv* _1nؘ'um=Ii9hs&GFV1o⑆*vViUچ>_))k̏h`8-6q' Ǒ6bU侪qUsG]3$cmrO^A踽9V}xM2qkp99<=;:>maqZGyAee.om/PKyVS& e"pip/_vendor/packaging/__about__.pyUMk0 "6` e :v#9vAlݼbDJ :АDgI*58UMoA78)lh,i`[ TÞNkL'ϻp)"3Z;08hK5@A"#Ȃ-,e dbe/fL3^');mRpRTa4vgw~X_%Y*|i$1;ހI6H1nj;w.\V8UR K XGzm,VBpb4EobCg_:lcqvY,Ŀ1EoҿnansßPKyVS!pip/_vendor/packaging/__init__.pyMj0 E 1i!2.Ea&C77QY.:7WG6@oB]4mq A;dA}xLe9XNCfs fxy{97u.`(X!_O -ɡ t(ƺP+3y6Ek~"S^QbU4l)}*rM\@ +J.Y#|a|6= b(`QMPKyVS*:c,#pip/_vendor/packaging/_manylinux.pyZs` A^5qRYlMeɵU9G*%&Ҳr2ɳba"d#o4,nodB4ʪêZ$wHe(G"KXq̳K$cg(xy,iOZ*/K+9R0^KYu!X6W:%ᙸa]Y,Y"XKQzXE'Hd E'2qb#Bitۀ N,YX `2=v_>9?e? c^,SX#<'oe."XlI{ĀQnũKO3y,#k?g(b8zP X4oKi[+h7GΎo^c/ACq|~tugSn4~:zs~OQ ݛ`.w˫z>?z{Ӂ!9%ٛ)OO'3x|n_L7_^Igd.s2UR!bzDy3f[?,MhQyo|A^.Ze)aovV{!zr3jN}$|!CHlDu~8s:>rO/ݡW[D{d(x&tS%Dv|R9[r2%OeU;+)KW4CFI 6}GNiue)jfTbYVx7J|RylQ#u,b)' C(D dr CW!2 yXj9JsxP[ERxxUD-aG|}qݡ e덋|gk7,j:nc 2!1֗}g2b' M:(ոcs3vx[S,Dd7lY]߬ bů^94s ܬet~ϰI 豼p"RYSM ̶XNu D(P&fYfdQnaP\0v=S55W;ΜQA݋g@_:-N+L=8;c6;#\?\!T<,p~>FL#B*Q ,O|$\PrJX"tZ!;ʿKrCx. 8&)( i"]VR˅>וG*6h2wof?Kjn*c"H4kQnJGlGݑ} 'RBȅ.Q*U{ƪ1#(ng"&.{ʣQ?˪ɎQDٯ03|E5hN ZISU4)__H6/<,ZG XLy,P/.JU#uU`0jJC]1NUX"8]d&"j KADWz@ KzX?P͡R" lpj̲XJ\[x1qh~AeIg ܎঵>9EjATdBad/QV-WT8!P=N{G: DP} PygqA.zGelҜEm4}P#8=\(M{A =[b`ZCFbb!sFAEV 1DHE,CJH3<[Ÿi()1>רT -5n *\hCҴ0mg.էȀnsGW!3ڙLJ@?˭gl@=$Ąq [z/x}/=? it }Y fiG<0mH_96ȪCMoxջ:d"e鞍ihkw_Ym m_qQ!y{>ۍğhVjۓl/I;.k`QJ-F3AZlȺ>e)JRV2ʑuxtF!V }cطXL &taFS1UQqK#Hjys+(+jO!RMʲ #9u`^55)mz!Y@Y=-zۂfm TB}3`]͌w-Ȏ6[r8+߅HF_uov\B(;{ּग़]v` eR'6)TR2Ih]ueZ4KiZ`M5[oF ]JֲAh#k5IkjI=pFn1 5z^p1;{_  8G^}IngG Ͷ};bXCjo$-EFFk͎0$I/†2:8zZwP;uF otf⃄J?SYK3=[YI^!э;9|fy1ob]Uw=VCmz@mkuu</jc9PKyVSo0#pip/_vendor/packaging/_musllinux.pyW[s6~ׯ8<,pNYv5&Ƴ/HPšYLf{/mg9p߹ k86M-Ln@Um)@VM)* l " [Y5{ЭRRmzoi&T"R Uk)p×-Yh#ih$Ţ.ʊ-[)Zٺ.MP7Bs[aGXfծ]gN2 ,/>Laa=)oZt>4uh[L&( Ղiq1C BQ)=R*;j6|VAYH8a'ef䯂$7\yJnMɜTH '}dt>.$ q:=k ;WDZ.x1zPlmx#eieuu\8?9=o_go/3.(w.z[4Z螡iDfªur"m=<{ymϘlkqJܗ{zd88N!:!bZ׺BV­a8 2KqMF ,g^* 6U[2=5"JpOBb #϶R ~!BFNx"uX}W 5`l4B@lAl:+QCKz. #מSk%N=3>='3>WWWm|_gpPקGkiو?~StO>y8S[0VC[/ 4mҍ v5I+*\,N|^;r#+zߓ$UQ;[-:~JV#j'E DAsq'%"' @-dr89Ȼ!:4ccZU`{]rlIo.s=+xhsw0 α$ 5Mݾ^[A<<:d%7ҟQ}uZ0B6uPTcaHeUHүy4&NZuU*v; N ժ+ޟσRF e1: B|Ln⹞N0ŧzkyu^+u'TwG˧~Zabn ̍>BЏ pVVA r#'h(MRvߍe!q++^DJC&TkE>FeM=:+ Sʱ"Ԩ E1Mhm+a$b6UEzqfvN?CɉM2_pLr''KCcܠ:{JPw J4p Î}PDžqDtt24Ƈ!elm,F{׋)JRDy*ťF"50ϴj\g>;xP,\ D&zx:91C0^֧m2Zgھsһ~Yg3ݝXۊ.o:d/48yJk=73;4xAF !QM Fr9f|oO[I~0> 6{)0Et/P)~uK ꟦HScI UKYh @~ebfr=O14V \읅ڇfđ&]1"MD$3lk 7:h(D%T> 3uqJbNdG+Ӡoqthyv /ʯPKyVSqt i]$pip/_vendor/packaging/_structures.pyk0{`eX`?:(^Zv >5#M\_v;^I8}),B"1)RT3UD 4G :o*aӕ 'w1peϻKM;vr~Ymw󂪝3ZSv;0Xi+H@DBȐ6 ZX\(Ae00Y`t FAx A/Yr[B$D]ټtNǠSjaƥoF/BX,qb&bh cmRyPDǽ=(79&(,ΈA6bI|a߾pBW9i/007} -⏘=czs=AbԺR5qPKyVSm?{ '! pip/_vendor/packaging/markers.pyYmo8_"!b #.v`I0VcmeQKQIsi _$R^qKpf8>3zIyM6yZ"XY5i5DnLj7e^ ~N4YLns^N^1Z$15xraqv0 jLp.l@׹m UyQ'I {mJlP-1!&hQOkTy%$Z亩pc&j)U^%+\$CENhHKR蔜}˄o\S?o*xc&'zŗKV7 l}%G?\0^Ev_$2DƌXor>qyG|}e'b>N&i JS2#K%40ԧT|f"+!h6y`k^].xc<;f}A@Ku0L;"vK0F3W1yduM<%Z4l!T/w^B8*JSve ĤG7"tۤcqw-]Md %w(dY]@X7ڵ\>C(BJ- f~bh[[ h^kfP3fzQPi^2MÚX8CW4*$.iʉ#݈Qq &QX \nH9dG=M)i O4} A.B䰫fӼf`OgƙBFY l* &C'}v!S7?9_- uvΚ֕5%d}Z.֎irSX9?| <ùOkJ:.M&LrnbEpoWg f3ؠ>kڧ: 8_Lqw=EWV^Z|e 9mH#&,mgb˽???Q\.h͢_\Ku|82=Ctn;X/#[JKȭ'řV: Q`uFKg*z^=1s451,تDX >:"C7ٞV/XЃZ g"c) Xz^K>5W}wlIZf ܲQ-HU9V_FJ#fš)T#d}5Ja*b$M.jutTp{-uA vbN:`wT; ,bP~fdDV~g[,|6kR 6L%\"(Kœ@qrX%yeĩz]iJuH5'09{HDa%MM _HމݺorQKzJ3ٌ&T6w]iiàHnvdONT^ /7 xql lL# ?y^jɏ! p$ J;jVrtxy`**5@ `B@^u0'[b ClTؚ&Y(I!6 a~ 7|{qIM ;pGX ,c N}Y v;n]RV& & ]5AAf eE!(Xobuf#^'L5>bBx9 \(1 {zqզ]d֑կ7 ^Otܠ֗n3mnSRb)4LGPוD6#.4Om[>A+i|td<g-b)ˬݷ\VZt{MTu~s)t:._Y%XyHa+,=߹i%t$Սh9i :sS64S Mg l1k<Ĉy0sܺS125g~EIk6\kΣ~ a@%K )ѿ $ Wy2_`,40{A]9 y( Hl]Z%QP?ZWު[18z_E/I[MU=pB|w)? +}kãIdJu⺩}6E^\,Nj" ыxLp4/PKyVSnAZD%pip/_vendor/packaging/requirements.pyWr6}W ɱI:LF02j+ɹTp`R@]@Jz,,vdv L3FlhFtrƉfD2XA X|d\E_gBDV22'^4F\p.D* ~eɢX3&Ii&IWKV_B4Fee\0gɋwkr} !㵄p 2*i˓;faxN_X]9 [w`fjt=:SvE g܄][ḃ/ ^[fҸ=JeKO*C4gRP"]]ݴA{>5i"B~fi2a7)g+K#6,~W[麮sĀa- D_lE\L,U<,:#>{`~GCr 4?R  Nއhmϝ~ 65gBx3i,^4g$%ZKpNse `\{\ K#4Qovz}OѬztŚۆg! p9ka(Fض]ܜ>&acq'}Kqw\"cdA%051R^}G,4~uЎ[Ʃh2=1ŋgu1W1 v$S GI _ aK Ժc-3ZzXG/LKoEOU:F#'(2<~G3$]sztj,AsMW^- Ǡ}l/NBy]]&A]"N ҒVJ·/f%=ʬUχ2Zh+҇fR'RA z f hgce\~ELHٕce8:M {fmnnP{ $?Ye{ph*UxhF@ g4!.ћl^SMui' 7X,H* Wgwd hF?{aesIhxYNT]DgglXb?9r/:~SͲ*jJwPjU4 X%W**PiϕZD,vbNI})'I%vqHlˮȨjTQ 5 6԰JX(IV EkrPxDVHγ(5 lLU [Y۹TjUb sx (.a%Hr*|M3V({q3ui"/B㾁yVAuQq3˾Uh9dL"c2qRhoa2 R܉Q [>ALl)6,,(Kq5J2 yTl"_6,po!!Qkb]ghQ]Y50mb T@MfávNB*+Ɗ)ĕ [sI^kRC<<ɤJ/dᆢmwTIVp18G`W|Ƴ!ΡgU\ Q p4oMP[~vIE#IvbMQ_(ЧC w'i0Ni"3©Mi>*;OP" 4|4:`N<m3HSi!yvH}mfAZfYm3kS\Z'@ C36h. *X٬$EԚ&7rhSv뛅rѫ>s}Y6x䜃)ʵ$ M! 5.mɄ꿐4!XJ{?C)/"Hm0`a1Ƭ\9\ eO')載uJKUX[F)蜵nVLJa[kPAMtGl:+JReH{!tqwZf32 'dP9Q}:f).7`$"/Q  #{[ 5B5LV5!7?1x?ZɣA} P0b$\) /c jZp p) :jfU,/gY'I:=x,7+"Y-؟`mY.I䵒4z6prcڴc-t`k+`s\kܹBh|]W =iUkWsڶ3Oa?O2ij؁h08t#op9=DNx&s{Xr!먼)@1ObU Z'H6(.qtppuۢ$;RczD$J8 (+J@Ԛ]!!Rv,֎Jb'p$p'#AD|>aG_89N UL&S;,}s%|Z$*] }]bYV`DK<2G#L*X/ fdšH X{!wu7csU,6Y|PS"h ס?uV7-udvz.LOF!h{mЁ-Ҭִ%``;:Mhˣy'Pu`ӒHƘl<(2@ԗt!hS,IJfe :3&:!fFsbXӹ)3S8'3qi va#TM\jug5|x P^aSK s6%;m8luX mPv" BݹG-9qX$ǎSXML#Zӟ=3Ts*%r5i 8h\Pv-8MՊ_? zx޻3z"UR˜7>cst@M?s}|Ŗ`&77M$&uV]mi*]j ϧ5n7H?b\sd| [{cx]Ou83dNLyrqU!;)BL['lo{' 2(d{7~nOHZI>oϓYdW5m/MDh( tͲƸЌoV3 OPzI_Msf `t?z`([3ɡ7ݽz ȴ IT/)B3si;#t_hj_]KM_dh[RjIupca^no2hT!=8V @+0qb eE`5iY5&7Z~_NxڮjP͸5;γJSjrfmᖱ8GZĆu@uyVXG]'<'2%)Q xspLΣMVS6xV %nG6]:&cѬ0ϡq-S9$;-ۃ>9'_v''2 L ̌*k9 ă2b&yV7C{4n@.qe5y8 W/@cr4:9THTYT ,HL`=D`Г~ N>bP wt,{۶{Fs45tbh\1r1>0ishc++xq7S+W$^,,y8AfsH5"lHVtw;N1)1tܬ0ܗ4}2w!&ש!tJ(V$ˑtpn흧qn1mKϘoܥiTrG1QSgS bnhib(GK?TvIBf} Hd)GyNa;SQ,IǐΦfWϏ Y-CLuc]br1uη5bbƘ!AMrTjؾnMi0z!:(e^z-Pl5MdeC6IZ w]8gx3)i0 v}t>i?ߘh 57 =\0 Hb'WeuEJOcߡ65RWVj$70: Bƨ"(OT|%:ʧ%/LD`4V;% χu[ݭ8Y2I13ʛ8XSaX`+k/YwK5at.v8 U<?IxXgwWըC^:us޺ =z Yc=+KZNwBڅh: `]`-K)ߎӂ2Jv9 ?[LiJV$W ʺRT ^*Q+Y ơ &qվBG\렠kAye⺄srAOsb[;sFC\o7Aq/:bv;b\ ib N;j]ֻ>hǚsJ5.s5z1Ĥ 7"/AsWzQW.7 w=dk:3޵ amsnYbڽko;˙Z.DBg~LѶ1saF-Xa.:x^xg6MԌ.a%/+wUp^ZZdkiu*6ĂU[  /wnߞPч'&3`a`]Gc$VA=ikU^ao`+&gbΗť |1nxaV֪"ۼpEv$ڐ 2$ȅ.)NNwOxD{pG8 sT).`WKzsu!WbUR>D+^Rë!Zر^EqdǏڃi͓4>F`Q"p4&#G F&n#'So=xri/F帨 &)Ȩ!4:]a` ߏ87FE$+-.jLP)oTөchĩK9X[`kϕŰP-F9XWJTwg)`+ILڟXOCd* ˪R<𕶂`֦ ߂@"30FdryL+bUd=<;z/2 =G]dr XD$t/锌*նMVPC-ʡ1la{-Bbh'_–^*BV!]Tx\ϣNuʼ;Ocj406L3f>,4`x1Su7/MnʟWrj \O;ֆd_||ϒp_\*t% [VPvۭ`+RG&M&7ߍF(q$mu踻 %F߀(t`0  hp[L3yaՂGBEeem*̈ݗ]!_EYTw6xwNuFKM SN?UpMS.WG[]76:{gm+]>ьTۮ3S:6hU#[7XWNī* UkZjA2m%͐re2aW~f5pnD^!oPox'Gs 0`BF%ꣳax*j|[e lfT0|޲,ʾۊޭgiR$ĈSr[ pM.5# 9Q]խ-vz%$[3N$cX\5y+V_qVj%Y2Y'6aE>b%yJ`N>pN??|r'YU T,j EŪxYDd2Ī,e/m%5 X{ȗrSO3q`%"Z?c?>'_>~<RK@#ĢǪ'O=v%w`AꨬkINo ziS_rRǎGP|u#3? aTtO,awZ@Pd^ƶ_9_,"~,`b0ODU-Ͽgn4ųi Bo'gol=f)ir0}^V\NPjՕ& T:NJT8 JVM mQW§; Pkvuu$,lJ;O#K#]ı̊Z1,,ADXɅoc'tsj|ʗC䢎PlIb68+nl}.r!r(+n@xpὙer  +~6.tO^ٍ2voIs`Xqq}0J1$d_To,.e+JN@_%kĊ \*hR)!Qur&IIX\rWI~WxBݓFvI9(Ͳ"v\)ЦV`A@YYi*e.foYr6%LP'+f~!H90)#X_Wj30 #LL 1hlXrlf-m&TEĒ&!`δC&8OG]BoLmQcR+p4ӅRD^-_"A":k1d6Bz~Ŏp#.Oh#6!ݑ bwėuwvm0.pgBq" BdIMCJ˹HBˆt7$y6!Gc"w)\CK){ ?̵qd)@d8)A\*UǼ#@ѯAk$&i3ml%0+JXQq̢]ߢ23whlGeX+Y8]nqּQ :Ck^XSΎ! E2:tZ&9Rg`HA}{>֎s Ye/KS@u.8^}kVpB а\ $g\%u VN2E䐱AI_p?`-Z=Wq2BjllXAƘA4* \pPJH^u)^6`z%\ !el-t^Ǯ]2LlӨ\@qUXLܺ%8 z]h40zCځiD?~'Wvߙ0X.Nhr(RpeXχ>'uУX㣙^\Y \G ۷;1=5j<Zg[-p(ƋFdPlAU*HR(wu:rajpɔVjSv8{rn(@ʹAqFE?ʰg })nֲyfB,6 D  I~sL ϐght{efZJS2?>E 05DŔ3j3#荰֥ԂS]wZM5h/ LFzk̽ JY.fRe)/6e'9N7sGu=?3D%}-xm2-@#A~ BԠC)v@-ѺpugDu桋Y킗5UUT >; h+ymT~~I8\ӓⲇ ; UR;׌-{U3ߏdh|O\ b*/%9ĝ C?Eo,ރ#pqC]dzCB@x 6덣9^r7@`+â sIAd!j1P9Մ2 "Mzh6칰iwߚwO3gIpq3Oe>`r}Źsw~Gq])x_;i t4) X*v** 6g"N)s4JI\I^JEvz~8{ IbjP|{ h Ν1ۭ KmnLųP@{B@v:co|OOpRi AvqiSެO&XX1j]r|CQ;wYqE_|qaI ںܓ=Y_O/ 3ڦDIc ﱢ{(sh'J DU?NlB6 8o7a=a6&uRu= rQfO f_."Ou\ 'hO Mآlbf=7 n__ qlOp]:EKó\I l78=:  0NJ-vzh}Yzhj6۝Np$xh.vCKy1s 3Y}f:Gw^0AR̵nm\B *vM4V$b&1۵ Nڞ*pk6X2eCqw͉N0|Dȯxy3a}SL9nhf;ᣔr Wי?B L| ,&3&aWUjX .ƒe.M%OO;hϱxI6zNLh2mS i{ޙ9ORر6L9[_dRh#Yn %14Scpq%Ft\o͓ jg !kX >Ne~)!>~K0a!oc]7 b.K{ ̽XP|A_[N0AC9*"ZuEjR*le7/n"r֔)A웵1dZ!UJl1:c #M_ Lގ>p-b'dL-dT̏ }V7L u_%\Shf"(bՉeDq6'5zsJŷS],-& fF7ܝW6{ſx O6 !4<3`6dMKZ[=#$aB$Ef^odb@gN/Nž))Mm"PXJ-Tdo!r]h24uyb]JeH[(s[b wX\7%ExY>fiW&YF2YFP&J㜎u\#sZ\'\]JY̹BFf޹Tk2syFRqoB<Ѻe  >'y'JIN< E]+5,J.T2U .'𷸵E&4kDgߓ%זe`Y !ӤH-`sK!,3)Ptm\s;=;Lp~⃑Ryz?xýʷ&>_P?oR nB7<\haQ؝ne\*;sGvHFz69G}O=^c.Kvy^H8tT[ٿ﫴n\'7Z^jgмjYgGމ6l^x~5d:_&=E]{!QV] $ttst>ʂ~.FΌY~2+Q,Vi.njD&>oR|aN69sp#N=n\G:Z(Vӊ'i)? (7X=XqSYRuǖ[v(.Dy\ooo়dPy2Pp)dD48ڙ+(̰Of*)9S2._' QGES$Bbt:5~٭tF+(Փ=S%GRiw@hS X4!76KN=4d{]CRBfT^)ɖJBj&C` 1C4'NbBD@/K #2Հ^Av4jKy[\[XEq Ʉ1W"Ҭ_e.DQiHI&Hq4slgm|:xK-5f"d;< K9ݎxm,!`d?\Ӗ1 luG]rB@ { IMrVm`P_pg'#yVU 3=h~ o. V 5'vhALߢ]C`ygþ܆yx-nM{Kõ kPt ݵɾRy|'O"I@_Ϧm<ք﷫yW}kD0ЇZd7ʠvtmSPlHsuE༣iO`"W'o|Y*Q[aG%%1ll4 WEEX*H!Vx YrE((Qf2J #LКǟϏ=YʄSd*U{Xfo}3XyyN*Ş{c@#J 27y9fwMt"V%v(khM$ahKL{ ,cn}VȎ57{klQz覩c؎ȳ 3_Ζ'2`]if852ÝN=%}:m6!nf)DV-3r]ۚ#'y2˺Vxt \d3#üݐ8`oikxGu 9MӞxۼ Ә۾yRJT7cOgWf4#yBg'՘u=vΦ#}{iLgJ4V 7n|6NkaFs,#Vr޼`Qc̀-bmbiV jsjD:tw3!sZ2;gm4y׍Ufgs lkݧ{~# p6?zx?}C@ygzHYSZʉ@g0']+Dw!KPJXAt*ojSEɃ<; S=<շTAt@ݽ =-j*6]wW4ks"2@snMmN(D=G$&scF{#u}F|':契r 3v`[@\;"$=v{Q %|ZXiJ)2SGe>i72֨]l/9+FgEC2'!T:1 .@xkq{ҫ&xX9oS&ByRo_qqV*9iA1!w>w更-K;D찦[MLf[ԫ}l*)``owoܫksqh訳h?ݑ7S4jUf}ֽN^B٥6G$ ZtFmQ('h w1F&KFk7KXFsBDul Ar/}&6 Y 2,D]ӘOșJkCLIņ[zaHP7A148yDZ ,LC:̟d,+*[:霜iQLہ- a -7 L~-\LR 8UNlNPe>`ᥧ#K4v-ι(KܳGx.}ENVpk'Iٰ+;Cq#Pe& |!c|DcJ6/ce/C\s:a46qn7)xbcF𭿴hat 6j׻X\ߖ<MN`%DMkrVV׋ !qYkKnj B7õq83:T4sDZ=H0-Nk .Y6`=o筼"m twA>P< M-S!Wgߘ#(yuͬP|ztZ%/(Aⅳcu(q8d^y%;Yt,Du##56'ӏ`yGM"oZ܍גzcnP5`06`EzPKyVSBwpip/_vendor/pep517/__init__.py5̽ 0\q(Ql!jmCmNLRŻWw{^f>eR <]{H2-JbcEBL DGY*ݲP8Ir!p\kuEPKyVS>R( pip/_vendor/pep517/build.pyWMo6W.F}nm" hils#Zk;p6%q#X,~ES3:>CeYoܳ[ӏ?RHE"Ni˸w\BŧF_O[$N [VkyZ f'ͻfaAc=yӀfVf0vm)`V6݈>){zY_kܪ)$}MMAge)y e'IRÎy#jn4gi31{-M{ v0 _zaԈԓV5Xc^Ld)ZmyNXTe/fƚBQc0/aك/c[(vJf;跚dZ0Mvj@:U+(w@~D#Bm%u3Ћe9Pp=e ġE!Rd{:c4g\"A8]kyaxz7|P輟8o6Y(3?KKɯBT~@"xF5{pp(_y,:ѕB|<~ E>c QkAVh(zo}vrcSG<ڶEqOn1nB|~[/YDxBUt]as7ǤӖ 澖T|R{K 0 w^SpZK;\h#/Fc{GvO=.>.[D+Ws(jˋ"}%cA>ǎ~W_tӸx+}JuÚ MYs'J䒖 f~,kW\!R}ٝw#\U-֠P:Vv>p-l؍ A&넲`t Zfwɰ0nu 2⑝mAjA;hs }Q. 56 gpN?{dmʨqKVRA1K3B$=)v#DGʹFs34SLo3o8I4M1;f c#uŸs.2.MŻ-ɜf9PV:;XvJWA}:Uck^m^TC1lK̒fcؐd9 4ɪKr,dj#%gP3A_ECXK_u ;pD+';tKV +YȀiPMq`]\{bfC>Q72u۾s-1 {ǃL_+C޹)cqˌOo˄bsW%6^+Dt(12"w{iP)@U ea Y,~JD  #rO =(_\Ckv& 6Ε.Sm''ʓ#+x>2 O82ʔqu6輙݈\L0is}XdB1_w-d0A&v*n]F @KyY ~®lfcCY)%x<f,m@.ufoZf\xk œ?| T9/Q[8 wI(nmBW5``׫b:x.7ddFњlPKyVS VЊpip/_vendor/pep517/colorlog.py}Wao6_qp0D^\%M?l^<EQ،D$R#:F;d;y|{|wGwݩH\ S` kHT*mNZ_KSȴ*^iRՇaɒ5AivVVKe T M-T2ŝ-.(apmp_@D0[UA$p2sO /-e.L61|-EYu-&fXyٜOף|u2ƀUBc[`%G0gPJs|hAn戒 cxQgob9]s8y|>g~< 7na87}HnßJM(C9'ysx)y"2`\rUε$\At/E2Kt 7TڒWPt:OLPtHk+*;N3X[ꅩJ6 za[wj~ &ЛT,؏n"SQoozylŊ[YQ׹h=.&?#/+1n@Zzd+q&juKQ8x7b|4OU: 0]ϛarΥzn}y",P>8/W:DzLk^W`$)M8LoOb LD)ncl3iQ8(%"\ 6WZu>@Yd}Q߰@a'Ů/P,ͱaQÜOL]PeKKf!5mZ="jt:bbVF>KPG+³-P!wZNkB>1 R] L,,/̡i'-*5 %U?ڼrxIc2WQ&!F"z^لF(+Z#zOQo?polAq @7~3EdYB^f0$ ˥;+ZH8H ͟j2)E2\*b @d-{?10hY|1S HxW 9yBK^W\6\(yJr$c1af#s^cӧOӏ!" y8]D}gT 8{*(㝈@&͊Ď*G *b9^ +#W{ʲ@@Vpr0!SLx7w!kսvpM]ػ  wCf򋻺Ow6Ľ]/]&jvWH,]Ixz[: wcnٕtܸ ozF3?pw˙Ow8@ȮaGY`c^f1[."׹~ϋ,W[Sm Η3&׳gGzeC Xlm{wd4ԍ&hl4ȭ.G`!m׵3[!'U `sZ BPaq*K&f42% '' ԅhB94;>*MU$}Q%ָNATv9*JA3ESfzO#z_>=S?|\o9_erno={'e Tܢ9PiF}XkDy9u4Ŧcڭ*#Ʀ.-!7]TĮySꨥ62!{X?W*fpխ+ǀ\ 3FVӉ]Pa 3*UevVAfo 9q/˼v}Cu,hefhDv2W_~^bN*x߬<MurQ@t. H Iw&/IuNkq ͟H(Άo׵B?zo=R=V_#BIHtVWJ 4"%I)z5J9ښO ?A#( ) T-,J'Нj;%^Qu [`UeJ$Gd ie=+0hENk?KM+8oNxu?WS]k<5s4>?XwAg%|Пo[eM_5`L&GaW/ S.®wc& tJ6Cƽ9,GW ~rA]fp*-s[޽!?PKyVS pip/_vendor/pep517/meta.py}VM8W`CCE;}axٕ"&Q-״{yi%ݏsϕlM^vKv0I6ZZ&=>i/p=q0"տd߃ٷIncZpNIkɿS6= #lxA5VβW wq_cd7OZ3ҧiQ`9.9?r!@(L"e+B1ILXPKyVSUx u4pip/_vendor/pep517/wrappers.pyk6oAm>\^E"\>, WmveRuf)Jvi^j`c=CqwlZnFK.6u#weڊnȎ a̎Jejn0Q3%?I.UHmWfnFL~-+nVUje]W|}c9ؽ[׼bġ<;rGv(WE6Gg<:ՌgL7sqG+^o+uCjܓ?֬-͕lMͽX5玳Ǹ3 \_fFڃmJ$^ږ]0CYUXsYҬٵ cI[.`Ӣ ޱ V#v볳7 # 劕y[FnZRP!dFY!48%V{E֯Cs1uS|O77Uv^QL(-u~RCDGBRJr`; ͙z3v [Dv+AWkqulZhs(.wV`eg .ABNH׭oa?f췘KXf?Bfii%kIN8B6Kk ]SpJsXH1B1B{ư^46J5jV5H+p;MZ­@CWTB{ 7A 鷄dm@!U2r:"oYu I HbG;;7:idEvYDZtfxrKGokPړ 75Rq37(PC]wd-rq+Q;W=d${f 42z'yNbK!8PCT7äޫ ^>Lˑp \xcRWx )MmRf% MDcm4$Q, L=d%y#?V6];ysd Qڭr] ]0:eb6bhW]Ɏ ݁QAVށL]˪{P,}H -,1vb%O *0>TL./`ͦ]+|C.ӂ,,dd§y(kJ c|Q֏,ZTI3ZR9uR ڑ2L@d)(rJ+ B]]Yz k'32BqWۥÑ#L364112&H`xXN| KY!," FeoA$_~]xR7pAsj#z,'%Jq 6EibFDÈ@d5pDqpG sfPQl3cE}u3[)+uv/J qQT]&W# ?筕0+ 揉4hcXuL4g)oكV 5LJG1Nw#f+|*вWMׅlۿgF!k-KxԕoPPE=J = X,;.H 5iۚy v Uǂ& *s;&[Sͭx bc ͇~M%X҉ H֐{DJ }F26Ʊ=Za}Ulh5}Jcy`z$N~OJQ0* F}>)H,gG3CEmA)f< ?;ŷ`ʽb$NEwJE+зk߂K/P37Q]{M2&0ZePE;Нn%y9I6"A @nP oƴ$KLrF߬a/FʊP1i* qC<4$7  q91QTt@P= `45z[ KF ~5d:M9֓W_\')_e;K暩r "+Fe~/O_<ٿ^}ـ jP6M!ՋVO'JKÍb_Ƹ.T4ض5,Jyuk]}[H'-Tjs=!< `\ݮ-iyQ(j&z f`$sKr|gJ&2R&1,ۖ"upؔ75O+XlO7z ti;2G>Ўy?狱Md7eBƜMHo61[9ϲ]u2O*"^ᴂ 85v|02\OQ1hme62vYޛ|A MtkLWs#ίvfÚ7(_Ev|ח~ CNʔԼfgZjLmdf\A0v*q_g ; #t,87Jv:N᫴ Ē;E+_@@q8os',L"J 3.%Ò""^iJd(d|w؎PZ^  Kvj3FWˇ_^WjITFOWAU z!W` 0ɠ)O,ЀFgO0ǯkCPKyVS$3)pip/_vendor/pep517/in_process/__init__.pyMn0 9f@qG!!-đmMNE#$QUe q KuW[AG>0z%^KEUtl61y\\d7izu+8nD"F!0SL(ySSl9\G0'bmMUUf`F7\'~ qZ6STOQ]l` ~eA(py?!-uŨ /F_[{dfN?c=5oFL>/0-H>(C2GEPKyVS"H +,pip/_vendor/pep517/in_process/_in_process.pyZ{oF_b &šTI/;Rژe5w̾(Ypa<~]%k~E&1( A*NqjIɬfiBf Yr~%"J e1R_QƓ:}SΠs*)F°џ (-C+ګY񜢣GDBdR8)k02/YQ:7=l37yE~gŁwɊ+:dUFH%s-QbW@ /䁿˻SミFZ@-P𚖂1$*}똥͜ FOA|3YB!1s3!v#tpcUA&JAQY$ t'9DgvC }|I ey-[X&"S*r2P" 2QT|Oy.h;AhءA[O{4AW| ,&>@U CQJlRe@~B/5͛ѡQp ts^fXP܈gR2gl#Ax^tniKmH6Ɯ䘁e7B; t,.D(x*Kx="OZ"tRmDo =|.](UKX@iP_]>*Ѣ%ku I @rZmH j}9jtj l5aȩ ]r"I&eft1ܗmn ,ՠ=~+# $hEE2A&$7,khNw0s*V U6Zd=l2oi"tdH4<<lJdЎ'Z;`!N|)r \b|XdvKCTS,3DUB*a5)8$U?4i-ӛ&B`xʍIзT3x iM:"CǞA uiQQI[CQKZR,'r$M;9Z6gS} F׊țpPn٬h7Ѵ_y3nXjug\P!o(tUdRغ-.S(/zUd: X6Ԕ3aHRF(i;+FQ z0ﮖl$︍\F~{x]Ž>}Em:Z 0ݢuHrCҝuz{itkX #}ⶄY PW ,I%G7ZZ"Ja{Ci?jj?ݨR-K@1xs?' j2y#WSۣד30Yd9~,I=k'AH‰-T"R Mh A&XBme7+]]w- ؅NК/қǏ9!x%OrCpcMlXey)!nwy)Q}Bp@~ c 9H8[%'!ΗrVpRyMi6\p[֛vj#rkHY\^#2hS)I/u:A#7**acD7#^T !!8&ٌblٵs ~pe@~KdvZjkgŒjp$F+KM0ҵ9FF1X:q F)Nfș@Eߊk/~Z8'V[k{C-,`NܱL #`,AnZ`N-seuZ1h ق> ]׌eHNQ~eiD;"ܜ5[͜. ~^AmW wwXw*t^ XmlBSH0R5w rsr OACHn6`rgk<4 Y⽛>]HEâ.zgWB뜯ٲ\iFN-|\_[vv{R?y3QF/,[7m~ϱ)sK@ia ZB0l=xWP-9*ZjAUnl*'C7R!KkJ&*~ T)gO\옃'{Y,ͩ[޽{]q詂c[1Cml9؃G_4Zkmwr\^^%fMo` 4<4;_ӇbYsFgW_rҸOUW*Đ|װhK8~0/<bG`tg"rgn`Q|[Ⱦ7ԥo7BN=wъͣt\LӾrnҁἬi6ID3>:9 ֐R,@zj~3U3U6~)iׄ$7qeykG;F#WQ/A4fL YFrE(2*A PKyVS)s%pip/_vendor/pkg_resources/__init__.pyԽzG OQM,Iv{W^G-cK\IE@VQ(G;y 'YY e7_EVeE"#G٬WlYഘ]e.fӗljQfEl.YղrV+=Κo}n/պm.\YYd4nú)ۃM]KPg -62l:}8uy^pKY0Mͳm[fMSfUB z)j]07VvQHPyS՜mu_6k|ݬod])ov|f< 0lVf,[K;n*ZbG?U+`Qmin>*kժYohre̋M1M7檙\5@Lv]N&YuCmn }p ak~mSݔ٭J0JXװC})ܻŶlrnHڽX߳fTE$Mv.򦨖Qt`S,o lur)DW}VmGwlֻ'[|uP~MX59Nwuy.eYOV 3LXjVI-T|xhe/w9`(y€QLnYtaM_HAG4,[&f *9<%}} sip8a їu e2Mn},PUDF.+IJ\mRq@vH$+ZTA׹*#`"e *ᚪɿs_Q[v/ŲK{,f;M0Ђ_d9;zrV"j8ʷ967€Wjɑ^38iaPz?yT!:6`!Ű&ߴ A:>GgI 4{> MںQ~TN~Xewu+]·- ƍk TA>kV[PâAs#YO+nt\4ܸsBb5nXb#,y=(֗=~ ,U9> ]q I9O^! x ͱ]D:%vҕqSS\7 i9?zc"ph^pˋ]4,vC"pv D7LfW /G H z[#||B>޶(6 ''BÜUZ +.on&c77 4l]Ế~+{r8ky3XԻA9&p:MK\A&$;bJ6e;oFr BBz5M+-u fv5Ķ .kIG͍$;۪>%VpOZ $Aw܇~E7ptRٮF>˭ƪ.fK-pPĒ63z&៊y= 0u0<$$#_@d 87ïzjё]7+`5o t0fG `OO;l,\|.\WИx,}nj%͒!*cxZ!,$%Ph7E]n&3J?ure%>4j`|3xx#}S@͜t} JU{/u %b-@r-#CuLS}Q- 8CCzp܈ mӋIvQ%qErK=%ߣY$,+Fn\.F.$a?rdGn]FÏ$h݀{q;-r)6-st"+ۍo$,[Ǹ3gvpTP LPd g<^ Z2/pnVZ e=:~LsfY܀qy83bg@v˅mĬo}Lec(,YRAdg#jZ3{-"AxJF%>_0?6hyHDoJkcoTZP%̹{/=G!^l? r 6?D2zXj}rC4쁐(8DЄIa跜{7GÑN)ym t ɲkm'ekP8l `6eCU}\V˚|%X8 0VnO6|Ee[yFʾB v`XA“GǏEbw2zﬞͦq)j\kVL )o`t_y`a 14~FL1L \fiXDC{sl4 dhN;|tJs!+c |b$QhFՐA^KQ:ÒA\Fh "󪥻$%rPݳd9 M_C- t_2ҙ\r)mykXrc3~q& f >#VNJsYlOUYE1tK? Ub 7uݖ;P@u3RoI.uu.ֻϚu\@w8 _CΊ!5I˫UW )M|k5os:~#97 Y R.`SnK*r511P|)Bσ\>< o^z2GOJF8 ΐX~QFVlXjy=-{6厡 o" t׎>(Ulɨr޲`=_3_W])4Q@!^Yγ̙ "K_<2 -VwLBH 9"*srpNï]0?{2b*hU NޅׅzΤ$KΚُu.0o*fǣ.B~Byݧ,xY ontQ!b,^(?L3M ԲMH>OKk=}w)xG/](X-5Itzm?~(7 _Go6ٿWhE \Ⱦ1gD 6&l2uøl2t;vjg"7A㌟A_7h 5uSєk[ɦT)[j:V|10n^u{6Ps\ j~Z(ffcQ|KQp nzj N`'+%QU[F55g a\ͣ녘:@-K* a aqݑq!ʎǠs>!GW+!GФcj$],~G벘܇^8K循6'sH/'vD%M8 Bj c]h덂n t]$WDY ){^eAޘ7PԆ 0b`GV4BW"QEz~IUBA^=ԛ| ExYkӓf70^^Лċh+Q;G- 0pHUGD :ěmٲ ~ ~]G-Nb*\BQm'X B՟#Jr@\ 1 D5)9b;Zѝ]rp!`gu۪/Υs;,`.8 Gc^go0CR<`F?p;+EZD. nB(#l7o.#@jYaٹu w]nA=^(?ZQё'"^Rd8"ZU|NJ0%j P}v$gǂ4bE$~P !DMjG d:gOy>DT]zy'>Ua1|tќz"vt ੎I!m!`8q*YDX5ܱܵL ^bP-ra1wNDo#T|`gN@ t`IjTb+2g@0u `{@u<'Oа|T qs,Bt7m cxΧRQDHCRp~iYNfN bupWѢ0dXW7:Џ5ұs g" ˖ϰcl؄MEFA)K T~|` ¤S 1}(##LNg58Ԏd L;H7ϓkV7fd+Kg"*S6!eEdDэPn (TQ Tl 1ci>Ck(Xe;{FXhYI[Sl $ZaEI!]Z$79-1<4;Bf 4f-.e׀Z7*AtB<6EhXCa**4>J 472pח tĮ,Ƙs sgXQZ[eڔIf2%*֤}$5ZJ`UG}% maE QUڝMqmP;:߲) G-BF<3 P-cqF?)UeY6ߙ ަ[bеG4bMqEF{9 Up IOIL8AsO158nìss”Xr9Ay0aɅź*&yސ:>8N? `1"OkL^TJ#mn<+ʽ(g0l3hCwmA >ʍt8<"}8 qh$A{㐄)+N&z!IN**1yo/\ ̌4_ӌBzcB<_Lv|Buh~Bx'd`~h-i3qú`oRZk'1חRӥ4AqϺ0>?q%I78+[b(>PU8LE4ˤ1G/( 3\ʘ%]$ L"xM0{`A&r65^ HyeeخZ& B q$/ ֞c&,cL-p4s~\+";8+j`QgPe;/qƲj.u: k*boC"1 @h]#A]~=~v}R[bh 45/k210=XDpIA{8㷅j˭\2>?P@i(/2$w@ϡphzABKԆ y IUaM0MCƖj4:z$;!l., sp}C<VrKC;_,pʎ](-:<-^4oqeLO}lT|lW5lO<g'OGrD<5:`ڏ=*.J\vFsޔ! 5"'((yi:$D"$>xauҀ,Zlıq)¥+\6<^S?s! Fb  ; dE} .m%b~NjVG7,%ddTه~4 1"D @k:$)/DKUW䉒~O dpMXu@W 3&\a,3'VY[b/ ē ft}~G$ԛJTg5M5^LG.R?޶ a&p]Fڷ2>g{C5`.'gHYX礘N&MSs Yy&MTsnV-'8ĭ9&[,L.iWcA($pl+z0 d PDG|y80\Hs2uvpϸmZ!')V\@M؁,شް."(s-1o.M Ƈj:RΘ|'r`? 2 xie;3|rZ'O1d[%2l鉹:"u}jd7R#V3XֽٮtmxUw|S,)`-x5ڪΙr}?̇ͅ"6ww=:'gMaQ8G ϐ^OV887igO6DJ/̱/SVW{Iu:ҷ\7ku]':]bD\?tlѷ[¢Rc<{H‹f%kN'|, aPҏ 5u/6@#Xh#?]{1q8m `(kr9ȩX\wY'.=^-eN0i Xvty\E<|Iy].E/fvrKLļﱒwϏD]=2Du׷A910"Z6y.'FN21K33kTXn8eoc9"Q4U>x>Œ} ;0IkzIa餳vu!\=9wF&80 ȳ. 7ZކyQN'kq}3&fd~>.6.l Z5И@"(o mOdxv⠜3 zcg[D,7F92-7 [6*tɦ\(트(q9'؟1vjXBU53MyxMi4˖<@$7e~ >`:y=c k1ߚ-"a(5uy~2G]u\q:UCw\ V8PiH6!w0g$E( G 6; 9KT !0W0՝@|&nh$ߝ9{ a$ 1Ӆ Sc!TRt3욒:rpV/hwv]bYtU{V7o 6cJ&d8DA+_zf{Iē 2.AdH;4!V]D:/v{`(@u:ҵ¸;8CjcI-W|$oPeV}I|8kMՊ'Rbq'Cft.ocT޵мM-8ce1 zbM []W-ĀF$ǙcV+oA =lj$TEQ<#-]dqs6഻(^ c^L-ZFXv n%aU\`$!s?.`gurjޅYinȉ %~O߄ H)6:X-?z {pPF!DT'*-kS/Ċw2ŝ#^6!m OB}pIX :NgdFû`H9_ڈrs,so7#$E02_?IxRLB2cԕ]Zĺ+b5Qg YA.Yza{(-c(UTqx% ]*F+^܆N&㽢8Qu)[믝P45C3JD]Z_=׻s{J932'E.$KߟA?i du0o] Hl5).uM'NE\qGy_*N60Mkpa,O~:3yU$Cj=jh67%|Qek% 3\K%"tn ywi%)g6$X]oߟe~#psU92 /r\_ͬk6[ӌƔꚬ *_zUԗ="rӿWI鳿Y}4agcVh'F;%9l(|+=,# @,46̉ yB Q[\d8J_@䦍>e2XP&`|1;r@ ${:Nem`%khc" =Qx Fmm{FٲDpDTA=ĞO 8̰{)]}ÌA%lj,V W 245C堵nSOEQp.;e;\k*sv:eE0_HŖ]l1HǒTfe;hbzʰ"zlk)t2-w@-$efcqF'bN|>qbhz_xŠ'ײXYz=m('r?ٙ^Os{IΟ,o04KՠݼsJ2E͸@1G#mCh6Uuܶ*xsl0E_0R"([Xu-vPγ ⺗]ͭ!_PfG4V ttz3Mӵz6Pvb!MO/K\CMDxvATOSylREKr &Ӭb.ɵrTv"CFY!,}7c#5t !1swmp4$TF \n{K ?-=XyS_.NY6WϴX=+MݹڥٌT<{&ռw ڱkcKνۺd>:7ͺXF`4{d)w(dqv"!T^t=> bMq\VKw4*yUq$jG/F@5 ?pv 3=No%<1v+oMC 0G-~IND=nUx`q>W88L#8E( X|9tiL$3m?;ס%nx]玎cl*Ż[~=Rܬ?{B+qxەEYŞ18a iM 3or;1ID"FbSCNͮ׈~F1NȺZe{Čz<t4e"G3@._ke-ݡi&jC%1i&l["$(J *s_uJNyp_U]X:5#AĚd b-V&N=LtΣ~o:u $wsv48=owpmY;kD[X-n2.j#a]iu".|ߒv*RN1ZD$FS>tށ\}8Eζj5ʭMN x=4OA Fރ^>py*6zǮnG⪬>l0&(//'13wƲr3<~7AJz"җ06@+ Fztg i!^/{ ~im&"o=n77y .8|ÁN X}vV1fv 4 O"=0IIg 99w;+upml h04[dW &R*flv8àcsjX3@q ɴ"J!C\-vu D@V/kEhqp&瘻٭)ZxI6A  ԟ Q4Q#&H{vyQK>>t.6uQ5Mɯho<ߓ 33SS2>35r0ڇSY0TGFeft =[g/ԉ]倦]d—+̌3&>!9$%u~XP#}e-Q/y)a8NU'Lioam ͊ I((l nZ4F'bA J M$~Y4)gL8{4e3Ln j`}9.e:6@>KXyℊ`%t9]YTyoN4x@ #+iahǘd%勄ZQ8cx|{a>d=>uFԎüV̚_m6Ç- niesIUò~Nқ&S͎1{૯"'"՜@ M߅;%`bMYUCVǟ yxQ͇MACarPG!ݵv]ta̱b/Ct}xرӖroonkBa?^wktxP Pd[Az<,5ԃ\"% %ԁk!w ]΃sp%s+~&&k؟ iפּFk 48Ԃ7I<_0C܁l& y7#]=1.H6#RtȇX)UUZ#M'ڀGjg`c‰KB,j$93;ci9[*0,t?m[X޴XmIgKw'->[jJ {L w99PĴBJQ$j9^06+@O`N(STgX.sb,3ތnl]!)ŒJ@YrwD26ihպqC{&qLG/S{q̮(18 (97MgDt#fW8,8p{>!L?C<ΛɤN4wrk_9;r…GĶn'!rjlvw3i? GsF}fGpGpJީ+J"3Tf- &s߿y߼K 4DoeU_\ k?W.WN$he,ٛ<ʕJ$DD(tp^q$Yh]n`& G=5j(W{#'Aî q#osc1$-#JǟbB-4|%gCkuy緢Iv T:}G9! )[YWk]4˴a0- gK 4/w`K~EyţEgayϐWOcr =^xE})B Db>OV*c?A |($e冖aB$dt~k;!nРPCzF;݌=z> +jl.&E+r~ |$9'vf%ȜXpvjAMBdY]a($] s829λCdf'&)/+sh ֮/DUL"'#a)J]s]Gybzbz*%B-О|Hdԧ]E>r'U{^5~Ge#nUaGܻY)Y'f:ff=9a2X`R[ vU70^` Z$g?QIf`m{`ip1C> åhks.?-/a,V,uG{߾B 6;$^Zv`?,jV()Q&A+"2C"ڬ(USKgH&B>aT7awXF)T7)=$a}?:{tm)ꓺnO\ogV}yj M{W;oQ;WJaFjaTM? Fo `{阝Ћ+cQ{[⎓0O7leZBU3 >;Am E[`r/]'cpb\cxѝ[L/A!\V7dP{TA=MM~Al8K|m=G$4lQebբ[: :v9sA)9|h Oڑ$N%b *6c"DN*tۘo,k<u. H*d0&jKLLI]5ޞ ml~;59odq᎚B-Xu@LunȏvlBBzViBߪDL-|Fvx"Ѯx__b@[㝥7r󲞳 ZOfPqM9HV$j%IH!8ވ?Eiop/*1#yLk-͆\hųfز\O89-~t _ܭ5P!D 5ʐYwgTeS2k!4[ wuc;wEX0HAr>BNBlN쌷TOtЛLw{R],Xjrӄ!3="Jn)2(#=ݬ$bUno.0C!=LjWqQP|ƢiI4 pY3z :'H?ywo]1 !YmC[=Fa_lb!Lo8Tu٫2#.;j zbvumcLrߞw>g\$+Hl2 PCO =r݉Y<^ky܀Fhy{s'wgNJy0}n[[HLҜsq%mbS6 0pcgW%@)dDZ|T@z oJJ,ii`_e^r0߬|tgY, .of֌Q T1aĉ_ t݁ `6Γ^s)hcx)>(]f_B4vĪ߸Ӵ̍û oՐ:n[I 7 D-oZNl;-A4]~B^$x*p[Bn}Ff )D_5+`yc.M?3ё؄pTxF'_Ie%*dGO@Y=@.jF{8 R!r<ݠ##X%eP @)ol։=Y񝘘١0s%L,5~ ~5[MޅADZKbV(e`']TXZMuH/^~2O={Wbd;L +RTfs%I-f;gXCIF/fKX ;2bNrggb=)AY{]9E% { `7P M{&SNJ#Wab=+OׯO'_}7vwFG f[0gUx!+mb0@lܥ+Xx (t?y\Zwr;Tp&.܍2V Fq»E(fЇIrQEԥo,R!׳1 Ir-9쯆݋}+&j rJ:VחmY ,"+G&eP2]ӟ WRfruNK46tL6Fdyk&1HcZ#;QEY ^RW1 >Spi0\apY^}&E=1RK)vE5EWaZ@ *RnjdMO>s/*FUK Q`H90I,޵m- Ӑ%LפiNsB,g7Z^py1.\V!. lī8Yi2Fkϼrۿk ͷ1+C}XtJCݜm3vQH>;[PGsUUJ3Ϳ{Ӂc}}2)&L^4 ó&vá88*PN1vGl ƗRt#8 6|b0a@GV-J$$N_,|i{V19@i֕(u?wT҄\Hp1JmGuf o&sq*Xo>>ᕺ%tjn&7 AYSV߬5' أ/>{D?OWqNs۩k(r隂 U e0&YMm_iSFK5%вXEMaLG8і3L/PRCh@crgZ'v*8 R_. Xq";S[{j%f1iF1wϞХ/SrƢJQπS\_qa~``ǎ?/f2AoA$PX`SG[(FZ@y$o5b+!a? L#9 -HIh2L_1ԹE#4J~Sa۝g Sx٪c]6]RGɎ^M܊OPZny84o Z m*}w=^TqP7 [`@eU/>Vߤ'S j|\ VE^(+WC2@s]aEv !;ڄX-)1^qJlwy[uf4r&ƞOȌ..o'0Y>55]ۗ}̞=#cn󑪻ZnPm<:U$(H"ٲ*n=E҈! = =%Uj\.|Qn%* z'M7%oS]`0V[]Zgwtn-#)Iy(t}@;`ɞtȍeOPg{kH͙+dY5 47CȦ2>EG@ߓ]yAlM 5.ɰC49b͙+M˜EDmdkB#ƟQwѽ9&t#4Im%G|".-|je]נuBl}DPSYȨA~l$[ı?nтfX_>'?uRV@K^3Vᡜ|:B8CpCJ6-( m1AG Nt>{|_ Bkΰt89U `o|6t<_OG+Zoj)0@UizǃW6͟^}/^y C})k60K13#(B%"dWca_UU{2I){mV@dB4w9oQDB7a\'_a3(5SuMϨD_O,'Scyȍ7>X wu4ѷ`dg`lSVN_c8Q[)N' !@,&/qX_|U7^v(Ob$QP)+>-ְorJzR*9Eu\} (Ir3&nD+6}֗[ PhVy36ܘhs啡td_~Sj7֙R2>ycZNj8%zƍm=Ӻ-\K76!:8w:~sx#D,IELavyqKTIId7ۖހ2% +'9LPİ6#me2'G*cɿ08 n1eـp5e`51877擄>No9eŽ3 P`ݡ`^&`?dx' 򿶗kalc7+JQH]'Xy똥te2$E-CQkSϵdV:F}+wpbm/W,U k Yb2nUbd?gu& 3nTw!^D )֨_mQ(&ZQ$tYI#Ía2]b-jR^'j q`$԰Wnu6˂;e5-ݾK{F9=ݜrS61Yi .9䛧+WF:@MFα6,@0kfH:NtځF7NY5r@a]v,=uq#ld ux7 x`6/.AeFm4{1vfm<#8dw|G=(ABIX wv(F2&3Hb2ju! ,Ƥk8=W% @cbIﴍW4>9 ?/<"wv=taUBiWE{5Y|%|lj$&^^i (Շ0H&7QT$ξ׊/Z')t-Wt$YORqNA]~ܠ՞A?m/P -4!_/BLGzujA: 7=)^܊\9jm"UEa_H|Z75ykJQEח7D oBnH+ Ej^`$pF{Jri)эĵ_pMǺb?CdVi!^v8 BTή)菡{l3XKlgva&щMΣF:shI>F:+c@ԥR`ᴻSfFے+^8' -Da+" wZS==_<CGf^̔3቙ !zyne^[MngWBheA)J>Y;Uڊ eSg :Ez5Ρ7uUvcuɬ#MeMmˉi_kWպ^J2ҭPrLT189 aQbȻܘ5Ie#LY_戾%=_3=򋃹P" s#T~}'9/V5/AFݜ::)&Q$nSi_ F0A{8h];+d`cD(SH*"9H[@5WĉZ G }Yh=>{h0WPH&;b/}5` -.mm5#cdWjz C͵"s_X AvQ\Hcj']]!ǝ\.~X$⭖c@1:%ub!ۮWvd:,ԍD\y\hW0 $kLxx.'Xٖ}x~5.IFDoRZN`QctYSj{f I$坲e$>}1u% ҖYisk5#0:.!\ +E>WhQ:D)Hg0Z8^=O@g?"J+_z3(dx.]NӁjM}yx?z4f5<ZUZ(ԈO)+I/!0-$r)[u!JX*g|DȥP# [x _ : =Wcdʑ: LAҧS@ S!#Y 7 wA\_()#T%P,]T$"9!> tJξ6+Eȏk&WNIhGDBi=xNdnp F]"euٱbw? O,qc7KI?o_n |iTׅ}`NNZaޒBo~V~#.EQ'+@rCirP=Ze) <2EuBHi'ڲ?]Cn%Hbh?T@nb4W8Ѻ2dۨR̛ O/sw.G.gBЀS<8aŨз~:0EYCq90گY/"(f,E g'Iܯ,Z@@ٵS͚z #Gr"궩FuU]ܻ?}GŽQbR郱2 ;tVZ]Nq[j@;С}\rn{W%25ru' 4IK9ҋ+#n:gKwݥgzvGE^~'UE&#V.W5~XX;jQriWhe!qF4KWmfY7X / jTMuiV R"%kf!tb8pk@O ^: {hM-45:qQ'5{ )-,j8{/TnKOp7R\?i99:]FB,f+-cV}*\okM !P?T[JLz#5th !b[P]ʣ^j%NgVԗ@)nrenicUp Nd  %u0Ν~6P3Gz>)Ғϫl^uP}4^ Rvy^4V(=Bg.2S:Lyf[`@ jG̬Zy;~vĬ6eЍ++]6,)0ǜǸOggǏG|>쿗Ei}뉼%XN.iw]TC{b{ Vξf_ph7% bR"R2CϮ_荁FbwT'g瞊k?tҷK >Xý\,5_-`d3͋wO?})ϋ g? f<@ypgF޳8^:o<#<#{i:*٧T5;^U񦨖]4 ئr0W45q?A; _IrH&]yWϤIyt~tcAQaʖb?d߄Gp bidHtv׏~ő1 bKNmYo]$d:r4Wß9; _\.C( @*kF# Dj0tKcOii1m5iA$9eB@边񮌲3 z FIp1')fH[OIr&ע602.7qT]#wn8 $`AYP?{K3Gi|Jk+9fZNvQX&+:ҰGb"Ŏ/C}S#ap)AZQ_s`iM̂72A@Rc;<[8_ /EV:"Iǿ:lE6\5@./|;#l=48f@nNZQKdMi Xp,RuC[ՇtlkSe:ʞ}i F&$^3NVd߾:dGa/$|2Zqy(NvqՆ Vfr ~`; zCp uy2QRKI_~*cLVvӬ^^vf1_"59+N 4 S|1a܍ܷ^g^˞=__;}oy݋0}KF]"e>R2bgJӒEQWsy.یWA4i8 ÁዽC>. 9= ?džSţrByR(ZPLF?GSLZC|*bGVMAʔÇJnI\짘c49m23;k6n_5ـb6JJy/j&EbP

*z.CȓRj*;޾ dC:ēH<Y7Tq=r~LL+JsL'A7y s),\:fxj- y!'b掩~ ȴA1VAh@$># #}B4T˳͹̝*U汥AWLb)?hgZ)_> _РJ14zx.v(L[8͇cnh`o^{1y7C@_328y\гD+A,Kl^ۻ.ڴͣ9e_t8,7#q'5[o@ (IvUb0={t*m@$D,efaxx&O%*.k 7h`9Df 9V@k ҫ<AwJ^ u)3n@sVBۥ(!e0Q(2[YK~܆iƬ|xrqiâb{4}-:M,IF|Ӻv'}NSL[8伥kb#90[M]e@(5xs"$ j+@&-8Xyj]cR`렄 @J"tJ_?=}isгwȸnQ;#ǥ\,H*aLjQyI I8:}sM\ S19"88qT.݄a*H=I<%v$tx{lv荶$qlg5ڼrvuZw7q5b!8οtCLO0-VW0v uuW˧ݒVyc-t? .k=\PHd3C~wIT 17^lq"iqTH'`v})b^@φ8$_fTHLjT]+QD7hU80H)>Oem9jj@ih=C7JqJMR$fEdvH4Өmir̢@5-ԨF6]"#,]mIf2 fB_OZи%8$pܜ:s-bX!8&:@F}7sh ᙚcz9oN-ɌPKyVSa$K2'pip/_vendor/pkg_resources/py31compat.pyUQMo0 WxB%֮#v$vNU ,$SDV|d9~T{"8V4du[Vj\ |ZY)epeh(pYcax&;.?tŘHI $=-_U"{ht~ixZۧ"hb}GjYغZtiC6x'yTA Je2K2fNبB c=ϳlOlR|u8MUeˬsR˘"ϧPΰq; {;帘[e\zXIv`1A}TB+PKyVST;2$pip/_vendor/platformdirs/__init__.pyQo6+Tތ:t[-.Z*-61IH*")S.iSsBt:NIaTR"ЊqIxAKZXRVI銦(\!FJL..Tneʱ3t.uLspk!@qtӥ0aV+ Ta >@WJl䋿?K~{w_#4$pŶ"-tU~|,?(,z*=hOb\?װWFtM$F$ӡ%IYDM`E4Be3d`Lkan?_'׳,8T;rH|~VAQ*ˌsF0B6FHV葖?aŒ= 5_0ps)pʚQCgKEYUms9'-!,%D9X=6&eKcxrhWtUsNJoN%} j2:Eoq#晀( dR-zLҊ'A56lj7$U0>ѱ4"}&D~)aJ]s!'hXg;QЄ(mVyK s\|V70hѼYӳtYԺ{6ۉ7m =g]ƚ=ÚfSLB@\H*"!eH2$7Ah,Դn Qdajcjq0*(- Y'r^URs`um8/jд=0݄rE:3T 7IuǤV}#uHgK=^w{VÌ ZV]k=Xs\gȮơimv@ Syht1͙GC*&;))h(=)B;KI r6O 1x:zSO!}w>vڮ?A )BPmQu`uh:$3Vw=H*p:ճv˺ڔaߪؗzϨp?_]׃?iLU]%pEajϛP- ! X l\"$y$j=Eu+wz wwet5xGھuH͛X m:#޵CSۧm;2)wINPKyVSV0\t$pip/_vendor/platformdirs/__main__.pyŔ_K0).yY AQ|S !.hަUEa>sϹiܒ}H#_z->zQ+e c_ׯPAF3b$nv.NyݴZfߚKP]$NrB5hLCyOUsZ9#6 wV;nĬe =eI󱿳c_HSXX E.L䤲obWjcgZ@:~cCW?కx6g"} Ǒܶ C~x/ň7R0mhxHLTncPE`CѾYPKyVS ->#pip/_vendor/platformdirs/android.pyWs6W!@=JH ByYIڮJy%5OS:NI^#{7ѹ%yiA Ih|߷+QYFxJ!NTR8[j]I\4Ic$|ATNOޜt:Mޑ{VDnZ' *NJV`2 $>.ta@b$Iho+)**>e478#h,PGMyDR]K># (JhZتSc"$`d^EgʲitVaȂڼTEgU晴H_Lyo0(ɣbg:z@lCwkHMڊo$\#>+yi0ZP$kTFZ}V/v%؊vLvvz|u:35}XN6ۡ45\N=5 m]x_ `dncN p<2(3U]A\ !_4T &4E1h3gW&7 oK?FQ2#5g܄%mԗY CTUt>RwoM'Jp׷s) {mvKͱ0o`{;a}>G =0M:?0)k%xzۻ/o^|#=]Jcށꏽ?PKyVSj?}:pip/_vendor/platformdirs/api.pyWo6~Wn4Xbb 6-Q;Hݑǎ81Z4yQVJ[Pf"4fiU['~isc5KlmR nqW8 ܴ]Y$+d"2}|)dLY *6%A*Xk&|9pafŃ=³'IR0c`6S,\eHppTa'B*-n BËlXUIVSKIc5c/g!?Xa揂hJ/R.{} P}&,_:Mr` v_&tȸbZ_9UVN }X'>l v}h'6ԉJ& ٢^pspH]q wP, a+KXזlƵͅ ؈ +fQj+i[ʊEi~C J֠`ۭVV+!.8Dpu^J -\Z LiDj '6--©6R gaJ;q妔pk B"όO>VE L{W}S":ձr 5҄AMuoD:e ( V[3̜3IwCM  3X׶oZrK%޿ /^oQ ̸M4_7ܥ(?A/A4Xr"2;6þp|±S`6Aa`E9v]lF|<=^F;vwmtxУlvQ6C_'‰t\gzħK5FruTQgl,#h'28#r|Nd8F[yzdk}p, Od6PKyVSŪ.k; !pip/_vendor/platformdirs/macos.pyVˎ0+Y4uh:MUiPRU%&Ėвvfnz8815_wGPKyVS9 pip/_vendor/platformdirs/unix.pyYn8}WJʀ#z`$ml"IX͍, $ wԍrgs9saRz~RϪJ S|VP$y\H2|Υ\,n~R1BziF"_r޷=3G <2,>HCl `SvuaObUJ(!gU:B1xĔzQ@n!k@(n̾I/豩3ED\|rM.cʚaOtPRH%eْ#]m&@uE@.=|z1Z ^B8+Jx`5\v,%kDZQ:ֲ>o)y5̇? ) &s hȊi|Ų`[i`B! %ṫuCsq7tVm fa)B5:e227qL̓!@9:;=>.7)ltd4pP#^=ǘ7~ m4Z$/~kȰ F{nX-6?3D;a Ge>o+pa_BMY!pq ju.c hF`LD h+hy(>ilKٯA.  Jr$׊ 5Sȗ B Dh(M,|- m*Rvd

% wPAy {^);# v;vhMu{mm͎AMA`|;(vE|s{r4~Q9 */EpG'RQHqȇEԜpRMnlK/>;@`s.qU;O b7`U[Fz"KH5:ry~u{\Ե%rfpuhg[%n[v u*:^,GTRdC|d(7hY䜈0912#`#zWWowOD]Ҹ}4 K&. "6,j"Qz_^_ 'Oa1u/:3BTJ$ 㓟@r1<$D<nb0NoWRvuusMnlToJ Cb,Vˠ2;`# q.K|)6tØCI}Qu}~^a6aV;m9sM~nNr0Imӫq`2K4_O$]*8تSPPN` CX0`4Paa#Z,8y5I`R$89{ 0HHtg?f1}9"2-y WŐ[YH>ͲVۤ?6i$vQu/׽RM&pU RFyL+6Ì r`p*^T'1!F746)8[x #vfT!&VJ6ΰłϙ} 1D4hxO4 T;O51PUdL/sE. A(Yzq$FFuZ VWNA`n3߭ߘ &m;fyK W֫!]dQi`3}o7d؛U>+r)+ Nejބ>);4``+5U0nkPKyVS/ pip/_vendor/progress/__init__.pyWmoH_1JT/tJ 9&Uu >v]^ nsH1,3ϼ̳)6$#tò|3A_ބO\Ї|deˏ)ݳ|ɅYJ2`C8e~4 ǵd$L9YC` :_e ª֒f4,g[a*YxLM/hPLS'^h,I35u{#F,ϖ\,/WY.Q\Ռ9JO#R!ʒQk֬xId+uDeb/_E'ŪPB,{2#\}pG=J׭<#*> ^/*J}Io^\_5[7 ($tS.= J_x PFV2 ^xg/X8:%B}Ʉ\уժтEO[h92 ZR.Ͱʥ 0Kf (& /!=S{ B :Ơ->*Bבf,X|՝Sj9LGKa4.e(e^&:afb~؈2$w;|>+Dt(/vC:[6-.ݻFXイqY[Z%G.͚C% vpj]noucvPԓPG| 4KwkPbp(㌕+n% >]?C 1e>X**;MYe;_)}vTh6ftZA#X/RM}hD]$$ǴO3jC5aIWn)FPگ|w>`'fvY%BwӺ[©5c]G*ف| T B0ݜ9LCq 7Xm,#m"`<[ TVk6G_C;W>XK;^uTKd7lH0h1VR=yo+ iFa%LVPE$Rz]F'F;v;m׀fl#+lq&eA5uG~hJ^[J!7Q\ x`VZu[%5mب>p<+lŸt>udxOvV_9(PIGx[;:烈GVPKyVSC_~ pip/_vendor/progress/bar.pyUADWRL{Ɖ52xm:Hi ]'& -dhBqɰ761Qkv}o:g~1ķ#۴cj;*5 ͛y~>߮ csZzb0`b!Yп_Ø0^P&J ҆˄-71% $F.VW]XbI59Ӵk #`\ύuN'Ǐ!(\s`d=:t .2l"~9C6ОcFS׷JĎ-y0 )xAچY` ;U2`{UC |qh.|gc8*588 ]C7R5Y)Ф;-v{pBּ3%MIJtY3.NIi 0{id?.եT*Xd*Jˆ2)H%fAlĴ(~55+5eJʤjNxI+6VH$}UIʶь3!0PϜɗwDgnt*[- w4ǜmQ}6R;۽ntfOmjA|5GQ['/,ڦ٨ ]d^:w7ws.TW@LP-#Dnc qe>=TW_ީO?TS/;{cQ.z.옗uXIg*X.'S ]I1$V$|Ss7Ϳ>?Lü3{0Zuh&߇_-q"^m/A 7޷ C#Ix Ю֖ѯHoݵ!Yqכ^q@ka:q,C۩Fehk;Xe(G<^i #6މӊzZ 'AZFk'ako5[-tרǰ>6f= p$xw\ =-Nc/PKyVSN|Mpip/_vendor/progress/counter.pyTM6W (lZ)\ Ȓ+A`h-f#EvJh_!e(r(GX?.>V\& ͭ۝f~fB-\ )^_ Y7zmp0{ٶR+VX a**5Z]ٛA-4it+F`EYdW5 1ZD⏀J_ ܵ qiDa@*(1hǾ|N)(0,d:{dAd &G1a $YIrF9J=e2:#2R$I:Մ%3cḛȉ%4 ,f3_|r>F HR4Ɂϝ_|]P)L H F!hp|CF&~B5,ևW}x݇7-2P>s2.qs ěp5g=y._|,R <9Ə|>a}PKyVS.pip/_vendor/progress/spinner.pySˎ6+.2l* Ȓ+(, 4 A,M@Ӵ/%e{&ݤZ9{-GROmN PM}#R8x}- |vݽ~fss*hFXa Ĭ!m443hn2-P SVFl6U@!b#ΪF>Z]\5ȮԵ@ձJ5r%\WzkideiRk+R8}F <و$9Y8AH، C@UBQVX:NHEhwgbĢӱt#zï{ u+\4N`j@J.m>Ižp93y8vD,EQk+Q~sʏ)SrDqwD?Qqtm6^ܼbZGDbՎr,)=(a I~mNN*Z+9YEA?\PقϢX5RR_$1?<5vG;+npV2\h7|_чQGO\u B#`S# &"*V\=5"ÿp|M\;x Ęh: gk'5xi^;m~*%$"bV_ٌ^}7ӏ r?N)opz<ӔcIr@lPMGtNGp|} 9ϸܮu ;=$2 rEHIoR͑Pl:TgԐ5Kτj4u2|Ip0z ]'iuէ.x+-M<ӃD{]zɖAvUuСCD8W6h4zW}22(@]Wu6lavOљ}H[xn%Wpe \=OTlP@jks_,u7n;ƶqH\+%J۪ F6'J~Mm@@Fټ⡼p[9ek|JźzaA ը'u]G[gx39Mg%<Z Xێ߿h2@}^\`P Ab{ Uͪ5t~7߿qZ~i7 $` r t}x?y7'{obcƨ)ů_\縘8WЋ7{lH;X)14k g O^={*w:-=(^WGE&$H^)xhh> ^f{I?zHw%#@s  8Ctz0$K %r=S歨JGZLj뙯nŏuOKGDSOs Ɲ50,aLtH0cl[-φ)wNjԆIJKLe]:|x)@th:{J!m~IGG$$]Js̥)dX+ 2}{)1yq-nCeTW¿D]F D̼xP{)P.ex'ۺ75ٙ$˶^;({GxD$yVq%dܩcDG]= fdśG38 PKyVS&e#pip/_vendor/requests/__version__.pyeOK1)zMcVARR 1;mBw5J?IZ̼1L"V8$zK}5$jiEε&c#}$z5ee ,V%lE4Q!a:=EvVzmfe&fK) u!%B|eZ`%(B%UqV|9gp)Ůd1v3(~PKyVS9H'pip/_vendor/requests/_internal_utils.pyeRn0 +dH|i0ðuC;MlɣGIf:Hwpx5>=|6"г/*c lz9Jݓ{6 zφ'h8;͠ a~ԙG 8 „ S$=3A~R 3Qa?{@eҾ6jW*#u ڂw5ISӣw18ȠHѲ"%yfc劐Yf|9uHh,RVTCgZ{b׋RNcLm$9QEϡˬ"̓e݊|K_^u^f#r4XqۆܳQj0c3~5ɾEf N%h9J`)D$| `0iĝl5Y;ObC$6Ҽ (IS4OIz 7m̊ۓgEjPo~>H/7'_Joi+|_xZjzDE77<Ԅ9X*X ?NW^_oO~0 LԢHb4+d{Q"SVRي,b%"8"I? ^W+kp& 0V1&4ہՋd v|JYGZ.*KA6pͫcF-Ez JPxia쑧2EqFPA>^%Wm2U@ `;Y~9qZ ߥ޵МIД&KC*')j<^ԁJP\kY"t̎1 "iӷ਒vVy&W f Vf(85ϔ,Ln|I 4yq37aVh|]B[C܉D+2@{C]uJ!iWLe ^X]^3]=H[B?* T;!1u o\uLFސ$baQ@f`xҕy ԝ6Oaʙa$= ] Qtj˗/ ΝB#[ J3L#r #ݩ%dR`Cٵ:3WvG^B3;PK[JSJUiiXl&"44Sjtsq/Ab@OiST>ATtvr$uTa༒js:Sc*%`X|O>8,r+hHa d|EE`!Qi֭tAnmmJ: f3bwFyft4&mwfp;;8_ѽs5I1\Vy5wڸh8%=QT*:u/tlUv=";卖eߊAJ2BR͌*i;@(X 3Onup ]ӠZ qM/=som8%i`a>؎ZEciG͜U5Ppw~h|w.m7$AG>߼*RARjYaNwY$aŽ:PϑTǧ$SSa"P5ZB&(z afӲLdo :qz7ScX&4;g;WW3ϬT|",N˳ 긝Tu2z}l-Yex tSJ|ۗf;'3ģ$6myMWnqˏ1SLa''QuTф!^7 GBM OnH0y:欄>$PnxhwJ:0 -p+qE}FQǐF R.c vsBuvoQoOPꊲ 80 :MV`&9 4uZ/eoELZ`sKfQqNtUnzqeX80B(ȓѐҿ0l 26G:P#]C2}5A:!U *j&º{I#TIb"ybۡL%0~ sIlMdf*@_׳TR~P̩=m?)8TQ&_x4̴_M&bPf^8UFn`+eKqwnʶ*):NU^V::z i+\+|I.0dkb ~[K+S &Ղ5ʐ-鱎65#|k  [|X}4>|8,K{tĽ}.Џ~7w%zJ³g Գ(DV;݀CK{b Y&-[/mY2 hܠp;rp-K ¡W#u>AUfL"q(G9=2SKQ\#y.(NbkQbO *VӃ w_v5m\d3W,8O4vk+AwkT iv])7bm@FӌtKceŪ>w'tO @ᙲ2'"VCh3{?RVu|#p|!t0}y1A3 s-.{?ʟNKQl 6\Yߑ4/F-=Fc#3iL ,fBYH-6*pgH=@Rhh6޻ j$[ dbz?ʴ`t;2uY ?SH Ig6"xum*^xik]x0Dy/Ѡ!c)X`cPupuHdT薹ia>8([+@8VHeC.XmĚvW$0Aglocwll|ђY.tW6u7=:Dt4n!eDgim_t {wN2M$?Bno JeoNɓ_+4 E);^t:ӥ4d^fca %« ϏNrbi#%}y%-e2`H3'J;23C3*!#Pc*LIq~/'7*@lm]^~aubQdH $Dq8<{1F"o= (GM1/P4tqv>=>;?l_Ӄ#$q|8>O1NūǠhmt߆R2qz=LRlEL0[[ʅY,W@A%Q5PnhO>]2L^bVt,<}x99;%cZL3.%b&s87+I9DKsܳBWp7jT)#y==Bs{ 5ck$LD_Mj{dG[q#8 H/~#fԏD%@j3'nchzw*]В>0@,C7_'Cyz(z:9jTBYnW2*'7=ʠ{o zb37Xoq1ddIQ˟ʐ ;fBE63?;CWȇ rX;4pO.j85WPݡxS|V-,K&jE(PXJZ|p5(Ə4><[_PKyVS+s 'pip/_vendor/requests/auth.pyo6w<Vk"1dMv-ڦEzm6YTI*N6>>$JCwސ~>b~L*=>7^IbJVzg.\V9BS^( 0+4PEAL*2\x~/K!5?I4_ 9K ( _,fR,ɘ*1q㧏Y 2NIJ/W2/Tl@ HC+Δg7Z҉L 6fyVi8S-Y֢܂12dSދwgߟexzŻӳS2"-yfZ`N6|?Dek\RM^7e3$C?qP%J&=ӕS3"@"VԸH8kCRV|>y\B-J"fv*@ȍق^sQI_rL@nHP fلdhA #2:;X ɛ*j߸ i (:ZBcCL cs}1AxQ+FIdЊ9-5(~H?g Kbg4܁ )>ksQ -TB\s&QUVߦee$sR`_36VT53c)ſE:Fjɑ$$X0*ɬ8cMviRsTi?pS2hABSqbof `R?y*1a&$- zGߖ?#Oro8$gLEsZncVacjk Xq%5qЦ XmeȣN҄Pqt_/M;IRD-ckL*yB`90guyS6ae`La  8 3 -Wwb dYX>Dr ~.+OMϤ2LsY@RX \)͉^F* V0@$cpA:\ExuVI7DT`Cך[s^S+:GdA+€D~-n숵5 ޞ>i".9y9xJaϪTúe(˳rHAjfܗ!?'믇>|b-OaN:x1Jb*?ŭzZ)$to70ڡtcC{U ~}jIeQK 8MfpO20V s!86{ [+N8^f$k$;t;={d·bkaVWp\k DN:gD`?5fx5j?F`ɾ+=ٰrPWSD5p&wT4gwToV mI#)VՁ[دg$jJ&$d;V*ֺ5 eH k>x2z|D6jLDe-R662ɦ\')!U{_wӤ]4I$dTAIrU3];5yau%b*8^!H-h適\A-6pD6ojȫY,o`› l֥:ߟCUc/lscj< -k(#02T*$&ɓ!Bspr\["{[<r.ffy,p7*(^]:z>hp)F g[I*|sՋ.Tͷޛ\ وַ <8%S++ot~f \ҵXtW Q -%_\q)ZR:ࣥG0^tm'z5YƠ=vhEڤ()u̠C$+n\>!gcxGd>̕}ٟ1>o/VFlsrLCfK= Ifsg)PKyVS':Gpip/_vendor/requests/cookies.py\mƑ_7N/w ;I{ڋl c38d;t 緧^4"`=^nzy%Rbz;mPI=3}yWVu]+nokk)6WZҷ1_𧆿}xHk֩r&cgW2GB;AVE~;]w`lVԹ0kd͙{IF]U`+0l}ak*?a>\UDoU)NRJAH$wyoطuU-Dl!J vU#^v8ӵ-F50$e˫FYySopԪj[,4hڦ>f{]4} 5U@a nyýk-+Vvkx6DR+qNv8mOD-QrXc^ U`MgVMAݫnNA-aZm>?7wǽRC-Lk,$ѫGܷ#mdWELw| ,Gdܜxp}!u$XHUdŻ߈|y$0= ({;] LU `a_*JPGV6RXwǶ 模x`ׇ.3=f!dfPI_yJ % `W dS!b&#N/q\4:'Zd etU6Jz+%w-RCFkN' pu!؁Ew瞷G3(#Z]IoE-wߥ/22.ee2 ؅w@ItSۈFҺI"7"ʺIGAFA vg<@2ɤvl2scKu@ Ҁ+]Xd (aŌ˒%Z|51!29a4N/KJw&{9Ha3cL Ǫ$}]Bfk~eeElMDe]SW<5g±B—}UpE 9hR|2(nvԀI/T!Q1iBptZ&AeC<*/޹NF Fj;_c.sy!XK V%N&OV, ) `I'y`(MG CVOi g²ߍyS;>l޿j+g~o#?iCcv?q;ʄO3ݓyŎӮ5ʼn}&dj"lĪ+{+28jySu. j8&|Jgy9O*EKؾ6qzB:|ܐk6`;T2+O nA ɸs0ͬ(g:@ՍO ;%nY f'rc.cQ8ul[vF>hz cc|W9!@ 7P?*<*#*;h{ȍ1ϹYDKIk'*'5h4Sbw{n}T) D `T\ AW0%bLjX\K#–h53u0ً^=9y(]=!!@n/$bxzg TڂV֎g30#U)5dpLr#|7I|6 {~R1E6v?Unfl"]:痑3|k\Lu盇'!eJB)d "'U q6IAm)6Pܷt CПn^K;BI|9j6A#ti8MԢ YN3$L`[NO'\mW`F{+h?LuUs 0sܬ@4xG]m#\oY0'mp*h v~ϼJIB^|h0{/5274=XQ..(E_ b*fJa,ů}><" }q9exLx?5w(OO‡ZBT'} ^"W%4`1h(#L6n9pȏ]L>wū76/Z͊*UFbϭٚ^BHbݜ-ީkܻjxhU z).u2=L% mTX^k & ۓx oQFU^˶AQ*K:^T~jL'vK9]ŷ:z[۶arGr g"MqD 8L8S8Ux5h;|yhuwS[<2EgQmm"~JPKyVSg8G "pip/_vendor/requests/exceptions.pyVMo8W CѥE=ih"mZ[Hx흡Dٲ5|2#^@WJ\\\$yKWF/IfrPYHX vHiXYSBtZֶ(%2×睵ƂpQ8$ \之`|pE ʥZצv(jA!gYm-JRʅLa q#jW®]=m 'K% ?EsX< V򛫨 3j SG,L5˗n4;{0s!#аzV0 i&"/=LZ[YZBlhMc@nC!eۀcUl53~;hcJ4Hr܈Z٧V>yzf2K`wC;fҥmmnZ\Q>=w-wPILL4y Y]GJY3ڶ(Q.:# G'V( 8G'Cm(Ai )Ct(-JSpFpfh0';Kf'P'b괺7\+B͓M;;ݰx%Rڥ VTZ->?hTA$T+N2OsiK&|>KEJKVkYx PKyVSa'pip/_vendor/requests/help.pyWn6}WL]W[ K"A1}) AhL$D];IuH|f gϫ3QF6)Pp EI\.l?B*U @F)\e[E9"()2Z6_eY䒖ILX΅9&V(GńD&0?*"Q=dB2$O" V(R_S6nNTD^TpcV8vƙ5/ N2Hx#z~ .ߋ<~XgK^E9A r$":' 2V UPZ8d,$2kiN`Ý"ˎNnnDt' ƌfZv iK'?\C7bm;؉Kj7GJ)/\b '-iaO#[딧q7Wu,0"2E:@] '4-LMxNTcEȧq]/%R(f] ƫF$ޢ- ˞u7`6yk,2{w+i!!nl!$E`uq30JФ]vn rfPϘS gūn)^U߭W~[/~x8st`)1$/:~!,P-x |c'vؓ=i߹-@m%qeo*)r 2BC H VОxE{ *g$L.S*kb[sp1QV##T rS+s;f3ڿǪYx SF?u !oVM4s}MXʹ8`LH-> oBW8 Gi{7D*KĒPKyVSJsypip/_vendor/requests/hooks.pymAo@+&zrnH<4ФIIZo l]c{DN oo>/!UE͗Od2a?4RþM) UTV'["{Q +Hȕv{s6c'.*'KV` 6?hPD-fku>%^?`[ohv s7֡5FK as wel h UL7FSsn;U|o@JrI,9Z`J3i- tկrm|Hjn{qco6~vݺm Ι~ۊo2yh!Sw]T(G`j=xPKyVSX,n&lpip/_vendor/requests/models.py=s6v@q[ю|S|htKRKܒ\j& fT3wIxxxժ΋|v>{^mۮM7uv?ӋUrWjQuYQjQgYn+6ۺTu+6|ooڽ}ut3TU}=S]-U)T&nދ뢻(*EfݨUk/w7MS7BW*Ϛ\Y+BL@nw ѳ73:WoJՉɓsqw͓m~b0h[7}յiW޺7j[lŕIwM y ] ->OukQAfWv6kźn6lĮ+Jzoƛ/+튺3M82zSzɓMի?֫ T0SǪmmuCMzk:.4hfv^e>Z]EW\Ūl]f?SPޯpC˚^iu X5yqdO@G@1' f긺"Ǚzu.uF;PWpBu>73 ɃN 2ŸO,s]rTb'Ջ*C,.0LhY* eU *hFn#l8aKki΋Hl{YR,` ha7y#Qu2Vhyv&3[[W [jKӟPSt7SKmzevV\~ʶ[D =͇sj]E->"gfb{  KC۳]ZpC gF~|Z,d_=Yk8C8\{ou5܀|Q+V P ݷM my{޼>:P>W:)o_=h]hgNdSkii\2ߌi*8Ma{^y㏧ :>e=ϧo~>]YxSu{oz*5</EizHQr-`rH@ 1%lZBgǧ-|74JpOHmS`k4i^b[Ƴ=~eΎn),[ A g M&S)oSXEZ ,5Zvt*kΊOjg"Y713I] ]h4!jM>b\ͺ&žŰ jT&u9SWh >cиix2̼ 3j?pnWm+L tUsHF_r2&#&`ї$¦y_nvNcoeg˘n6;=u(ts3Jgjx{&{V79Կp.@=<? 3WȱL=;##?Xw-#SE9KdC@o;6JGoi+60}4Y΀9!^8-%SG}+} XYS!~u4 siܷaV"7+o}VC!![fQ|^ OLWdss9 65l}Œn^p+_wrڥl1SCT~4Σ̟=AYp5e1Ĭr3n"t0ƌ^wHvAgZ1Fuj}mfP: ;x~xu }  Ͱ-/dMй&sp:Br>$!3FAPT;}4Sy1xtrhͬ`Hd"Q&tmdΔH xFVɆb H+E&A( ڭ^rv6aԣzzr,qdlvB_:IQi:s->d^kgk*]l 6@D*ԱqFxH`8b#v?A-աbG7yT҈4֓ `/U!ͩ|)=KfFtnQ{l$/l/2)tk0A礝+V/ƻ6#inDXހOeYZ3pc90lO 跤-yD}(Qk0zeш9;UT9[2ZY5`oc~!|0> #᪼ցYS$n9kp&Y]%!gꔉ`ε=y>7,7ps#6Zj8ޜN {VTiݜ?9םE TUkӯ>tU-="%'7"CLz } ʞUG>N=ccqJo NK@  }z)&cXܘymnXX|= |~CA8* A1I۾tɋ[:ڮoA}NMRP 60@R(53H#xK`؟Eh5zmz%& nzD俰`^RaǑZQk93Q#=iʜasn$Y4Yn01;hYE+4l-g=Ga6c~ ,=oXTeF)?YZRD*bbANCZm1z,P7O.nRvnvEG@#O2ᠴFu@ɀyYCUł`04V gwk3P^Ի27NKLmj!T^Tۚàw仕V*m ^1J83wwkiDi`iKU>H";%oE]=3Zz٦= ! _#- APai%16hNVTdhv.lNKݢ ']}aNa͹Hk ?lkwR̀~5>Ȯ͘`>[؞ߨ(䁢r"pĢ驛D@1|!%^`bhŠ4&^H+IbbUVDiەɖ1љT~R>l36t)`# ;D)'ݯO|G"tKd" e<E'N4 * f(0\#hC>@?p;D ݥYt^35lysUɦ4 x 1ԅYNWBFa?`98딽g|cj dp$̖ti&mIga$&KJBdLFa9 3vYǦP`p[Nx^_Ƽȩ>Qn&ؔeV{ &%_dWZ" ϿF@߿I:+q=`cgeQ % VoYN,RYjUrhIDl2$;-%oZwo>y |5w۽]դ ゲx0`e6P| '*5l3M]>ODmGs P4X2):|VSt&+ʮ^PKKYY٧Iܦ-a6 5~{ F[+w/,= 3Oп C9 -(iJ\JJBVL4n4tωq]l+ 4t(!ԱB6S$z8;T&;ߠyԗ[a1&k3fyȱN{kzG㗧:}2ȶwc엧4 Mѓuɀ1{ڼdDK-D8s7{Jx+q$k 4}mGT 2AX]rE|&H6W[bJ*3]0 #i79> @qo\JwČKgƨd^ӓ.9R?NB@4rQC [yzؘ͒ v@%Kr,<ê1Gk>^e;H)6QQ)1Ԉ13G1qtߖ+o"c< 9V ۜ_&0 PVEq[2=0+e", O ]2K(PG>OH; laøhe{{CH)bm0E@Ĕ GbCzMLjhJ+p()`~Otx)@84_1e9{Cn v.ĕ#;3,ļ-X_,{׻ x`iJ4PadW!SEb~}%Ut DIƼJz6i \66##b73*! 8h&nn >UV$8s y EY7(+bhq\ XآJό:?\˱tpYm¸h\ NY&|XO%UY}>Hϔ$LK 0?rK&u9 p[^5T}IN*$%2cs໯qV5!wl鲜 ^U^ 0V8pځRq.{ya7ǐ݊DD`h1НMͩ1a_VrAˋZ#r0@,cW q"WKےJ)X"dq&If`2<ؑ:cFШfHI}2:{+Qd*: #6] 3dT&JӉH'(GRGbV ١r ȓ_@ЛMCX'L%Ǖ*8 >i9%au"apѻ G!}R\F&YF:YFdp7+rҡ;IuR޴I}0nin{"&1ߧNt;m3)LHx`Jp3pOx:h.JhG%]0 &֮u K&d_}\DXK-nUr7Z,Hd0-(鹷XAg)!88 -_^J7ypNj^V7 Z|Qqau;/.i9K (^$ #F~/ +B 10:ZmlM:EWÔr~|{xi\ǹ2LO)>|8B;>B)aJ6uסZ/B*#${e!IB)s4"qHҌ::*dlSxw 1(p@):%IZ-`:,k_9O N!"'2gcKvd^U:"]1),+@3],Ʈib4*Xa]AғSro&ibB}.-qDhs8h0mʻf1\Kj+hk'{ 'ЂT}kgObc`@#굗1lw0K yK{čdp^Ir?a>KuZ:[J1F o6i8H %tN~<詇-M{lkxIG_.QIylLnRܩ8 ôbhB?WLvI>Ȯc\oTM4{Q+et%vfuDྴ%`>sVL@ n8>u`v[JO4&%\o̹Lm0W@ 6IT_v{(B%]@ o1%G(vŮWԼڊ.Ac;yf#8f#%6 Gx.\xP)J\eP9eM\UhJ9vg4WB"kB)LX9ejUT5Wv4v^]扈Q,#zZa95a6g.5F|Gi87#V>X1E | j5qVcPV|,Oǧ^/}:t@fʺՉ/I)wRr]-*^ ^|8a(3a#2rF{M1 I$e ''BD4>i2nƗAaGHwٴjد.vZ;3Ս{S/*nh8m$Q}^]^lۋ +_`pۃvM_h[.@ o5Ӧ-8tmXEK '>I^;Ek؞} Rəɶl0;,z9nyع J* Μ u~NOD`p1Me9mAe[HKwwY6@S}1oq$\P[36v|)܃,i%/#mO9j& k-븓 x=.Daʿb]=|q8]VH?3ӚĬhIQaMomvdѺKLU6gL} gJYO~䭓x=xE*k=!{Wz`+Rۺ?Seo䒿޻$Fl~3[-0cw:{& .?h& NWZ gV:tK@'+~Z~wG3qQ>X=?zc,ӫ}2HK"")re-IIRSƠl6:͂NE4UuX^v13zL #â;̘"` pO܌'Iu< itRB4y^ȿƷԔ=9c;*8j^ ~'Ӳ HIЌTo5vˋVvtVo@+걖RΛKh@{ ([?RA$Ng"bսRʌ~k-ᗦob\S6:R1BlHv957=wGD4Pt[o7&*,Mvr=lN9$z4r8ROцa{A- N3[^C0M)%C |Twsze.5jܲe}G*2E)D22kӼvx:lE4 i1n#Xa{c";8c/NY.~orݡ:QCu Om]j0;kn̕@զocyWߚRw^R[[M\?dksmr3tb%ac,fFrXf-fRwM8aN'>"%\o3%$% /[q>Q,Oޚ֏C!6'ä_pK0݁̿ 78~`jl~5MMΖEYij^X/.F~<7>f,1m"Kf N o5!-=|1S >ռ&+lCR 3e'64^Ƃ\G0}IkCEd? A^aAdg8#)i:nq._F= ЁM6w6 =3ixG݇r{_p)RF~{%.gzQ +pXЄ0^{+'R KYn&J(;/rT4dTl/?w0}E4ҩ#j[3M*#pX-"_t`*.)(<~Đт_HWČK1z7l_*?!qg;1\w8%s\0:IQh2:"P#[xE)A}H%7YX7UUvD!լ]1f]Omiׄ}oʹ>T@Ҽs53h!;# s b"'u1k2V3.L CwD..ǹQ9%_ zZdʢE1Eͣ2W8Ejea+6FyO_Z63mXܬ?`7mmFw)W%(PN?] +U8~L>PKyVS48!u pip/_vendor/requests/sessions.py=ksǑ+&>p)YDA(:^GRK)*` 5>H!._ rr\;3ӯk̍s/&шK͵nop:Rnߝ y7ae&-zU@y}RlytZ/ UU&JѼӑ{hʑ(IQpuLKt- Sk%WM1#g L%@#@? mzUPZit:u{yICb#Aqn#`U₩qxvtrW)j ̈́Ic-W6b &` EiUMLV6K<`ՉQLw`U[uÑwO7#FȘQ8F}oA~ q/Z Ys,"kD ,iC5:@DɀdQą." n`A>{T`zC ߞYIH؃HD0I K"!ly[6HA30d2|i֥ {CR < b4du Vb#ˉfbѬir&[EvYUlAуnW, HvP'倠(= 3y &5 @6SڸdǎX-t%=LXUm3 H5Ό潬:JfЦA80!:wQaZo:jd«Ըm ">U?r15[UM8O0Ò̉yi^e] d1s(7h/hH} ̟1\%w~^eFd(^Bd !y],#0zL'O*0F/(z@g].K4h`rEZ wӶ@(9Umd9dsr&sd"&HZf,Ǝ1KÏ){S!ؚSP >"< Ҩn̓.my <):WE DT|C=?rg?0b B=a;xzE&O;FQȈNY>u1gL[\;#]cy~i&%F54Agfb@)E|X=kRKE9xJtj8;j֭i fL8Uwb\#;>D;wGXzubkU(Cz_Hobo#?M(m AJG;Bwm":F]dξw cB`h*r|4&sBϰP#F`QRPFYc෦#[1wx*J1gg" = cGY9Ô<.\#gw7`cd)@ @; p^{?~x8=f 8UD%^$Xr]#ە&xQ MFQc Gd`O•}c 囩uvyՐ}$iXh% GҍQXj823{@ZS5%Tn&\x h,d],~r YMVe˙#5T·$ŏPՉfļ5 k}7{?,Bc6zgd|o9]f >Iab7Ι! Ɓ NIcm؟Omp%|@8~  $=p&+j>a|%b_ib\pR|BM6*gL.ׂO׼DQlߛ>:SV=5h҉ (UBt'uE#w>nLg2kȎ CZb[p'&k'0@mY-A[G'GF fQDeQ^(=\Y`hGz$D/ v @ČVs |V]T-,U#{$ 񵴉y5e XURVW:?V >m,yК(~Pn:[ L7S5 JiCjYbP'Xlj>u'iVeG4%sr*Ϛ)6k$=g/pSX'x)`R=g_RkV'8tACsa/Х_c7C엙2!L{}<À^P0Qbb/^b(Q3i߀SEVІ69V{ ]9xtZ*1KwӪ ﯝ0R{ r GWHɦ,5}`s__3&5.٨lY3yGuH~ pm{B3w`&ϓ0*>sqL^>J6z& skMNŮN2a#Xq|5Ӕ2g܆㙨=:"a)/{5QUY&i5yS|~G.#zͳO|E2S)Mvf'X> ~KIfIa[L`Ֆݰ41  )D]a, ̇4 fiɓR}'^%.h"؈n(q*:{"vgmiVM&޲!;}F&֊)2 lզjdN3ai0 20+(gg+zbOR)*_Ilְwn7/8@5.3|JmYk1\44g~@ .𠃘{l3&A83q[",Ho<1eY<ҍWM)H:֞MxPm#QJ3;L\yUG GQYT83w Eb-2%l"8L{aȁ_6'ź)!кjoic͟ wA"$$~J w@ ƿ9ۅw4_9l`?gG8uCюcUHoP)r% 7y.;c by~ Ƣ k~hP:c UyɅ)0@K+ \f7:.P+XeCL2ҫ|(MafO50hu`g)B)qN@(˼#yXbBVҎl:M4k{`-HSX0 #x2nVT1tZ`3($#ʼӼ)c>bh0V-Tz^9šBxwtn6\OTj+UxD# ULvg\ @cǕ=7Lx)† NdNI6>LrұhjeBb\몈,' = &!`GKw;`QF8M(yla9^'l{bG\\h< VWG"e4ɵ7ˬv\>{;ajw 9M*vSM;* r|.yր++}!̐+U5acx۳P᫨ X( 8HDؔu KJ{9(g zvP/oX>~vD}#8]G?yٗ:d*?T:~}|~Db1p^&"8Ū ^W =IǘK1mx EY+6T6o%qv%l't{&zx:]?wTw6výX*陣>ChW8ojKX4`nY&PG-^^x#`н.B|G̴bOeKQfz3ɠsn\ hE GQ\y .ρy҉2Ѕ\+k}_+/iE?u^<F!.* z!9[Sz*na'̋]q9/6O7t0N3V` >' AUZ Ake - |xسb÷ͥ#D{ț`c@F|i9^nehVr"C]uI]HE#rHwx!]=x?p u1+/Qm݁%gM91?wO^iYfFxcnٙw=P~hGWzqͿ%Nsmffr ]]O_S/,悂I[Y: n6)_z~gu}&WVP`SMb $H<9I^',G(r {ҽlŰ>͎g/?}RV/pS{b=2Xevowϰz@l^GAtőIJ9M c.OԮۙء⚖/rJ"Ƅ b#oCmIKMZ]PB6^x& w,C4oz/%Z7G-ɌP&a(HnZSpoޖ}| "Ms/i)ƥQWs* r`.Dͫ7]r_,n \dao>H}5ו+C /B CV_zYX-Q/]K>9Elk'K,KG{ G5Dk8/%c3[z]/ػXu+{?\?lXL:~-Yc0 $MզpӿR& \ϖ KX z9$ofqv.kH̱ܱC-cCHgD\E.AsR\daҜ8J9:2;2#Z 1xM,&1mSA~ZvRñL61'jzDҺQXQ˽ڏzTlG 6Zgs :]^t|$f.JW&'c?L wH&iM>~vS~﾿*QNO:5Ny&g'Hq2̒%fe+OƠd`b7YǶ|Lf^tƇ]R?.n&^L3rEqC4LɑPNn8*{4nl,'pWPc kx9qt́7Ig~?!JWlN˜r2>1l,W9t[zmu7>ȕ?Zɘ@ꡋl߫r_bxsST:-ḡˢn(f[sQoz4&avI|zzd>GWÞYxNB$Y<;(nl989_>8V{Abꍔ'hՋ'7/_^|͉'i BC%|yѯՁI(xijȱUK]\I,Ud?s}"?xwͷ0`<ɘPp_ Fˇy~4EºlK=<-xaHf(3EUj2QPў76TQlbV")IB5_9V)rrzdB_)˼< zJZpxRlrí̘G58-y Qg }^w}W{oEnd^@ S \he=ZAcgh `G33S^8EB(y'$|1x v+`+! ھ("Ul6 f0>mZ%Έ(1‡.AeSd{}T_F1 G)y B 7I`BdR.r7av^e1!jQ~} $/t!Ć/X04)cH["^EU: oGfyQU~ fW{,nBp-$N\VEtpp0E2UT<,L~vmXO-u@6$#EAP x8d;K9̲$\ =OYA[xV{TdnϪ-i vJ@ $ 1.™A"kn pD\@FPHU肺C3= B;Xल*8Mٰz{$ x)Bk@}^@Uj٘~v {7}ty; <շ{[Mt PX| "ng1KKYhAC(MдE#H0M++0hIOp2س icyU MޅfxWZ䊟uSevq~m]m۳n5rWސM#oc2jRo0PI0=e#keLjH<j<>즽ao^]7.pp!giWt_DE()-br: ̺ӝgnU&F_AyqR>y~t#ayE96rۻO?YmEl=O"J(سGM: A\:*@ʰV 碚Ůem+ۈ aS(yp9?Ei+UdQǏ*FuE)1;Z"@^&\l}N)`}=wIi8Khբ| b#^TNĉc<2Y~Zm3_0@B3T{N [ EwPWߩ$nWՈ02]>tmXZmEo&#H'( t1ʒApR5.I)iQ+U;ies h’M MOHes5UOT\,Av.%Xo) qH+CICNN9%),kZOxS#Klh|(Vl{&B &(;<& >F$"b%xfg90u5]BẌJK3\ 6Nqlf?J[ڰx†?Pmt\ CzExKbz mebl|yк YW4qL)QLKrP|>]̕9k @oN ,KpR@/lYk] #G쳞 TP2 (pr%W֠D+góv=viR#,HHj2{U!`v0=lw58~}l@r cUƐ5,^ D{LGaPxgYI"y^oIר/(*9hm8)$8`w1x zPΜ_l{CClll1B`<Ж Z!9]10yy &ʪZpRͯ`> u5($4@ϓi6~K^[?Nu@vog3yCkBOuL Q]Gybg4F-J$JE7!⤼Bǝ./aFfʂD$#&&5{:P3,KͯJL #gv.îE˂ۘʻ9hiE%:VߑÆIuk #e\ntHtpi= gдIE)"CGM0@$=pu{aLnd0-CH`e<ϟ;f#o蘖5zl8 M =EkaFD pag>&IaƄvD8|E~ٌy6~&3h FZuCMwzOyWu)eQ ]BvsM X5c?;B|-[]ͮ7kb|+4Qȯcz,ʄ=uys^a(eT2ʷotq^^(:0IloriRѱsw~^'O1紪=9?8=]lyUjbs6 kKɛc$K0p[̈eԋ#NhDhr-nlҋR<1ObP ԜEKc"i %aP+ ndrLRdgxQVT2 bWI5@O̪NJi.Nm"Tj|6#{\`zCP8,>.e)P1d( #ld!0;Ȅ~y3&o@f"nlm ˯&6;2;?'I$ϱf43A  7e7{w?gc27ՈE6a XLTe96f0'R]1>`Gsg˜I5ʎ]V_q^7%-ѥ!I 8-l#zyt  ?Q3:4ֻu g֝%|fr(P1H4] 1`#/`zn. G`PMBˡ%:X}iH Ѹ{Ajm50+d~k=8SJXg&lxSd1ۀ%Ro%čf:cayaʧ&́ermrNQB)edf[̥ڔt Z/1٥ximɌ3?l0ͩ$,N7yċx j[ßז=iӗ;xCvub)a>O|ҘۼHN.,HS?5vӸ~e| ☧2Js7'ؖ42! n8ϒiƟ#嫐!Gqhr]ܶ}Ԫ-$#:jQEM%dʠѡFa0" S: ErIm7 IB t W)ʢ.l{&yJkcZDwcpT^hnUw~۹z&t@syfƩB!OQhtY{e|2I|T,Is qHs{sG_b:%kTB(-PoT={C?!~|&Ԗ@Et1cJ ,`oi<{5_fdƯ(X  Ijr_CF0򘮩Ca'lK[W6-w\u7?Ayv|0PSŽW8ZHIy 04,<{ÎW:œ!zt9R&jK jAH2\ ku,hQQe%Xp}v0t}@h%XgVC&9= ֟Y3*C v-,]R\ؕ?SwrNhRkA(./hu:k9HPPp>J_0% _3|W145IBCXCŞdQ77zY P[X[7dsH=8HDc-RelY1tϟA+''rlFR}מ"?>vclEl}G&4eaj'8|A6,eڧAӔ`C D5QmʮÒT%Lm)VjI 5HBC;4f^!kg@&,=+`edr.3 \% dI->VК9H!l_V.B~))ZxحE[<[fOA/uۣ?>}28}stp|phYbfo;_^e*g⧢7w?@ vv=q+90 "iiD>Ƙ1S<2Q4:}hZuU( z<]/u%=H&Q*֋Cx]eĸ͖ &#dø*ϒg;Zwc!!ARFiޥh#d{ɳ\?ϫf7PeLgh\wHC D8fo}oa7cu}&(ΉG3x`W{֎7dulߊ|y\enJ"4:GxRAF uxr4m|l5dzf TrjJ,GN01mqN~RB)v:+MjDthՐ~q'@o:]M'*V VxH|TIڀq.&/ 4 TmFE߁vq-&aZN# %O(?O.k]5l=n2?CjAL kldbT-2s+ߛFRl0Hr~RTڒF$໓nPτf{K/R#{8l JL#W74BoƩS&>\ [V3m-x;#=yѬޗ_;E,L?=SԖyn].*|0+mc P6x{zk\I'tQZWr YlIF$. pY?h*Aɼjv5]GEL5#z~A]T>=т\)^\\el 3LρgZx.+ݻM fII ݪG:tiՠ,(tp{d::BA=Ddd6*z5`x܏_1; R_|ۈ2+~(F f\SHIdML"ePnI\.H3_iec Bn"F8 4o$s .,9'*qm[ Mnt6ЂmW`"vlSط$,̣0*0dM"*[Jy*e I~WwڃlJ)#u1)t \j{{*;V6 ֔ZN`0 ,A0qL> %YY(xσ>6}QGyAT4 M6ZkҊ1-Kp芔mn+J$N J{靖]& vUţCw+,?C/CO"K(nuHHxN}{{M+qpcI{m-#q-`_L7v8}[1>ev 4J8Z0⽃ElU1` ̋`re8RpQVQC07MWq;b[M S'mQ6n}YNE螂"cmz[Si;ID~ dBe2,@iIrf!i0v~]F7i DµSW)_gR<}ǧAx?ӎLs趖QE3H lmϩ$X.Ho엉k mLb0ט NX\Tl/FsxExD$8 @}o{cF:/p^C{yt%so86i@3ۣ;8Ů܂&g2B,' >>\-s;X7%^6Zd9{xPn?(=@I]g;-Y銨1W"Kʧ08)oGB {p Ǟa.t=~e|C _Oڵ6 Ց4ɮ 5*X\,U|} Y g B_< ȗNW8$s6#c~ef@3sNŠ{o aNjoTD/mDթЈ=mmzkl𣫺Y13h^p6IHA/h)C W={NڬcJtK?Y_g }f6 ٷt/l$6/nl7KkO; 0C'S:a!jf/xRVCЦ@g>w1=LO$H?z~xtwHn*;'hdocj>! HQ5"@40+.ǷK17[% y"#VsF.DƏ?XP.fa:gAգHu~sе)ўOhlqƏZ[~Okaie"13'=+'dphuЋcbZ`9{,о{ L g6n%"yT\H^5CsJ[끼2c3 M2Zh›{R Slw([>oSQ/%ض~ɗ+*rfhVtkD@ ٞY2 7UR@D?EJN'V4^oX(4dmҬ.I9_D8^r~jPB#PJt~Ÿ.7zI Ȳi`wf  "5UP +&l FK"W/qiGҀrco`ƒq⣤2kT#'Ћ*ùT&Z[pXs,9CךCu YRx #0r-Wz |G@)n{ޢҫevLnj:?gc{ưq7kaw,ӍEwd*}O_eU&aey=`kķr#{i#}MG~󷓃us`sUNG:/p'qRu \+IthX^5fh2sq'Tgha8>I!7/^65wz1-ܨlVwu벹tj;UĊn&M6dFқH2K%qv e!-b"˃x&  R1srĕZ Vā@hYT{fL퀖H*O ITs'lyUxްΔş ɀQ&G9U T(1rI)LQ/H8ݎȄKu6n62]*\]w2څ;+Z.@bfxC̏q/2S,}J?'p[s,߾ͭ I2؅ZN)-PKyVS5"pip/_vendor/resolvelib/__init__.pym?0~IB]7fLSLU>Uحサ)UJnUZ 6JNILؚ (s`4C +ѱaﵫL=|xA!C֔ӇDHsT7l sxhqb5HV:[%B;a-22ʘ@?I}arJdt#Cf9/*_6bq;=T, PKyVSe4#pip/_vendor/resolvelib/providers.pyXˎ6+3pSMY4(YZD٬%Q%q="]Öj1l[yeEBT_D+ VV/eZ3J-:[&{+t+=[7 Eð9hu*b}-k(O&*_DxQi?˔B;nބ_0Fd)Mlv:XO*}k4sPc[ a{QoRwͥ쓲:ɢ1<{awТ**h_̅{R&> LA8K^ HB5-ϳ G$3eGqv޻䤵Pr Y$ˈH^Pn챃vH&:/HmInIㅷ#iYҳv- ik5L5m0?mV|LJaSyfE(6,VAxTSƜ:DҮ|d'X5޶gМږ}PLYFl.[gLQ&E ԙUoZYYvk(f<љGA5lPȲM6\?(,f$e&`4ߋWpCÅM}dյXfy@f&I9u넦2*_Tic# #Wp'Rh>FE Nf,%H3IӜ/BLff#NMK< g5{f{dYd4ZULSBH)s- [qإ7isBIAS%'ٶ؁), ̆ -(Gw(B׌}E%rG鐩iX)崾qZ"#M ˈyk}?@[4'BNF]n@`1f.QOP"+# `̒2t ~ƒI[LݜV-+`ׅc.VUfƂ¼~yg.zki޶ms!(] nblK<{MUa|/-!_?^ࣙ}E( wXNT,빨1gw~#D-ŷ zΟ, IY^H*qV"ɍg߸ 2#M"OMzMPި+tF WpMe<ֽ8K.\愒|yD>%V8~d}<IQ+kvZ|݀_ )I+h:Ɏ _=%iݤ'斕J\)ioN"ZKdH%0tkSY|6I$xf`#Mb[IM<7Uvs7wTt(BrOSN22bp/߳NvS3#>Ș;I`f^q| Q4. pjԩsTYؾkDșPڎ١oOITLC_O\8x9;0ԗqn/e׉avכ'ϳ&x36?[HMy0v5_<{ wegKXl_R2yF?])9S3`V5fd})$†Vc͋M 'o'XWrٱlƈ&>4w:;9 2³Rf<f#޶-amsrjPCӠوF Bb9#GEea8n>({=1XxK|& ^}6/h, HEV4w_{{$LArb\YPNnXT 9Nf5gWI) Ab߿%?~RrojdM;0 *IhLXq&,%T&=Rj#c Kc`gc[W=^Ɓ5 gL:ˇ@y[D/ (޶Q#ԪhBV!:Qрφc90ٱP =uNb\f7i о?[K;0XqF$B 5KiZtkZ9 ՈII F# Jm#ٶa`rBin`#oh=d Lnf7[?0J >]jY, $=q#!0, /OP#{E01|癍 d7zTbRڷA _h1F(g =Y{6+4e 4+$5ZZkWLE.` :Z܆avkEvϼ_76ȡCV7x Α-~bt{2FbAG RMs72e58xTX(+_k@=~nۏw/`ߎMÿÕ]j! ~!$Z{F:dsvRɆ Sq4N a}_]?1w᠇*'B}Gf?4֫;`'=/Dʪ `wIv80:dm xoi<|n뙔,pƇ Pg,' "Ւ?Ϩaf6[d~"Mw:|\%_L8V{T3eL~uݰ;opp47o? a,Ɍ-b}E(9+/e:,r{]:}|^a_\?5 BKz" v<#1ɛ`a{eLLj1\`X؅hʔe@ M0R yVSzVuG^gh 7}@W 7eLq% -𚠉H9/T`A|aA0d_`|&~%Y|LTfM|;(}rf] k3P2%佰%."JLI%"p֕a\T`y? .e1 wmPfzׁ h9gSoy`=ۤ#"kdCZV ׊a h"SHL +2vt{6"v߰}qF~vފ +nnBzR/>0^5N 5*Cx̗D\fs;>;jb^SnUv?y|f7.W+/ @j NLq)TaUV MS=?y՛.ai̪&MyqRtŸARq=sQbcr;\;|xrY!OF6Ѡ}/ hpbܘAK!9(l.'/PɲM bs 3+0_6ԨO U*ۡ6L_WR5cb:floXþmOL%w_a\͘w_aŒD>_PnsxV7~MҴ2x7!>7?,ʁ,)*4/*=ztj{dVW~OQ:IxULh'cqܜ5_bB6ө'YeYrJQ @E&nl\CTy PMK:1%K|.lRjz`H6ܐ>߶RYSIbb Jvc)p$Enz!Tqec'ڎL5jE3iBIUDnSEA 8P{K$".tpax*>ZekuWMyzzws,C!6}y"-P4: l$8tF"|X+[~=-ocP.4K J26*ARDJpLN୎9u~b#oVZ31G6RTġe!\Oh G@rb3MTh >ϖܲ6ӛ 7%:i". _CldYXz&DVx9-KJZE|ٝǐD'tMB{ul?׍GaOcic/{(Ͳwao3u-;@ʮgعaSY <&jkhu&i:=p;Eqj֩ݸd&:V }`d)SjY[' s']OG-0VElnlkNto*F iHZ2y7\DJVt 8RDӚ ^掊O.Au>^5SM$Ճ_w]'4lޥԹۗUˎ. 5^ :zEm ?r(m s%= 4l{Qa|lD*TSz|)8Cɶ&T=#CT^ވ)Z%B`BDidt@^7%U!6n Y.[ Т?ElؾWiVl[ɡuTH{[H4`D .K*o5iXx(Ube&R#=)y8P[[渴#CIpUraty)s$:FwnDǝSu0Wr0jIYÂK.PKyVSTpS!pip/_vendor/resolvelib/structs.pyXmoF~bz*j?Zu6mjTQjɲ89`rgjwvY^8Jr:Y>YYs)Bn6% /w&JF>l6IK 2A1]u'_l>gA*$'rH`z2$ƍH1(bSQI,2I0:,A ByqqEOl_ëp=J=^e蹮SD{J4ᕊ<[*YF5 ۹yJN8EOx*gR/UVwj̓qOw0G-8Q|!%8W)MRM8/Nk)JX^Qq fc\4\x[Tr;~%>jδ nϴϐi_`ɏx. `5cdHA@]/ Õ"κ7|Sr4 v"VQs[1=Ta)Cvtx&rC_l.J> ֲۅu7ԉwUһX5)+᪋8rLS<.i;FN-MeX,vd i G6Ijg@"X1BzA?-"5Eы<5'ӵj*E U'+.zSݽIV䂞4tRyW8-?4jst5}"i)>Jo1?|#ې2_[>Ґ5ZHrN=^gDM쨵ʢ(xQvzz[ ~ pxἷLߡM4*Vu[hΜø}l,n}! ZW]7LrsGBRϯ% y ޡ81 ;m/tth8m#]Ԟ3&4پ O?бf"ԝY'd`.cUJvTwohwۙj; HE ^VT)"9ǚiyَ󯻬pADޒ.%"L" P6buAVY2}+鄫Rچ:AԆZ [h߰BFoUB Z6ygnKAbɽ9^>*<Ԏt!͚( ms,;e}-R8hu H>?H>yy8@s_܅c"UzlBNyx*Շ󋫛c`RQԲeζπÔoX^0(U9)d%ńBDU!gu pu@T(@MCYR4zyKVngH>;x-)`#,Ea2`ْq'WeZYCpH!NQtOr&䜡 , 12Dv/`}A@/}=r|Z砄kQ[y;4A" ٔYcvm󤖷e98(Ţͳl;aGG׍~"\ ?[wԪC%~`=hF$\0 Ć+W_ V&O׼+[祱2xQ旴Ul7yElӿ+#`ߩ |T&zdPj;gNOkk٩'wcBG bz`aq 7 `~?%rf| cL"⏠3LV6>0DE++EO$'~6@O:u`|*쑄\p.3Y x$ vJע;'a8qڸ+N{TL-h@l^hvl6jƒࢋ@ m$2nFc@p\j~|!QYK`蠭8>۳N T*쏎Au%_h\ihW닛?V-w:=Wlnq+F1@)>JzU`Gz9k)ˊ8fQFESUqʿ :@?"MJ=/ˋn{^H2HU!/zHŢR:AHcSZLpsV0򘪙x~~xUN>Cw(b~\ cPk"d5QcpTw1K:7=N"c%?O#Z*= q4tu0L;zL-,;OÜ23a-)fĄ9-c?u[Ԕl`G FT8}g\AvnР6hc6pMĵb;u3Ӿ6;M9Y!mstV4TRWjY:Zm|jc5PX@.6hFRFߎRn] W[+ceYɸl\d\a xϣqH`=-ΰ6U%[Yx*`7Ȉ/X`!ny׃ơOĽ8tSONgM}& VhmgHOZOǧSZ=7>D)5 tؚ>|g?&3fXypԺn_t"rp\ryw\*=hb9 ͮͦTZ&mFWq4;TQ=cpE;n1eE9VxD]z5M~= -WF<w,FBW5m}s'hc0+8w5([^ήY ]A(vSl453 t9>Ȧ +]`MX>uW65fsrAl`|kF X@k&_;RÒwÀ{pћkw&}3v B?ڛipffE؇Rg(V R7=7w UPyuZzWoJ웁"i{A$\˰EuI ^җ*^dU^|/TiKZ:~|ŚM*xn3RGS1%~>AH˖[-a5b=7[EjP qTEH"@hOkxO+ꤻL xM*D:0 '̴R@kg ew>^J߼qr4BՠJN3*ZԊCIq4Nۙ3V_SN %.%,?q^Ué{՞;ɭ֓@ǁ4Fm`u4QE)3pg!*lϯ)YoWQ-X {1TTmsLI_ pTMo3dԧ$['qOamH{ZU@5qMm 'ηd6Y]&ֵFUm2TvAσ-Dsۻ13E[Y |oazT7gXH:_ucd·Ԯ} 64u0{ނ})eo!SźZeaïU7|y,"\(펷u]-꙾+,>yFvO/EbD<P;$ԏ ew͗ÝFC#;"#513:67+iiv*?`[k݅2H>zGV=`6$gkQvPR'L4U ]ۯX&@ W}N{~qϥS D-It{/;bwhێtǮ*^Pjt*|~{;g *pCp1<4]dXb+qTZT^Cꛜ4 0~}ߗM>h-C63}$UPsn"S-Qt5xlZK᧱^;.r0+;܏O(\/qՔ_WC'썓 A|vb?!NNd5(TpEiNVr’ !6SH2#-֥↋YZf)FIaZQ T*&`ԁh0 `n p<-\ ͐fo? W! K,~m qj5BlHe H'l0Ϩ9v#5/aƭ$y@ZHzAYK\+p V{(y?J,fUEn~e_0hԪ A~ɲlDC{P(Ы W93wY'** ko)x1gxu-%#"mb]b q7qkҰ܊.lE~|L3SX KF| Aq6!NG`Xp 2tA \k9wm* A#c#gB*9QN+ŵ0a3KnQXim}hO]JNN3.kVݻX[QUK|ʝLE/!=B ]6*66ɱC[,Ўmm OqATSۏ."3٢S*q.W꯺@,˯ݐzUײAO ?դi41>fDTC>t}%seO6SƉ_L:H*OEF/V(A<6dڅof:hZ_rC_7*AC? -bC0W $8Or4v9 dLvSlb葑ꀼ[ 8sUUjg5;.: \ l+r};֩X?kmtnD^rL| Es:}M\7 S^9|ϮU9K7rgF Zͽc2PKyVSUox]_]mh]1ЋcucNCC>9xO');pߑ:O@L5K*it-ypd0i##9P3xbnsZmm56*SBԆ tOb[,*R׾}Fi:[QfD&'`ہňQ+ῒ~4_ݏQc<9:Gh2lvX]r)s_*?EPY)<4돇o?#j |2 zPKyVSpip/_vendor/tenacity/after.pyTn@|WE4@JBAQEa͵{{i yȻ3s`Vf KRHLW/gfb #h\J%B4DcVp@?2Z3E!CTȤPB%iUiHi( ~Jvq "*znUUڤl`8Yl RZ ҰDZ"d@AvZ+#IV'T KKF.K j8*awzMpݟx8 x1N>w9&^qYtb# HF-0ؗJK"b;PɥuiY^,% +L !uC! Gk*IZC K幤 7?`4=`35\PmOg Ĝfw3oo_z޶뭻V(O6J_PAyA{9t%WI٩o2bV27'HBW\wDbDV <> ; bZW3Բ+U ['UD:&Jd{RBUK4' I;&9gM<^ՄPKyVS}婽`pip/_vendor/tenacity/before.pyQo0WDIM{ȤIi-lU2tUUUȁ flfR4ִPss19Z͋ӓWoS-8J8g򛪽j j/>7f-,P{$)JԒJ`wӊ3JixGNK9LP$ n S,p *+Lnw6IH׃ZFjF:`Y[MiBցJcы"͗`;h hQsM1-XR%BPX֬r"r0qc5voPdF$؇iy\ErWzDVk&Z-L9Z4&+ b#2<)E Bݡ*%7AE[fʋPцJ[mEkUBū0C)Z,嶅AԖ C}n̓b>L'5h*F"&^ #O,__9:ӎ ҋQX 6V"V)5ߚNGÆ8 fZa'%w2 ʖ-mJ&Nw6m'T!jBz+%\]ľz^ү_|Ko6`33w7y+δkGYiO7y} A*;.kpA*Ni&iJ`n@mojO|nc7!/P;&ڨ`914j鎔<'lYҰTSخֻ^NKy+ѥW<'~[B q})w)ϼ2n,fZ4,l[zmYZgae@oWPKyVSlgpip/_vendor/tenacity/nap.pymTn0}+ L4tݴN *L &3vf;M خAGpb{M0QEypqm9Jpڰ} \ 5<̶d0cZ3{ KVLuK4B)i a\veH W.s9@.PvD* Qp`aKHԮv˴$!Qܷjc zq UU ·|2[)lnj`iI؆ VrfZin`Tf+XRn(*&!0x@wlqr9Vi %L|5_D=|G@S~]b#j|ɼd9BQK7 KELTڂ;qRކd6OjdwRĢg0Q25#Ȅbg RA1vPDż&&f!EjC`R:4? !݆SǴ$\/# vO;p1 T]i=o)9@!<_"^6dٽS*ƭiţ]6t^zIzME6h#S7]nCp_6Y&Ğ"s].d&sq b5mG͛}E߂W[R;mz)r@fyB!@?J|A*X&CWl~/PKyVSR-pip/_vendor/tenacity/retry.pyXn6S8Gia2u] q" (LUW{JD'uҬ[U|"%/>zu1bF9<#:kRB9Xo?9KùC*;(4AB7*S&8z]u@\dDP.d)EBb c@C I(Zj BAi r@TaBrZUo@wJlIښv>lLwjk1k3{`<q~|;%eww0S=8:z*g:27_f2I^IU/WCV?| 1 oG\BW**e[~՟m 6"ǟT7-N߽PKyVSVu pip/_vendor/tenacity/stop.pyVMs6W#ӎAgJ$#wD8 -_JtDb߾}u#U5/^Ͽ_Fpp ΎPNEi/5)g( HZ@,M6\IM(D k@ERYh 7Pp k \BZp&3% _fCÆBfr\F ./E 2&4_X(h65d,%-Ai`FY.5\}0KXrnic*~I1L I'O9|f|2~d>[Ndz$ jH,#(T+Ԙgԗ,V"vF]qiH^N,W2Wj* Xm/& 2?:NFwe}؅F{[-u<̕,JqghzĄIA F}$eIG4|3U0d9,0pgKXr4A[ ?; ɪ.Ts, I2$EѧBj <5JVT<萑;.I,VZH= mmK+r}]ػpx윥Sxpz3%s{V0$`?[x$:dqpYmlp=;={VW?6 cxz4|/+ΙWzK}˄q^Ut4eBi(6<;A$/ [6諒Wo¢NXjh7^ګ-tplY\S| '8^ڱNZtTz=)Os s 7+6k? س~zF_gН~8skX PKyVS"9a"pip/_vendor/tenacity/tornadoweb.pyVKo8W J6Ebm-"rRd@̭L$UGC=l9qVK$c8$}SY֊W k?&H9-ۄߋ_+VIav \!i7\hSH()Fue+#JN"G.\Oy>O&|Lo7vAw0Y<¿MHi"|.O"M!6_⑀Lt xBD^!P %-v!5K [niz^ 2k$(ɔB0"*4(XM ]5xF67rb nK+QX?0Y~˛bq7qi,2F{nG`^B2tbV )%)d/lfp~*9ף^D>ltKi]Qx~ӢCv,["hDBB rx.B۞nLjYm63ط2q" a{A%Qb R<x_&p0t"hd㌯1}k[ܴr?&fܖO&ƙ۟Z أ{A]4)[-P ǃoh-:Tۇ@^w^uU9)Y[ ˳nYI%15'W6뷗W?[ 5!hgdO0$㵯c ,N9N$lLDIwULWA,ĬͳZiF&*NYQ:4 U[D3;PKyVSZeY#pip/_vendor/tenacity/wait.pyYr)P!L )yRJ-Wc9RJ\.8I3,1=; $(b rF~ =agXX:v<:z?s<:>boL ^rQ'[T썶˒dz~b|^l&L H~PVX`ェV_X;NFGj+8ur0Uڱ e&OET,yIRV-1,U,qPsxt;/ҹd8\V &,Y _//o!'2a-3R9]3^@O!aWLF`iuej1`V݊.3im 0W;a7]fw޲]]˗WxzƗ?.._p/ !%P^7Bl0A [Te Ԣ 0 B\Zrx3pd.wJ%1>Mu^?uƝH&MtkVNJ'3 jOr瓳gP'ϣ\33e7Zt:4̊K7r+z4rx 1ZfLE[-9& =? ^ʅ[?s68{2Y(H3$ nÿy Ч靈f| 6.CxFLݖ.bj̨c)oB桃C+'DcFPQ.!{(2s3mOKC-s6/UJChx)DڒwImZ8.^zmhE!|Ur0R9A2­Wqۨ Ԇ&^'!/V^V>v;-blZFA^23-K`p$l<[cJQ4t-FDžMy.jnf6HH^V];D1cxX|jYG֤ o {B7 D^8;`Z%adnw?&ڛE! &qT&2 32X&ToK SzƁnL*5"B>BmTVYT,a3J#">܏$OsL0R,PBC OՐ9+ϫ쉩hO$#mjQ'wF}8!s|o.5"bICE0*D(g ExUS| g .FX ųT vbnJ?.(ӫqQGt.@[5S"mTf4!6ХB!Qlc"mHWr"7e(jD$V'>JXiQl h16`_0(6tVKAe4ӥIEͮT3Yv('WJG!8_iwy:>@MR mhz*N]Xqk.( K9 wJ@ {G> D}Eoeu @ٵ1rBc Vhi&P!{$gEc샗vvSBLPKyVS@Wpip/_vendor/tomli/_parser.py CO9,xV (4 bkğG}AA"Z/q? %\fO4Sid 9R\l8 9=ߝ^Xn ]|5/[Mn3G+n ́5{nNjh-֝Njs׆=FH +'@ߝ2BZ 1a1Gz.*1ȇe6{-dbmp}v v5t~z6@9 ;}N>Y2+ s\UQ?QE0'G q>Ԡ31lQ^ ,;\,m˱[{x(Ƣ9: {!E I;л>iPx{sw1w~=/wY|Js%Dd,L/<+p n3eV 8p..qch(8'&f"B#q-bpe";]`?a+q ҄d z(Ǵ1%V9#"$<]f!HQlavA]?D 1-T`h͘ HammಋWe =A9n/@`B,@e61Dpb-$־s}?=te]r 2X< $ atj8#@y4}i$>6 _ s%!QYx{)䋂 )pcN ~sLi5`kլ5&| ~qL_mzJٍXM!V'SWI3JʰnkmHfR~|4PL8k ndkB+˗Yħ>,,oN;'G2JN'n)cY+|_qͩ?Z$"Ea)Qbǐ!yӬ^,zh(*EJ˺GIT~;lr=_, H{i?81o1U s8EZ:H|L$rLrEUJD`v,0SdbRpr=ø"/ DU៵imi\GDW{p4 Ks*ph,x}kX k&L4`KOhncsRO?>φ#GL&Z&1`}y@-!c!X])t u;0w06 <hP*,4ta|%dؠHmD 8Do 9PS|> l %h ycBPd5h(@x ;R.}PvT]G$t PM=/wT755S$MJUs!`^#dEEA`#gd01]]h3!Ԧ#]YsX2(͂Lms|ED,M:i$? r̩I+\6҂u1qb5$Z͘,']CFS̔RNɗA򁋿yGHTDi$lTv-E$(\q[=k5n/q lK]%D]@ZQT^` *nq1W9N*%yK$<#?ὔ0%˚-Iv '<Px;Ɖe`'[n6Hm8w4MUgI!>,D>5GW0Ez} b@T5A}[Wki.ENl"p%W{1,S eSzٌYC $ʊ0{|=, St0e\1X􍴅leuQJH^++˵KGQeZ +lS[.XI~,``0bDQAoA;6B c70@tie-4-hooMk{w6O@Fw! D\ HG b- /g{^ȫ]۟, ױ;?cBCtRzM"Խ8e+;+7'$ ;qmDy4 mg-vj^(s H-lLd?^SXe@j1͹^C]Ε>x]C%6ִYLݪKLrt1> jӸa},3(SZ]ٹ*.0Jlm6.wwl8%)J\L^aq5q! 3˨"<ڊb'J⟀wYCLP m[ riz\Z0B2ZD;wzYRk!X^q|Yk6}j*\N":vlVuJ5ʋ̸KVJzܶ5TzT>]+^%TySoŰtXcke 5\ R [ X lUn9K ~NБk ~IQVzkRBV{,xml?5^p.Sq6VUz JHqaP> y?j :K!ԇĥmWvॐP=^}no=H~$xIݩ2(uYݘi97Gڧ tOE<;u>/] 1cDݸaϳ5#_ωwzT;:54ZvC)OOCwiy (r5tZW>ky0U}mC,ȋ](_ٛ<)7Ib#.;q_&>oꃘ %1ai)Uȕϡ쳀|lcR*ߺUkAnY銫+z*aOJ3* `HܹG_fzX04m!aB?VzAz[^7$4 uKs6l;;ƻ_̧+AMuA[VCY=h, :U-'A7=-Zy-PlK6>HCr4L l37'J۴Aq޻b/5'g;}W yaRҌ8֦$ngwY\(RCǫ4e/FNQrQ{!L3lFR¤d2dw7?\ 8M|{[+fj]R jFj ?U}n``[U n`}*d~ڋϚڜbTɯ& քv=@\nT{"pK$GLe\ѩ m dͪe}#k[#B؛0wSǧtxb= t ^Rѐ,QֽDԌ:LrCݟS m|͓577k#,Iq?7MVu(a:& א/! atqex`O{G+(NFvbQ@t4ܳtjK[iaޓȿ{H^E˱)*QEZ8͗ :=ϻd_ֈ2._ސKH`4ͦT]m,,0h7#@zϙ;x;vٲxR.Lx[d W<4\b4#rɘzZTGx3񈞁ضG_n E}7ٯ1y/W W {([ijIzF #۷?'oxt|\pppp :; PKyVS1y pip/_vendor/tomli/_re.pyVmoFίXm@ 6'J* E=l*Eb(p <3=(rhG7P!F@k<[ʐ kCS8[W^ۆ+|²Y7b4eg.0)W59i9 }@DgQ7Y:X$$.ҧS8Kn,bq,e1'O xߔ KmR{eˁ켄zsu3 5<4A|~ߍHfqKf<%! l P#"'eaUeYZСuTM'YM {^q2ò ϗxS9b~@2i:UL.X3k/dv5H4 p8O6gF)nĤݮE=˲ {>$ř`[h"@x?ɩKTpG'P 6׋ drTE-=:plR ^֘-&"yU"2:P5IRa*X񀻩UYwu-NA[۱U]&5*Y('{(|sbX5X:eWɫx`(6fqhhP@`Q7l" A#`ޞ%͹(s9M*S`Q Et"T=I~WR `$z:<P"\ƄGmΑ`Jo^h12D~RA`5ObKwf(ّcWbI;UEļ<n&"iFqU5bACLlSOÌpv)O үLrwd;~a#ia7,Ɋt36s}! 3xlr7PKyVS!ImQ pip/_vendor/urllib3/__init__.pyVKo6W b/\]z x#AK# EjW%I!μyreYf Rl 7ۃOV k'HZ [A+$eXڸxZ#P5rXW t`Qk]4r/Hۑ7$pj@V{ՔpF{4dP2(|R'")TYkCe^PQecTPj(Ydyɜ<Ccsl j%znkw\i4 .kkGԹKqŗ7x'$}8Ed{ *ͩ b.uC D5(,ݡc{*FQ0=N5X~dGC-KFJ$>>lM{HًS^v\MEY6r3/+cq2ɛYG6,4eQ#,Jdidcr3=vNqE1? )Q%05.dV>_Qf~˛6NG aLx-~,n+j2F>y)"MFho]N!i 喞"1&pU$TCKjFi*K[g 4m +Mz^`xR~4щ4O8Ѫ,?hDs-)q/-\gZoir|F7'br9$Q֢k44oݒW4pE8LUU'\+Iyz5HN|f}-|uCyS($$ mj4aٽQ׎o ,hCii|gK8v鰦< Q5]mp_)p ; ma;s\kiKL9.d@5neuӿoy>9O9g:Dp\;E])N\K’htk=Ew@zo㉳+/=y3w<_QXKQD4=Nt/Ģw 3pF߄W0osw;E,PKyVS?r ;*#pip/_vendor/urllib3/_collections.pyZmo8_A8(,wm@nomC.phN%U II8Cb[37Ueզu%V+&eQiתj-Vٙg -H,EbNU?\k΄|&(5{g~P}W fǟ~l3هMQmR8muf'W+XJZTUDuUbY 3Rw;x+5OG=dU**~/bO.&2:Q؊ߊFCIދwFdHZ,C,H`ek.sQMl$ʬ~ P'+g1&Q~gdbLw^\(,^L a;ܱ->%Ӆars%77$LbOOe+rkq,,qpQo v W$m),@I srԖI.Y<ە77>w[`y4fo(>.>1oZ]f ~K {Iጳ|bȅD5xwshg=N]x,7̀:C%.UA }^ R\@PKeKk-y 92Ru8|W+.2"iXtkΙN&x'i2](%29H^4Ŷ`a˾-jdyI x6lO5&ͧډ %Vivw|*cg k 3UxٵyN]|t:1<2cü M1cC,\^4{|!rEM m=p1@PA";on6ON*CI!N8%˹1?sT\*H|lA?QVsS` t zӃΩs"kѠ#xrdIhxQYYHBQˡXb s/C}RgɐqQI;<"HNa2=LL4CebA;C)ƔRH zebrT-e'֙;KrJl#&ۡ%\َ+"=n}iTRl( OiB7feu޸jz\tQQu%1k9taݐB*l'!Yq&y[Kݫo_Ĭ(-Ö "#YO,N_ssjhP`R~_[eI;sݨėg;X sdJD[q|#raghV= hx41%gYQN[0F~cC /'&^mHs׉%:\F hPVXXBi'ׯ COBFOB/.^M7E\j1HZ5c>WS |˭ƙ~7}s8j\wx6S >&zC;puث_r; +'JTO|i9* Υ(P̛㰙)6Dr׺jOLf`ǷS) Kzls#Kf}P iZxڄpVT"f3\U'XctŃ6v’CJgFJ|e UgC`^͓E#qѮ0auu:ph(ihf9FBmFXC'e\sf`=Znf "'fIJn!Eoq+ˆ2.NG6X*i>H2fztY"Z1~Yg%"cС#6/b]n~%Zrb[,֊  ,X]@8fc9aQ ]@,^1Ϗ5Ӡn]a>l MK @)s#;S;&z|{Xhbdv 6G)5{, 392fL{5bb/ɻ1d ^%>tuJSS9b!:97b=D;J Q[S8*Axۺ ) LHQ7߈I tllbƣx%:&szdѴ+RHۚtzEj=b֮;lK}L< 74Oα?R!#j?nh.8g ]֜GiƘwZ\Jw"CHzֱ=mWFtc|%ʱ7?S35g,6"eqk8Bwh1>Avy2L;:$‘pξ/(Vsa;sZ7MbH'p\ow[MdE.x>{6ʆۥ+񽭐Xػ wr~'Ym!œ =>fA56t[0Czdu)-ꉘ0Z}_oԩӨ 6\qkjnnPH[f<+W$3PߊiaMY2Ǽk*\åZWwGDfJN%|;{q ,J1n[ld"͵v>%c[+?rwYIu~+uZ6E˼*kKv41tKNxpaF/ J+"T:w27ʻ3oX5OZ`o=M=PI%w:CLOƯ8w<5yF8lcdWR6lP$EiQ0^s|:ehGwlñyM,slCA.3Ee8|;~`O/+.ۑxz{N!0ER%h6^MHO İL:\ _ n;.% PKyVS[r??pip/_vendor/urllib3/_version.pySV,VHIUE%%) e .~A\eEřy JzFfzJ\PKyVS+_ pN!pip/_vendor/urllib3/connection.py8o|h7=<4Y|~ug$pnlh/;`YS8a^'P@B%?cf3^*x=ήjL܌YjMlN`q"J"\&HW` yÄtv>+S,Eػb(d)aP7E,Y]^c$Dr}yir봽+d-RXd bAc!'Q}޵C]psyl>fMSlv5=?ad܎|',%|쌴?;Z@Y<1#928VLة00^:e{լ`\:f!P}c0@RءHVD+Z#k3ŀ0 *='Ӏ:u>A%YRHY/r%ϗw[áKN&Om"/ԑ1~dɏ*!KMٷ_k'lNJ>FQZ˄Mac::,ՐzP U(Hhg ]iq?)rП^k  |+ :'-~P:Rz[`j+,&nMx"xDdɳJ fN!\i;S)Gq>#_cYu-V|p7r%YRй!{́LX׫88 Ն\Me^IFYgNh^[6)` hl pӋԈޟM4BiKѼCł֏dCEnjZk!і͵yH!UOzK nQ -@XH#8 TbJhɫ̤ LPꗨAZsW2ڣn-eZ|7Qk *w-ŤAun@oD(!H9dLyG8!iS%Ze:gS /ztP[8soO48D3+Dn]8hS m1z \у要2tI JHٞ7COeQ-ԳJ:UZP 2nVnU3NO?YkPmZN?a#hchI+t=[\7gml*LCL85g5p!U RWطy1 e1\*%AP) !ϢRR:qhv/B(yj dyi,md_^Nث=TW>L yfᅅH33ay׉BIvu s'-UwRjr\gpAyv( ڛ_Ih#hllhEaem쨢D]G%9MVԯN >"n y[mȚ-0Y)T a1݃F{v|l8~z jrNQ7@3';шP; :JuSGwcv8ۄȂ0'x8ƽ/xwXN7;ޠLK4TpWN$fm$u|9u C!Wht8K _.ݪ]cF쿙Qkf З[||9d \5!/wyX<.o2;[ea*0L^ޡK 6|+ԿP_Y3ThZnNֺ:u{N:t(/]pit#hs7;s3eMl;Fa3 XXnl4]Nt9ZC!e{zbHٸ#v|w:AuoFZ'}lXъ4R`TSAqo=O1vee!;Eɥ^Uz k| /J+ԽL0&C[qCZ*՚쓓X_U"> cMƶW+JB5CD/ĮNu3 kb@x$*- =[}C<&QRd8[4=$ cK}E=6q{KmX%? KJ,,,A GrvX_=I۳Y/eX \\^:V@*{uIvTھJlhV0?F ClSr8wU۸An( *=>Q"Pj{Kp  fQDua QoиIoix֞v̖ǩ(4" 4ǭ<& ;PK+~]vB,9?B(~A>MLb3{={wR&s Hۇ5L/C 1I 4ԡn%Z&2?Աeߦ96/Zh˺tsO%(F@m+&Qe攮Tvntn$"&Koƥmvٲ,W6u'SyՁeY!P!aK~!ڱ$pBǗs}d3{??n2(ZP׷[=HJcz*=뢦?C^.rMU'ο;=;= }^MM}G E^Tҿ}sY.Djc1 N@cF=MjJt;(X/L/ kr& M c ͙r*!U:` Ýqݴ9}"ͩ4sq'WR!TCH/(; oDvlf`5VJ?gtO=-T%받꧷22+I?vQ`(F^Č*]U?fv9iRTR\ PvđV>GlvF͛.oT(%Wlrꋮ~S~IH%<Lo˧OJtG:-PKyVS F(Ӓ%pip/_vendor/urllib3/connectionpool.py=koɑ+("}Ȳ7ٍpBYr` G&9 3QLW~wSM80,r^]Uhu2.otMtIve߉)>8ETQeQ-϶ߊNڵ6k*h,3nudmrEg]WEw5_8鼮*1RO .k;+M}+b#zJ?\__ƮƽthE!Mf7m+パP[wʺe]֐כn]Adh.|{ʪȭU+ğ{vxޛzH5;;5_6uWϝ{0^Uw Ul~-FT[{wR;fًU[NY7_MW*[ h:XC2m[D[ѭm TA^M(#T[Ssq7f#r P wʹFi?GWϽ)[ؠZME)- ;m[t>Dcp?jwjo[-Tw?68j^beRk9f_-.2ʞ"'81[ dhJ0B.a ONHO`Z,]}?fCxpWڃy xLf›C9:0 4[KgYZ.q̙8gej,ɪmpHH:qcA4CQ GI^ ug07;Kx풏.Zp*`EX dWj Dv'^~C0 ~G_Z+AWh7*[f6dQt:lE'6 i|xq\Ob!W9y< 5#)Ҳyz彙y5rYdÇer3/ԟ)Rvk(V /#u P ză'{=cf37Rq 1l՛iBVmOٿF2X05D w/?^_=4wrj.J:Ȼ8Ñ7yy4F䶎:KgO!.#-\A/ l3 :Uԑɭ#76/U:T{w,-ԦkJa.Љb9L6MП ҡ%2܇mWpn!nog3dfIfd@ݷrpZ>O~6+Pk]l 1O}8#S p Bt(m‹Mٞ](VˉڤMrkgFR5U2Ù]glY;&7O[H2 )1Yn.A+:5HA]uB)҂2*.ч \^X@GxZfJF"i$y[ X-)#FJ z "./ZyZӆ&=%OC1}舛Q@_ 4bTxNGTp-kAP״cwE.Oyd bH* ړ #րz`0Ѫ@= 9QA 6oVӓ i]pgŻ)gOvA^@<֜G+|zf3$6GJtB>~PK&0Q9$aIyGy'5?*ǷAJ7haWI }C \ۂe) U;61n O%Rg{2ӥqL1.5vW&qG:?p-P L? 1 Ibj[@#k#q(54{u=rÿ4˕Vo2}S^ ;DyϗGL(<:_j*8OQKywGBm x}'P Sdt^o)ʢۍ=xiobc֌1oDӌ#u޳@.XVXy <[ipLFA-x ,@`HPTؚF'dب W?i)Ùa%W X\.t/;Ʌ9aE!ʬëC:@9!quVMu9 6@q"6D=8?8ᗅW4Mݶڙ6PQ F6g{p{Q#jQeeJC^Tza3A JtkǂmۋgϿ~n10n^94OZpGBH7tOOYph+y~~ Y/V(7w&H&OW"Gӧs` h,ёEuo>RvrRQtn<*+gb#(&/>tR:FtU?Ω1~,BF }C A#Ѩ 'WLd5=ENnOtKP^Go"2@ڢsT'-08GڬL5n3x&t>t !g s2-J Xrr դ u9`yAj4CI2|pEkA+Ųu E4kibyf48ȲSr*t>G RBUoK9&cx­g *9's}&9P6#b8N0^$:(6'YBg%l R]Y W=p'MܸFmj28NdE%+3C:4H؇vdv t|pRP.yJ I"ZS'LjDX?_;tC he##WR:_>rW7FQŞ2\$45bynbry$'9@uXc `@@2Șj D4&zI^y£%%A;7[/qf`;E0a|* 9J1O!f[ŭHv5E7JzAۂIY׷ uQ"XuY d_e|ݘ lX0UnvOj1et(8~]qXJ-8$G ]k"[>t 5E~477mlqCHaR|M T?sX2Z0g2 qO!7CQ}&gX;NS+[s?K&/2Po8vpz.Q PeNKԻ=8s An&9c1KLhA[TN90Z vG)DcH + IXٳ(!k߀O+?wa^|>~xCꞤ[`Ŀ ueDʕ `kMA}8PnvhgևgOhHqx9/꘬='ewFGC7tۮ-Ք b%(c'oѝŸǓk1FNj y_P@*E/ԥUpAz$~^H5++]jLg#)3Q:mTi c\# ^[ѱ&_T@jc.@4k+D ҉]/`>Ob;}N19mյi `UTZt1Uފ#+1 ZxefBÓLA=- Jsz(! âG"^ǟ219;lXdf۪jjꑴ2GYjBNiq`=ޖM<0u5)HO7;9N"ω*kGUSKpK_*eKB‚f7JxXT4Aג*깺g*yOZq1)*V@m%,Ș@uxit1{bz}"Kpmu1ސUWe%{݃JSwal}5v6[j7 u\w篮1FHT͎'ɆVu`FL!K:Iud9P }dpQJ'D;n]R614jȋ Qzvð΋/L]R2X.0c=l ΪxBJy7+bం<&…~Oc!VQq#\ M\d*rklmbNy-Y476U=* ò 2b[FHRuHPu5m@+m 6@>n"R IGYe<p*19Y |BU@PgB{{L Ӡ5[1̸9Ժ q1N d/~Ə*ll'rnozi6ո 19C*!Ee cc:IKW&{ƪL*P]&W{ZԽ mY썤hܭ€1RaBC.6C1k=qpT'4 Us`eW[ȹ(RA_z.r$僇TU;Ǥ|f.NBՔ TwNyX{@'|;꿒ÊJ >)SOnra(<엾QΫJwV+JV^8\GP UO 2^{[FiѼe8xŅjT%Uυ)h_:!&HxP32)r0'$1hyjV2oQvצ*s8O<m}!W7 L6ֶVا8Z+M0OEz$㱬|0GcVX'GܸmS5Mch.vu~(K]Pc--뜝&O"H=t\7#Hd)[.P}B.JTAhD%I`U"`IAL¨Ұ3Lk]G$\3ĤTUu)OWp$[f^ `|9%kxE :zj<V񬷾VY8ey5fsYc [s_o&as0Ĭ׫yh\ީvP K brdѨL.Kl@f2]S/X'g3ie2Pnr(×!!{Ge\%-~5DM*fbG@蛞\VyO/b3dzfRÅ*hE˹%=~#-sso;bS>45Oާl^(tg86ǖi\ 4h22 2dx<0ƳrTeY]FEX+(:^z&><:j3d{&p3AkTq'n. r#*H@}Օ-Ȳ)\>Қfw%ǵUoNW'% \T½ _wj,Y;O7MF5+GƎ:5h})TĔyF>PqZv1'E)"0;Z7ǎsyyMɦH/{ߜsx7?%y#fx^a]̵}*N)h^A|>PIILؚ@̨%Ӷ:E;*WO[:n I$cIbGC*!Jh=id?eFWlZb+!T&5(aAzbqUQcYUTQ͖"]urCPr#!5`DԺZ2e%ZCE-ڃOYTe{`ϸTndKPܡ)âϷ[Ͽ=ժtniQq`zN;YNP]t i@d,Sqn1#U+hL7X,FsT6y)FrӰ&(%JwʚY.pL-3 1nUM#I2R~պ(UZG7`r/Z?< $(ka(ώy8"ᩜgXI.A:>AWd,z UZTE|}~}4NTCE,'fW쫦5NyO:;=D;t3,;jg}ZrgS"(b}TcjSŗS/F5atVq)H`y2ɕ>< c ?7of<&KAiWK?iGϓ(@TJ@}{LY x'_ddfP 60sHSv~<%& gQM Qn*?%OώO9RUDUPKyVS^I  !pip/_vendor/urllib3/exceptions.pyY]o}ׯ  E z_:@4'qMR]rKr% o̐][CĒ wctƢhSˆ(b2˵qN '"{4YGvf,{s.T U<N 0nNJ:jFo |EVv4)}4FI5x917i(ŠeNT`ֈ %(ّzO_!֩yҍ6L bN*4(Ybj ؀W.&V)MLX p]ќj2g֑4svaMR/ڍHXU oWF. k PM0IyâP)h% ^V݊:`V,x;{Afu,HoHfN4oR .`rI X'72؆2AE!bVJĈPtn3!d0OY1Udka1R@[ d9Ϲ9kcEsnsd?p c6CK #+<4Y0ad@&abEivVʯ,T"LzDi!$КtEѡ6?N_Fh8-02TRle0I/kMSgL[ڊO:"U̇[,M1ǿBnہsD `ьlS3#J*d&tV,"A6@Apd ΋'<Ɗ+%f: cÁ]pQ'dBl9+ȦvK>Jšpr[=O?=筵鮎Iǔ]$<0(S$X=pᄥ7f/ YPmD qtYaEĸs<ށ#mѻR턑ry1֦m̊փoġf&=&MYO4/u*4+qh=]]5qQoT_]GІW*}Q6$+lh.ܡ^7@ Wj#!}@T!n9;J1چ_؉PMM`(jml˫p 9MO [ (I^ b!]I0AkdxhxouT}f iomgjGO_a*b#[VX~^6sxkʝmUBmX[ƤY 7oTh}p>arlN&1݇HNOkhb*HѳBaF[_Oܐm=Iڗ9 7㎃.$[O*e+tOP ruiHlutм^ a2@('Ҭr4kZۮ:Z+d: HZ bT~Wx-d r88q7+z62(/%Xqa\.%gk U\AhCK}DU+b_~Nܻ1D[99r5m;0D}_aoAHzPЅĩ916VK?|}  Q|kDw7 x^VPVv;^Zq\h+;N]_7-& ï+"JLo=mL@];a_|ye꤯**E\YYX;7.<Ǘ9꽱t>]/NB?:)8 "j"Roo2Dy#̺S>𾭿whLk9h \G";;e+#R%Xc6_xq\Н_yJ/Z//[Ķ PKyVS(K, !pip/_vendor/urllib3/fields.pyY{o_b ֶ|A*sq$9mP+r%1Hw[Mޙ}ˇ|ɡ˝y^ẒUÐ$"/%a+~:߱$LRavɎ}녒FkdK ݱ HFQdSq!($d:Iyv|B`RXQIdgGy$ Yr<#r#rˉwYN?Kk2#q^=JsDZ+d2|Y+F5.edYhZ Ph)P? ȳt RX- p.SC&rKᲄڀݙr2ծF,A{MB,e{נ9@˺4*NhD~{777M6hc RTxc򔴰]!l)ϴ4LDI5C I.Զf%ׂ6 pJUKP{ٜ'^nC6RleE7BUO[aȪφC*¡9;Erܞogv@ py󋫋7gn̛ϧ=1zNjEPdnU`Ҿ`*ge,P`Y%i"q f`4@A^&<|\ۉ3фqrxwkϦB~z=mNrM2v8ͨf bh;aqɩ*ڌ`$Y"l(܈m_\'&fTEnsT191rPþfgfv1ϗ1̓u C+ &xO ͑:b&ϜMF,bj'vQ"*/@75 H %}u/j_ o!='z<]M\(+H<Rt cN6|13|HPduJ)dI֐zr1^xN+V'7쎣 mVJ>=@5ͫqС/9KkzfC@g]Qbfaj Zc~DЏ= Ö \Ec=\p/솲v5_93NF`wd b½B? +PFw09%3H8~:h;YѼzr ;nu}WԵg&J_4ҟϟxY&3ѡI-rM7z;;=kZ|6xgmϠ2] 㵦Փ~ma|u s3ݚ1 تB&v( 0 MnKSzNvuk 4gw`+SV.؅,=V;Nާn?fRNJ"׷MX>-*u30;;anRu|C &LtOa0ifG7S f|4&:S;֛i7R ҧ04]'t\!^&7Ao:en[F<5ݦn8ZuB.ԯuA򜘻چe{mo. rZEFIK2{+y@6@u!`xAj]CQQ8P`W q=Rj ྂ"ݹZ=\cUtHՃ;̻M%h$b9vOw#[֠0r`T^}m[u/D*ϛ{ӄڏMKT`vz:LxGo3ׄ_iҫCg?PKyVS~ pip/_vendor/urllib3/filepost.pyU6+* A (\%hHz)Bњ]THjw}ɷgHQ-{7%r{aktEn0X ^Z;Iz%wmz<'7{v1Y+P6v__=o &V<,v-MUūIo>K׮O_$I-;-T>7 Гi8pk(`/y*6ЉҲ$;V)4)}- W<؋mLn3J, oTMړS ]٨-qт%a{-HaI"U UxF릖D6)E*ώu/c)AqUcu"Nѯ (-z$x!`y4.F_~&#Z1 kl%{@ĿwiZy tv;aT2Sȉ"lѦ3 Q74 ՔG PK; eDg 6 P&9SJIǪ|_XviG#{Uwaޖx0F $Y^0ccad]֛SwF(@AZF7Ñ\ Ǽ ;eptGieQ @[^8m3_\{ykoj;%A! `KIyA!Uͺj/y¦~\6ٴR#JuJ10͆z -hC"ݤSjgOC`lL` g)9V+ScD[k#a ڤMgZ00Wavdt)BjktlVd+ OtY@r63,$PjXΗandS#9)3Oȍ w!f!#6BmEdWhU7{Y73>$BXO3SQU`P[(B%@U6`iͦ#"3BsQAUetUF@LtR Eh(HiVz'@A[xǺ7Hv䚷[`xΥ,Y<  WV$UV+KsDϴ'@@NUscghE{ hK@^T*|°=0GIM8H WbPhc߄5h~l?x-3  95:/ąFgsľkˈt8"+E S1lHݱ}38Ǔ>Nmd򉴽,+ Y϶؃DNfx!$!I[Y UkqoeBͦMos1 z@8! 0 㻪nۺRa]|v Ljof"^Ykȯ:JS Mj0Kof=^2z~5K ݳI7Evcy`yղbl~LIeRnc5HNثGŌ^l}lpzQ.Q 4rs#0A{ޏAC$Ui8HnHxf#mOK%3d5 .}@yJ,DW2| ia_BXy`Pꍆ]wVW."_DqLQhRR:>=x y/;iѴza,r/oC\D1#ϳy2'%6;凳8Ci=˺_+R(wu=y[KG O'^Og6 J4%B9Dmό* '_/,qBERz-6>ΆXՖjߡƜ޲u\M }D?_P 3O&gECCbEF(TCbhNm>jJ#.F|e4>MfFLS3t-Kha>es9=^y7Rc]On+p@"J_-j|%|0Il#L'ϵMkl~$oGHZ΂BZ`;q^\]$'IOڱܴXk 5t;8(Rk(*\W_%vB[3!^с PU/B蕹lV.ܡhϧ8fZIS+Q@V%%8G*!:aIpwVZA Q 'B0 l*F(K+X }rjCr̡Slk^cCI/vaJ ;)ҟ1%+& .]op(srÛ~,99QX ƞ%b 3B\* + ;Sҁ+71]-.m/wxurUt\bZ \=;rq'*KBX5B9~BG 7 }?=kd1L0BxVt;*v -j=h`z`K5]|f>Vҵl4i3S\cS \*Q>ZR9[mUosPz!Ή yDYy~jyRg cȩt5"P>~}Zd?Yا'~ت"]kޡn+ȿ_?{(bHk-gI5=HYQ[U{V9', 4zzOՍACӻ~2s9P3a?\,=0 nG, C g^YF!WYGV|S^ ?-SQ_4#KK$rq 裆&,YĢV/ ŃcLRg= ^B8JϓcGsNoW=D\X`n_F&Ys5'IEi%gɚ(悞YO.^ 3:4bjp,D,NE(x6Ȱ]l1M4} hs\JBnH$83RH_ DRaJk& %vY]tY\Nbq>{HqlUܺ Q}ˀ]X_NUzcׅOOQpfrQd^i#R)_)ކKƘ6MC^1ʠy!ŋv H~1y{h=Vq?ms`(\?)n4> ޒե9(@T:]_#A; JpwA5 UE:ptؖ@nJ1"Bv2,›)`ē`aċ߾}C<.XK"=~U:zbZ"lhb2g{8u:ɨ7kO/mTDq5 :UŇ&nݱS ɒtU q=^*SK{8Cl3bdVߞrG dTX5lS7nȋPIl_]kt9sO '@"cne 6` MG7`ao4<n'̺ijcS7D0qkt6dwlDO*܍P.&Xw0hP(3sLR'w 3 Ey<"8Ch+wuqj; KWlRip' ɝ>8p 7앵Y ~Б ĵ BځS*/m!RD 2x̑٠42m ѣs`=wbnt%&OH԰k>{<8\MgKPp?0M:.Aѽ26,8s6@ZHHtՆUވɉ/`es׿_單ح r׾ Ls$"x/vjsexשSj!k]P˝T{S7WԜGYW$ͱ3 L#6b67k"Iqx}JL\$dw pzSKnDDCiu C9'%\_r no]A٬YYNjLg8171t^} \a+? =Dga-}/D@m=6M|i YO;`;Ii9ړz}lCpIfWB.$P\ ʽ9ӚXTV/-ykq(.$ .2u7:& n<<2K@*l 1nIDm|G(t#XpTCb`)egۇ$ޯJIt,zqeJ^Kz%Q $!Iq?Mv*R7j'A y 8ڼ XW6Rub)5VGkTFslg/+$>6ee8EfE׽Qo}-QȌbFgP/"T!;a_]V6అ{WbB?ɮ,o8|BCaFE(E]o^=եPshKj7x鷃'-z~⟵嵽'ܽIͽ[0T˜j1EY8iNE{hã?MZ{L[oYA ڙip~dQN5C/s1iSV{dwKq ʺDWIf%.MbMgGV ΰŽ1t{F"ޚ^FfAy(%+ *1.{5*t -D/-̳ImRhG W*J!d}4Dw52\~7m"*ﻼğfvE,nݱBtJ[SeSJ{'>R4Y?Ff `~̕\?#,(J.}Wx鹘>J ʷ?O$=R?J]BYwln7q7gZAoRMOѭWx\F @t]̻*I)z)Vܨ3{۹z` VQG"l|%1bR9j>(biLB!9jL-PKyVSapip/_vendor/urllib3/request.pyXKo8W7"nL:{h4lڢbN$QKQq8=l~ %oxC-Xܱ$ΚpRcAҏ?|O 6'?n>2w:>/ۅT%3jK~<1ߵ"<?7oE^#'$eҔR*XqEataK&,{H:Jb"{-3 R͢i6R\~Ŗ>5^cz|VX 3Ȼ[ҭST.tŒ^\<rEڕֈmP'mo'kcrN3|>Ά`x>W1mMtuκ7QMe# zcϡM>qwu3n:im4f'J$]'GD?y9jEh(R ԙ+vH6[K{h8e\Ke+u:J*-g.-}R.c88WhDh BDJ "`sZo\&aȞJ=M[AXU,~0z| A||w*i./LFiU'Wn=ӲDZ,i};́ ܮ'hy1Dm"*uU{o .!IF'0Քpf*OuD6vp[.z6Pw+F|QāO/.1ކ"m~; Ga񿜷Ic@# NURnlyszc~v+SSE<]^Q-'yz,Ҋ[%]}ز]VV"e[H nﮝ9TVfY4Eo/[j6J~C;+YD;a.’EɂIwd_Ip?q ?]8v?$'e#+IAkmAz{O2O,MjHȡb*FBaȣ3P[ۼ@o~{c;Rhr^`/@ Ԁ3) n+)E C#G`GQΜXK[ؔ YBL9zeEB0A)̳s've= ;kBJ\Rb; _`N6шq I 6x0l7zOG?yFC.>@]~@򨇟 &:Ъ(u;n``Qf;u5,)+kVu!jWRͺtjsFE%M3x zzVxbf?1Nw|'&S1kpN3pاS %,ϳ5b!d{'y}L8 FV9<:mAce^"AkJ`mPkq ?f 6cl2GƆN/0*%]jKg\Vm^ - &JLÈM꣬V;)Ğى7Dt! P5Y#7h+ 艖B67Oڞq&!r &+a@Ә9BCU*['fv,:ĶM6|!(>:Xd p}F[EƏ+iMY~Du ֥`ҧyRwKGrh V9K9#ƟS+ق(zY4ʺ27=):fѺf=ƞ(XT 7+OVźi}iJ\@zPr"py$Jso&:,fy~?&یsdv =GPś۟o.hGc?_~l=6p s>CS>֭V OžU6of9؎^vf0ئ, /*xd?x3 }I'uE0Iۦ8C(e.jrAg:Ԯ@Nrg8I[=k9nv9#`mfXT:fkلTl12;`V1gMY7o9 &`s47HEVW]N_=C;2AQP er DXQ% 5:2A` ):ZA{!{t/_2q 63b`=⮕bzWE?Qbs4eQJMZ<9YAԕM\ aLA^b>1ViFWS CUC:P,D} ?nChɮ&nr;׬ HFwb^Y`|AgLβ,(:EGQG HNJ:ze| 74dQUvr z,*T-_L(8&?㇫ٔ:t$ ]Gĉj^@5*@i#+S4<9ߍ9>:A> |܍?^z34?HJBP9ynhFxYHr$jŭg <DQM¼B͈".5.00ţU(T }#cykrA[EΠ S &Z%Cm;lȍ$}.ZKѷp hhG@@eL>=nT7J0W7^^)_1DUnDnE4R54-=ǒ" 7%BʰؕqCJ0~ywbu`kr2S ]zG9H}D:\YӵȤSnw~nw06JA %ԢpU9OYoE)kTzk0\yB"-1Ull/<бXe}xԸY$y35K J#mބ vdGԗϗ;ef [?q,QUIXfE ^bY݇&>̇50.t9x @'VƁSG[?ESc>C2xdS3$ y$4Ϟ.7`;pYXuOSK,ڹ2+\?5`T;wlO<-Q쀖AZ3*9Gs?SdO`/jB)xݮV;]J>G)`rKm@FdM4D}JG.uv'1|,͑;4s%{[ *u,NO9Y۬[yɿr-T8AGǔVbtdϧImg=6G"ecuJڟ2DJ++sz S[l/҆˩pStXF_YYT\f)aY<7T$B1R'@.z"p*[nrum+ȷ|9AxRP߹InFkgԃV`C FaZCw2SwA#AzOW]K/8M,Z?]' E㐼Hj6QP;y= c|cnѺgxN }@5H%]bNfsm4s7,UQ@b:-߯N*^XbϥUxXsKWÕ}I~Nr$?t^KiOy]:p K;qv/TF'Oq:S"c]k}N0W~{AgqK:BZv>Egķ50iʞ\iJ! YS忊[Mlij,rYj}ר bҾx}͹ 52mw49eyɿ6cd {s>1HxB9*B܄?+w=ŢH{>DO/`G iq'e_)V?׺@+ȋxU uuJ#ɋhj:7tn3,ƚ\S(H⩒]SLבWOmQ8 gbf3pk= 3rOs]Cx)[a /{gSFڜ!w:s/oUL1 = & JnPY|(O RWGlowN t} ^L<|>PN8J+]S}&p0etI.$J!wܤs`H5ϯ߽** [cbr6kaK^>'yȘ8~Ј]<:^DI~PMp?&ƍt|ߟPj(i%V&(Sz-V4MH?"2OCl]#!Ƽ\eso3v4.6 dP>48&;WܜnfQV/ }Ov3=}C*rΞʌzqug6CԪ}7Ќ/VɭU}nG "^İ}_ޞ:ΰ}X].n+{wFw z-X8;ფ/ЇYU=Ni:Ǹ,Ďy}jH`y۳xOtL 7$w<#*r[MΉ@]mED& 7lu|Aa-AH.MQm ̞?aj<7bũ2( t˾4O! e(:Ĺ.F[egeWŗՎ{_.݋`3dB1mVp)׭זϏK"K1(6Hv{BAO/?'̼cEu+XnM`ҍCg emu _DnXfHA >ҍ "՚n*."YI/eV בx}?}?k' 8O酄U@ei=O^:{Ҁ~q6PKyVS'pip/_vendor/urllib3/contrib/__init__.pyPKyVS*1pip/_vendor/urllib3/contrib/_appengine_environ.pyœAk0.bC.@ qBgJ14k!dvgzoyzo,(v0-4vq& ,Df(Eq&{W' Oi^?Q:BN4<;:Sk:atθ{"CdC1Q˞`3$?d@HF$-O5 gZvtT=XIFFp L9j!lQ}v$qzfJYuuKECǙJUG]\:`i~:+~z{v??pOJWM{u6W3`\7_}~}4Xefp8%9ߦ ~iC`;LC\-J2,;i#PKyVS8e g +(pip/_vendor/urllib3/contrib/appengine.pyZs۸οU&cFOu_<\XN{CC$$LZUX:99_&Dݐk(=HQ65_Nt*6seIl%4|=r:;WlP[ЍH wq}EVl l͈LYɤc$LQ\2YzEJHXsQokƷq~ol'/'srb,f¿ۜ |26Yt=VC_ZP0%ܐF(uI"P h!  V Zd/ښ9O1o5cDTZ~n%/7E(a}D74RxKyurǝ\y%9e5K{:m 2EKr&*t S@e!^Q0O] o1njOXIW9 XLh.]l^T ",@{O+*9(ba^,B,1|٢Kzе90Cҷ lE~{ksrhTLv቟ɗӛ.7ɏ99iꖝD\ŸBE&\g`lr(uY.G ! v\M[+e@ kQl;&Pa"d6`$ ۆ%{q.S.64%|c4$Seie]zꁏ3kGs]F"nn>xߖ``32"T43X;xqoj &xigj~_?20t&;"YjٌY9./n(bHW̌+wԀS):!WjXJsPC~%| _ k>#T$)i( )4vu: r SL8α/tƿoDY2r\aP  q/^װRn.ehF$ +ZГeHIHd`.26͒@ϗBS ebh밿ҨȾC?C>^ZAAP59~{ˇ+V=Զ.À*m+@3 5wЖff6a߀{sT_5EN2Axv_V"")٠f `ikO_wټ _ܘ@Cb=<5NGEH,F~6 ,vXJb([:a~ ,n?f vڢ﻾_?5$JmFBgH7b<}! D([t>s ;-/E8K0u~ykdqLl ̯J%ClM!HN `Mm?᧫}p+e2%2WaZzX)YWglĢFnz̘o}aq̔KPP8>¦xP)6d?76.u;zAa; JۭQ;`ҝڈ`?^s@s[( ;=Zu)eHP&;:w5ղa9IYZM ?Jq @Xޔihס7[ީݵ[j/:5ɑrl/1Q{ⅶnIX/^cvn 2yDA ҹ׋ƼLk߭s=' ^Z9g6 s{=DžV`x.fMn%9cUT0Oir3j7g#nKr=&g^0 99B<^>~`ăj 2S1!a:>;N4 'ȉ:HamE3̖JaZALIRH;Ҥcpju, cY DXsN۳1tGTGѩw_#UX,شHV݂ˑa57ڻE]LgR lSB"yӄ0Ѷ$9YzܰPr{#ZƧlӉ>ԓYA!CFɰe,_o~h)׬~ܖ/^{ki{|֜ĝVdNmy2cQ,e{$ybY);)`:!m+6TIA@Ms =^}hf2f[{QѮE [ %)x,9;9xEۛ*}qWֆmȣŘuxR 8jːTWLW&6MB'RN +pw8|40"91x4m&>oMyܮ78{MVp/S}w&#$sd˽os:S5[uKT0+t V'U_\?=<_ cO5RՃ0ϡZj{D}H845fk_̂.v~j[o> 0&UƥYV*6%9 t)v)4XG@8@ bCMԿfa[P#Uy~iMSؔDC&O%a#EV̐l81)v|lEPKyVSCT'pip/_vendor/urllib3/contrib/ntlmpool.pyW]o6}STTIW`0` A%Ҵh\! Z-6Tl%rR`<<~ОMVT*b1QBy@ %غR4#PcP! dr!eE,IRan9f4y~XV"!aBao,Y&"Cm*U E d-yG{2ik" )orgBeytS^4VII{2[;FqƐ'L&-P J)lx^CG3rT&!RNje >?Ϻc48rFA7 )DJ)(/!Awh!p?/$dLUU[jmmuyG^T P)jZcyn,p#vzqaXvt2AaKGQAr \tp>[О#8D0^&w"q$P dOFلn0YTf@YP+"B֞fhA%pK-E8$J`(kS[1DO-[T1[}xrqu{W5"'wU֭娹H{½hE1:maG./ [>-3eAjsхA2|TەPSw{psrLfvb vlˣ@ʜ(h2>RɶMQr{j54rj A"{oy%3T:e;bjDD.4 *$s4LݔZ!p&)*xI_~lsgHT{VnRׇ7J'{I' c$KǪNQ^)_ctAz[GA\1[r*%v 4(# W1vz:Z}X]sڤC8 O6Z s}`~M,MrC Ğ:v;vM;iEHX|vf"2-lٱm^ܢy~GB1gބaxuG%l6H<L y.Ft1)CS?θ4f4(+"5'lz6c@t)h0>M+)9@uT+NIbKGQo5z Ye8uVӛ[\KR71A ț[̘6eSɛ⣛6K jO/Xu%}.vs1yҾQYgyr)# W.E0SnPKyVS`^B(pip/_vendor/urllib3/contrib/pyopenssl.py;s۸(&TNfzsO:9ribYΥLHH"X]$HQs>ބ$w35]M2 습L+]$[VE.X*"i%ɒmeD$Axc9q Jb(Y$L U He\ ,\>NWES!"\xHOuOD$`RASH~ws‘K|EOzl3fY˓ξO„jrx!&NMagk![*aNTq ė,q1 Dk,@cV}u$Htb\Um^$/ÎD˲(k|ˤ,D{=eM;N}__7 +k3|Gc3#t\>.mu`*GNKKHQՇY'8C^ǰ47L:{Zm?Ւlk9`# ;$L>]pdYh!K7@ۜQ|ʓj5qKOc}MXl J*|pDq;Pl"WW}F0[YI/v|Q;vs{}w}q.H\Nz H^5f?d}P{"rʳr|1S@<2231nH) W! jSz1 xs~=zܽ~3?:՝p֜Bu.qO`< f#mHAa[ S$9F&(M2t5q=N٫'PPWe /"0A5?D{1 &Mir;M뛻N;ɛm]0*0@|h?a ҆~@=qP> ͭCS%SL P W=>N&/2m+}z=UQݼ:OLϠE0hB;x A:RmO2QP?FULNh0ų׉t`@^" A[DZ3^H(l-@+&#Yp ixsreNQ>xc* e DuǞ Ȗ.XxDD QBVn b .VYV'"xۜ͝hmvT갵 H"춀Ch;3l{xpJ&԰6BIx}a*2\ MC=| DYf0o`ux E#"=U&s1U$nR>&ԤM0A-F 0WiV[a`k_F)M(\Z8i t/"F>Eg)>JH$*[-RE"+_c+Z'aE‡w'?0b3nv4`p h4ſ lukJ"j!\"MCER2T(V`WOvBl}e@vBn:^ǵ%מG .ŖZ&"`\H%|6aeQa&v2%o ξ&j>B5b?iһ(DDm+?82ɔw׭DGtǶ"?4 !} h#Kb鹃}X#OH-0}7ȼ>8[HR10M@(o[ Qu!L`#Q967㶌FJ,W^Z6PG},c~KIBaݡ8L4 b-%6Cv(Q}䇾^AEksO JL!"]yv"V!a.X9Wݸ@+'MX:񦄇(;dbb+ߊ CaryтcVA-XjLL{0+uA R7d2Fgj %"-=l #ֺ|*Nա2,ZjWJ^D V330wx<μhnS+u{dPtr$J>ׄ0yqf, h$d (!Sks8cï?@<}S{Bϖ{_n@ 2n2bաP%B8|+]5jWZnLXF flQ~oErihN̆tP _Ds 4SOMzisAX A;>7'6SN[a"ZkJ~:+.d}KC wZBT],wGvLNlc6 XV8%c/Zxq': vi'dm4튦qD/ o_\i%j!vTb@3~Gg7f.\S#90ʸ %oijrjuYDrgGRɛ`ݛW,##GctChse`{.m-D/U(} "`Onctw3~v| x@a!\@i[B_1F7̭YWXLa5^{g=`ܶrȼHvcӿmᑱ{{Ky%Xb>n%IV!@gO˘Ξ0n z(r# ;U`,q\6cp1خݫjڶc?,Kuk:'4όd[wo& Ge<%)6zoOW!yRQ1ROg>VOUd!Rw ^.#a$c]Q&S| GVd[dư7PCc'mǿSDВCFҡ.O\w]m|1M;e|xQSmק>N i< 9x3ᣩyb̀B%>k:us}dYO~q-k/ۜV 4i:87G҇VŮ̠9v)¼Q6(T'!4GC A R i8)^:9"i{CZ~4w'y,%ḧ́=0*P\sF kgk0QLos4-}ҭaGU?U }r5t{v!DrBGV/sK)=mܷ&釃,*\*ZKsT("u|X +Ih4nv2,39˂df L3QfqOCmdu:Uup/sڭ]MEDq0( @,iTlb(VD0@DD"ӵÉ8͎WJfPzYFY\e\F&UX@U _:@OB/RKGi6!A8MAzaa,v$B& ~`Ή̚#Wњ}A (V4M O^t/E~%MqS(_2sY"O)} .@`4JH+Cޮ5+T H,fX@B%Nir(Ђ9p͋y$d=Jzի|By (qeF3SGb:}܎x*nn'_.Dw0}e<8<vp=YL.g/Fv4-zxb|AדlB*p~?Ÿg^a_Nn@ ng|{3 }=FF3Fgb7CL?p87 3E\prǙ8#pjW񧾸||Q @ybST|88Ɠkpr=?0Yx:x (4o'0 zM1$$J<8]Ws^ 2>eWJcNJWwr_ p&_2˒T&(V|Z<'꯼~QdeX(V Q$[gra Qϣ%XVwƫ = 'W>h'|z=Sm}W#:L3y)`F6WT߆NJ}@ 1X)ףAʬz?/xq}n? LB5Se 3\4Lz9C=%e!1Pܩ͋lw&+q|#hgڬyGi޼z>jaG~ cz3©>Asq&#? =tNANJn  |h1Z3Hq0Ṙe%@OAx~Pi4OcțLA^8 O V8wJ@퓊-vlDQ밒CkZ=1] 3kex\`kˢ:B3eibJv!-R?ѽkI;[9,ЌU 4[Q ŨcWB])sS__*#]64lAF nGk{&  A*$5sv؎D~BK0YGVhhjWBp"6p' ~6馌rX$.,՛WkpSl=6b]UbܱK,H;2*Ȍpq^ i`wmRe09}P~n5[=%jt}67~L.zu|&=u~:>c%3{^2ӹ{ mdJ=!*Y\YZ!QU&HPNQ1& .hKԧx4!5gIK.a2 *:DUЖaHXנէ+'o`i>5(+M En:gg('k|B-~ "^M-:xv_s79ZOSq<} HtA%~zGqt<1}_Ot?k/KzHbP&rw p-9j~.c%|< +@ЁxB:#*6hv4\Oo0(66혦m!meQwlY&o-cl5!=вf货2u}} ;Z"(^.x~OzW|:Օ ijpPm:Ӈ2/X(lqTRЀ Tz) nҎGEH5@t%ڮ0 l4AT㨈iџaf\ ɐf=} M/򊥈=:gF5AQQ&ku{\cPd2y䑼 Lyu$\ɽGA6/ ʞ5J-sfRS`jqpeknNi.@ieB5qI# (;h="=>^^ś8&o5'־k5y2oG f|3j'ub wPM,Za{ <|^Vt(e~P (KA%/V`tqh|πS]!st0{UYj=oZhnj˿XcZVbj=xeiֵv?(g|.1mJ6ǔ|t3~&Z%4fuo]wgbxYGy^G) ^QKY͔w+;Fh5mp/1x@$ē8ztL ć&:T2ih ih?sj  kN1V%RxW`቟eS9gQ<~:ì_R(6Bn}q_c),_x\=c nǘ%<Z:w2EBVTi=_dwć VwDr\|tQ;lu*L x.]mG/dZ2^FV^^U\QΞG}H/͍[s~/ec; cYՎvW<`R-N?ԏ认,jUEPC! (uv-C( p[PN2ZIQ%p:I,A4%e f82(娋˻[a7!t!&j]x/ڍ/A^7xٝ1A"9T/5`xi[dQ䰼b.9] a7T"?M(VAr+Yxif a:g!9DJ6ۀRM d ¢^O1-N 8W1`2T58.̍w60B蜸R<{E4 &JU& hTMËdWl&I ufPĀd$lĞ^{X&A'}[UD$*l !ab$*R@C2UK⯖0R`%@l\Sc_[\̋tMrr׈@R("/ȷV^A}NkeEΣJ֫JH>H0lMmdS$˜ii suҳRzo)#mg`JbV+XR5Ja^onv8r8(7Iu+ <5;hMnroNm}ji!(-N"AMtkx{knHةV=gzxFI5zU%{k-5[mAAQQnAȥB(ˁ, .7n6Q&QG9K0nD!=hDgnQQ*ӈg]^Z"S}*jjT );3ƞ˪LV>`Dsq d8Clo]>'@.OJg_)# >=Ѕآi3hl^~D qLo>\tE7st^Ӌ^y;~Ȳd(:Oy&b6`H_)RFO %JxdJfBcrE^Ϲ8c|:z@+-df5Gkr >i.j5\@5.nu8۽>=um9EOvVᑏa> @r-#)$C{tp_FSs\rS:Jf,Y2mql%ˍ%:T>@JhIE7w)ڸl5%,ң3]:,w=c*J,M YJ|@Q /])PLوbʎZU9byrW56òڀW- _n0fNglv!.\aS+0bPfb89s ,ҥC Xt`)1<?a Gވ "B<a$ì~𓄔!fVofo$w7虶 (H[TA IqG,f6Bn@˴¢HasH`' :I^2[6%.#%*4\uoid.3]{]pw=:,uUp/kӍԁ M_(CܖZWO1R{aЈh6]3˶YeAnh{v*rOL\r4xiNEZe%Ȱ,@kKtU(2G6r5K1 GE΂2kt79bU\n%hghgjKcdF@6n1.4Zq;Ύ'xr0~(w+g 1ՒMBu-wb0hhyWXZE;jICH()viI1 {$uY_i;UCt*cp ]oU(oc ࢴq[JY9vsI& >o魣rX젢rG0j4tөC:~SNGeO`YOQįPf(p/o5; o2=~' ew?XX%L_Y!5krB+i~UcU1yUetҤN/H=L0߈~ln\VeIy23['$Z;)^Zlr19bf:%(i `̘cE܄IPe7b'qdn,^sQx71hcZ]Ht{v&"LeG`1oF%>v:B%ԹQ*Dxhs*,6n-I`S69Bm[/6=(KRy:d _JL^&'7;SCN8$W8*%*`́tL 0əͰ QYs!V dڒ`nȚHWF\=9ʼûaDwwlYtԭni-PL'L6`F^M+oIyZ mETq/O3JgԇNzkOז@K6:IN&6= F( +AtAO8j@Y>&=| qzg5'$(imltfVvL_˻8= _;(ד'O!Hm@S9Nf']{ͺ 9s ysjUnVwk_/;ШkHD"0a+S%:x )6>H6g4nب?us.ۨ8/Qmc,5a&p)>g *+@1,$Qowv-)\BZ]C7rT"FZxUմ]ԫ@K:~ESHt e}o䪷7-ovut%*p|QFMSI\OCW]踍j]^{gՖ{ivdf ʋʫ[Hź_dP)te.]0N}´>Iĭ4Es(#dӒI ]P}pz'[?XJǩBK$Fo/j!fu +ID8f /i^JD(w#4Z4GFM6`9壾wT fpt;oGvtQ?JQ]zr=Rٺ6gQܶ8ref6"V4U}@VFx͖UA7 X'rx [%џpӐB H|YBrD7X&. ?Fp\Ҫ72skͨ ׮%$BL 1'a F9L_f}t;>ie)[u!QehSUf6SsmpRݬ% ]3E&"#cE,.DnAm֝+v>EynV]mu}}Kȡ˰z+eӋU_irqAV]H3\@fnEɼk<2YUw5ʝqnê<[5?LN`7C ٪!{UAǶY^zE:- 4T(\4.שR0mkkhEug ,wW ψI"e9]7XfNsBWd|97vsEDo%z`IL9"KjjZ/$*zoʱK|H]Z~u.9"Lb5Xgݝ\2]n7m [uuws~ђe u R4ܸhjy)$_E> K8|_N ft#Q~g CyzmfmmfOdܥx.5O=9]4?<*"Ƴ:QCgob~Խ۾[]!B5=z^;/kM{e{xV^n{o*޶ ۪; uɏ1Vq)OΤZT(b%cd)RqD[g1%X#[h?HȮhsL@1ܐY4JFQhmQ4ڟ5%%*so xFNa- %0љȆ##F6 >jJ[]:߷g nzVՈ2|?%BF{c!=t)Ev!qwkb`.a}cVAuav|n(]TCJ=SL dSZ:bfVcnӤ>S]@w:2dPHJHyN0$;T}GhF|JR^{Uעx]QS"%Tt* xB/ӽp%J Ϣ6MEL^gaZDb3YA#Eט(&| = Cr*pƣd"Q..&%:Eu,hۮm(.Wbfk<)P@ e)8A41 hC)Πtss]]m0F(qgDpVL׊SEK~G P 6K SxHk t&N,f {e hK'ۢYU`^+*̳.:[eBR/ed!د$\|<ћ(ѱk 3`hj7eTP/Z4e 0dX)!L&+ry@޷ aD4tC|=L%R5D=<.#M}F@Hdg`j]ϓ1ez>åv{hWg96^!!ћ9naĨwkGѶZݸޫWWl,wv@l9D y_v7$/;}ȵvm~6sbB?B پaP+7c~%%tȎ؜g{CI}{g| JLA@6j1fDl@|ogOw݆6\@M on3 #Ke]`;aC@X`o(kߺd2Ϣl:Dh2>Y;4i;9 d| Òv,r6dM`J_P kYw@eQ- CG"KAƼ7~܅ei̔$|~YK 9v00tgLvPJ}#ME2Smw f&$fE{pǜlm4bf ߃;вAw:d9eI{o .v0z-O^v`!A@~`#!tP0.PA)pAɄa9T+06]`-ab 2b7x]h"lAd>X,d |f HO8wtFoAd8=h@Y6_.l2ԡF 7]`|!αf@I艠BRpc3_m9MQ%m6]./);d Ps16hbqB;%4:J4i h܃SYQ)"=|0`DJ|7q!dǽ穸%rp2㤍\xZso!M~cZyɗgɇw`NWa)p k"w|,UB了Ô &S  wrc?#yDk[ /hc,ΚPP1ԡ8{ n ]_3:o:-E-ƑB@UTE3#p}{.$R?>-AzGբ^0gR'X -_+:J!SR!^Pi^K$|& yb(='#`M$`mҖ65!ḩ#@ESd4E.:PE(7;Emh4P *u%oh{^1VoMwQhZ35.HiZB.BF.~ܶdpL-Fɠ~̐ ԘTP_'sG#V7O ک^N=@r_[PA'A޹TYgzD CDPn!Å9;'Pm_C껦K=,$7:f ÷3Ww䘏ALT \ ` ۭdiL> 1lNFd4`R[P)JⰙv1n9a yCԢi2] \y ,5錘@eU55iu4:jIKR:=H%*9PTbY IZj JC9dlZ䕄c]FE:\u(uhKk>p0RNm#SHe{AÒ$$d*h>ALDD>qN*KEHڵ2#JTxWO5Sy0gJuN`&y@P(WA]y%*Zho@$m|TmN7IN=i5Ϛ?}TL?ƕ57ȵǻ,lSxHi[vBgDqk]%Y0g4PU.c:EԼ LJ)s?> t#oL&۪yM)< w lzYK1YduJ]'(W.HF>0d{HS($_t7|:*C}ʕrQNn 42@Cji_,^kUCj5:(OV(veYI5(%[|i0ؿEcV|9# q@U9q-ny]o6!stLk%?X4wij/+*1JNa!!T/>a{:HG(RP.SEtbmgӉ ,Bq-Hi֖Ri,r**xQAa %HB&|IղPʑ_䲂Y*sIn9pKNeHi.Uul5iJk"|30_+Posd4!ud9fģJA\sWrǛ'iF4)ge->eD*_9dpgd'QNl"EGm!+ [^)PS L:)6 q6E:'T]:z; rѵUz-Sf5fMUvv2~ª!B-wD{7ޕ;iF  s'oQGiܔ @ϭ38P/1BҾzI2::}7i 2Y bįyL_;(J3)Q@U-9U-3'}%Qd%}rcˉ(nBZtZ T5rXTU쩷iÓc}%.U~Br )/ 53ΏrDzS'T`׆XIV$kmzB wYMC* <2՚9f?9Z7G%X\ƲuXN.%U3s=I1[\v>VE*rYSy"ԫ͹{$2,qRdW%i5*HNrGboKvU}qvm|CcgcA^oX,|Z.DA>.30+:i<Ӣ5'>|w]IJ[Uz2/P!!]-x~5L}<-zlp BHZz^lOf|/( Q$0ȝx>~@g>Sׇ>OHf"+ߙَ5s`g= 15%6AQעc/m}lk&P]ML!uqବZ,(>9|fQY1xgb8Cj@ . 3=UbI%' pVŊ˴³9*!tmx`O'WX^\jw+vpvu Uj9Qa>ӫ >7h{[gޅ >Wq;x"1FyTP GIrt~l(WAM&1|bL˛Z1Y~ K,>;fEO$ra)S-P:,.UrA)'GƐR⽯`N t4Q(RaPKyVS*Yb69pip/_vendor/urllib3/contrib/_securetransport/low_level.pymo۸šҗِ])t+Zc]dQGJqa} )ݦݾ,@">od<*՝*J2V,J[lM#E(F+eFwf+l)tealK(ʛr+*ezUܬܮ.h޺5*WJʼn7k|hs v+*/.6+]*1WB/E)i^ZHI& .\>F6YJި-r]E;V-QZF\ (b`"dP\*yki65<צ>З֪e[ F!sqB1XU??oyoELu (dm=6֨u,H~ LE)+JlGF`Q7m)Pk(L pc\;#`Ez3xίgչ8i5 Ǐ/oλxuqvz}NUI_'UvΌ!>}z4F Y Fلo parL /ŝ~TFlQ/Yo6L/W 4ߒ",IjZS D23B9?IgNRZʶY )&8w q0OQ%0`P ʍu~Pu^s*bmOG4é`U[;Yʒޑ+aZ m4D$ǣO i g8490hGZ`W՟MpQ`2Ǚ*-:2޾ 8FHU oW ;~PZQs)D=#`]B|C3XeY&;̼1,E:!=a!s@ˎdMJCeMi9/)B@C JVCxfύ+TF%=i-[])<ޑbrX"V1Qg5j9t.o ~{*1$PZC,J+$rFUq]"#k hq$c V;; 'ӺVՂI̙:bKٲXIE}U݈0~N/I+q(}x# 5}/Ȃŏv,~Nf|x*0QM윕LMY_RbS=lg")ء(y_z&&X q 7]kJ}}y( `:YϱYxgi&n]^>J:k&.:z_T\;0NP _Ł+ q[qzM˥ C ;h'=<>z,e؛Ч߇ Gzf_9efʱHf,껬cvv !](Rq,=G3@,tFSg,/a 64MA"f űSf-сEa:O99GǁN#hJk΀$7X(>@] =c g{Z. uhv|95is}o:5ᄓӺ$u03a0XZ'/P J1ԻRZBBG)*tn3*sH|&Q,8ʼn2Ԣ=`SbQ-dvF8'ԿU2f~H:3@6_7Fux2Acȑ.р K?ySĞ8V 1WŲ)Z^ߚ%~KdߛNBX_Lve r (=L&Lb!NCK/Ȱ!l}GK q΁2L 4+87jBWuQT v{,4.Ai+%ǔ2){gxQXp_VE7 tZcWa}P!DP/(!Ghb:fi%n lk][` (j̅_Q!(Q("C-7@գ|/(k wĖm-H&D+y=j(+o;?aCon"e$-;G0g@x *~+&2*ܐc$YjN,t k8Uk-6I \IV r; wbCi~?E@-XQ[*|V2E>R*.zRr1u6!F#E/Id# n+Re{Z@^AM, tP+vЬ RQmtCz !2@n'- ώ"ˮq'B۴uGI9>YU9??4[u5Ϗ?M7N)P>;wK׷ |N\lH*|Jy'iG$uu07]lsqh*iiܥiBD7yNq'?w kƩ#WyGYD4O=Oj*"lܻ/Dc@"a9SE$$@GpXLEmT%ο?PttʸĎ;08wYL-x|3ޅk0E FANO}' (\AAǸӕDW܄I7PKf>`?t +P@Eg&#}7} _2L\@.D>ƺt" >r1g@o'TGHu)}(,YBW`~cWˎh|,5+c.=+۱[7M` @>5`BN=:xqPQ=]Ƶ w[6(V3(tngP5]>7F뽊s @U=^Jbi0DKB.=摃Im<KbG}(p7* N◎6&}ޖet ԏ9>#% \q*3S7=;eA{q*"a8OHlE,`7hO_^ê8ҽ3+_Nv=* *W 웂ʒ×uU,txVQZЯioTFc#|+ywԀ$cjDH'%/o5b4RÀMH otX닭n@ofPC-Md+ I`ڠ g]D3B D~xnHg ,{AX[iڦMcʂq} KQmO,b(r:Ӑzʓ0d8{jCZ#8uYByT4FSq.eDT(3NHdގ`QYCP7C@ԦCaîKe jN(S7-:+Z2(HPWE؁[]D֖lVQ\n pIWp 22"iknmpƁs{=R_ߜy0!SyDrGQA PkyQ0Ytݒ ) ;3~.uZs7R 6eWt*3RGmv5lzFm3ѥ\; /2,~E[ ~ T!$QrqW 4C_ ,Sz=Ew6Y\}L`r>xݓ. Qʍʻ69ii*n@>~_/G}B~l\Hjzru{אIw{\ ӠC"cK#c&̏)n0 bn508q7q:uuM"%s$ <䇱C@o7 /vr^}0?HgxH"L0D.FhzCM*k1ܲZI[_Q@|S5QPS4 쯗} TbYuۿ8ɇud|,x$LOqTAt >S?$~↟} Fƒ5 k*e&K] ʯ ͱ{ሳ@e}dc{ɵFl|d9wD$X,vg(^fl(^zbKVFx 2IGgg,4 HXDN&L#1O\^r#G"ŊE<ͼ  6[4g^"^ƳgRFa d ) ?b|@\)lHd% h>`2đ9 Pv$WRV4 H$8bA]5ND*Cd h=ո 6SJ1~/˵ yDP$,?QY)00 ^j+*qGq3UVPL rJJ4=P a&>W'b|).&翎KxħՇW &gWwٻ8br|y)'@l|zq2>w_[<;':"%;=}÷# ~|utߟOġ8\>N1O+ʅ4q+''XP;u WɻcH|{ =9 jG'ӑxwxz1a!2ӇcL2 +st~v5ure?/Gp2Db5QsNdm ؐO%"sE5YƁNA)x&=%hZWYEbn{y <"Xq{y48LuB~*{YtmJz9@i_syǩJyE:qrS 4^9~p^B @S9C<^JW4LB{@8s4 ǟ_~b۳1uc/1C\蛞?tVэKI *kW.2Ad! #K(mD^֩uƌ.}X^zi0ͼMTrZ"v>#̺:y65*2hۥ.R@?;?3^xSCR1P,+bPB,Lt} 9< %%$/bfâHmSwaqz}ּӌbgd]*áI39h$23 P)qrbZ?ފZ o^^3UK7`oJWrbƞ/[+YPYIp$h]nF(q'<Qg$ q !ę;<[ʂ9缑jRQs(&ʙ)]۳l(1 *xtuuL)HXܒ!4!,YY0 1_fhH…8EěGBEw-XӮ[A0/bra} FMe1( #ݪYjn{;aU7n;/H`@ك>ٮ/1(vg nn|+Bׄg^JsT 4qF`%YTՂm[eB[0>2y̨iYu[YElhT%*󧑋-Z 6M0.wHD7@گ5R3^%z7hܬp_{BQK7GY-b%){O-i5WcNR&8to"ve`RxTipJL Z0e"M/lM|?e/"{+i 2tq8S@kLc@Z¼USa%hK5&L]l-Ps:>=~[JA 6Bi\N"R 2gezdYwU@G-e@hVL U)RpLBT ÇKܱBSS~mq˸PKDᒆ3eMtf얣 qOM2'&DƳ[~Ψq;_<&얂YR`xaLyRyBvV'ctJg =VKT+u$(c=W"]FNՏƂ.,hw\ Yz4|LF q2[ı) r 8z ;{[VIJ8#l͓"^u %LҎS~łVU{۝7OBKܱנqZq* {.:IjMIv2qV5WŘDa&pr(mvz28*:.E躾PA)͍`&"Ven^SI鹤zW=|MIo0(&x#)S7(W^ k3OTgI~Sʰ{x⣾:~մ^ s}5{|Qf?Vi] ˵MnDZыei^g)ޟfe=1;:3gpdaRGD;MQ;SJΣ6hƫo<}9M hQFq*J<=.یEVZhZvXBYUSPͶ`20bmكW6"c{ ~[4-*Ǫ ؼh X_r7jbZCTRIWLh#SWx ¤3"拯/QGMSSG:-1M_pFږW|pyWĿvvWBROibs/SSyQ΁ȩ2X I*L(N>GOJ->^pD<]\6,sϏ\ؠق7I_|#֣!/\\rruVjq&Ab'ՋAgbᩜH sS/T !UTǶ M50/mU(%~^] CS}edu2 Vl6f^zS]?vacz^`㼳Lj{#-)UDIi]5%ṣiGO:*էy\mg(.X-[x4߀ܾ͕h|nGlͭBHFy\0$jFQVslQ-b`*q60Ć|Ձ|'{ 6fi%UM;I +}Z , ʴV?3_ZtoTqR=<18\`s1?YCIQ4NHv.gmYj<|E=%<gIUl:ȦsjcP$z> 4[Y>٨08M&]Χjeӆi:ܤoM-P>"*)k2'=gq.ܙA[SxPIpiCV1jMʙڱdԸiw ZxW#jV=*L{YHﵒe, Yy`zSW+oɵ"g"YKeNP=>&\fU|׽6)t}˺Vr*+64hoˋcB,k0\ n #k 'Rny.R4;a1疅3FHl498_^qձPwʚW4_ y QWk- -ܾaLɥLꦉPf#t,mb" X)u7GMoMcL(O[G劌4h5adQ NR+NZtǀ)0fԦi§M뛋bY^DwO!74ŏDK?HKKc-CPɐU{\M;Is)nfxh91BYq/V<qﺃY+Rp:Rz7T-uϕ4;{T2zC 9<8 nׁBy Y㶎O,$lb 3Iׅ+5pc^>4obє{$g"L&H){ 퍟%'˚I}9y  #Ktͻ䂱K/oUfh9fGǿW1osy.eq 1hnr?×R>-Q} /ua]kF5{o%r U0.¨*5t?F)Z6 P# rTQXѓʈŗtdy6}=#sgt, %yޥlݠmxojjy-^&*k(庀7*7uH-\ZYYGȾ i,ڱQy6I1W❤~~ypXnDܭF?V!P4Z!E seVㅕ:y >I78ɔ[y+ӲOjb[}Dua1tN+Bc--:`pT8A0<ܖKf n@Cog4CT W/ gB}- F ꃈkJb_j /Z B#8!|32PmwaE$ýg44:@QjZ%0F J?D~BL52M߈È׎%YP3fY\P'юC ˺'$s-!l!gX).VX) CQ"P;D-)r~0Qf{Bʎ<4΄5n(J_a򽘅Sno KM9(!3څ[h{19ʾ|LE?pzRm` PKyVS2pip/_vendor/urllib3/packages/backports/__init__.pyPKyVS޸cH2pip/_vendor/urllib3/packages/backports/makefile.pyu]k0+xKR_,XK?MǫV[ m7 2 7$qYGޢmDQ/QCVGf{A)8~(Rbv 9LA~ z_97|-=\ qh"ˇU,#};a4^ |r .d'#hmy\R4m~Q"dYl6CTfKLѯ99X^K rz{m 0HUY QQ*' |܀ms>olG[W_8&&K}M1q~ qDqnFkep%Mܤ N3I;tlN]=6RUUW=\ 21 Kķ&m}RKeZ˲ 鍛?Y,Ata8o}AUc*NdyNGPKyVS>Kֱ/Bpip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.pyXmo8_1PPDNin=@ЦIi Zb62%t߯gHIw"3oBZR*}MWW$IF:ۅTJ_Brty*Km% _um!̺R5Ӄm\ⲅ[VZ6qddd7QFvZ@\\7Ejz={LJCu$։&:tɛpx!tS.OZ%W|:v?~S f Ն^_ hfV1F?%e] f?f#g6Jzm.Gg1i7.lOq.") 6y>aBV"?I+)JO$ρ)s>ӞZU-`\R:#hz%*+ ,Q?ڷQ-a\ 3Tʥf7G0,%Y:!X>< (cap1`}x+d #m?mb{άv'SkUP!5*D$g*7TY͌RB1 2c #L\@${ -  iPӏ|uھ.s5W(ut;[䕂;ꗋw_[Q$A.tT$^|1E:_#r]016Ub.P; u/g+%17ɭjwo+\Tʪ0!>n3GV |E[ɭOP˱ QA7Kq+r.}Cc؈.t,{YF|5W(L\429\le짋N|>) j u^} Z\LU#GPpud صFϕuq}~c78s0r1__?$T+qx/@泟/ޜ8:Y3]vlki= WI:'1,xPʺ A鲦Pz4x@ ET4D\)`\iqq]G:tx ad8ي9yvIYi.HޢLZv/K9Ȯk/}y.G1i=}J.V]u#q5R.x r7r]2\+aڮ^*>aX5i4傍hG] { 7fiXdI[aǝܹ^p?f!.K yG{iRD!Yk 5Ux5pUH]Ki0p9)Bt>?|Yڅ:\ Q˚mYLhw1sbRbBw.~W^! B#dQ#hwjYllm2 wFF`2$N R}2/j[;I;C/N߼]\=8?y7;{sr_L柖C힔ms 71s&Ai4P/MwA=_6ٗB=>8 X꜁(1xU~*%ŵU|[H4ݽ>}~'(I_ō4Vjn{0:2 =<|7u4Y쳆 Mlctr9 }f氧ptŪRqH`Zԝ۱|n+΢nBƑ3"`u-tƻ-8B9td;bۆa(((TEL$Sq~èZ 8m_bm?}۶7޿w mw4e jcOvo= mR!{2ݾ둭Ncﶴoܽ^pi{G6G_xR4)'=8vÆ{{ 6GN'|k@yԸU7!h1pO@+]OPKyVS抙$pip/_vendor/urllib3/util/__init__.pymn0 ~ !l7!<4hvz؉Plb[%'OEGi%ҝh7MNȃ8IߚAVڊJwF:uPr0Ϫư wN k88"SҌ=UrJ|pP6Mw@8|]-eEz MHN(k$;WFUmt֋1Doɷn۔镽,K()Xٶ,73)[w~\\oY?7Z$ߑ >p$ݞQ{< nN.$ X]TzOa*{Rj9T>HKօmMʲNoᅔ$mg9X.G,bg#[Vn^2fvN ¡qPKyVSs8&pip/_vendor/urllib3/util/connection.pyXn#7}WdnJ{f2qxc'ه hQݔĘ";$[[-iv⥮N2z Ej\cxQ8`KexFq]E;*mH)Kk'Ig]2Zxb;cL' V\W;aJkV5dZ%npiAs%'m^XҦ0UѨ+@/ɺ2W-Mf$+omKg<8F Xpšp F7%*y!f~/Y;[lR \| R3@&[m]xN MD ^1d?Ɠ59f n xPcSRyU(Vl+dMxeB4.E6{Io8^1^W k{F^Meov~XXTmE 8# gmL5o$[r Tb-Ei V1|㭙LC撤h5|Ocn=[F;=) 91!.1c` Q}J?Tܸq tQwة*.:)b1$凋GکeEB>Q:)Z=y1YW'eÓ 1eM<{Fb=> ?5:Lz?7{怌P$i:+Wm:P 8>Y2(, GW  g 'nfW+qd&8MpalzBȡaQ ;H移aM^jnyWcDlކqD}]EBGɒ \:^DaXwW[ J`[юQ+JSv=R{~r?M_4`PO, ibI:K<8'M sڲ56fE> o߿Iy;'A~ѢK%4ăg{B*<ճg:Bb%y&??u|vr;f2r?{hexsk7၃2]А$qa[(IEξdz[$PKyVS \^E!pip/_vendor/urllib3/util/proxy.py}TM0+FDB\z[)V%bӱI6c\|f =t%}BE(,u~/+,~XVH$gf ? U$56Q +:H,[k|e ӓ(Hg/ р1 Z^ gzI]]?蒳a@}4M&:lf*;RD1[܈kw)HM[A. nEV HE.4%b̥sR]9 myfDb p:N&"^mle똉NqŽ>Z.ľ\o*Gfv|Epׯ;Oz㓳E(R_QYtBQ*Ji8pC `UU&Ap*^|xsoNS\ga8@:FLrB4uZdW"ųAf8>-)byZ̘Lӳ۫٦W1qc; !7W{A7Xd>hSF\Gs}0: 2)íwU*-c6NDNJ0ߗr?)6@<_ QxVw)tMp2z𤩅QP-=̳*}Pr+;P)dҺ)"Z?8#eX.Q7PfೖjcDh. ad1(JFPAK7|p/4b([ZZ,jz!ԭE|JGTuF뺝wmZZ7*gej賓'L iwbre55oHduJ9fw-'"95k̆z"iZ-GQAPnQqTL|$ɉmE"+X iQNkD&nD F`Om-{%㍒ S*Wޟ4+wKi$T3$EnJAM }^Jy k܅0yUuLO1l_ɭWxr|4ɬTgYqd˚>y ӳ4FI|Oጬ_TBhx_"ܐXyNΕm];dWoe{9]"T).Bj_4nEoqq_YlѿC:QkJ݈UpMR&QWN !f8ws X28PeѰeepUEtcp1`@øt?s& OSp)3t;0v.笅ayGPKyVS S!pip/_vendor/urllib3/util/retry.pywc|.]x˫pɋ Q4zE\Ŭza ^ypUqw˪1ӊ% {bv`.wG}V+E8beWuW_%P=JeL fWWc=;e{Ζqw6LSg+eeIa+^t15a`"T;>J?|@V|./޽EoWWo.gח7`r~ІxDw% 1@BQpI$Y!NQޕo @ D?t`^^ ]{z]ȅ%~Q N^QAlGDzzqy==Egg} !$T/s^%{)OÌx?Ϗ/cO`xH]N7%y>!['uA; r#8Hex`3@&ޭ7`g40deK!L6 8TB+iZlNxܳfN$KL£x59–N3DTho,FWABi _!;›}.U@, UɊpwp*LYNFA;2^@ہ_i; dxgmL:8FixaZY,{ąAgqɉ˸&4P"N,wqǜWqjt .x!Cp8M}fU O9_lɉ, 8euV ڿmO$\j^eqh4F'!IQVmΐ/:pfsTg>TrBx$lg pNFJ Nɬ6{0/[u¤mڪŝ Q#gzMì"@d1 A[iWF!Ƣ/$jZaЅJB4h$郤4/cF8um,Ac8'dQ8P@="*6oC:&.OPـHWO<ٛ>CPbbj6{k4Y2 1i]qbt2?Pt&_$] . 񦈑lIŷ_=(&nWKi\c姓'R_P΍teݶUe; Ȅa  EnMeUw:\T0=J}<]a%Y* =:;$ڀ-'k]74>Ȋ@4q8sr7wD(Jw1A`M^6+rՆ80ȉR$ה7PILpvV lZ4!V`C$IC Ahr6g-- T慡l?o ~u !kH)? zE@J!U{AFd'?#]B|3 B)@H} ! *Hb`{qD{l){4|!b4NjU*T[n#HPD)ԥ:a8:Z*/l'L8IHX-9K)+L4#g*#v?B6M4 ^ BU |{J" ܞ*%)gi>o]f;$~1%_3Φxuvvy ^ήΧ4yئk6ϠHupoO H!nCsi`fIZﲊhz6[14<;9t?Х"ÐkfNNFJȢ1d(^5n0ֽ-ݤͻ DU6}PׂO,ysYT tS$ZPT 12GOM2 KNqx&"FdȢ$ykt##[a $ ĥЯƊās@5Be\`.d5^ϡ/?S/T"@: o-}W;0[**'@c"HjOx(ǔ 6uH6:}(R^YEp bЧ|pq{O }LA{>q#kkabɺ6+̦\xw^7쁷:hxwSB>ݖr@[0߄H"A- 烞;0Dy|[uHz ÜW(>>COf&M#i-t Ĝٖ*3i7iڎY6Mwll<"v>No30? JM r FAĐ ~cb[<g:p2z !LȸR/hκT6dadAhE4 bUCws4z@{ -`HTy^~b-K/[WO>|Z?~B%gÚ*Z C D<AaDvzVG+RVy#˯-s`ܿbd?|#c>!ic/ؙz7;G@YNIQR:RfU&ឆM()mbTA^rAk6HQul?#\ٲ\,VX<婕}Duah8'^y¥|(@ HȨk5mG_TA\; p 8C]0dRQ JeU.g"{D`S9LE|/ʼn)؁Э}$Z5pDTlH{naq -`Fm׷,첽nw@d# >/$_6~cT$2h ;pA<{$ⶋ/?T"'[X+V ew<[&ko٨LHb n$2h/az^KA/D"g`)g݅g: 4PQRއu.@V>"RxgKxպqDN4I|OCj *7ϋcnO) hC! ۬וSwY&݆,YsiH>"HoU䦺w NƎksFC&r k6Ddܾ4IimXn:S..*okgRIJ vbVe96Ж Ii8Z"yRF6n<릫9L=Ia/1ޓ &;/dTؒ;d/*u=/v"ŰytQ;Tҵ]:I1$R63`0E~%h%[P]WBFH[Ztbn݋mT{TE!q:^_֘pl<#-zjF͵p6ʛH1Kl-xUOAOK0TXJ ˫ZUrA_@t]X BFANjCѝAޞ [j*QűiU5H@#C1ch˖CQ 2-3YUsxN @VcP[6է4.p[Gnw1 l+~}:{pSi&^ #$Pۗ֠wT&N~h'YnO3^.i~ Vxَ7nHdQ!y*ҨtӅFtZ؎vnI%,FO|Vӯv r~\nvu7tӗ%Tvz^nB)iߜn4hupQGԁ"ڡ&\/_חɡ6/$[ zrt{y;=|6_x{m_5_?~8I;>OY /|xu`s3$=;7$CzB3fdҒզHM^[?b$.]lG.58=AYcpHV;FQ;ɢ1:SB`71P[ @[&wq*?; Q_z?b_PKyVSǧC pip/_vendor/urllib3/util/ssl_.py/t[Hw:jcs/2-eXJv%^RlK*sNX; , K,6̳: ~&Y)bX}ʫE^Rd_x%sVn78N$vI%zCӛqQE雩7<~Ka'.~-Rh|3NwpQUbW6>y,xVpipL-vzh_ ooǻyxss>Mɪ`ȖՊ Y"a}ry*hobv)d`,$Ϸ&b6ہz3t:X(m)T،ڍnKG U\\"[Ȍ^BZK5C[X\jeHt &%;W*`/.aOZR\"Oӝ4y jψxtK\ D`M!keV/!$Z(@'2 ¡YYxzg\D@ \TƄ,bٞ GV~/*$d81˰vYsm@)c !u)d,9on߂`HZ{i8*v=*J:3D0k%&{HƎ`-)rE1m'jౠIsQ@loD  fag.S;7hrq;e?Y;٥8<磏Cxi?xZ̉'aX}=O@{}ND`)?剀{Bࣝ|}a5Wt`D\qFhG@84=6=tQSI U^V_ ĐxEk`CCnn-Hy"_:Hp4 ]T$7@!yBE^0SLn.smez4W4DpBfëIx馕 Gǃ[Ex|4SpHHl뎆`Uy!h`%tG {̉D9qзOL-sJȦrJ cu U_~ћ\5&6E/z! @);c8X 3gH%i* @>Kc7`N6I)?݂ E 4LJw!_E!oatƶel ?KCH`.#f(g!G@.n{K9.dlۭd2ڀ4LrUӂ1鳵ד(H mK*&/7d01<nTBT$I(Umؖ½2@3Ӵ0UPusp\7^]yp'YVo h6fQԫ ^DmyzT!Fuo  L~} B4;0"ol KA$D%cI8*lh^E*ġzfng bZHGh_)i!Mjeufjꠃz/ijR fYwCB3L ?JPZbUX<2j! 'ġ^Fd:)S}]&.gkUdi臀pʦ ry H6u]A 5^ ~c|0S-2 ac$/Bo+:Ȏm'G߄EbnX kpxzv^V"ag3$?llFg3mY!0Heu??:Yk E 5e f( 6$)i?͞ u+\ab<ߔLtSu=-%"P(c5ԢYF'B!˝m>"Csp Fɟ[KS74[OG`KZ?a Im u;+^۔$|[ lsH3€E]f _:#,3ȾӘA1Gl<|'!3<=M!_-EM*c ap ]AR9x90g'kH~4XϪn< Muk2d^p^`*w= ^:h*~e"I0#p"݉RUNs SԣSЫI3bPg%|F"h W)3NzRzh?ڊ()=o˲5dl  Sb.X{a7⛒t@c%:UpuբJ޺\vyẹ9&|¾!>3~>2ýņDaR%s%VϪwVHZU `GZ~ 2vJ!˃4+)ؘì1}C)Mmq B3|]@H}nV[29{=j:\k#M5ͬE5zΫ\.6?>Rrl Fɿjl7wPKyVSl (pip/_vendor/urllib3/util/ssltransport.pyYmo_pXeEqX%h$mQLʤJRvܢ3CJ&-)õřJǤIp/[FkwUZiզ,L<rR+b+v⓲ujg-ϣm~y{7bs7hTZ ÕEG >]lErWqijNM/KiHZ)QBو?j 7G4?VoHS#%k}$^`QVlTK'-n,Pm 7NuMyddrMxJ\+J_h ov:j JRf9,X@8'+frŝ!8rÉg]isIt4N.-Z~ dPc2z/W;BdWo[ ΪESh44h#Z:x/~ӂykS%"ҎmΙ 36nƑI}vws4Ӏ&jdFaJ^b2e,` u]v<=%<xI} "IT+,dҌ%ɷqgČ2TᛍXB|ajAHPN]C y\[nC T!?6˛4ӵ~OGdy|57Ԗzw{Vo^Tg~H^_{aF%Z-,TJ*R$;[Ib JܞqF(M)}:貢VL",/g0b[h3ka(r J}{]_DP!՜]ג?Wh3cBSX S#Vc%6Zz<}g"Cp:u1<:aC~r*(pF {ܙhIPnZz@$K-Y}cx,n{)oʸekF[7_9TH d&вyncͣLOP%60xold$٠u)Zt‹Ij*֯Ƽ+1qPC h'*R*߱_j:u*J3em#e͢BQY6>)-Ydjb^ 8[(tJhLx7WAt%V0t<a޵MS~E6r/ŒTP׆M>co&c5+fӨ3cC(bԴ 9İ) 0uTKpVݻ4N;7'$Ýq|e;-e~y~`9-0n A{Kxξ`|^7/w.|xL|V@(b/<oz LZ }n`۔޶N,dYRӍx~j|}xh2sFțx`f,$ېOn/C FWnbV/5e@i%}XxYJ ]jZjx!s} b[Nh]}cDQNci "bOY&NjJPsO$u:IHnqCˡ(/''b'QJ=,DEi$8zx{ȯi.^ |A+0תMP|wǐ;%wߵ/gP%o{.Wt)SP{h9ReY;%t*9:XrSqlzϋO3spvaxь'E̘WY/ED1[b@7QPKJ_u:?];NԬbdPk.lE$]Z} f>PKyVS$ '#pip/_vendor/urllib3/util/timeout.pyZ[s۸~4캳iM3d&QHHBC,ZQ{HNδzH$88\uvlXbUa|Us#wmĚaZ}z^lkL[3ɦ%7-7Q~1s t+Jv@;[#k;~??x-ǻ36RFFwNuC3-#Q{^!-@jS-SgxG-TS2%k﹬YwpEy#Ugî̒rYsx3{8c9??wYGgڽӬ [A9y0ZuU.<bx"Eڃ٪ƾm` nuW7s?o6޺N,]y' mfW_]e 3ߵR斱wkE`V[EJh2e,1F[8˼n~s3ԈBa-;aD/nⴽ3~w0:Er ^ +W ҚDZxSS$xxu@n/A[HI:rZ QH1FZ7 >= +e~$2ȭns]kY<]z?[T.gOi9T  N.!<7҈pPWNArn͕0{!<F oS[Ͷkq"[Y[2p꧑o~u[MH9mBEQ >ּ4)[CHjVOP"/rʆ}ZM~.g=J|ŪFhqUbX k@:~tA4$O$lʺof^@o)Mņ7 eL :iE uLK+FG{hܡ% &;`r,zT ";*4L@+ViК3A;@Dk6КT߱ҮN 'ט {,J|QPcX)yLDVs4X/?,m#pTG œ].Bb\,fZա*핫LyY^ _ܺ/srG$Hk'[،~xr'Tmxg"L`5pv.}~C~9n~.,l#AŢXdɂDSHV6 8b[QZӞwU-ŕ!D6oIH/T[U ['\f `'|PC-W=)͈`Ȯ"US <{xQF$BLLL)~0 j@h=T#ņ=S )~襱tl$F>%^_1 P hRgM,j`U`JQ%Ct  Z2"pZwLA\POc*TliJ-3*$Q^S$y7#lNax7.o3(4];"RiaXegllv#ƳHoѪRӼOt&aփgj{AU6`^'1t5IZcۺ=*]>aU}#\wm csύ۵|tV`>7Pa!qc+ faiZЇTGc,d" 2Ml{{mDx܄_hLQ-e)T _`*+a)|H#_μnjB=<1WH/(lA\virMH4<~tq0d NP-ܥa|CB[bCpL]g "`G4_7:cHƃ;0(O!kpbzGSG/cȁQ库68Dybưh +M |Trs1̰FJ]kGw3Y  J2Yc}n++[Ep^;EǩkҼ^TFSRZKwLYI_'zN+̶ٗU [`(uic wtE՜cv뜚b2~;Uur@Γb0}p\߼=;;Of?(NLl{gwo?b?lg_`WwD[g`p$1mϏ,_=9Ȭo}]Л!%iu[RoqCHyux32:ݡ~;l6:G7Sٗѽw2A&4 PG*]IãWŅ"t4[53"Mo/.K}dg_?T!v/t6Z`^lM&-e1VVmSz{4h)㹐psAs=q0~_F?0d!N^0 eB_vo Na^`_ *!_޼`L ht0:IeYܦh.[#4fvi10Q &^7Dɂ"[M&qXJ_b|u03HX-1^3 ^Cwv5ʾ4@ciL ڰ\SC*~Yy vA+NK.@1XGTnƐS{YI FCZvCJN#HW='4+Pk"Ic`/Dhfé6uX`cH?ymkCoAX# ^M/p9j7&6\%NaYHt$d(A4 ah`H΄Ԗ_ +lT)ܪE<I˼x)N?Pľk[JAu%}R iO ,w8XI8Q*]@hd4,Iͦq00l]{ Z1gBH=$yB oWiq Wb"?\3*6Yܾ(Jrơ}u0d3B{[v7O8Ro b+ q|ҁߟ{AZMDկDn*VOG8 `> x~իmu >(ԵV BqoC"Vj5=h#М@:fh`,n`fxμ$0>Or2fo#P=I|B5\r/OSАȤB* JR`fc}?A`}?K䠅oְ˵~(OЦ[=*w8Kt&>y91RsÞ{"Is# 79V/ `YSJpRr h`(Ds`w!9d!uFHL#E(]:4nGWQw>8AN)j,S_w1 230`>BC/Mrσ 6ȭ"P}X{Dx3{D6mՍ|cRZ5Cx dN6(}6 $kB׎Dy_+s`-7ˣIs7|w2# Gpx!E8QF/t iWVR*).a ᅿ`I_[("-UQ !4T!`džK9ȫKS^ eƋKK]cua*(njm+ݡ++ie1PK `TBZHًe,蚤夔XʼnL@8,#rk삋(y|j]C՚\Dys޶wɜ~"dW8bV`S39z'Q!!:1p)I$%M\iE^UA6&BV@ BpD]dp$ZL".2B0F6VߣhAـpZX @*S++l [1%Iqksj>! nR$NHӢ5*I$*1˳dZ B2ƔŠ|)NXR!IBhaV#&6O[#mK2 ;$z E%z0zF%ᚊq;2nʴ[MVRL`Sj+U{e%J'Qփ]LLYlRҖi`Hӎ&\a@\1"/]"K^ZyZBLv#vUN NMG]*:P:vg+IK 7!{O5IdϚbU&kvyif{Ҳ= 8.d|qFq9G}^ee 0:Gn2fa/:i)L-T9VR^V;Qr\&wdt UW )8h5-)MJAQURU_* `m2y# 9ԧvn4%}Of+ a.D"'0y.fuC_L(IUb@38tBWcʒↄ ,hY$);B̸w FN1ë@2m;\2r7݈p+hd X;jv>ۈptnUun"BniqJ:=lXW$[<Uo8>{l,] >7R։ˬ+22h\m(IqjG} PjGx _8K҇!Ov*RUquK}trJuTbsztTv̀i0ƹ4No>Y`IJEw򲇦f&eibE NBNx\Ρ6fDܭ=omEoI o-끀ָH怏&5Mw`޾yՊJh% +9(U~-]cq;^%걣Bq-zٲޕj5Zm2#"Ó-]0]zlEqE`5P""e$YvgW'!.Ϗ6$%OحA)>υY{连0d#N@T4Qd{hB]-(5Ĵ E1Z.4f{ӜJxj+ Ά->U9V} >9Ht QkFtHgZЈrsQRNe`2tysϯ75#r횆[x{NkVPKyVSFq pip/_vendor/urllib3/util/wait.pyX[o~篘R\Vk\ 8T2l~ b{rvg33|g.%E9[Ke/X/LTHSo{7E57F;NZiz2vz" t^ՔIjoUL豠֋w K,݈uZIW+Nog4)vs12veIG?Vy~L&Ήgaf4WQ`4p[R$YF~\~K0x{ӉB6s$ QPfZ-dSU,μTu:h#A: ٔhGrMpVK[',pmJ/-5A|FBϪ0VVx#vDV;Ś eMy] - ʢF pp[tk@l&Hp$i.u$az`Sd;Z4 DG\V uv%LjsE59pqװⰓZҨGrq"#: 0!$NaH.hKrf! X%fho6<VmMWY+j.Hk٢BWx|[0$nNҲ>4=(|H4& $~ Ґc'C+ +ŚdAf0j8J/p+TF})fT.Ŀb=6xBĀz8d}gb*z;f:U3g(xUďRwl[,$4JgSbz5:1@o9Imw090ǧlB8os{#^-ˁ$._!:KxǬWFtnܯ7_.j(\DRƵAJv "3 ?-8x냁r+'/{,(\ވ7'W#1+;2Ƃ0 SJ]0jvCy?\H?g /qaۘpgÅBd5ƻ@ ( ǡ6009sC.*П83]ًK񗛛CqM0H'OB$݇tg7xb$oߴc5֩y(L+&W0V$èEn ;L0t.Bi-a<;wE(37 IghQv<ÚR&"NYh;ڷ -,ܨV/nhjYGޝH'fxJsK5!#t9G\Kr73.-?TSչ4踘+㉞.m<8S|`Sa/= MTex).;pWIVdL2@F5rz>u LN6V 'r_ӯBg>hqV_לpl‚<"18?a=3g:ruݛIC7XT$>بπ@.w(U;!=2M*}A娭ƶu-rGjVz3އ~FPKyVSxq2 S)$pip/_vendor/webencodings/__init__.pyZnF_OJ\{.18A䴸KjE,R.-y^.Or3A.?PHcG#Q'* zlƸ^{I. 8I-T&5`dӳ˟'c#IyLX yrܿo7ŊEzAGSL#OyŖtV%"|`xjђF qPL,,rNxoh~NOQ܋ٜP%Ifkሃ@9ܰ$9I 2#Dx<,b1.?-&V4K7JN^kG [456ALR($q=uQh*%ce #hH*J TdJ!vD ^ dBf(Uٍ~XG{k#NsCu `"_m\5%0@uꬲ^$BHXxN "D& r<;$J]@+#{ha/?< /~ʳ^ f >K@QŒFw7æY[Vr0YB5 Q1cdNT._bT2}>*xylB:ͲbW05l9%=ظ3dD k*"FdzŤ˷cEJ,|cWYRo9)V!MD&`z&QD':GKH4A7S~` !"ljRk5GÝ/ =W0 z~?6YWmALov{o[v6'@0%b28a]a~aƺ>Bŷ *K7K t̖&.$`(ԅU<"ภJ27hC׌?'9N{mQo$U A6lWmc$ʊ]) |::u\EFH(*UYb*fc{'hdVH*~JaH"4alP 4/@N-J,ɬ{v(^dy8gWXtih*ADa_tsO-+#o2Om[rFmzWSW@YO9rٮ1P%vXӧ:[=恵(!,<|X{m`YWgMLÒUxN2 X 52ŏq50iL `]oS-Y&UAwJ)3h a N^CK|Wvݯ_{|fݰU[8 ^wb0$k` 4Yf7J|xB\ dϼOqGŴo{0xw1 *dgAcv le=2,Úߡa8ܣc2_YlQOldn4\T /#^izo4X ˓nePřv9N i͓\]b \7Ƴj5<_­ YIZ3wpu35$Uy#z`W{}̖py%(5Y wv}pW-l *`16kPT6c3vuNș^R*rD۵5{8*3 }fVVKu|.|;m,QWE&aii<{ 1DOpe*P7{ZxX&i((n'p<Ɵ o5a52($)TZ- )LQ|KEA7-rFkӚ=nP;,8I iyA:O8M/™s~[,yl}Mw2H P~AÿJ~S':٬q8hI6=랴~9tQD;ZmYQGg9D/;?{;SC[ |XJ q>4Vgk,UXTau.o'd^2`Q,10g 3kr%Лˉn_vMq]F f @CDR9{SB8Pd(3ɵAuc7fs$h3rbGo?= ̶GS8jOh֪Gxp#H[B-7Z;ДCw>vl _y%yft!H7l1yƇx' %՘JZw UDunVu"nF-W]DnL̦盱Rnm/vS| hLݴ5uOO>cQw[4g4!l/\ PKyVSǭ#"pip/_vendor/webencodings/labels.pyMs6jz%X`>~:b+81H+9$^V%՟YT/x+Ӳ?~~U '|6I~,PmY m?٫:{*ѩJdV 퉕R߾~;{ UҲV3,ܩZAMտKJ%q~dWr,}:ެ~>><+ӚdvMrQ\7.6czA F>FBmO͕J(TiE-a譹k$6ЁR`4܈ut41!C]2. K;㢀AptmwQg؁MCLFLCWdW(+ڙ XNX|8ރj<+#&!IH냾i.S=ڃYYx@Лcc43Ӄqoapt?`l4FKr+֭%@zX+G8 ׼$TL/Ms^w "2/<"0VyiP_pDmj2<J%La`I!Ḭ`y"y@8 ߮!C\-ܮ=_ QfBkkSЈWX3R%NgqH!}م#IgOCiu7٤ҳl *H$ɔzۢI ]f[XZcD [[.+[Y:Oai)l= 5:ׄ7MbpMkrt=Q!Gݡ=w !/P )`(( ]Nq*/zaAC(k/SK-e]q2),$H$Jw3Ev&gxP@RrJ/3@ vz4J]k;:ڏa2Qxh<h(BiJ֟6;F&kh9JGёl!,[;h)4EhGaDŐ!#K ". u \}ڂxDYJk[ *b],j!?ߡ\ʄԖtkH5RIY[p¨_g@\+@fppLjrJ#Ktc1@uzRh-!oTo-' Bo$TFIuƠj}A E=9k=̮` 5k.'~P`o`)mRʂ[4i'CPKyVSP1D$pip/_vendor/webencodings/mklabels.pySM0WLf="qrXPz@bQdul6T}T{#%=`02kڢ);5Rwܢd9KWJ )~| y$̙2PZafo)lzA(7ZV̙8Rlj`okhOi+eB h,qZ4ꀆZg|yH;`2igEclwZF/&1ORif , !O+*|ehq* HbR9w~ǎ[plT^:dOY6r7ͽ,3*-Kà>ET")ٓ|!;$BV+2'2^<0-mI!VӼѳ"%V<vXCO'lر(~F-=/[G}J^C;Z=IC- ]pNJδr&ϊKAo 4eeø,KҶ*G~";c-ޜ)yPKyVSg8p!pip/_vendor/webencodings/tests.pyWKoFW,I\$``@q 9YV64)p|#({sKK:"/aGLr97FA|=nzB'>;q&|)14=%5 CBon}4?8Bӻ8Bsi@"FFh݋A''H/ԉ~Cr(.h7QH_D#"~ Gߊ(vO8lk9s%iwOe|ϺwGVRw `!42;<8@D߿.Ű@t(|@P"F9FCNzKJDjܸtl[xGeZm4E'dM04Q)_)t\bJ f>]I} kLW)[4ՠ`:/ N`;JtL2zvt̶~4Xd40J˛S)Ɠmqn ʽTD=ȓ{v@am#{2n<*%Oz4 */)h6zOoAt ۚt;kxӅK#9`q3c56dc6FSz?42sfq[8;P{N%aWjyGGLPϧ CI<,tG6z`t*:}>Qs5vHٞ*3,˛gTs8j]9J3<*M5niu/rn3Vs7PiDBf} -W[n]$XFAHjܤ5'/dS-w_651o/0Iǰ7%V#dOzZRDv^lP੿+D9>+v0b(* fwC_0l^&TO5 iSklPKyVS֥*pip/_vendor/webencodings/x_user_defined.pym{4W!N6  cu0`/A١-/(Q]?"_s,K:ܫjr$6f!뱢w=Yd'L8E:[d4e:܈rJzg/&.zH76#qC1G܈t4REyA#+ "ʼn0s+42 te|&&,&U}s[eh%Y*6\1.gdQWM*j V{QtE/eWs[[EiSU\M9SSigr156fw2ɨtsƭ-%,W~IU;[wd6>uj=ybby}WWߧ͎n{XEiI^ՙlW!}ܚgj=9uu/2iwNXi?sѸbPi1TPH=Î;+lʏme?VU16p殉r$\\AڃH,ljmBMݹq>`(ȝ"<Ӄ~il ͭW~kF!p7CpyLB|h5 YQ4 F(TJPi*B0qeC a7 &s!sQfYma~|2w='L,)&# ߑS#sCcysɜ2Soߙsoל`W'yͼa_;p~g`$d.&1dLĘHLL&}b'F}b'F}b'F}b'F}$H#GD>}$H#GD>}I'A}I'A}I'A}I'A}(QGB> }(QGB> }4hGF>}4hGF>}}}}}}}}}}}}}}}}} 1cǠA>} 1cǠA>tODT66ׇJi۷MPKFtV"ÁB pip-21.3.1.dist-info/LICENSE.txt]SOo0S1{o36KsKO 9:b{nAhBíIҺ$X<(i)&ߦBuPo,%?[݋\߻VhVoODP>I\7NzǜC)zQBHy 3VyVl'X"O*/K ĺOcymR?qԐH%Jp-5W l!2w1,Ήs H6SPlT!Ki\K*|s=GU(W,&۟%)ҰYrLEƯR*ɘXǐ5{J"ۮtz ?2DZ1ƔJnEc`JTRI:!'BUç7%r!WI/PKFtVdpip-21.3.1.dist-info/METADATAW]o6}ׯ Ї%KW ݖ9!M8ݰ'hDj$~Rr,ISo &ǹҟw3˒Un]O.K21u0E!U#ЊU<㹰QQ"9a *12EժqpR42 EeLZW;Y牴Ƀ e66H]"a:qۏumR1̥[ԳdO}ႫpgB fVX{Â[+RӦN@X[${2d c)װRgRT醴m Y\G줪^5\{.)s#z@k- f&+5웆>jKTjWxgw;{=wtU!Vꨶ@'VFذLOsY``/FóY\׍㽺|x/ɒf7#,M%SK?vq Q(U3d?tj{xe u'Fk9![a/ [K,նRצjFM-3\eLÀ4+$u!8b:~'gw;dYW_=&p-$ش=K0g)p4*#= nՀC:Z:tɬ0Vna#vpm 6Y/Sai1)~tĕ<#f2G/x=ʈׅo:M(j:j^, l Wy i & c']0\P} QT0E}L:fdp8K4;W5rsx0@m2[W6>&t!D)NFs*ֳ RЛeٟx0 RF'Œj[4U36TK9ѷw[^ZxCg4xZ9M%s*7 |Y_fp3sQ,^ DtɖFvo!Cy:y-3aczȆ/[^k9ܾ&iW^9(;M"/y˞tAhʢJ+Oi渕umtZ1꣙_l`@߮ݓZe ou[iA JG!BXam#!%ٞߤ +cP&5av #j11pLUk҆+y ^x1W(bnzOPLkksn³ B(~vannM;y/I}9JVI`W:M5wmOS8E\%'oDًȒP⌫iv,?D.qN]`]] I s8x>8-RhqL.Cn7$ o.'/i6'_3 naT70SAW\m@Dn0yA`OPcUBq;c9u⚕8^Kt6 c 3Ofz@ 58q!RTUKzqwzF^YuS=ț@>#'S†}")9zHM68/6u;^˞Ux@#E^oh[ B |  / 'd,3880+8,QGcғs B^u˓騞&wkG>ր\ZPQJ7g2$s$ =N_uP**7&3k }GT[fJ%9Rj{xj8A|6Umk+;X^nѩ[K0O2!"l$&P4D¬JmH9kҼDba /8k܀8@~tmemSrpQ8 cttCf%wHUΎoP__v<[.HFT-*x30ւ2w;6V%z>meGL(϶ qqGch=E -*P㮂/IJtg3ebzӵ3 DqSD Z(LC5K.\ BC%w_ـj~2mx=4Dq]*i2'zT !wyDk7_sq(GQ;6zq٧ %z7N,G[v۱TG.ٞ(m̽puɚOVDz :JIƚv%`TPM}?'4]/=\Gcsg[%dwZAO+v)owo餺!&Q2ߍjXwP.B75ULAXW-o -1uW[Md(ͺ W- V+j(J`^u{_h{AcA9B >dғ/i1ۊf1l9iϸ 'KtiI^ ob=^u%mNS[ν.czO Ƙl(q~k˜(_o `ZIvI&+C&"S ۪oo:ncȦ3O(nΪ?U> E_#αrzƃhe$ŝ*_uˆ8>QoYDs֌h>[ʭ":駘ϧ塢}5rGuI7hES5[DQkQ{TRͅYd˛n夯Ҏ sxєd`څ뤋 j\M$kT@x}>j ] 2Kޝ}Mn95IP+k'y8X=T3]m ;Sܘ6aM/iC%޿~Ҭ6g< ~(O+?Nܸ^VMt_wS}sbtǃȐCG{)ifejm:[$@(5*R&Or PĎ: }(4^#IMM͓9s+;*ME2]tEoحQ! 0kBT|,wm6ރ[9pVb;Nͣ=o_wW,fF~fc^QNcݑhvc)"\qgUGҶZ;tΥ)ȤNC׏kv( H[I>~ ;ʹBi#au }Zr,vKX"8"^j~Gl3240)ú#dkv& Q.QC=q*0y9 燣2?׈ \ |Qo4V#pv,!|MLrx. ޽[] E0ZɷS&-RxYk DW+%!=)/քEE&O9+ЃR7KWx"V~<QI&;v$[̄H WQא x""Q>k:!Wq :B"wu;՞B0eҫ晽9g\lKWhw# l]Lj @0f_6ҫT(5>֢}g:ܰUA/T(Yt)Z3a-\$2jVۿkwR8HWc$0( PkZg]^\a6C ^(f L]_RBmhDyyxg%]{8xWPjžv>kvM(*<{4zn4Ƈ(m i>6+~]u P'Lm@Z*zȔ==q;f>U ^5GǣR+k(GtnX HEiMkk@υlo&I oEC\.ev (ZH˾UR wᰱp]$^s=/OpD2!vtCeU,SS i$>w'M~l#6<֢8O#Ȕ{"ap1% #=1=}V6(ے{aΒm{b:K8g42= yuOIyYT0)G@Rzt/:ӻ 哟[x63D*Eܷ1ktMρB"_GOȷ:em/hRþc a|@ X 8X,n|ҿI7]yo5.ۇ7{3ܞWOCTLT?b j.sElV}׹5o4(]+OV=*\oDqZI,f9ҡ8\VX \AF!~'52d_ k,U"w|e(]I?+îSZՑ !*MT} |tV_G߅0Ry>l): aVލڻvQg S+ӣ[ jy4<4v8Df;'De;̺,Yv '6kfTF~៎)|h~\j| 1"M].hlpmȷ_5m뮪VUyvW&,&=F\\ 'Bܪ2F뼟'͐ 9;R-O2d֦aqV^Pm T.|4k0,TEm$UaÚ^^ ֏ލv_ܾ|hw\3qkoAL@+Ѵ,]bv5QX!-_[2~M5Ur.w4%C: ܲdo_!iɿ_"@l+ 36Ҍ;xP5 M'ՕR9rsrW N╿ {L`uʣgu ǘSR pu ]mU yfif*}uyVF".=؁q`c {C0:[s&Cg{XPk @!K`qv3{(qA5)>EЬf=q%tٖe pPb[?{$>z 캽(F,yakrL},ZYcԠ~-إo \ L+(~|39-=0Odׄ BtyAW' R6u(>ӶMKUPu{{f ^c)odr8, &9於lN ߷_5ox,euƍ#EX#vDg+~ԧ"[we?OwNzg^vɛI|RXD> Ŀ{nA2' j^"A;K0>t>ǀ63_4R+vFE1%&lO J9/}2fR8d=JVmJ}`o6PQ8>!hg _S},A%Njj[':#qw dV-w0H5R vA?Pn4$:0ğ]67xTe@ -c:~zշs3d^i{cE!"4zB+@^;tfeh$=YM{l_#ᝠR~>";ηVnonpȣà ;)!L,Z}f:,OzL kNAѯrN4N"+PXO{ q!?y1"[! TwtKlsru.9"MZsv2w\i?#2'Ԋd>쉟2CfԈFSQχ<+.=9 ĠA~4NL$nګ"vi킈s\-l"Z-ˬE1bS쩕 r?M=*r= 6[B]nܔ eF~0ts/ Ek+0/gʚhVp3A1M#^iҾd qT^P;;`r =Fꣵ?:{f~F i(W#$ڍݑBݗ:]C)##g Y˯2s% ΣXē*%y蚷pǏ1Z>ƈ ^AjJ:TV+ .^{Ȗ*v@xZWau"]Df4*75x^2뺫u勿ʆcNs?B@7".}`,1m?pZ<2>LZl8\9+od~jF>N{(i31~bYRT%h%<> }%m:V+\"[.,9ы[p!8>7?:/TUFIU|۴Hse0ɱ_[]gs9W/?d:8<)$p{rpOa+ILeGEUrO?~bL;yԉoyk]>ܷM-qzB?y CU&L;l[dt"M@gX]Z A~Q(e1+s?-<3HQV3F*ͭW}&/> K! sO(++X#h"g9Wv@YBJ#P-[y/mvnE962_jKe<đItWaSd;c-6 Vsy[G {?:T~?,cD蒔l^ǝ28|"w= q*[P_/rqmy/HGҝp>4)Qef5u6yNzawN>}@RXW@ 8-VnH'])zVuG[PFz%$ÊUnytzrB@r3.pJiP".եO9$ :{_R{1],)0v/a}L+l>Zx6  /?-ruG09t삄9b;F 6$yo xbf. ĽTrx]Aw>f?=4ɳd ɇQiOnev!!x?)8er4vs^մFm߭B4/V쥮 x W:{Ȉ7(N[iM m.6IPh5wk,"K\D[t8.MBuڸ #Qvr?7ksԹOsNʺn5ff$e:GeqTXA.ШocsRFKbNZ)% ]FuG =RÌ=~"1L2FQdp1vI;' Tcq  lv<*$JbͰy[|5F5UC![ic`pp1I)"kPy08v:`C2@۳MG-2̥wM7^|X@e%!/Sz6DNj|a.2v+L1Ma8 '`>ʹbGb!}htUWc灚Q  E;|ˡ%C'$))ֳuA47/93#rP;'[* 5tynL3uŧ тvYXs\W'yMګU16޶ &{מ9R3nb~‡k_f`ryT:kZpl4剪嚇Л.&/AT x:nDЋԈLܐEȳ |`>>~NVOb@DoD|-/e=ujGG.RC)r:Hݟk҇`%q.%е3  |"~k K;t &*ieDRXaPCL{'>Rrd"ϲ?ұ;H#[)_HUTN%)`s+N>wb[飒\SWePSivKm/v ¯FiO;/=e8Bi9oznG,pWǕz?o^9b&^Yo`rcۓQ|^Ёר{6@|jGө@f"$\rPg.IR<^!^_9nտ){usSqcۛD/`v,\s_Z7RPå)~Vv7CiulMSk?l&{^؁$4V6Q)$޷+|AH=NHϲO5PvEBz#犨 ?7l$TĹ ! a`/".NTBDlHIw 3=?jS*a!l-0dOGҺ!DIV/0/X"kJҿ$L\dCmbO*Ng#KȚ\6|)Ls\3vAWK䌷 _Cr[5p4k1)_LV3KuC]qZ[5?Ƞe$՛j94hbGf.Rᤎnxյi].$>c_C! VN#EdħR8 ۇ"棻X((΋&>4{pJsCã|5A9vKj(xEz]"N . |_G繶P J YSMky"g> =$~w7QB>P(G]/;,,ɎD](c*-c0YiunP[E%=zgJ /Y1iBSĚ}9ƂG'ÅƪdЛ=¤f7?7 ((w?}CYR+zU.̮LLR_{w룯sWc_09#,9wr&hmyRlM"2Q4i&)W!}TԹ=~`d!ao-&#"$vQB{`Tv8W~R~]S,p[_hYw`m ;E-]k^T~S%w|3#`MLQJ=n#tclQfh.-Y0Rwv53Zt(KIܳr"˱+ώC85PE܅Uϊ8x4lwr&Q{gnv"K=E}R'.5=C=ne'"Yc^ ~޶'8@/5;pA~P3<⷇8&g^$&<. #­4MPv>`_Z|.N%(8^z$S# 36WN;&XaKoKJ݈;3Mxi>ßnn!T}9Χ^0q~^\%ł.~{o ['- l7Ht yr8QA"Xw˭_VJ|gR7$84VJ ]z|zttD>n&> 4;bAtTI(8Z9Ww*n=sFF~jc^a2JbCpm [eɷdܺi J3g1.Fwy;i;l΍ca++AGӲ:4 _7%K⚄&\#rgfJQ9TLÀ| or \Ez\f;ν3`"͎Df}H7/̋4&|Q nΩ#A=#ڲu|S|:&iɍ-YWEMNAݓfjGVh|23Y6}(aeեdRD)F,sbς oy,"<9) 1G!Ѡf01^2{cw<.NRb&b4Oe.6٥Fq,r"m|la^N#E jm㣁Ǎ!ZՖoAEVi֗-ds-óH[""Kt1ϑ=T=ml6/wh.M`ˆ=Oս?(ctʺy}᥂kɯ~LA2#7&;ۚ۔׸]46s;oa;mox1lFF^Hwܕmnk GXD<&ӶK&:'>{va<ĺas|ws>K32쾜!_NRMLe)XU&v,zg{U1 Q2Я:qRU}6ce!ڴT/x:6HrMK!iqz27ZЁT}BX/ _r-`>o+oe3/+{UAb.|I+ pۨ򍑡$H|Ph9&ܒD؋xJ[f[V+ɛz)~͘4TW;o(Hotv;!8xjۓXּ:ВQU;ԧuQHm1 pAH0 29KEn6L3/3@t{=\I#X 'Vl4n|~}uК9uao&i@Edg8I`($IL:6 A$D7= `V˔͏|P"A%"E%w-%5tV2$^+L%T_?Cߪ')3NKR{H I%G"K2*ih 0JYV?cD}fXh ߯2A}09J=\&z| ;=.Q\j@_DA2TՂx'ʥg:v|aT$Zsyoո_.q ɜ;ydcmh0 q"ry-ɡǨ;wd0;1<3iU4ۃ#` rP>P@mn{\. ~,=Fnֱ :~b~IݲQ079.qZ^Xitm0ilbÌ$gNIT Y,Sc=G>Hkh\!7 li >1oI=XLL7#m^,Jƻ>xׂ`G ABè>:b3IzAݽߦjyjEnB{ewQrSIWi}&d#e3.#?U{)U޴Z\az 4<~4 tjcdo.3__Tp7R2<:F ./kTEZVJ|i'z(ѣ\㍁ڡ/r5rD0"c`Xۈy5xy?U_ѰHӰ{%l'n8oSz& n ->=B߶uf`NE7Yŵ= Y_-ϯw#U8"f^8F dws wy^/oDe' ctE /QZ2j֯m&7 -2dEF^[㾄6І|&|B&!' °RpGZdWu+~#;l9,Ϛ;%mHA<3gYUy 7w?ZUT5*%jYfP aw$Df?POƺOo31c"!GP#"+(jʠG D`}l▆\wl2eayؘwQ! ~x5Z!j,rmevd،5yB4O}2Y6\_90Hwz~䟠i(o\!=2gдz!}怡?]C\kPg6e,T*k7x f^jGkڻ,a>kwp)nS8N[5MGjnUWkZ3 eT,0z` Lcź$i8zȮA{ۧ {ԑz+#YR*jp t.̣9bzאQme/N8 lN#d/iARe&_ ~H~5Iy0oX;M`7l$F2GTve{V_y.DD{'&p>S;^S{|n9=@,ۏ?PKyVS:4@epip/__init__.pyPKyVS#m%pip/__main__.pyPKyVS5 pip/py.typedPKEtVb=_Kpip/_internal/__init__.pyPKyVS_4  &?pip/_internal/build_env.pyPKyVSՕ $pip/_internal/cache.pyPKyVSZa3pip/_internal/configuration.pyPKyVSyfl 1-pip/_internal/exceptions.pyPKyVS'T'>pip/_internal/main.pyPKyVS;Ւ@ /B?pip/_internal/pyproject.pyPKyVSg$Hpip/_internal/self_outdated_check.pyPKyVS_ /Qpip/_internal/wheel_builder.pyPKyVSw4h^pip/_internal/cli/__init__.pyPKyVSp#S_pip/_internal/cli/autocompletion.pyPKyVSv n!ngpip/_internal/cli/base_command.pyPKyVSIDineqpip/_internal/cli/cmdoptions.pyPKyVS f)$ pip/_internal/cli/command_context.pyPKyVSjYd| vpip/_internal/cli/main.pyPKyVSo6 =pip/_internal/cli/main_parser.pyPKyVSñ $*pip/_internal/cli/parser.pyPKyVSsO l "pip/_internal/cli/progress_bars.pyPKyVS%߇B \pip/_internal/cli/req_command.pyPKyVS$%!pip/_internal/cli/spinners.pyPKyVSzKXt!pip/_internal/cli/status_codes.pyPKyVSACv"~pip/_internal/commands/__init__.pyPKyVS8~d4pip/_internal/commands/cache.pyPKyVS6l1pip/_internal/commands/check.pyPKyVSqC $]pip/_internal/commands/completion.pyPKyVSҗ "'tpip/_internal/commands/configuration.pyPKyVSZ]pip/_internal/commands/debug.pyPKyVSm("fpip/_internal/commands/download.pyPKyVS 7pip/_internal/commands/freeze.pyPKyVS6pip/_internal/commands/hash.pyPKyVSÔ<lUpip/_internal/commands/help.pyPKyVS- g{pip/_internal/commands/index.pyPKyVS`+`l!pip/_internal/commands/install.pyPKyVSX> /)$pip/_internal/commands/list.pyPKyVSW/bA 0pip/_internal/commands/search.pyPKyVSBI C8pip/_internal/commands/show.pyPKyVS/# #rBpip/_internal/commands/uninstall.pyPKyVS wy LGpip/_internal/commands/wheel.pyPKyVSu .;Z'Npip/_internal/distributions/__init__.pyPKyVSh #Ppip/_internal/distributions/base.pyPKyVSr](RRpip/_internal/distributions/installed.pyPKyVSJw$Spip/_internal/distributions/sdist.pyPKyVSPh[$Zpip/_internal/distributions/wheel.pyPKyVSC?K \pip/_internal/index/__init__.pyPKyVSI~D ']pip/_internal/index/collector.pyPKyVSW_"%-spip/_internal/index/package_finder.pyPKyVS+ϕpip/_internal/index/sources.pyPKyVSőkl8#pip/_internal/locations/__init__.pyPKyVSڝ{%6pip/_internal/locations/_distutils.pyPKyVS8 z %pip/_internal/locations/_sysconfig.pyPKyVSY+Rpip/_internal/locations/base.pyPKyVS sB|"pip/_internal/metadata/__init__.pyPKyVS. _+(pip/_internal/metadata/base.pyPKyVS /3:<'kpip/_internal/metadata/pkg_resources.pyPKyVS{(;? pip/_internal/models/__init__.pyPKyVS0Oy!epip/_internal/models/candidate.pyPKyVSU=*"pip/_internal/models/direct_url.pyPKyVSbch &pip/_internal/models/format_control.pyPKyVSߗpip/_internal/models/index.pyPKyVSZ0tT Y&pip/_internal/models/link.pyPKyVSlpip/_internal/models/scheme.pyPKyVS W7L$pip/_internal/models/search_scope.pyPKyVSs'!pip/_internal/models/selection_prefs.pyPKyVSQGe%5pip/_internal/models/target_python.pyPKyVS'TA 7pip/_internal/models/wheel.pyPKyVSJ22!A pip/_internal/network/__init__.pyPKyVS=.RJ / pip/_internal/network/auth.pyPKyVSG\A847pip/_internal/network/cache.pyPKyVS/P!pip/_internal/network/download.pyPKyVSw #"pip/_internal/network/lazy_wheel.pyPKyVSaݞYA ,pip/_internal/network/session.pyPKyVSӏV1Bpip/_internal/network/utils.pyPKyVS-{_Hpip/_internal/network/xmlrpc.pyPKyVS$Kpip/_internal/operations/__init__.pyPKyVSͯ"!Kpip/_internal/operations/check.pyPKyVSľ ' *&"ORpip/_internal/operations/freeze.pyPKyVS:e]#\pip/_internal/operations/prepare.pyPKyVS*vpip/_internal/operations/build/__init__.pyPKyVSY0_*vpip/_internal/operations/build/metadata.pyPKyVSj3>ypip/_internal/operations/build/metadata_editable.pyPKyVS 1{pip/_internal/operations/build/metadata_legacy.pyPKyVS<F''~pip/_internal/operations/build/wheel.pyPKyVSA}0pip/_internal/operations/build/wheel_editable.pyPKyVSȇe  .pip/_internal/operations/build/wheel_legacy.pyPKyVS{53,pip/_internal/operations/install/__init__.pyPKyVSZ3ypip/_internal/operations/install/editable_legacy.pyPKyVS s>*ϊpip/_internal/operations/install/legacy.pyPKyVSX* k)pip/_internal/operations/install/wheel.pyPKyVSt pip/_internal/req/__init__.pyPKyVSecu˔;!]pip/_internal/req/constructors.pyPKyVS' D0pip/_internal/req/req_file.pyPKEtVS"J Vpip/_internal/req/req_install.pyPKyVS#spip/_internal/req/req_set.pyPKyVS2 5pip/_internal/req/req_tracker.pyPKyVS\" pip/_internal/req/req_uninstall.pyPKyVS$&pip/_internal/resolution/__init__.pyPKyVS˩G 'pip/_internal/resolution/base.pyPKyVS+H(pip/_internal/resolution/legacy/__init__.pyPKEtV!4_H+(pip/_internal/resolution/legacy/resolver.pyPKyVS/=pip/_internal/resolution/resolvelib/__init__.pyPKyVS"Hd+>pip/_internal/resolution/resolvelib/base.pyPKyVSjk"G1Cpip/_internal/resolution/resolvelib/candidates.pyPKEtVj.Tpip/_internal/resolution/resolvelib/factory.pyPKyVS)ZI7ppip/_internal/resolution/resolvelib/found_candidates.pyPKyVSU- #/wpip/_internal/resolution/resolvelib/provider.pyPKyVS~p_ /'pip/_internal/resolution/resolvelib/reporter.pyPKyVS\O3pip/_internal/resolution/resolvelib/requirements.pyPKyVSWc l%/ƌpip/_internal/resolution/resolvelib/resolver.pyPKyVSpip/_internal/utils/__init__.pyPKyVSJx9?<pip/_internal/utils/_log.pyPKyVSrB—Kpip/_internal/utils/appdirs.pyPKyVSck\pip/_internal/utils/compat.pyPKyVSs)>pip/_internal/utils/compatibility_tags.pyPKyVSl=pip/_internal/utils/datetime.pyPKyVSжŎL+"ͩpip/_internal/utils/deprecation.pyPKyVS&] )Ypip/_internal/utils/direct_url_helpers.pyPKyVS#/ %pip/_internal/utils/distutils_args.pyPKyVS[w]>۵pip/_internal/utils/egg_link.pyPKyVSaVpip/_internal/utils/encoding.pyPKEtV_^j"pip/_internal/utils/entrypoints.pyPKyVS;[!?pip/_internal/utils/filesystem.pyPKyVSO-X epip/_internal/utils/filetypes.pyPKyVSR=U/& pip/_internal/utils/glibc.pyPKyVSPgdpip/_internal/utils/hashes.pyPKyVS-xpip/_internal/utils/inject_securetransport.pyPKyVSɓ] -epip/_internal/utils/logging.pyPKEtV7#݆*Qpip/_internal/utils/misc.pyPKyVS; wpip/_internal/utils/models.pyPKyVSf=O~ opip/_internal/utils/packaging.pyPKyVS>S| +pip/_internal/utils/parallel.pyPKyVS>9$ pip/_internal/utils/pkg_resources.pyPKyVS90Y' pip/_internal/utils/setuptools_build.pyPKyVSɘ J'!@pip/_internal/utils/subprocess.pyPKyVSE H pip/_internal/utils/temp_dir.pyPKyVS pip/_vendor/certifi/__init__.pyPKyVS/ pip/_vendor/certifi/__main__.pyPKEtVL epip/_vendor/certifi/core.pyPKyVSfw Spip/_vendor/chardet/__init__.pyPKyVS99z#pip/_vendor/chardet/big5freq.pyPKyVSgf<!3]pip/_vendor/chardet/big5prober.pyPKyVSY(== $'`pip/_vendor/chardet/chardistribution.pyPKyVSw)0jpip/_vendor/chardet/charsetgroupprober.pyPKyVSaZ$npip/_vendor/chardet/charsetprober.pyPKyVS\7H)vpip/_vendor/chardet/codingstatemachine.pyPKyVS  Z|pip/_vendor/chardet/compat.pyPKyVSB1h?"opip/_vendor/chardet/cp949prober.pyPKyVSD}pip/_vendor/chardet/enums.pyPKyVS=5n pip/_vendor/chardet/escprober.pyPKyVS `)pip/_vendor/chardet/escsm.pyPKyVS 7*"ґpip/_vendor/chardet/eucjpprober.pyPKyVSAya4 <pip/_vendor/chardet/euckrfreq.pyPKyVS&8"ۯpip/_vendor/chardet/euckrprober.pyPKyVSI3:{ Spip/_vendor/chardet/euctwfreq.pyPKyVSA8"pip/_vendor/chardet/euctwprober.pyPKyVSEc'P!<pip/_vendor/chardet/gb2312freq.pyPKyVS~6#pip/_vendor/chardet/gb2312prober.pyPKyVS )Ӳ6#Upip/_vendor/chardet/hebrewprober.pyPKyVS9a0d//pip/_vendor/chardet/jisfreq.pyPKyVSy%jNL_pip/_vendor/chardet/jpcntx.pyPKFtVt˜) spip/_vendor/chardet/langbulgarianmodel.pyPKFtV?]݄%pip/_vendor/chardet/langgreekmodel.pyPKEtVc&pip/_vendor/chardet/langhebrewmodel.pyPKFtV&QaL)Xpip/_vendor/chardet/langhungarianmodel.pyPKEtV`̖ (V'pip/_vendor/chardet/langrussianmodel.pyPKEtVe,(z$Qpip/_vendor/chardet/langthaimodel.pyPKFtV1v'pip/_vendor/chardet/langturkishmodel.pyPKyVS#<pip/_vendor/chardet/latin1prober.pyPKyVS6uMU &Bpip/_vendor/chardet/mbcharsetprober.pyPKyVSr&Gpip/_vendor/chardet/mbcsgroupprober.pyPKyVSi cKpip/_vendor/chardet/mbcssm.pyPKyVSrs&Xpip/_vendor/chardet/sbcharsetprober.pyPKyVSUZ&apip/_vendor/chardet/sbcsgroupprober.pyPKyVS֑g2 !fpip/_vendor/chardet/sjisprober.pyPKyVSNjƃw 0()lpip/_vendor/chardet/universaldetector.pyPKyVS=E !ypip/_vendor/chardet/utf8prober.pyPKyVSlj~pip/_vendor/chardet/version.pyPKyVS2#_pip/_vendor/chardet/cli/__init__.pyPKyVSpW %pip/_vendor/chardet/cli/chardetect.pyPKyVS(qpip/_vendor/chardet/metadata/__init__.pyPKEtVzgCK)pip/_vendor/chardet/metadata/languages.pyPKyVSkճ pip/_vendor/colorama/__init__.pyPKyVSR٭ pip/_vendor/colorama/ansi.pyPKyVSq )#gpip/_vendor/colorama/ansitowin32.pyPKyVSoEr{"pip/_vendor/colorama/initialise.pyPKyVS Pܪpip/_vendor/colorama/win32.pyPKyVS(&gpip/_vendor/colorama/winterm.pyPKyVS]o]Epip/_vendor/distlib/__init__.pyPKyVS,ӽ`+,pip/_vendor/distlib/compat.pyPKyVS@c/spip/_vendor/distlib/database.pyPKyVS2HQ pip/_vendor/distlib/index.pyPKyVSIo "3 ) pip/_vendor/distlib/locators.pyPKyVSx5gl9k\ pip/_vendor/distlib/manifest.pyPKyVSy}m pip/_vendor/distlib/markers.pyPKyVS=X$Ř't pip/_vendor/distlib/metadata.pyPKyVSՀ D* G pip/_vendor/distlib/resources.pyPKyVSE8E5 pip/_vendor/distlib/scripts.pyPKyVSmH pip/_vendor/distlib/util.pyPKyVS2~[ pip/_vendor/distlib/version.pyPKyVS )9 pip/_vendor/distlib/wheel.pyPKyVS#g)~F pip/_vendor/distlib/_backport/__init__.pyPKyVS/e%G pip/_vendor/distlib/_backport/misc.pyPKyVS9Qkd'I pip/_vendor/distlib/_backport/shutil.pyPKyVSo9 +/f pip/_vendor/distlib/_backport/sysconfig.cfgPKyVSy룩h*nh pip/_vendor/distlib/_backport/sysconfig.pyPKyVSDBCbYi(_ pip/_vendor/distlib/_backport/tarfile.pyPKyVSoD  pip/_vendor/html5lib/__init__.pyPKyVSʧ--XA! pip/_vendor/html5lib/_ihatexml.pyPKyVS6=a~$ pip/_vendor/html5lib/_inputstream.pyPKyVS5mO,"E pip/_vendor/html5lib/_tokenizer.pyPKyVSӉIC4 pip/_vendor/html5lib/_utils.pyPKyVS[2&@HF!; pip/_vendor/html5lib/constants.pyPKyVSD#l pip/_vendor/html5lib/html5parser.pyPKyVSs=" pip/_vendor/html5lib/serializer.pyPKyVS]TZm& pip/_vendor/html5lib/_trie/__init__.pyPKyVS灦#$ pip/_vendor/html5lib/_trie/_base.pyPKyVSKOn pip/_vendor/html5lib/_trie/py.pyPKyVS(f pip/_vendor/html5lib/filters/__init__.pyPKyVS6 pip/_vendor/html5lib/filters/alphabeticalattributes.pyPKyVS"ĕ$ pip/_vendor/html5lib/filters/base.pyPKyVS? 3 pip/_vendor/html5lib/filters/inject_meta_charset.pyPKyVSdl;$ pip/_vendor/html5lib/filters/lint.pyPKyVSu*\), pip/_vendor/html5lib/filters/optionaltags.pyPKyVSQaM i) pip/_vendor/html5lib/filters/sanitizer.pyPKyVS`r*/ pip/_vendor/html5lib/filters/whitespace.pyPKyVS9qZg-l pip/_vendor/html5lib/treeadapters/__init__.pyPKyVSR)T+ pip/_vendor/html5lib/treeadapters/genshi.pyPKyVSiD( pip/_vendor/html5lib/treeadapters/sax.pyPKyVSekLS-E pip/_vendor/html5lib/treebuilders/__init__.pyPKyVS>>h8) pip/_vendor/html5lib/treebuilders/base.pyPKyVSP"(' pip/_vendor/html5lib/treebuilders/dom.pyPKyVSMЇR $2*a/ pip/_vendor/html5lib/treebuilders/etree.pyPKyVS9/9 pip/_vendor/html5lib/treebuilders/etree_lxml.pyPKyVSvjW,aH pip/_vendor/html5lib/treewalkers/__init__.pyPKyVS!-B4(IO pip/_vendor/html5lib/treewalkers/base.pyPKyVSeo'V pip/_vendor/html5lib/treewalkers/dom.pyPKyVSC)X pip/_vendor/html5lib/treewalkers/etree.pyPKyVS0Dty.^ pip/_vendor/html5lib/treewalkers/etree_lxml.pyPKyVS_m *d pip/_vendor/html5lib/treewalkers/genshi.pyPKyVSxQg pip/_vendor/idna/__init__.pyPKyVSecq+} ?i pip/_vendor/idna/codec.pyPKyVS-hl pip/_vendor/idna/compat.pyPKyVSTA: 2m pip/_vendor/idna/core.pyPKyVSv2o#nwy pip/_vendor/idna/idnadata.pyPKyVS,][' pip/_vendor/idna/intranges.pyPKyVSC pip/_vendor/idna/package_data.pyPKyVSyנ pip/_vendor/idna/uts46data.pyPKyVS'WX^/9 pip/_vendor/msgpack/__init__.pyPKyVS1L; pip/_vendor/msgpack/_version.pyPKyVS;;ӭ9!; pip/_vendor/msgpack/exceptions.pyPKyVS<= pip/_vendor/msgpack/ext.pyPKyVSM$D pip/_vendor/msgpack/fallback.pyPKyVS& e"$b pip/_vendor/packaging/__about__.pyPKyVS!c pip/_vendor/packaging/__init__.pyPKyVS*:c,#d pip/_vendor/packaging/_manylinux.pyPKyVSo0#s pip/_vendor/packaging/_musllinux.pyPKyVSqt i]$6{ pip/_vendor/packaging/_structures.pyPKyVSm?{ '! | pip/_vendor/packaging/markers.pyPKyVSnAZD% pip/_vendor/packaging/requirements.pyPKyVS4dx#ώ pip/_vendor/packaging/specifiers.pyPKyVSm:b=" pip/_vendor/packaging/tags.pyPKyVSA3h pip/_vendor/packaging/utils.pyPKEtVdY8 pip/_vendor/packaging/version.pyPKyVSBw pip/_vendor/pep517/__init__.pyPKyVS>R(  pip/_vendor/pep517/build.pyPKyVSs  pip/_vendor/pep517/check.pyPKyVS VЊ pip/_vendor/pep517/colorlog.pyPKyVS 5 pip/_vendor/pep517/compat.pyPKyVS;ϸFi pip/_vendor/pep517/dirtools.pyPKyVS" pip/_vendor/pep517/envbuild.pyPKyVS " pip/_vendor/pep517/meta.pyPKyVSUx u4+ pip/_vendor/pep517/wrappers.pyPKyVS$3)Dpip/_vendor/pep517/in_process/__init__.pyPKyVS"H +,pip/_vendor/pep517/in_process/_in_process.pyPKyVS)s%Apip/_vendor/pkg_resources/__init__.pyPKyVSa$K2'Hpip/_vendor/pkg_resources/py31compat.pyPKyVST;2$؄pip/_vendor/platformdirs/__init__.pyPKyVSV0\t$pip/_vendor/platformdirs/__main__.pyPKyVS ->#pip/_vendor/platformdirs/android.pyPKyVSj?}:pip/_vendor/platformdirs/api.pyPKyVSŪ.k; !Qpip/_vendor/platformdirs/macos.pyPKyVS9 pip/_vendor/platformdirs/unix.pyPKyVSj=P#pip/_vendor/platformdirs/version.pyPKyVS80N#rpip/_vendor/platformdirs/windows.pyPKyVS/ pip/_vendor/progress/__init__.pyPKyVSC_~ Apip/_vendor/progress/bar.pyPKyVSd_ @pip/_vendor/progress/colors.pyPKyVSN|Mpip/_vendor/progress/counter.pyPKyVS.:pip/_vendor/progress/spinner.pyPKyVS0l pip/_vendor/requests/__init__.pyPKyVS&e#Opip/_vendor/requests/__version__.pyPKyVS9H'pip/_vendor/requests/_internal_utils.pyPKyVS9.D,T pip/_vendor/requests/adapters.pyPKyVSipip/_vendor/requests/api.pyPKyVS+s '[pip/_vendor/requests/auth.pyPKyVS.pip/_vendor/requests/certs.pyPKyVSftqpip/_vendor/requests/compat.pyPKyVS':Gpip/_vendor/requests/cookies.pyPKyVSg8G " pip/_vendor/requests/exceptions.pyPKyVSa'pip/_vendor/requests/help.pyPKyVSJsypip/_vendor/requests/hooks.pyPKyVSX,n&lpip/_vendor/requests/models.pyPKyVS<ށk ;pip/_vendor/requests/packages.pyPKyVS48!u T=pip/_vendor/requests/sessions.pyPKyVS=z\$^pip/_vendor/requests/status_codes.pyPKyVS| "epip/_vendor/requests/structures.pyPKyVSVm\'zUjpip/_vendor/requests/utils.pyPKyVS5"pip/_vendor/resolvelib/__init__.pyPKyVSe4#pip/_vendor/resolvelib/providers.pyPKyVS_T#3pip/_vendor/resolvelib/reporters.pyPKyVSc2D#^pip/_vendor/resolvelib/resolvers.pyPKyVSTpS!Ѯpip/_vendor/resolvelib/structs.pyPKyVS)cpip/_vendor/resolvelib/compat/__init__.pyPKyVS.gc0pip/_vendor/resolvelib/compat/collections_abc.pyPKyVSQG ]pip/_vendor/tenacity/__init__.pyPKyVSml pip/_vendor/tenacity/_asyncio.pyPKyVSUKֱ/BYpip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.pyPKyVS抙$bpip/_vendor/urllib3/util/__init__.pyPKyVSs8&dpip/_vendor/urllib3/util/connection.pyPKyVS \^E!lpip/_vendor/urllib3/util/proxy.pyPKyVSN!iopip/_vendor/urllib3/util/queue.pyPKyVSdT#ppip/_vendor/urllib3/util/request.pyPKyVS6(մ` $vpip/_vendor/urllib3/util/response.pyPKyVS S!q|pip/_vendor/urllib3/util/retry.pyPKyVSǧC pip/_vendor/urllib3/util/ssl_.pyPKyVSl (epip/_vendor/urllib3/util/ssltransport.pyPKyVS$ '#pip/_vendor/urllib3/util/timeout.pyPKyVS竕6pip/_vendor/urllib3/util/url.pyPKyVSFq pip/_vendor/urllib3/util/wait.pyPKyVSxq2 S)$pip/_vendor/webencodings/__init__.pyPKyVSǭ#" pip/_vendor/webencodings/labels.pyPKyVSP1D$Rpip/_vendor/webencodings/mklabels.pyPKyVSg8p!pip/_vendor/webencodings/tests.pyPKyVS֥*pip/_vendor/webencodings/x_user_defined.pyPKFtV"ÁB pip-21.3.1.dist-info/LICENSE.txtPKFtVd|pip-21.3.1.dist-info/METADATAPKFtVP2\\pip-21.3.1.dist-info/WHEELPKFtVq<}%1pip-21.3.1.dist-info/entry_points.txtPKFtV"pip-21.3.1.dist-info/top_level.txtPKGtV0jGkpip-21.3.1.dist-info/RECORDPK=JPK}0]"BB,licenses/alt-python311-pip-wheel/LICENSE.txtnu[Copyright (c) 2008-2021 The pip developers (see AUTHORS.txt file) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK}0]58O8Oman/man1/python3.11.1nu[.TH PYTHON "1" .\" To view this file while editing, run it through groff: .\" groff -Tascii -man python.man | less .SH NAME python \- an interpreted, interactive, object-oriented programming language .SH SYNOPSIS .B python [ .B \-B ] [ .B \-b ] [ .B \-d ] [ .B \-E ] [ .B \-h ] [ .B \-i ] [ .B \-I ] .br [ .B \-m .I module-name ] [ .B \-q ] [ .B \-O ] [ .B \-OO ] [ .B \-P ] [ .B \-s ] [ .B \-S ] [ .B \-u ] .br [ .B \-v ] [ .B \-V ] [ .B \-W .I argument ] [ .B \-x ] [ .B \-X .I option ] [ .B \-? ] .br [ .B \--check-hash-based-pycs .I default | .I always | .I never ] .br [ .B \--help ] [ .B \--help-env ] [ .B \--help-xoptions ] [ .B \--help-all ] .br [ .B \-c .I command | .I script | \- ] [ .I arguments ] .SH DESCRIPTION Python is an interpreted, interactive, object-oriented programming language that combines remarkable power with very clear syntax. For an introduction to programming in Python, see the Python Tutorial. The Python Library Reference documents built-in and standard types, constants, functions and modules. Finally, the Python Reference Manual describes the syntax and semantics of the core language in (perhaps too) much detail. (These documents may be located via the .B "INTERNET RESOURCES" below; they may be installed on your system as well.) .PP Python's basic power can be extended with your own modules written in C or C++. On most systems such modules may be dynamically loaded. Python is also adaptable as an extension language for existing applications. See the internal documentation for hints. .PP Documentation for installed Python modules and packages can be viewed by running the .B pydoc program. .SH COMMAND LINE OPTIONS .TP .B \-B Don't write .I .pyc files on import. See also PYTHONDONTWRITEBYTECODE. .TP .B \-b Issue warnings about str(bytes_instance), str(bytearray_instance) and comparing bytes/bytearray with str. (-bb: issue errors) .TP .BI "\-c " command Specify the command to execute (see next section). This terminates the option list (following options are passed as arguments to the command). .TP .BI "\-\-check-hash-based-pycs " mode Configure how Python evaluates the up-to-dateness of hash-based .pyc files. .TP .B \-d Turn on parser debugging output (for expert only, depending on compilation options). .TP .B \-E Ignore environment variables like PYTHONPATH and PYTHONHOME that modify the behavior of the interpreter. .TP .B \-h ", " \-? ", "\-\-help Prints the usage for the interpreter executable and exits. .TP .B "\-\-help\-env" Prints help about Python-specific environment variables and exits. .TP .B "\-\-help\-xoptions" Prints help about implementation-specific \fB\-X\fP options and exits. .TP .TP .B "\-\-help\-all" Prints complete usage information and exits. .TP .B \-i When a script is passed as first argument or the \fB\-c\fP option is used, enter interactive mode after executing the script or the command. It does not read the $PYTHONSTARTUP file. This can be useful to inspect global variables or a stack trace when a script raises an exception. .TP .B \-I Run Python in isolated mode. This also implies \fB\-E\fP, \fB\-P\fP and \fB\-s\fP. In isolated mode sys.path contains neither the script's directory nor the user's site-packages directory. All PYTHON* environment variables are ignored, too. Further restrictions may be imposed to prevent the user from injecting malicious code. .TP .BI "\-m " module-name Searches .I sys.path for the named module and runs the corresponding .I .py file as a script. This terminates the option list (following options are passed as arguments to the module). .TP .B \-O Remove assert statements and any code conditional on the value of __debug__; augment the filename for compiled (bytecode) files by adding .opt-1 before the .pyc extension. .TP .B \-OO Do \fB-O\fP and also discard docstrings; change the filename for compiled (bytecode) files by adding .opt-2 before the .pyc extension. .TP .B \-P Don't automatically prepend a potentially unsafe path to \fBsys.path\fP such as the current directory, the script's directory or an empty string. See also the \fBPYTHONSAFEPATH\fP environment variable. .TP .B \-q Do not print the version and copyright messages. These messages are also suppressed in non-interactive mode. .TP .B \-s Don't add user site directory to sys.path. .TP .B \-S Disable the import of the module .I site and the site-dependent manipulations of .I sys.path that it entails. Also disable these manipulations if .I site is explicitly imported later. .TP .B \-u Force the stdout and stderr streams to be unbuffered. This option has no effect on the stdin stream. .TP .B \-v Print a message each time a module is initialized, showing the place (filename or built-in module) from which it is loaded. When given twice, print a message for each file that is checked for when searching for a module. Also provides information on module cleanup at exit. .TP .B \-V ", " \-\-version Prints the Python version number of the executable and exits. When given twice, print more information about the build. .TP .BI "\-W " argument Warning control. Python's warning machinery by default prints warning messages to .IR sys.stderr . The simplest settings apply a particular action unconditionally to all warnings emitted by a process (even those that are otherwise ignored by default): -Wdefault # Warn once per call location -Werror # Convert to exceptions -Walways # Warn every time -Wmodule # Warn once per calling module -Wonce # Warn once per Python process -Wignore # Never warn The action names can be abbreviated as desired and the interpreter will resolve them to the appropriate action name. For example, .B -Wi is the same as .B -Wignore . The full form of argument is: .IB action:message:category:module:lineno Empty fields match all values; trailing empty fields may be omitted. For example .B -W ignore::DeprecationWarning ignores all DeprecationWarning warnings. The .I action field is as explained above but only applies to warnings that match the remaining fields. The .I message field must match the whole printed warning message; this match is case-insensitive. The .I category field matches the warning category (ex: "DeprecationWarning"). This must be a class name; the match test whether the actual warning category of the message is a subclass of the specified warning category. The .I module field matches the (fully-qualified) module name; this match is case-sensitive. The .I lineno field matches the line number, where zero matches all line numbers and is thus equivalent to an omitted line number. Multiple .B -W options can be given; when a warning matches more than one option, the action for the last matching option is performed. Invalid .B -W options are ignored (though, a warning message is printed about invalid options when the first warning is issued). Warnings can also be controlled using the .B PYTHONWARNINGS environment variable and from within a Python program using the warnings module. For example, the warnings.filterwarnings() function can be used to use a regular expression on the warning message. .TP .BI "\-X " option Set implementation-specific option. The following options are available: -X faulthandler: enable faulthandler -X showrefcount: output the total reference count and number of used memory blocks when the program finishes or after each statement in the interactive interpreter. This only works on debug builds -X tracemalloc: start tracing Python memory allocations using the tracemalloc module. By default, only the most recent frame is stored in a traceback of a trace. Use -X tracemalloc=NFRAME to start tracing with a traceback limit of NFRAME frames -X importtime: show how long each import takes. It shows module name, cumulative time (including nested imports) and self time (excluding nested imports). Note that its output may be broken in multi-threaded application. Typical usage is python3 -X importtime -c 'import asyncio' -X dev: enable CPython's "development mode", introducing additional runtime checks which are too expensive to be enabled by default. It will not be more verbose than the default if the code is correct: new warnings are only emitted when an issue is detected. Effect of the developer mode: * Add default warning filter, as -W default * Install debug hooks on memory allocators: see the PyMem_SetupDebugHooks() C function * Enable the faulthandler module to dump the Python traceback on a crash * Enable asyncio debug mode * Set the dev_mode attribute of sys.flags to True * io.IOBase destructor logs close() exceptions -X utf8: enable UTF-8 mode for operating system interfaces, overriding the default locale-aware mode. -X utf8=0 explicitly disables UTF-8 mode (even when it would otherwise activate automatically). See PYTHONUTF8 for more details -X pycache_prefix=PATH: enable writing .pyc files to a parallel tree rooted at the given directory instead of to the code tree. -X warn_default_encoding: enable opt-in EncodingWarning for 'encoding=None' -X no_debug_ranges: disable the inclusion of the tables mapping extra location information (end line, start column offset and end column offset) to every instruction in code objects. This is useful when smaller code objects and pyc files are desired as well as suppressing the extra visual location indicators when the interpreter displays tracebacks. -X frozen_modules=[on|off]: whether or not frozen modules should be used. The default is "on" (or "off" if you are running a local build). -X int_max_str_digits=number: limit the size of int<->str conversions. This helps avoid denial of service attacks when parsing untrusted data. The default is sys.int_info.default_max_str_digits. 0 disables. .TP .B \-x Skip the first line of the source. This is intended for a DOS specific hack only. Warning: the line numbers in error messages will be off by one! .SH INTERPRETER INTERFACE The interpreter interface resembles that of the UNIX shell: when called with standard input connected to a tty device, it prompts for commands and executes them until an EOF is read; when called with a file name argument or with a file as standard input, it reads and executes a .I script from that file; when called with .B \-c .IR command , it executes the Python statement(s) given as .IR command . Here .I command may contain multiple statements separated by newlines. Leading whitespace is significant in Python statements! In non-interactive mode, the entire input is parsed before it is executed. .PP If available, the script name and additional arguments thereafter are passed to the script in the Python variable .IR sys.argv , which is a list of strings (you must first .I import sys to be able to access it). If no script name is given, .I sys.argv[0] is an empty string; if .B \-c is used, .I sys.argv[0] contains the string .I '-c'. Note that options interpreted by the Python interpreter itself are not placed in .IR sys.argv . .PP In interactive mode, the primary prompt is `>>>'; the second prompt (which appears when a command is not complete) is `...'. The prompts can be changed by assignment to .I sys.ps1 or .IR sys.ps2 . The interpreter quits when it reads an EOF at a prompt. When an unhandled exception occurs, a stack trace is printed and control returns to the primary prompt; in non-interactive mode, the interpreter exits after printing the stack trace. The interrupt signal raises the .I Keyboard\%Interrupt exception; other UNIX signals are not caught (except that SIGPIPE is sometimes ignored, in favor of the .I IOError exception). Error messages are written to stderr. .SH FILES AND DIRECTORIES These are subject to difference depending on local installation conventions; ${prefix} and ${exec_prefix} are installation-dependent and should be interpreted as for GNU software; they may be the same. The default for both is \fI/usr/local\fP. .IP \fI${exec_prefix}/bin/python\fP Recommended location of the interpreter. .PP .I ${prefix}/lib/python .br .I ${exec_prefix}/lib/python .RS Recommended locations of the directories containing the standard modules. .RE .PP .I ${prefix}/include/python .br .I ${exec_prefix}/include/python .RS Recommended locations of the directories containing the include files needed for developing Python extensions and embedding the interpreter. .RE .SH ENVIRONMENT VARIABLES .IP PYTHONSAFEPATH If this is set to a non-empty string, don't automatically prepend a potentially unsafe path to \fBsys.path\fP such as the current directory, the script's directory or an empty string. See also the \fB\-P\fP option. .IP PYTHONHOME Change the location of the standard Python libraries. By default, the libraries are searched in ${prefix}/lib/python and ${exec_prefix}/lib/python, where ${prefix} and ${exec_prefix} are installation-dependent directories, both defaulting to \fI/usr/local\fP. When $PYTHONHOME is set to a single directory, its value replaces both ${prefix} and ${exec_prefix}. To specify different values for these, set $PYTHONHOME to ${prefix}:${exec_prefix}. .IP PYTHONPATH Augments the default search path for module files. The format is the same as the shell's $PATH: one or more directory pathnames separated by colons. Non-existent directories are silently ignored. The default search path is installation dependent, but generally begins with ${prefix}/lib/python (see PYTHONHOME above). The default search path is always appended to $PYTHONPATH. If a script argument is given, the directory containing the script is inserted in the path in front of $PYTHONPATH. The search path can be manipulated from within a Python program as the variable .IR sys.path . .IP PYTHONPLATLIBDIR Override sys.platlibdir. .IP PYTHONSTARTUP If this is the name of a readable file, the Python commands in that file are executed before the first prompt is displayed in interactive mode. The file is executed in the same name space where interactive commands are executed so that objects defined or imported in it can be used without qualification in the interactive session. You can also change the prompts .I sys.ps1 and .I sys.ps2 in this file. .IP PYTHONOPTIMIZE If this is set to a non-empty string it is equivalent to specifying the \fB\-O\fP option. If set to an integer, it is equivalent to specifying \fB\-O\fP multiple times. .IP PYTHONDEBUG If this is set to a non-empty string it is equivalent to specifying the \fB\-d\fP option. If set to an integer, it is equivalent to specifying \fB\-d\fP multiple times. .IP PYTHONDONTWRITEBYTECODE If this is set to a non-empty string it is equivalent to specifying the \fB\-B\fP option (don't try to write .I .pyc files). .IP PYTHONINSPECT If this is set to a non-empty string it is equivalent to specifying the \fB\-i\fP option. .IP PYTHONIOENCODING If this is set before running the interpreter, it overrides the encoding used for stdin/stdout/stderr, in the syntax .IB encodingname ":" errorhandler The .IB errorhandler part is optional and has the same meaning as in str.encode. For stderr, the .IB errorhandler part is ignored; the handler will always be \'backslashreplace\'. .IP PYTHONNOUSERSITE If this is set to a non-empty string it is equivalent to specifying the \fB\-s\fP option (Don't add the user site directory to sys.path). .IP PYTHONUNBUFFERED If this is set to a non-empty string it is equivalent to specifying the \fB\-u\fP option. .IP PYTHONVERBOSE If this is set to a non-empty string it is equivalent to specifying the \fB\-v\fP option. If set to an integer, it is equivalent to specifying \fB\-v\fP multiple times. .IP PYTHONWARNINGS If this is set to a comma-separated string it is equivalent to specifying the \fB\-W\fP option for each separate value. .IP PYTHONHASHSEED If this variable is set to "random", a random value is used to seed the hashes of str and bytes objects. If PYTHONHASHSEED is set to an integer value, it is used as a fixed seed for generating the hash() of the types covered by the hash randomization. Its purpose is to allow repeatable hashing, such as for selftests for the interpreter itself, or to allow a cluster of python processes to share hash values. The integer must be a decimal number in the range [0,4294967295]. Specifying the value 0 will disable hash randomization. .IP PYTHONINTMAXSTRDIGITS Limit the maximum digit characters in an int value when converting from a string and when converting an int back to a str. A value of 0 disables the limit. Conversions to or from bases 2, 4, 8, 16, and 32 are never limited. .IP PYTHONMALLOC Set the Python memory allocators and/or install debug hooks. The available memory allocators are .IR malloc and .IR pymalloc . The available debug hooks are .IR debug , .IR malloc_debug , and .IR pymalloc_debug . .IP When Python is compiled in debug mode, the default is .IR pymalloc_debug and the debug hooks are automatically used. Otherwise, the default is .IR pymalloc . .IP PYTHONMALLOCSTATS If set to a non-empty string, Python will print statistics of the pymalloc memory allocator every time a new pymalloc object arena is created, and on shutdown. .IP This variable is ignored if the .RB $ PYTHONMALLOC environment variable is used to force the .BR malloc (3) allocator of the C library, or if Python is configured without pymalloc support. .IP PYTHONASYNCIODEBUG If this environment variable is set to a non-empty string, enable the debug mode of the asyncio module. .IP PYTHONTRACEMALLOC If this environment variable is set to a non-empty string, start tracing Python memory allocations using the tracemalloc module. .IP The value of the variable is the maximum number of frames stored in a traceback of a trace. For example, .IB PYTHONTRACEMALLOC=1 stores only the most recent frame. .IP PYTHONFAULTHANDLER If this environment variable is set to a non-empty string, .IR faulthandler.enable() is called at startup: install a handler for SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL signals to dump the Python traceback. .IP This is equivalent to the \fB-X faulthandler\fP option. .IP PYTHONEXECUTABLE If this environment variable is set, .IB sys.argv[0] will be set to its value instead of the value got through the C runtime. Only works on Mac OS X. .IP PYTHONUSERBASE Defines the user base directory, which is used to compute the path of the user .IR site-packages directory and Distutils installation paths for .IR "python setup\.py install \-\-user" . .IP PYTHONPROFILEIMPORTTIME If this environment variable is set to a non-empty string, Python will show how long each import takes. This is exactly equivalent to setting \fB\-X importtime\fP on the command line. .IP PYTHONBREAKPOINT If this environment variable is set to 0, it disables the default debugger. It can be set to the callable of your debugger of choice. .SS Debug-mode variables Setting these variables only has an effect in a debug build of Python, that is, if Python was configured with the \fB\--with-pydebug\fP build option. .IP PYTHONTHREADDEBUG If this environment variable is set, Python will print threading debug info. The feature is deprecated in Python 3.10 and will be removed in Python 3.12. .IP PYTHONDUMPREFS If this environment variable is set, Python will dump objects and reference counts still alive after shutting down the interpreter. .SH AUTHOR The Python Software Foundation: https://www.python.org/psf/ .SH INTERNET RESOURCES Main website: https://www.python.org/ .br Documentation: https://docs.python.org/ .br Developer resources: https://devguide.python.org/ .br Downloads: https://www.python.org/downloads/ .br Module repository: https://pypi.org/ .br Newsgroups: comp.lang.python, comp.lang.python.announce .SH LICENSING Python is distributed under an Open Source license. See the file "LICENSE" in the Python source distribution for information on terms & conditions for accessing and otherwise using Python and for a DISCLAIMER OF ALL WARRANTIES. PKj0]^^doc/alt-libssh2/COPYINGnu[/* Copyright (c) 2004-2007 Sara Golemon * Copyright (c) 2005,2006 Mikhail Gusarov * Copyright (c) 2006-2007 The Written Word, Inc. * Copyright (c) 2007 Eli Fant * Copyright (c) 2009-2014 Daniel Stenberg * Copyright (C) 2008, 2009 Simon Josefsson * All rights reserved. * * Redistribution and use in source and binary forms, * with or without modification, are permitted provided * that the following conditions are met: * * Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * Neither the name of the copyright holder nor the names * of any other contributors may be used to endorse or * promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. */ PKj0]Ee?doc/alt-libssh2/READMEnu[libssh2 - SSH2 library ====================== libssh2 is a library implementing the SSH2 protocol, available under the revised BSD license. Web site: https://www.libssh2.org/ Mailing list: https://cool.haxx.se/mailman/listinfo/libssh2-devel License: see COPYING Source code: https://github.com/libssh2/libssh2 Web site source code: https://github.com/libssh2/www Installation instructions are in: - docs/INSTALL_CMAKE for CMake - docs/INSTALL_AUTOTOOLS for Autotools PKj0]: doc/alt-libssh2/ChangeLognu[see NEWS PKj0]GNNdoc/alt-libssh2/NEWSnu[ Changelog for the libssh2 project. Generated with git2news.pl Version 1.8.0 (25 Oct 2016) Daniel Stenberg (25 Oct 2016) - RELEASE-NOTES: adjusted for 1.8.0 Kamil Dudka (20 Oct 2016) - Revert "aes: the init function fails when OpenSSL has AES support" This partially reverts commit f4f2298ef3635acd031cc2ee0e71026cdcda5864 because it caused the compatibility code to call initialization routines redundantly, leading to memory leakage with OpenSSL 1.1 and broken curl test-suite in Fedora: 88 bytes in 1 blocks are definitely lost in loss record 5 of 8 at 0x4C2DB8D: malloc (vg_replace_malloc.c:299) by 0x72C607D: CRYPTO_zalloc (mem.c:100) by 0x72A2480: EVP_CIPHER_meth_new (cmeth_lib.c:18) by 0x4E5A550: make_ctr_evp.isra.0 (openssl.c:407) by 0x4E5A8E8: _libssh2_init_aes_ctr (openssl.c:471) by 0x4E5BB5A: libssh2_init (global.c:49) Daniel Stenberg (19 Oct 2016) - [Charles Collicutt brought this change] libssh2_wait_socket: Fix comparison with api_timeout to use milliseconds (#134) Fixes #74 - [Charles Collicutt brought this change] Set err_msg on _libssh2_wait_socket errors (#135) - Revert "travis: Test mbedtls too" This reverts commit 3e6de50a24815e72ec5597947f1831f6083b7da8. Travis doesn't seem to support the mbedtls-dev package - maketgz: support "only" to only update version number locally and fix the date output locale - configure: make the --with-* options override the OpenSSL default ... previously it would default to OpenSSL even with the --with-[crypto] options used unless you specificly disabled OpenSSL. Now, enabling another backend will automatically disable OpenSSL if the other one is found. - [Keno Fischer brought this change] docs: Add documentation on new cmake/configure options - [Keno Fischer brought this change] configure: Add support for building with mbedtls - [wildart brought this change] travis: Test mbedtls too - [wildart brought this change] crypto: add support for the mbedTLS backend Closes #132 - [wildart brought this change] cmake: Add CLEAR_MEMORY option, analogously to that for autoconf - README.md: fix link typo - README: markdown version to look nicer on github Viktor Szakats (5 Sep 2016) - [Taylor Holberton brought this change] openssl: add OpenSSL 1.1.0 compatibility Daniel Stenberg (4 Sep 2016) - [Antenore Gatta brought this change] tests: HAVE_NETINET_IN_H was not defined correctly (#127) Fixes #125 - SECURITY: fix web site typo - SECURITY: security process GitHub (14 Aug 2016) - [Alexander Lamaison brought this change] Basic dockerised test suite. This introduces a test suite for libssh2. It runs OpenSSH in a Docker container because that works well on Windows (via docker-machine) as well as Linux. Presumably it works on Mac too with docker-machine, but I've not tested that. Because the test suite is docker-machine aware, you can also run it against a cloud provider, for more realistic network testing, by setting your cloud provider as your active docker machine. The Appveyor CI setup in this commit does that because Appveyor doesn't support docker locally. Kamil Dudka (3 Aug 2016) - [Viktor Szakats brought this change] misc.c: Delete unused static variables Closes #114 Daniel Stenberg (9 Apr 2016) - [Will Cosgrove brought this change] Merge pull request #103 from willco007/patch-2 Fix for security issue CVE-2016-0787 Alexander Lamaison (2 Apr 2016) - [Zenju brought this change] Fix MSVC 14 compilation errors For _MSC_VER == 1900 these macros are not needed and create problems: 1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt\stdio.h(1925): warning C4005: 'snprintf': macro redefinition (compiling source file libssh2-files\src\mac.c) 1> \win32\libssh2_config.h(27): note: see previous definition of 'snprintf' (compiling source file libssh2-files\src\mac.c) 1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt\stdio.h(1927): fatal error C1189: #error: Macro definition of snprintf conflicts with Standard Library function declaration (compiling source file libssh2-files\src\mac.c) Daniel Stenberg (26 Mar 2016) - [Brad Harder brought this change] _libssh2_channel_open: speeling error fixed in channel error message Alexander Lamaison (15 Mar 2016) - Link with crypt32.lib on Windows. Makes linking with static OpenSSL work again. Although it's not required for dynamic OpenSSL, it does no harm. Fixes #98. - [Craig A. Berry brought this change] Tweak VMS help file building. Primarily this is handling cases where top-level files moved into the docs/ directory. I also corrected a typo and removed the claim that libssh2 is public domain. - [Craig A. Berry brought this change] Build with standard stat structure on VMS. This gets us large file support, is available on any VMS release in the last decade and more, and gives stat other modern features such as 64-bit ino_t. - [Craig A. Berry brought this change] Update vms/libssh2_config.h. VMS does have stdlib.h, gettimeofday(), and OpenSSL. The latter is appropriate to hard-wire in the configuration because it's installed by default as part of the base operating system and there is currently no libgcrypt port. - [Craig A. Berry brought this change] VMS can't use %zd for off_t format. %z is a C99-ism that VMS doesn't currently have; even though the compiler is C99-compliant, the library isn't quite. The off_t used for the st_size element of the stat can be 32-bit or 64-bit, so detect what we've got and pick a format accordingly. - [Craig A. Berry brought this change] Normalize line endings in libssh2_sftp_get_channel.3. Somehow it got Windows-style CRLF endings so convert to just LF, for consistency as well as not to confuse tools that will regard the \r as content (e.g. the OpenVMS help librarian). Dan Fandrich (29 Feb 2016) - libgcrypt: Fixed a NULL pointer dereference on OOM Daniel Stenberg (24 Feb 2016) - [Viktor Szakats brought this change] url updates, HTTP => HTTPS Closes #87 Dan Fandrich (23 Feb 2016) - RELEASE-NOTES: removed some duplicated names Version 1.7.0 (23 Feb 2016) Daniel Stenberg (23 Feb 2016) - web: the site is now HTTPS - RELEASE-NOTES: 1.7.0 release - diffie_hellman_sha256: convert bytes to bits As otherwise we get far too small numbers. Reported-by: Andreas Schneider CVE-2016-0787 Alexander Lamaison (18 Feb 2016) - Allow CI failures with VS 2008 x64. Appveyor doesn't support this combination. Daniel Stenberg (16 Feb 2016) - [Viktor Szakats brought this change] GNUmakefile: list system libs after user libs Otherwise some referenced WinSock functions will fail to resolve when linking against LibreSSL 2.3.x static libraries with mingw. Closes #80 - [Viktor Szakats brought this change] openssl: apply new HAVE_OPAQUE_STRUCTS macro Closes #81 - [Viktor Szakats brought this change] openssl: fix LibreSSL support after OpenSSL 1.1.0-pre1/2 support Alexander Lamaison (14 Feb 2016) - sftp.h: Fix non-C90 type. uint64_t does not exist in C90. Use libssh2_uint64_t instead. - Exclude sshd tests from AppVeyor. They fail complaining that sshd wasn't invoked with an absolute path. - Test on more versions of Visual Studio. - Fix Appveyor builds. Daniel Stenberg (14 Feb 2016) - [Viktor Szakats brought this change] openssl: add OpenSSL 1.1.0-pre3-dev compatibility by using API instead of accessing an internal structure. Closes #83 - RELEASE-NOTES: synced with 996b04ececdf - include/libssh2.h: next version is 1.7.0 - configure: build "silent" if possible - sftp: re-indented some minor stuff - [Jakob Egger brought this change] sftp.c: ensure minimum read packet size For optimum performance we need to ensure we don't request tiny packets. - [Jakob Egger brought this change] sftp.c: Explicit return values & sanity checks - [Jakob Egger brought this change] sftp.c: Check Read Packet File Offset This commit adds a simple check to see if the offset of the read request matches the expected file offset. We could try to recover, from this condition at some point in the future. Right now it is better to return an error instead of corrupted data. - [Jakob Egger brought this change] sftp.c: Don't return EAGAIN if data was written to buffer - [Jakob Egger brought this change] sftp.c: Send at least one read request before reading This commit ensures that we have sent at least one read request before we try to read data in sftp_read(). Otherwise sftp_read() would return 0 bytes (indicating EOF) if the socket is not ready for writing. - [Jakob Egger brought this change] sftp.c: stop reading when buffer is full Since we can only store data from a single chunk in filep, we have to stop receiving data as soon as the buffer is full. This adresses the following bug report: https://github.com/libssh2/libssh2/issues/50 Salvador Fandiño (21 Jan 2016) - agent_disconnect_unix: unset the agent fd after closing it "agent_disconnect_unix", called by "libssh2_agent_disconnect", was leaving the file descriptor in the agent structure unchanged. Later, "libssh2_agent_free" would call again "libssh2_agent_disconnect" under the hood and it would try to close again the same file descriptor. In most cases that resulted in just a harmless error, but it is also possible that the file descriptor had been reused between the two calls resulting in the closing of an unrelated file descriptor. This patch sets agent->fd to LIBSSH2_INVALID_SOCKET avoiding that issue. Signed-off-by: Salvador Fandiño Daniel Stenberg (18 Jan 2016) - [Patrick Monnerat brought this change] os400qc3: support encrypted private keys PKCS#8 EncryptedPrivateKeyinfo structures are recognized and decoded to get values accepted by the Qc3 crypto library. - [Patrick Monnerat brought this change] os400qc3: New PKCS#5 decoder The Qc3 library is not able to handle PKCS#8 EncryptedPrivateKeyInfo structures by itself. It is only capable of decrypting the (encrypted) PrivateKeyInfo part, providing a key encryption key and an encryption algorithm are given. Since the encryption key and algorithm description part in a PKCS#8 EncryptedPrivateKeyInfo is a PKCS#5 structure, such a decoder is needed to get the derived key method and hash, as well as encryption algorith and initialisation vector. - [Patrick Monnerat brought this change] os400qc3: force continuous update on non-final hash/hmac computation - [Patrick Monnerat brought this change] os400qc3: Be sure hmac keys have a minimum length The Qc3 library requires a minimum key length depending on the target hash algorithm. Append binary zeroes to the given key if not long enough. This matches RFC 2104 specifications. - [Patrick Monnerat brought this change] os400qc3: Slave descriptor for key encryption key The Qc3 library requires the key encryption key to exist as long as the encrypted key is used. Its descriptor token is then kept as an "encrypted key slave" for recursive release. - [Patrick Monnerat brought this change] os400qc3.c: comment PEM/DER decoding - [Patrick Monnerat brought this change] os400qc3.c: improve ASN.1 header byte checks - [Patrick Monnerat brought this change] os400qc3.c: improve OID matching - [Patrick Monnerat brought this change] os400: os400qc3.c: replace malloc by LIBSSH2_ALLOC or alloca where possible - [Patrick Monnerat brought this change] os400: asn1_new_from_bytes(): use data from a single element only - [Patrick Monnerat brought this change] os400: fix an ILE/RPG prototype - [Patrick Monnerat brought this change] os400: implement character encoding conversion support - [Patrick Monnerat brought this change] os400: do not miss some external prototypes Build procedure extproto() did not strip braces from header files, thus possibly prepended them to true prototypes. This prevented the prototype to be recognized as such. The solution implemented here is to map braces to semicolons, effectively considering them as potential prototype delimiters. - [Patrick Monnerat brought this change] os400: Really add specific README - [Patrick Monnerat brought this change] os400: Add specific README and include new files in dist tarball - [Patrick Monnerat brought this change] os400: add compilation scripts - [Patrick Monnerat brought this change] os400: include files for ILE/RPG In addition, file os400/macros.h declares all procedures originally defined as macros. It must not be used for real inclusion and is only intended to be used as a `database' for macro wrapping procedures generation. - [Patrick Monnerat brought this change] os400: add supplementary header files/wrappers. Define configuration. - [Patrick Monnerat brought this change] Protect callback function calls from macro substitution Some structure fields holding callback addresses have the same name as the underlying system function (connect, send, recv). Set parentheses around their reference to suppress a possible macro substitution. Use a macro for connect() on OS/400 to resolve a const/nonconst parameter problem. - [Patrick Monnerat brought this change] Add interface for OS/400 crypto library QC3 - [Patrick Monnerat brought this change] misc: include stdarg.h for debug code - [Patrick Monnerat brought this change] Document crypto library interface - [Patrick Monnerat brought this change] Feature an optional crypto-specific macro to rsa sign a data fragment vector OS/400 crypto library is unable to sign a precomputed SHA1 hash: however it does support a procedure that hashes data fragments and rsa signs. If defined, the new macro _libssh2_rsa_sha1_signv() implements this function and disables use of _libssh2_rsa_sha1_sign(). The function described above requires that the struct iovec unused slacks are cleared: for this reason, macro libssh2_prepare_iovec() has been introduced. It should be defined as empty for crypto backends that are not sensitive to struct iovec unused slack values. - [Patrick Monnerat brought this change] Fold long lines in include files - [Viktor Szakats brought this change] kex.c: fix indentation Closes #71 - [Viktor Szakats brought this change] add OpenSSL-1.1.0-pre2 compatibility Closes #70 - [Viktor Szakats brought this change] add OpenSSL 1.1.0-pre1 compatibility * close https://github.com/libssh2/libssh2/issues/69 * sync a declaration with the rest of similar ones * handle EVP_MD_CTX_new() returning NULL with OpenSSL 1.1.0 * fix potential memory leak with OpenSSL 1.1.0 in _libssh2_*_init() functions, when EVP_MD_CTX_new() succeeds, but EVP_DigestInit() fails. Marc Hoersken (22 Dec 2015) - wincng.c: fixed _libssh2_wincng_hash_final return value _libssh2_wincng_hash_final was returning the internal BCRYPT status code instead of a valid libssh2 return value (0 or -1). This also means that _libssh2_wincng_hash never returned 0. - wincng.c: fixed possible memory leak in _libssh2_wincng_hash If _libssh2_wincng_hash_update failed _libssh2_wincng_hash_final would never have been called before. Reported by Zenju. Kamil Dudka (15 Dec 2015) - [Paul Howarth brought this change] libssh2.pc.in: fix the output of pkg-config --libs ... such that it does not include LDFLAGS used to build libssh2 itself. There was a similar fix in the curl project long time ago: https://github.com/bagder/curl/commit/curl-7_19_7-56-g4c8adc8 Bug: https://bugzilla.redhat.com/1279966 Signed-off-by: Kamil Dudka Marc Hoersken (6 Dec 2015) - hostkey.c: align code path of ssh_rsa_init to ssh_dss_init - hostkey.c: fix invalid memory access if libssh2_dsa_new fails Reported by dimmaq, fixes #66 Daniel Stenberg (3 Nov 2015) - [Will Cosgrove brought this change] gcrypt: define libssh2_sha256_ctx Looks like it didn't make it into the latest commit for whatever reason. Closes #58 - [Salvador Fandino brought this change] libssh2_session_set_last_error: Add function Net::SSH2, the Perl wrapping module for libssh2 implements several features* on top of libssh2 that can fail and so need some mechanism to report the error condition to the user. Until now, besides the error state maintained internally by libssh2, another error state was maintained at the Perl level for every session object and then additional logic was used to merge both error states. That is a maintenance nighmare, and actually there is no way to do it correctly and consistently. In order to allow the high level language to add new features to the library but still rely in its error reporting features the new function libssh2_session_set_last_error (that just exposses _libssh2_error_flags) is introduced. *) For instance, connecting to a remote SSH service giving the hostname and port. Signed-off-by: Salvador Fandino Signed-off-by: Salvador Fandiño - [Salvador Fandino brought this change] _libssh2_error: Support allocating the error message Before this patch "_libssh2_error" required the error message to be a static string. This patch adds a new function "_libssh2_error_flags" accepting an additional "flags" argument and specifically the flag "LIBSSH2_ERR_FLAG_DUP" indicating that the passed string must be duplicated into the heap. Then, the method "_libssh2_error" has been rewritten to use that new function under the hood. Signed-off-by: Salvador Fandino Signed-off-by: Salvador Fandiño - [Will Cosgrove brought this change] added engine.h include to fix warning - [sune brought this change] kex.c: removed dupe entry from libssh2_kex_methods[] Closes #51 - [Salvador Fandiño brought this change] userauth: Fix off by one error when reading public key file After reading the public key from file the size was incorrectly decremented by one. This was usually a harmless error as the last character on the public key file is an unimportant EOL. But if due to some error the public key file is empty, the public key size becomes (uint)(0 - 1), resulting in an unrecoverable out of memory error later. Signed-off-by: Salvador Fandi??o - [Salvador Fandino brought this change] channel: Detect bad usage of libssh2_channel_process_startup A common novice programmer error (at least among those using the wrapping Perl module Net::SSH2), is to try to reuse channels. This patchs detects that incorrect usage and fails with a LIBSSH2_ERROR_BAD_USE error instead of hanging. Signed-off-by: Salvador Fandino - [Will Cosgrove brought this change] kex: Added diffie-hellman-group-exchange-sha256 support ... and fixed HMAC_Init depricated usage Closes #48 Alexander Lamaison (21 Sep 2015) - Prefixed new #defines to prevent collisions. Other libraries might have their own USE_WIN32_*FILES. - [keith-daigle brought this change] Update examples/scp.c to fix bug where large files on win32 would cause got to wrap and go negative - [David Byron brought this change] add libssh2_scp_recv2 to support large (> 2GB) files on windows Daniel Stenberg (17 Sep 2015) - [sune brought this change] WinCNG: support for SHA256/512 HMAC Closes #47 - [brian m. carlson brought this change] Add support for HMAC-SHA-256 and HMAC-SHA-512. Implement support for these algorithms and wire them up to the libgcrypt and OpenSSL backends. Increase the maximum MAC buffer size to 64 bytes to prevent buffer overflows. Prefer HMAC-SHA-256 over HMAC-SHA-512, and that over HMAC-SHA-1, as OpenSSH does. Closes #40 - [Zenju brought this change] kex: free server host key before allocating it (again) Fixes a memory leak when Synology server requests key exchange Closes #43 - [Viktor Szakats brought this change] GNUmakefile: up OpenSSL version closes #23 - [Viktor Szakats brought this change] GNUmakefile: add -m64 CFLAGS when targeting mingw64, add -m32/-m64 to LDFLAGS libssh2 equivalent of curl patch https://github.com/bagder/curl/commit/d21b66835f2af781a3c2a685abc92ef9f0cd86be This allows to build for the non-default target when using a multi-target mingw distro. Also bump default OpenSSL dependency path to 1.0.2c. - [Viktor Szakats brought this change] GNUmakefile: add support for LIBSSH2_LDFLAG_EXTRAS It is similar to existing LIBSSH2_CFLAG_EXTRAS, but for extra linker options. Also delete some line/file ending whitespace. closes #27 - [nasacj brought this change] hostkey.c: Fix compiling error when OPENSSL_NO_MD5 is defined Closes #32 - [Mizunashi Mana brought this change] openssl.h: adjust the rsa/dsa includes ... to work when built without DSA support. Closes #36 Alexander Lamaison (26 Jul 2015) - Let CMake build work as a subproject. Patch contributed by JasonHaslam. - Fix builds with Visual Studio 2015. VS2015 moved stdio functions to the header files as inline function. That means check_function_exists can't detect them because it doesn't use header files - just does a link check. Instead we need to use check_symbol_exists with the correct headers. Kamil Dudka (2 Jul 2015) - cmake: include CMake files in the release tarballs Despite we announced the CMake support in libssh2-1.6.0 release notes, the files required by the CMake build system were not included in the release tarballs. Hence, the only way to use CMake for build was the upstream git repository. This commit makes CMake actually supported in the release tarballs. - tests/mansyntax.sh: fix 'make distcheck' with recent autotools Do not create symbolic links off the build directory. Recent autotools verify that out-of-source build works even if the source directory tree is not writable. - openssl: fix memleak in _libssh2_dsa_sha1_verify() Daniel Stenberg (12 Jun 2015) - openssl: make libssh2_sha1 return error code - use the internal prefix _libssh2_ for non-exported functions - removed libssh2_md5() since it wasn't used Reported-by: Kamil Dudka - [LarsNordin-LNdata brought this change] SFTP: Increase speed and datasize in SFTP read The function sftp_read never return more then 2000 bytes (as it should when I asked Daniel). I increased the MAX_SFTP_READ_SIZE to 30000 but didn't get the same speed as a sftp read in SecureSSH. I analyzed the code and found that a return always was dona when a chunk has been read. I changed it to a sliding buffer and worked on all available chunks. I got an increase in speed and non of the test I have done has failed (both local net and over Internet). Please review and test. I think 30000 is still not the optimal MAX_SFTP_READ_SIZE, my next goal is to make an API to enable changing this value (The SecureSSH sftp_read has more complete filled packages when comparing the network traffic) - bump: start working on 1.6.1 Version 1.6.0 (5 Jun 2015) Daniel Stenberg (5 Jun 2015) - RELEASE-NOTES: synced with 858930cae5c6a Marc Hoersken (19 May 2015) - wincng.c: fixed indentation - [sbredahl brought this change] wincng.c: fixed memleak in (block) cipher destructor Alexander Lamaison (6 May 2015) - [Jakob Egger brought this change] libssh2_channel_open: more detailed error message The error message returned by libssh2_channel_open in case of a server side channel open failure is now more detailed and includes the four standard error conditions in RFC 4254. - [Hannes Domani brought this change] kex: fix libgcrypt memory leaks of bignum Fixes #168. Marc Hoersken (3 Apr 2015) - configure.ac: check for SecureZeroMemory for clear memory feature - Revert "wincng.c: fix clear memory feature compilation with mingw" This reverts commit 2d2744efdd0497b72b3e1ff6e732aa4c0037fc43. Autobuilds show that this did not solve the issue. And it seems like RtlFillMemory is defined to memset, which would be optimized out by some compilers. - wincng.c: fix clear memory feature compilation with mingw Alexander Lamaison (1 Apr 2015) - [LarsNordin-LNdata brought this change] Enable use of OpenSSL that doesn't have DSA. Added #if LIBSSH2_DSA for all DSA functions. - [LarsNordin-LNdata brought this change] Use correct no-blowfish #define with OpenSSL. The OpenSSL define is OPENSSL_NO_BF, not OPENSSL_NO_BLOWFISH. Marc Hoersken (25 Mar 2015) - configure: error if explicitly enabled clear-memory is not supported This takes 22bd8d81d8fab956085e2079bf8c29872455ce59 and b8289b625e291bbb785ed4add31f4759241067f3 into account, but still makes it enabled by default if it is supported and error out in case it is unsupported and was requested. Daniel Stenberg (25 Mar 2015) - configure: make clear-memory default but only WARN if backend unsupported ... instead of previous ERROR. Marc Hoersken (24 Mar 2015) - wincng.h: fix warning about computed return value not being used - nonblocking examples: fix warning about unused tvdiff on Mac OS X Daniel Stenberg (24 Mar 2015) - openssl: fix compiler warnings - cofigure: fix --disable-clear-memory check Marc Hoersken (23 Mar 2015) - scp.c: improved command length calculation Reduced number of calls to strlen, because shell_quotearg already returns the length of the resulting string (e.q. quoted path) which we can add to the existing and known cmd_len. Removed obsolete call to memset again, because we can put a final NULL-byte at the end of the string using the calculated length. - scp.c: improved and streamlined formatting - scp.c: fix that scp_recv may transmit not initialised memory - scp.c: fix that scp_send may transmit not initialised memory Fixes ticket 244. Thanks Torsten. - kex: do not ignore failure of libssh2_sha1_init() Based upon 43b730ce56f010e9d33573fcb020df49798c1ed8. Fixes ticket 290. Thanks for the suggestion, mstrsn. - wincng.h: fix return code of libssh2_md5_init() - openssl.c: fix possible segfault in case EVP_DigestInit fails - wincng.c: fix possible use of uninitialized variables - wincng.c: fix unused argument warning if clear memory is not enabled - wincng: Added explicit clear memory feature to WinCNG backend This re-introduces the original feature proposed during the development of the WinCNG crypto backend. It still needs to be added to libssh2 itself and probably other backends. Memory is cleared using the function SecureZeroMemory which is available on Windows systems, just like the WinCNG backend. - wincng.c: fixed mixed line-endings - wincng.c: fixed use of invalid parameter types in a8d14c5dcf - wincng.c: only try to load keys corresponding to the algorithm - wincng.c: moved PEM headers into definitions - wincng.h: fixed invalid parameter name - wincng: fixed mismatch with declarations in crypto.h - userauth.c: fixed warning C6001: using uninitialized sig and sig_len - pem.c: fixed warning C6269: possible incorrect order of operations - wincng: add support for authentication keys to be passed in memory Based upon 18cfec8336e and daa2dfa2db. - pem.c: add _libssh2_pem_parse_memory to parse PEM from memory Requirement to implement 18cfec8336e for Libgcrypt and WinCNG. - pem.c: fix copy and paste mistake from 55d030089b8 - userauth.c: fix another possible dereference of a null pointer - userauth.c: fix possible dereference of a null pointer - pem.c: reduce number of calls to strlen in readline Alexander Lamaison (17 Mar 2015) - [Will Cosgrove brought this change] Initialise HMAC_CTX in more places. Missed a couple more places we init ctx to avoid openssl threading crash. - Build build breakage in WinCNG backend caused when adding libssh2_userauth_publickey_frommemory. The new feature isn't implemented for the WinCNG backend currently, but the WinCNG backend didn't contain any implementation of the required backend functions - even ones that returns an error. That caused link errors. This change fixes the problem by providing an implementation of the backend functions that returns an error. - Fix breakage in WinCNG backend caused by introducing libssh2_hmac_ctx_init. The macro was defined to nothing for the libgcrypt backend, but not for WinCNG. This brings the latter into line with the former. Daniel Stenberg (15 Mar 2015) - userauth_publickey_frommemory.3: add AVAILABILITY ... it will be added in 1.6.0 - libssh2: next version will be called 1.6.0 ... since we just added a new function. - docs: add libssh2_userauth_publickey_frommemory.3 to dist The function and man page were added in commit 18cfec8336e - [Jakob Egger brought this change] direct_tcpip: Fixed channel write There were 3 bugs in this loop: 1) Started from beginning after partial writes 2) Aborted when 0 bytes were sent 3) Ignored LIBSSH2_ERROR_EAGAIN See also: https://trac.libssh2.org/ticket/281 https://trac.libssh2.org/ticket/293 Alexander Lamaison (15 Mar 2015) - [Will Cosgrove brought this change] Must init HMAC_CTX before using it. Must init ctx before using it or openssl will reuse the hmac which is not thread safe and causes a crash. Added libssh2_hmac_ctx_init macro. - Add continuous integration configurations. Linux-based CI is done by Travis CI. Windows-based CI is done by Appveyor. - [David Calavera brought this change] Allow authentication keys to be passed in memory. All credits go to Joe Turpin, I'm just reaplying and cleaning his patch: http://www.libssh2.org/mail/libssh2-devel-archive-2012-01/0015.shtml * Use an unimplemented error for extracting keys from memory with libgcrypt. Daniel Stenberg (14 Mar 2015) - docs: include the renamed INSTALL* files in dist Alexander Lamaison (13 Mar 2015) - Prevent collisions between CMake and Autotools in examples/ and tests/. - Avoid clash between CMake build and Autotools. Autotools expects a configuration template file at src/libssh2_config.h.in, which buildconf generates. But the CMake build system has its CMake-specific version of the file at this path. This means that, if you don't run buildconf, the Autotools build will fail because it configured the wrong header template. See https://github.com/libssh2/libssh2/pull/8. - Merge pull request #8 from alamaison/cmake CMake build system. - CMake build system. Tested: - Windows: - Visual C++ 2005/2008/2010/2012/2013/MinGW-w64 - static/shared - 32/64-bit - OpenSSL/WinCNG - Without zlib - Linux: - GCC 4.6.3/Clang 3.4 - static/shared - 32/64-bit - OpenSSL/Libgcrypt - With/Without zlib - MacOS X - AppleClang 6.0.0 - static - 64-bit - OpenSSL - Without zlib Conflicts: README - Man man syntax tests fail gracefully if man version is not suitable. - Return valid code from test fixture on failure. The sshd test fixture was returning -1 if an error occurred, but negative error codes aren't technically valid (google it). Bash on Windows converted them to 0 which made setup failure look as though all tests were passing. - Let mansyntax.sh work regardless of where it is called from. Daniel Stenberg (12 Mar 2015) - [Viktor Szakáts brought this change] mingw build: allow to pass custom CFLAGS Allow to pass custom `CFLAGS` options via environment variable `LIBSSH2_CFLAG_EXTRAS`. Default and automatically added options of `GNUmakefile` have preference over custom ones. This addition is useful for passing f.e. custom CPU tuning or LTO optimization (`-flto -ffat-lto-objects`) options. The only current way to do this is to edit `GNUmakefile`. This patch makes it unnecessary. This is a mirror of similar libcurl patch: https://github.com/bagder/curl/pull/136 - [Will Cosgrove brought this change] userauth: Fixed prompt text no longer being copied to the prompts struct Regression from 031566f9c - README: update the git repo locations - wait_socket: wrong use of difftime() With reversed arguments it would always return a negative value... Bug: https://github.com/bagder/libssh2/issues/1 - bump: start working toward 1.5.1 now Version 1.5.0 (11 Mar 2015) Daniel Stenberg (11 Mar 2015) - RELEASE-NOTES: 1.5.0 release - [Mariusz Ziulek brought this change] kex: bail out on rubbish in the incoming packet CVE-2015-1782 Bug: http://www.libssh2.org/adv_20150311.html - docs: move INSTALL, AUTHORS, HACKING and TODO to docs/ And with this, cleanup README to be shorter and mention the new source code home. - .gitignore: don't ignore INSTALL Dan Fandrich (4 Mar 2015) - examples/x11.c: include sys/select.h for improved portability Daniel Stenberg (4 Mar 2015) - RELEASE-NOTES: synced with a8473c819bc068 In preparation for the upcoming 1.5.0 release. Guenter Knauf (8 Jan 2015) - NetWare build: added some missing exports. Marc Hoersken (29 Dec 2014) - knownhost.c: fix use of uninitialized argument variable wrote Detected by clang scan in line 1195, column 18. - examples/x11.c: fix result of operation is garbage or undefined Fix use of uninitialized structure w_size_bck. Detected by clang scan in line 386, column 28. - examples/x11.c: remove dead assigments of some return values Detected by clang scan in line 212, column 9. Detected by clang scan in line 222, column 13. Detected by clang scan in line 410, column 13. - examples/x11.c: fix possible memory leak if read fails Detected by clang scan in line 224, column 21. - examples/x11.c: fix invalid removal of first list element Fix use of memory after it was being freed. Detected by clang scan in line 56, column 12. - userauth.c: make sure that sp_len is positive and avoid overflows ... if the pointer subtraction of sp1 - pubkey - 1 resulted in a negative or larger value than pubkey_len, memchr would fail. Reported by Coverity CID 89846. - channel.c: remove logically dead code, host cannot be NULL here ... host cannot be NULL in line 525, because it is always valid (e.g. at least set to "0.0.0.0") after lines 430 and 431. Reported by Coverity CID 89807. - session.c: check return value of session_nonblock during startup Reported by Coverity CID 89803. - session.c: check return value of session_nonblock in debug mode Reported by Coverity CID 89805. - pem.c: fix mixed line-endings introduced with 8670f5da24 - pem.c: make sure there's a trailing zero and b64data is not NULL ... if there is no base64 data between PEM header and footer. Reported by Coverity CID 89823. - kex.c: make sure mlist is not set to NULL ... if the currently unsupported LANG methods are called. Reported by Coverity CID 89834. - packet.c: i < 256 was always true and i would overflow to 0 Visualize that the 0-termination is intentional, because the array is later passed to strlen within _libssh2_packet_askv. - silence multiple data conversion warnings Daniel Stenberg (23 Dec 2014) - agent_connect_unix: make sure there's a trailing zero ... if the path name was too long. Reported by Coverity CID 89801. Marc Hoersken (22 Dec 2014) - examples on Windows: use native SOCKET-type instead of int And check return values accordingly. - userauth.c: improve readability and clarity of for-loops Daniel Stenberg (22 Dec 2014) - calloc: introduce LIBSSH2_CALLOC() A simple function using LIBSSH2_ALLOC + memset, since this pattern was used in multiple places and this simplies code in general. Marc Hoersken (15 Dec 2014) - libssh2_priv.h: Ignore session, context and format parameters - x11 example: check return value of socket function - examples: fixed mixed line-endings introduced with aedfba25b8 - wincng.c: explicitly ignore BCrypt*AlgorithmProvider return codes Fixes VS2012 code analysis warning C6031: return value ignored: could return unexpected value - wincng.c: fix possible invalid memory write access Fixes VS2012 code analysis warning C6386: buffer overrun: accessing 'pbOutput', the writable size is 'cbOutput' bytes, but '3' bytes may be written: libssh2 wincng.c 610 - tests on Windows: check for WSAStartup return code Fixes VS2012 code analysis warning C6031: return value ignored: could return unexpected value - wincng.c: fix possible NULL pointer de-reference of bignum Fixes VS2012 code analysis warning C6011: dereferencing NULL pointer 'bignum'. libssh2 wincng.c 1567 - wincng.c: fix possible use of uninitialized memory Fixes VS2012 code analysis warning C6001: using uninitialized memory 'cbDecoded'. libssh2 wincng.c 553 - packet.c: fix possible NULL pointer de-reference within listen_state Fixes VS2012 code analysis warning C6011: dereferencing NULL pointer 'listen_state->channel'. libssh2 packet.c 221 - kex.c: fix possible NULL pointer de-reference with session->kex Fixes VS2012 code analysis warning C6011: dereferencing NULL pointer 'session->kex'. libssh2 kex.c 1761 - agent.c: check return code of MapViewOfFile Fixes VS2012 code analysis warning C6387: 'p+4' may be '0': this does not adhere to the specification for the function 'memcpy': libssh2 agent.c 330 Fixes VS2012 code analysis warning C6387: 'p' may be '0': this does not adhere to the specification for the function 'UnmapViewOfFile': libssh2 agent.c 333 - examples on Windows: check for socket return code Fixes VS2012 code analysis warning C28193: The variable holds a value that must be examined - examples on Windows: check for WSAStartup return code Fixes VS2012 code analysis warning C6031: return value ignored: could return unexpected value Guenter Knauf (11 Dec 2014) - wincng.c: silent some more gcc compiler warnings. - wincng.c: silent gcc compiler warnings. - Watcom build: added support for WinCNG build. - build: updated dependencies in makefiles. Daniel Stenberg (4 Dec 2014) - configure: change LIBS not LDFLAGS when checking for libs Closes #289 Patch-by: maurerpe Guenter Knauf (3 Dec 2014) - MinGW build: some more GNUMakefile tweaks. test/GNUmakefile: added architecture autodetection; added switches to CFLAGS and RCFLAGS to make sure that the right architecture is used. Added support to build with WinCNG. - sftpdir.c: added authentication method detection. Stuff copied over from ssh2.c to make testing a bit easier. - NMake build: fixed LIBS settings. - NMake build: added support for WinCNG build. - MinGW build: some GNUMakefile tweaks. Added architecture autodetection; added switches to CFLAGS and RCFLAGS to make sure that the right architecture is used. Added support to build with WinCNG. - MinGW build: Fixed redefine warnings. - Updated copyright year. Daniel Stenberg (31 Aug 2014) - COPYING: bump the copyright year Dan Fandrich (28 Jul 2014) - docs: fixed a bunch of typos - docs: added missing libssh2_session_handshake.3 file Marc Hoersken (19 May 2014) - wincng.c: specify the required libraries for dependencies using MSVC Initially reported by Bob Kast as "for MS VS builds, specify the libraries that are required so they don't need to go into all project files that may use this library". Thanks a lot. - [Bob Kast brought this change] windows build: do not export externals from static library If you are building a DLL, then you need to explicitly export each entry point. When building a static library, you should not. libssh2 was exporting the entry points whether it was building a DLL or a static library. To elaborate further, if libssh2 was used as a static library, which was being linked into a DLL, the libssh2 API would be exported from that separate DLL. Daniel Stenberg (19 May 2014) - [Mikhail Gusarov brought this change] Fix typos in manpages Marc Hoersken (18 May 2014) - wincng.c: Fixed memory leak in case of an error during ASN.1 decoding - configure: Display individual crypto backends on separate lines This avoids line-wrapping in between parameters and makes the error message look like the following: configure: error: No crypto library found! Try --with-libssl-prefix=PATH or --with-libgcrypt-prefix=PATH or --with-wincng on Windows - [Bob Kast brought this change] libssh2_priv.h: a 1 bit bit-field should be unsigned some compilers may not like this - knownhost.c: Fixed warning that pointer targets differ in signedness - wincng.c: Fixed warning about pointer targets differing in signedness - tcpip-forward.c: Fixed warning that pointer targets differ in signedness libssh2_channel_forward_listen_ex uses ints instead of unsigned ints. - misc.c: Fixed warning about mixed declarations and code - libgcrypt.h: Fixed warning about pointer targets differing in signedness - wincng.h: Fixed warning about pointer targets differing in signedness - misc.c: Fixed warning about unused parameter abstract - tcpip-forward.c: Removed unused variables shost, sport and sockopt - wincng.h: Added forward declarations for all WinCNG functions Initially reported by Bob Kast as "Wincng - define function prototypes for wincng routines". Thanks a lot. Also replaced structure definitions with type definitions. - [Bob Kast brought this change] libssh2.h: on Windows, a socket is of type SOCKET, not int - win32: Added WinCNG targets to generated Visual Studio project Inspired by Bob Kast's reports, this commit enables the compilation of libssh2 with WinCNG using the generated Visual Studio project files. This commit adds WinCNG support to parts of the existing Win32 build infrastructure, until new build systems, like pre-defined VS project files or CMake files may be added. This commit and b20bfeb3e519119a48509a1099c06d65aa7da1d7 raise one question: How to handle build systems, like VS project files, that need to include all source files regardless of the desired target, including all supported crypto backends? For now the mentioned commit added a check for LIBSSH2_OPENSSL to openssl.c and with this commit the supported crypto backends are hardcoded within Makefile.am. - libssh2_priv msvc: Removed redundant definition of inline keyword Initially reported by Bob Kast as "Remove redundant 'inline' define". Thanks a lot. - wincng: Made data parameter to hash update function constant Initially reported by Bob Kast as "formal parameter must be const since it is used in contexts where the actual parameter may be const". Thanks a lot. - wincng: fix cross-compilation against the w64 mingw-runtime package - openssl: Check for LIBSSH2_OPENSSL in order to compile with openssl - wincng: Fixed use of possible uninitialized variable pPaddingInfo Reported by Bob Kast, thanks a lot. - wincng: Added cast for double to unsigned long conversion - wincng: Cleaned up includes and check NTSTATUS using macro Removed header file combination that is not supported on a real Windows platform and can only be compiled using MinGW. Replaced custom NTSTATUS return code checks with BCRYPT_SUCCESS macro. Daniel Stenberg (16 Mar 2014) - userauth_hostbased_fromfile: zero assign to avoid uninitialized use Detected by clang-analyze - channel_receive_window_adjust: store windows size always Avoid it sometimes returning without storing it, leaving calling functions with unknown content! Detected by clang-analyzer - publickey_packet_receive: avoid junk in returned pointers clang-analyzer found this risk it would return a non-initialized pointer in a success case Peter Stuge (16 Mar 2014) - [Marc Hoersken brought this change] Added Windows Cryptography API: Next Generation based backend - [Marc Hoersken brought this change] knownhost.c: fixed that 'key_type_len' may be used uninitialized ../src/knownhost.c: In function 'libssh2_knownhost_readline': ../src/knownhost.c:651:16: warning: 'key_type_len' may be used uninitialized in this function [-Wmaybe-uninitialized] rc = knownhost_add(hosts, hostbuf, NULL, ^ ../src/knownhost.c:745:12: note: 'key_type_len' was declared here size_t key_type_len; ^ - [Marc Hoersken brought this change] pem.c: always compile pem.c independently of crypto backend - Fix non-autotools builds: Always define the LIBSSH2_OPENSSL CPP macro Commit d512b25f69a1b6778881f6b4b5ff9cfc6023be42 introduced a crypto library abstraction in the autotools build system, to allow us to more easily support new crypto libraries. In that process it was found that all other build system which we support are hard-coded to build with OpenSSL. Commit f5c1a0d98bd51aeb24aca3d49c7c81dcf8bd858d fixes automake introduced into non-autotools build systems but still overlooked the CPP macro saying that we are using OpenSSL. Thanks to Marc Hörsken for identifying this issue and proposing a fix for win32/{GNUmakefile,config.mk}. This commit uses a slightly different approach but the end result is the same. Dan Fandrich (15 Mar 2014) - channel_close: Close the channel even in the case of errors - sftp_close_handle: ensure the handle is always closed Errors are reported on return, but otherwise the close path is completed as much as possible and the handle is freed on exit. Alexander Lamaison (6 Mar 2014) - knownhost: Restore behaviour of `libssh2_knownhost_writeline` with short buffer. Commit 85c6627c changed the behaviour of `libssh2_knownhost_writeline` so that it stopped returning the number of bytes needed when the given buffer was too small. Also, the function changed such that is might write to part of the buffer before realising it is too small. This commit restores the original behaviour, whilst keeping the unknown-key-type functionality that 85c6627c. Instead of writing to the buffer piecemeal, the length of the various parts is calculated up front and the buffer written only if there is enough space. The calculated necessary size is output in `outlen` regardless of whether the buffer was written to. The main use-case for the original behaviour that this commit restores is to allow passing in a NULL buffer to get the actual buffer size needed, before calling the function again with the buffer allocated to the exact size required. - knownhost: Fix DSS keys being detected as unknown. I missing `else` meant ssh-dss format keys were being re-detected as unknown format. Dan Fandrich (6 Mar 2014) - knownhosts: Abort if the hosts buffer is too small This could otherwise cause a match on the wrong host - agent_list_identities: Fixed memory leak on OOM - Fixed a few typos - userauth: Fixed an attempt to free from stack on error - Fixed a few memory leaks in error paths - Fixed two potential use-after-frees of the payload buffer The first might occur if _libssh2_packet_add returns an error, as fullpacket_state wasn't reset to idle so if it were possible for fullpacket to be called again, it would return to the same state handler and re-use the freed p->packet buffer. The second could occur if decrypt returned an error, as it freed the packet buffer but did not clear total_num, meaning that freed buffer could be written into again later. Alexander Lamaison (28 Nov 2013) - Fix missing `_libssh2_error` in `_libssh2_channel_write`. In one case, the error code from `_libssh2_transport_read` was being returned from `_libssh2_channel_write` without setting it as the last error by calling `_libssh2_error`. This commit fixes that. Found when using a session whose socket had been inadvertently destroyed. The calling code got confused because via `libssh2_session_last_error` it appeared no error had occurred, despite one being returned from the previous function. Kamil Dudka (21 Nov 2013) - [Mark McPherson brought this change] openssl: initialise the digest context before calling EVP_DigestInit() When using the OpenSSL libraries in FIPS mode, the function call EVP_DigestInit() is actually #defined to FIPS_digestinit(). Unfortunately wheres EVP_DigestInit() initialises the context and then calls EVP_DigestInit_ex(), this function assumes that the context has been pre-initialised and crashes when it isn't. Bug: https://trac.libssh2.org/ticket/279 Fixes #279 - [Marc Hörsken brought this change] .gitignore: Ignore files like src/libssh2_config.h.in~ Peter Stuge (13 Nov 2013) - Move automake conditionals added by commit d512b25f out of Makefile.inc Commit d512b25f69a1b6778881f6b4b5ff9cfc6023be42 added automake conditionals to Makefile.inc but since Makefile.inc is included from Makefile for all other build systems that does not work. This commit instead adds Makefile.OpenSSL.inc and Makefile.libgcrypt.inc and moves the automake conditional to its proper place, src/Makefile.am. The automake conditional includes the correct Makefile.$name.inc per the crypto library selection/detection done by configure. All non-autotools build system files in libssh2 are hardcoded to use OpenSSL and do not get a conditional but at least there is some reuse because they can all include the new Makefile.OpenSSL.inc. Daniel Stenberg (27 Oct 2013) - [Salvador Fandino brought this change] Set default window size to 2MB The default channel window size used until now was 256KB. This value is too small and results on a bottleneck on real-life networks where round-trip delays can easily reach 300ms. The issue was not visible because the configured channel window size was being ignored and a hard-coded value of ~22MB being used instead, but that was fixed on a previous commit. This patch just changes the default window size (LIBSSH2_CHANNEL_WINDOW_DEFAULT) to 2MB. It is the same value used by OpenSSH and in our opinion represents a good compromise between memory used and transfer speed. Performance tests were run to determine the optimum value. The details and related discussion are available from the following thread on the libssh2 mailing-list: http://www.libssh2.org/mail/libssh2-devel-archive-2013-10/0018.shtml http://article.gmane.org/gmane.network.ssh.libssh2.devel/6543 An excerpt follows: "I have been running some transfer test and measuring their speed. My setup was composed of a quad-core Linux machine running Ubuntu 13.10 x86_64 with a LXC container inside. The data transfers were performed from the container to the host (never crossing through a physical network device). Network delays were simulated using the tc tool. And ping was used to verify that they worked as intended during the tests. The operation performed was the equivalent to the following ssh command: $ ssh container "dd bs=16K count=8K if=/dev/zero" >/dev/null Though, establishment and closing of the SSH connection was excluded from the timings. I run the tests several times transferring files of sizes up to 128MB and the results were consistent between runs. The results corresponding to the 128MB transfer are available here: https://docs.google.com/spreadsheet/ccc?key=0Ao1yRmX6PQQzdG5wSFlrZl9HRWNET3ZyN0hnaGo5ZFE&usp=sharing It clearly shows that 256KB is too small as the default window size. Moving to a 512MB generates a great improvement and after the 1MB mark the returns rapidly diminish. Other factors (TCP window size, probably) become more limiting than the channel window size For comparison I also performed the same transfers using OpenSSH. Its speed is usually on par with that of libssh2 using a window size of 1MB (even if it uses a 2MB window, maybe it is less aggressive sending the window adjust msgs)." Signed-off-by: Salvador Fandino - [Salvador brought this change] _libssh2_channel_read: Honour window_size_initial _libssh2_channel_read was using an arbitrary hard-coded limit to trigger the window adjusting code. The adjustment used was also hard-coded and arbitrary, 15MB actually, which would limit the usability of libssh2 on systems with little RAM. This patch, uses the window_size parameter passed to libssh2_channel_open_ex (stored as remote.window_size_initial) plus the buflen as the base for the trigger and the adjustment calculation. The memory usage when using the default window size is reduced from 22MB to 256KB per channel (actually, if compression is used, these numbers should be incremented by ~50% to account for the errors between the decompressed packet sizes and the predicted sizes). My tests indicate that this change does not impact the performance of transfers across localhost or a LAN, being it on par with that of OpenSSH. On the other hand, it will probably slow down transfers on networks with high bandwidth*delay when the default window size (LIBSSH2_CHANNEL_WINDOW_DEFAULT=256KB) is used. Signed-off-by: Salvador Fandino - [Salvador Fandino brought this change] knownhosts: handle unknown key types Store but don't use keys of unsupported types on the known_hosts file. Currently, when libssh2 parses a known_host file containing keys of some type it doesn't natively support, it stops reading the file and returns an error. That means, that the known_host file can not be safely shared with other software supporting other key types (i.e. OpenSSH). This patch adds support for handling keys of unknown type. It can read and write them, even if they are never going to be matched. At the source level the patch does the following things: - add a new unknown key type LIBSSH2_KNOWNHOST_KEY_UNKNOWN - add a new slot (key_type_name) on the known_host struct that is used to store the key type in ascii form when it is not supported - parse correctly known_hosts entries with unknown key types and populate the key_type_name slot - print correctly known_hosts entries of unknown type - when checking a host key ignore keys that do not match the key Fixes #276 - windows build: fix build errors Fixes various link errors with VS2010 Reported-by: "kdekker" Fixes #272 - man page: add missing function argument for libssh2_userauth_publickey_fromfile_ex() Reported-by: "pastey" Fixes #262 - [Salvador brought this change] Fix zlib deflate usage Deflate may return Z_OK even when not all data has been compressed if the output buffer becomes full. In practice this is very unlikely to happen because the output buffer size is always some KBs larger than the size of the data passed for compression from the upper layers and I think that zlib never expands the data so much, even on the worst cases. Anyway, this patch plays on the safe side checking that the output buffer is not exhausted. Signed-off-by: Salvador - [Salvador brought this change] comp_method_zlib_decomp: Improve buffer growing algorithm The old algorithm was O(N^2), causing lots and lots of reallocations when highly compressed data was transferred. This patch implements a simpler one that just doubles the buffer size everytime it is exhausted. It results in O(N) complexity. Also a smaller inflate ratio is used to calculate the initial size (x4). Signed-off-by: Salvador - [Salvador brought this change] Fix zlib usage Data may remain in zlib internal buffers when inflate() returns Z_OK and avail_out == 0. In that case, inflate has to be called again. Also, once all the data has been inflated, it returns Z_BUF_ERROR to signal that the input buffer has been exhausted. Until now, the way to detect that a packet payload had been completely decompressed was to check that no data remained on the input buffer but that didn't account for the case where data remained on the internal zlib buffers. That resulted in packets not being completely decompressed and the missing data reappearing on the next packet, though the bug was masked by the buffer allocation algorithm most of the time and only manifested when transferring highly compressible data. This patch fixes the zlib usage. Signed-off-by: Salvador - [Salvador brought this change] _libssh2_channel_read: fix data drop when out of window After filling the read buffer with data from the read queue, when the window size was too small, "libssh2_channel_receive_window_adjust" was called to increase it. In non-blocking mode that function could return EAGAIN and, in that case, the EAGAIN was propagated upwards and the data already read on the buffer lost. The function was also moving between the two read states "libssh2_NB_state_idle" and "libssh2_NB_state_created" both of which behave in the same way (excepting a debug statment). This commit modifies "_libssh2_channel_read" so that the "libssh2_channel_receive_window_adjust" call is performed first (when required) and if everything goes well, then it reads the data from the queued packets into the read buffer. It also removes the useless "libssh2_NB_state_created" read state. Some rotted comments have also been updated. Signed-off-by: Salvador - [Salvador Fandino brought this change] window_size: redid window handling for flow control reasons Until now, the window size (channel->remote.window_size) was being updated just after receiving the packet from the transport layer. That behaviour is wrong because the channel queue may grow uncontrolled when data arrives from the network faster that the upper layer consumes it. This patch adds a new counter, read_avail, which keeps a count of the bytes available from the packet queue for reading. Also, now the window size is adjusted when the data is actually read by an upper layer. That way, if the upper layer stops reading data, the window will eventually fill and the remote host will stop sending data. When the upper layers reads enough data, a window adjust packet is delivered and the transfer resumes. The read_avail counter is used to detect the situation when the remote server tries to send data surpassing the window size. In that case, the extra data is discarded. Signed-off-by: Salvador Peter Stuge (15 Sep 2013) - configure.ac: Call zlib zlib and not libz in text but keep option names - configure.ac: Reorder --with-* options in --help output - configure.ac: Rework crypto library detection This further simplifies adding new crypto libraries. - Clean up crypto library abstraction in build system and source code libssh2 used to explicitly check for libgcrypt and default to OpenSSL. Now all possible crypto libraries are checked for explicitly, making the addition of further crypto libraries both simpler and cleaner. - configure.ac: Add zlib to Requires.private in libssh2.pc if using zlib - Revert "Added Windows Cryptography API: Next Generation based backend" This reverts commit d385230e15715e67796f16f3e65fd899f21a638b. Daniel Stenberg (7 Sep 2013) - [Leif Salomonsson brought this change] sftp_statvfs: fix for servers not supporting statfvs extension Fixes issue arising when server does not support statfvs and or fstatvfs extensions. sftp_statvfs() and sftp_fstatvfs() after this patch will handle the case when SSH_FXP_STATUS is returned from server. - [Marc Hoersken brought this change] Added Windows Cryptography API: Next Generation based backend - [Kamil Dudka brought this change] partially revert "window_size: explicit adjustments only" This partially reverts commit 03ca9020756a4e16f0294e5b35e9826ee6af2364 in order to fix extreme slowdown when uploading to localhost via SFTP. I was able to repeat the issue on RHEL-7 on localhost only. It did not occur when uploading via network and it did not occur on a RHEL-6 box with the same version of libssh2. The problem was that sftp_read() used a read-ahead logic to figure out the window_size, but sftp_packet_read() called indirectly from sftp_write() did not use any read-ahead logic. - _libssh2_channel_write: client spins on write when window full When there's no window to "write to", there's no point in waiting for the socket to become writable since it most likely just will continue to be. Patch-by: ncm Fixes #258 - _libssh2_channel_forward_cancel: avoid memory leaks on error Fixes #257 - _libssh2_packet_add: avoid using uninitialized memory In _libssh2_packet_add, called by _libssh2_packet_read, a call to _libssh2_packet_send that is supposed to send a one-byte message SSH_MSG_REQUEST_FAILURE would send an uninitialized byte upon re-entry if its call to _send returns _EAGAIN. Fixes #259 - _libssh2_channel_forward_cancel: accessed struct after free ... and the assignment was pointless anyway since the struct was about to be freed. Bug introduced in dde2b094. Fixes #268 Peter Stuge (2 Jun 2013) - [Marc Hoersken brought this change] Fixed compilation using mingw-w64 - [Marc Hoersken brought this change] knownhost.c: use LIBSSH2_FREE macro instead of free Use LIBSSH2_FREE instead of free since _libssh2_base64_encode uses LIBSSH2_ALLOC Daniel Stenberg (18 May 2013) - [Matthias Kerestesch brought this change] libssh2_agent_init: init ->fd to LIBSSH2_INVALID_SOCKET ... previously it was left at 0 which is a valid file descriptor! Bug: https://trac.libssh2.org/ticket/265 Fixes #265 - userauth_password: pass on the underlying error code _libssh2_packet_requirev() may return different errors and we pass that to the parent instead of rewriting it. Bug: http://libssh2.org/mail/libssh2-devel-archive-2013-04/0029.shtml Reported by: Cosmin Peter Stuge (9 May 2013) - [Marc Hoersken brought this change] libcrypt.c: Fix typo in _libssh2_rsa_sha1_sign() parameter type Kamil Dudka (4 May 2013) - configure.ac: replace AM_CONFIG_HEADER with AC_CONFIG_HEADERS Reported by: Quintus Bug: https://trac.libssh2.org/ticket/261 Guenter Knauf (12 Apr 2013) - Fixed copyright string for NetWare build. Daniel Stenberg (9 Apr 2013) - [Richard W.M. Jones brought this change] sftp: Add support for fsync (OpenSSH extension). The new libssh2_sftp_fsync API causes data and metadata in the currently open file to be committed to disk at the server. This is an OpenSSH extension to the SFTP protocol. See: https://bugzilla.mindrot.org/show_bug.cgi?id=1798 - [Richard W.M. Jones brought this change] sftp: statvfs: Along error path, reset the correct 'state' variable. - [Richard W.M. Jones brought this change] sftp: seek: Don't flush buffers on same offset Signed-off-by: Richard W.M. Jones Guenter Knauf (9 Feb 2013) - Updated dependency libs. - Fixed tool macro names. Daniel Stenberg (29 Nov 2012) - [Seth Willits brought this change] compiler warnings: typecast strlen in macros ... in macro parameters to avoid compiler warnings about lost precision. Several macros in libssh2.h call strlen and pass the result directly to unsigned int parameters of other functions, which warns about precision loss because strlen returns size_t which is unsigned long on at least some platforms (such as OS X). The fix is to simply typecast the strlen() result to unsigned int. - libssh2.h: bump version to 1.4.4-DEV Version 1.4.3 (27 Nov 2012) Daniel Stenberg (27 Nov 2012) - RELEASE-NOTES: fixed for 1.4.3 - sftp_read: return error if a too large package arrives Peter Stuge (13 Nov 2012) - Only define _libssh2_dsa_*() functions when building with DSA support Guenter Knauf (8 Nov 2012) - Added .def file to output. Kamil Dudka (1 Nov 2012) - libssh2_hostkey_hash.3: update the description of return value The function returns NULL also if the hash algorithm is not available. Guenter Knauf (24 Oct 2012) - Fixed mode acciedently committed. - Ignore generated file. - Added hack to make use of Makefile.inc. This should avoid further maintainance of the objects list. - Fixed MSVC NMakefile. Added missing source files; added resource for DLL. Kamil Dudka (22 Oct 2012) - examples: use stderr for messages, stdout for data Reported by: Karel Srot Bug: https://bugzilla.redhat.com/867462 - openssl: do not leak memory when handling errors ,.. in aes_ctr_init(). Detected by Coverity. - channel: fix possible NULL dereference ... in libssh2_channel_get_exit_signal(). Detected by Coverity. - Revert "aes: the init function fails when OpenSSL has AES support" This partially reverts commit f4f2298ef3635acd031cc2ee0e71026cdcda5864. We need to use the EVP_aes_???_ctr() functions in FIPS mode. - crypt: use hard-wired cipher block sizes consistently - openssl: do not ignore failure of EVP_CipherInit() - kex: do not ignore failure of libssh2_md5_init() The MD5 algorithm is disabled when running in FIPS mode. Daniel Stenberg (21 Aug 2012) - [Peter Krempa brought this change] known_hosts: Fail when parsing unknown keys in known_hosts file. libssh2_knownhost_readfile() silently ignored problems when reading keys in unsupported formats from the known hosts file. When the file is written again from the internal structures of libssh2 it gets truntcated to the point where the first unknown key was located. * src/knownhost.c:libssh2_knownhost_readfile() - return error if key parsing fails - AUTHORS: synced with 42fec44c8a4 31 recent authors added - [Dave Hayden brought this change] compression: add support for zlib@openssh.com Add a "use_in_auth" flag to the LIBSSH2_COMP_METHOD struct and a separate "zlib@openssh.com" method, along with checking session->state for LIBSSH2_STATE_AUTHENTICATED. Appears to work on the OpenSSH servers I've tried against, and it should work as before with normal zlib compression. - [Dmitry Smirnov brought this change] configure: gcrypt doesn't come with pkg-config support ... so use plain old -lgcrypt to the linker to link with it. Fixes #225 - sftp_read: Value stored to 'next' is never read Detected by clang-analyzer - publickey_init: errors are negative, fix check Detected by clang-analyzer. - [Maxime Larocque brought this change] session_free: wrong variable used for keeping state If libssh2_session_free is called without the channel being freed previously by libssh2_channel_free a memory leak could occur. A mismatch of states variables in session_free() prevent the call to libssh2_channel_free function. session->state member is used instead of session->free_state. It causes a leak of around 600 bytes on every connection on my systems (Linux, x64 and PPC). (Debugging done under contract for Accedian Networks) Fixes #246 Guenter Knauf (29 Jun 2012) - Small NetWare makefile tweak. - Some small Win32 makefile fixes. Daniel Stenberg (19 Jun 2012) - libssh2_userauth_publickey_fromfile_ex.3: mention publickey == NULL - comp_method_zlib_decomp: handle Z_BUF_ERROR when inflating When using libssh2 to perform an SFTP file transfer from the "JSCAPE MFT Server" (http://www.jscape.com) the transfer failed. The default JSCAPE configuration is to enforce zlib compression on SSH2 sessions so the session was compressed. The relevant part of the debug trace contained: [libssh2] 1.052750 Transport: unhandled zlib error -5 [libssh2] 1.052750 Failure Event: -29 - decompression failure The trace comes from comp_method_zlib_decomp() in comp.c. The "unhandled zlib error -5" is the status returned from the zlib function inflate(). The -5 status corresponds to "Z_BUF_ERROR". The inflate() function takes a pointer to a z_stream structure and "inflates" (decompresses) as much as it can. The relevant fields of the z_stream structure are: next_in - pointer to the input buffer containing compressed data avail_in - the number of bytes available at next_in next_out - pointer to the output buffer to be filled with uncompressed data avail_out - how much space available at next_out To decompress data you set up a z_stream struct with the relevant fields filled in and pass it to inflate(). On return the fields will have been updated so next_in and avail_in show how much compressed data is yet to be processed and next_out and avail_out show how much space is left in the output buffer. If the supplied output buffer is too small then on return there will be compressed data yet to be processed (avail_in != 0) and inflate() will return Z_OK. In this case the output buffer must be grown, avail_out updated and inflate() called again. If the supplied output buffer was big enough then on return the compressed data will have been exhausted (avail_in == 0) and inflate() will return Z_OK, so the data has all been uncompressed. There is a corner case where inflate() makes no progress. That is, there may be unprocessed compressed data and space available in the output buffer and yet the function does nothing. In this case inflate() will return Z_BUF_ERROR. From the zlib documentation and the source code it is not clear under what circumstances this happens. It could be that it needs to write multiple bytes (all in one go) from its internal state to the output buffer before processing the next chunk of input but but can't because there is not enough space (though my guesses as to the cause are not really relevant). Recovery from Z_BUF_ERROR is pretty simple - just grow the output buffer, update avail_out and call inflate() again. The comp_method_zlib_decomp() function does not handle the case when inflate() returns Z_BUF_ERROR. It treats it as a non-recoverable error and basically aborts the session. Fixes #240 Guenter Knauf (12 Jun 2012) - MinGW makefile tweaks. Use GNU tools when compiling on Linux. Fixed dist and dev targets. - NetWare makefile tweaks. Changed to use Windows commandline tools instead of GNU tools when compiling on Windows. Fixed dist and dev targets. Enabled nlmconv error for unresolved symbols. Daniel Stenberg (11 Jun 2012) - Revert "config.rpath: generated file, no need to keep in git" This reverts commit 1ac7bd09cc685755577fb2c8829adcd081e7ab3c. This file still used by lib/*m4 functions so we need to keep the file around. - BINDINGS: added PySsh2, a Python-ctypes binding Guenter Knauf (8 Jun 2012) - Fixed MinGW debug build. Daniel Stenberg (5 Jun 2012) - BINDINGS: Added the Cocoa/Objective-C one ... and sorted the bindings after the languages, alphabetically Reported by: Mike Abdullah - BINDINGS: document the bindings we know of Guenter Knauf (4 Jun 2012) - Fixed LIBSSH2_INT64_T_FORMAT macro. Usually a format macro should hold the whole format, otherwise it should be named a prefix. Also fixed usage of this macro in scp.c for a signed var where it was used as prefix for unsigned. - Removed obsolete define from makefiles. - Renamed NetWare makefiles. - Renamed NetWare makefiles. - Synced MinGW makefiles with 56c64a6..39e438f. Also synced MinGW test makefile with b092696..f8cb874. Peter Stuge (30 May 2012) - Revert "sftp: Don't send attrs.permissions on read-only SSH_FXP_OPEN" This reverts commit 04e79e0c798674a0796be8a55f63dd92e6877790. - sftp: Don't send attrs.permissions on read-only SSH_FXP_OPEN This works around a protocol violation in the ProFTPD 1.3.4 mod_sftp server, as reported by Will Cosgrove in: http://libssh2.org/mail/libssh2-devel-archive-2012-05/0079.shtml Based on a suggested fix by TJ Saunders in: http://libssh2.org/mail/libssh2-devel-archive-2012-05/0104.shtml Guenter Knauf (28 May 2012) - Try to detect OpenSSL build type automatically. Also fixed recently added libgdi32 linkage which is only required when OpenSSL libs are linked statically. Daniel Stenberg (25 May 2012) - config.rpath: generated file, no need to keep in git Guenter Knauf (22 May 2012) - Updated dependency libary versions. Daniel Stenberg (18 May 2012) - 1.4.3: towards the future Version 1.4.2 (18 May 2012) Daniel Stenberg (18 May 2012) - RELEASE-NOTES: synced with 92a9f952794 Alexander Lamaison (15 May 2012) - win32/libssh2_config.h: Remove hardcoded #define LIBSSH2_HAVE_ZLIB. Rationale: Everything else in this file states a fact about the win32 platform that is unconditional for that platform. There is nothing unconditional about the presence of zlib. It is neither included with Windows nor with the platform SDK. Therefore, this is not an appropriate place to assert its presence. Especially as, once asserted, it cannot be overridden using a compiler flag. In contrast, if it is omitted, then it can easily be reasserted by adding a compiler flag defining LIBSSH2_HAVE_ZLIB. Daniel Stenberg (14 May 2012) - RELEASE-NOTES: synced with 69a3354467c - _libssh2_packet_add: SSH_MSG_CHANNEL_REQUEST default to want_reply RFC4254 says the default 'want_reply' is TRUE but the code defaulted to FALSE. Now changed. Fixes #233 - gettimeofday: no need for a replacement under cygwin Fixes #224 Alexander Lamaison (13 May 2012) - Prevent sftp_packet_read accessing freed memory. sftp_packet_add takes ownership of the packet passed to it and (now that we handle zombies) might free the packet. sftp_packet_read uses the packet type byte as its return code but by this point sftp_packet_add might have freed it. This change fixes the problem by caching the packet type before calling sftp_packet_add. I don't understand why sftp_packet_read uses the packet type as its return code. A future change might get rid of this entirely. Daniel Stenberg (12 May 2012) - sftp_packet_flush: flush zombies too As this function is called when the SFTP session is closed, it needs to also kill all zombies left in the SFTP session to avoid leaking memory just in case some zombie would still be in there. - sftp_packetlist_flush: zombies must not have responses already When flushing the packetlist, we must only add the request as a zombie if no response has already been received. Otherwise we could wrongly make it a zombie even though the response was already received and then we'd get a zombie stuck there "forever"... - sftp_read: on EOF remove packet before flush Since the sftp_packetlist_flush() function will move all the existing FXP_READ requests in this handle to the zombie list we must first remove this just received packet as it is clearly not a zombie. - sftp_packet_require: sftp_packet_read() returning 0 is not an error Exactly as the comment in the code said, checking the return code from sftp_packet_read() with <= was wrong and it should be < 0. With the new filtering on incoming packets that are "zombies" we can now see this getting zero returned. - sftp_packetlist_flush: only make it zombie if it was sent The list of outgoing packets may also contain packets that never were sent off and we better not make them zombies too. - [Alexander Lamaison brought this change] Mark outstanding read requests after EOF as zombies. In order to be fast, sftp_read sends many read requests at once. With a small file, this can mean that when EOF is received back, many of these requests are still outstanding. Responses arriving after we close the file and abandon the file handle are queued in the SFTP packet queue and never collected. This causes transfer speed to drop as a progressively longer queue must be searched for every packet. This change introduces a zombie request-ID list in the SFTP session that is used to recognise these outstanding requests and prevent them being added to the queue. Peter Stuge (23 Apr 2012) - [Rafael Kitover brought this change] Update win32/GNUmakefile to use OpenSSL 1.0.1a libcrypto on win32 now depends on gdi32.dll, so move the OpenSSL LDLIBS block to before the compiler definitions, so that libcrypto gets added first, and then add -lgdi32 into the following common LDLIBS for gcc. Guenter Knauf (23 Apr 2012) - Changed 'Requires' to 'Requires.private'. Only static builds need to link against the crypto libs. - Fixed 'Requires:' names. The 'Requires:' line lists the names of the .pc files. - Added 'Requires:' line to libssh2.pc. This is necessary so that other libs which lookup libssh2 info via pkg-config can add the right crypto lib dependencies. - Updated dependency lib versions. Peter Stuge (18 Apr 2012) - configure.ac: Add option to disable build of the example applications Examples are built by default. Any of the following options on the configure command line will skip building them: --disable-examples-build --enable-examples-build=no --enable-examples-build=false - userauth.c: fread() from public key file to correctly detect any errors If the filename parameter for file_read_publickey() was the name of a directory instead of a file then libssh2 would spin trying to fgetc() from the FILE * for the opened directory when trying to determine the length of the encoded public key, since fgetc() can't report errors. Use fread() instead to correctly detect this error condition along with many others. This fixes the problem reported in http://www.libssh2.org/mail/libssh2-devel-archive-2012-04/0021.shtml Reported-by: Oleksiy Zagorskyi - Return LIBSSH2_ERROR_SOCKET_DISCONNECT on EOF when reading banner Guenter Knauf (17 Apr 2012) - Fixed copyright year. - Updated dependency lib versions in static makefiles. Daniel Stenberg (6 Apr 2012) - version: bump to 1.4.2 We're on the 1.4.2 track now (at least) Version 1.4.1 (4 Apr 2012) Daniel Stenberg (4 Apr 2012) - RELEASE-NOTES: updated for 1.4.1 release - always do "forced" window updates When calling _libssh2_channel_receive_window_adjust() internally, we now always use the 'force' option to prevent libssh2 to avoid sending the update if the update isn't big enough. It isn't fully analyzed but we have seen corner cases which made a necessary window update not get send due to this and then the other side doesn't send data our side then sits waiting for forever. - channel_read: force window adjusts! if there's not enough room to receive the data that's being requested, the window adjustment needs to be sent to the remote and thus the force option has to be used. _libssh2_channel_receive_window_adjust() would otherwise "queue" small window adjustments for a later packet but that is really terribly for the small buffer read that for example is the final little piece of a very large file as then there is no logical next packet! Reported by: Armen Babakhanian Bug: http://www.libssh2.org/mail/libssh2-devel-archive-2012-03/0130.shtml - [Paul Howarth brought this change] aes: the init function fails when OpenSSL has AES support The internal init function only worked fine when the configure script didn't detect the OpenSSL AES_CTR function! Bug: http://www.libssh2.org/mail/libssh2-devel-archive-2012-03/0111.shtml Reported by: Paul Howarth - [Matthew Booth brought this change] transport_send: Finish in-progress key exchange before sending data _libssh2_channel_write() first reads outstanding packets before writing new data. If it reads a key exchange request, it will immediately start key re-exchange, which will require sending a response. If the output socket is full, this will result in a return from _libssh2_transport_read() of LIBSSH2_ERROR_EAGAIN. In order not to block a write because there is no data to read, this error is explicitly ignored and the code continues marshalling a packet for sending. When it is sent, the remote end immediately drops the connection because it was expecting a continuation of the key exchange, but got a data packet. This change adds the same check for key exchange to _libssh2_transport_send() that is in _libssh2_transport_read(). This ensures that key exchange is completed before any data packet is sent. - channel_write: acknowledge transport errors When draining data off the socket with _libssh2_transport_read() (which in turn has to be done so that we can be sure to have read any possible window-increasing packets), this code previously ignored errors which could lead to nasty loops. Now all error codes except EAGAIN will cause the error to be returned at once. Bug: http://www.libssh2.org/mail/libssh2-devel-archive-2012-03/0068.shtml Reported by: Matthew Booth - [Steven Dake brought this change] In examples/x11.c, Make sure sizeof passed to read operation is correct sizeof(buf) expands to 8 or 4 (since its a pointer). This variable may have been static in the past, leading to this error. Signed-off-by: Steven Dake - [Steven Dake brought this change] Fix suspicious sizeof usage in examples/x11.c In the x11 example, sizeof(buf) = 8UL (on x86_64), when this should probably represent the buffer size available. I am not sure how to test that this change is actually correct, however. Signed-off-by: Steven Dake - sftp_packet_read: follow-up fix for EAGAIN/window adjust The commit in 7194a9bd7ba45 wasn't complete. This change makes sure variables are initialized properly before used in the EAGAIN and window adjust cases. - sftp_packet_add: use named error code instead of number - sftp_packet_add: verify the packet before accepting it In order to bail out as quickly as possible when things are wrong and out of sync, make sure the SFTP message is one we understand. - SFTP: preserve the original error code more Lots of places in the code translated the original error into the more generic LIBSSH2_ERROR_SOCKET_TIMEOUT but this turns out to distort the original error reason a lot and makes tracking down the real origin of a problem really hard. This change makes the original error code be preserved to a larger extent when return up to the parent function. - sftp_packet_read: adjust window size as necessary Commit 03ca9020756 tried to simplify the window sizing logic but broke SFTP readdir as there was no window sizing code left there so large directory listings no longer worked. This change introduces window sizing logic to the sftp_packet_read() function so that it now tells the remote about the local size having a window size that suffice when it is about to ask for directory data. Bug: http://www.libssh2.org/mail/libssh2-devel-archive-2012-03/0069.shtml Reported by: Eric - [Steven Dake brought this change] Tell C compiler we don't care about return code of libssh2_init The call of libssh2_init returns a return code, but nothing could be done within the _libssh2_init_if_needed execution path. Signed-off-by: Steven Dake - [Steven Dake brought this change] Add comment indicating a resource leak is not really a resource leak While possibly obvious to those investigating the code, coverity complains about this out of scope leak. Signed-off-by: Steven Dake - [Steven Dake brought this change] Use safer snprintf rather then sprintf in scp_send() Signed-off-by: Steven Dake - [Steven Dake brought this change] Use safer snprintf rather then sprintf in scp_recv() While the buffer is indeed allocated to a safe length, better safe then sorry. Signed-off-by: Steven Dake - [Steven Dake brought this change] use snprintf in knownhost_writeline() rather then sprintf Although the function checks the length, if the code was in error, there could potentially be a buffer overrun with the use of sprintf. Instead replace with snprintf. Signed-off-by: Steven Dake - [Steven Dake brought this change] Add tracing to print packets left on session at libssh2_session_free Signed-off-by: Steven Dake Peter Stuge (2 Mar 2012) - Define and use LIBSSH2_INVALID_SOCKET instead of INVALID_SOCKET INVALID_SOCKET is a special value in Windows representing a non-valid socket identifier. We were #defining this to -1 on non-Windows platforms, causing unneccessary namespace pollution. Let's have our own identifier instead. Thanks to Matt Lawson for pointing this out. - nw/Makefile.netware: Fix project name typo to avoid needless confusion - example/x11: Set raw terminal mode manually instead of with cfmakeraw() OpenSolaris has no cfmakeraw() so to make the example more portable we simply do the equivalent operations on struct termios ourselves. Thanks to Tom Weber for reporting this problem, and finding a solution. Daniel Stenberg (17 Feb 2012) - sftp_write: cannot return acked data *and* EAGAIN Whenever we have acked data and is about to call a function that *MAY* return EAGAIN we must return the number now and wait to get called again. Our API only allows data *or* EAGAIN and we must never try to get both. Peter Stuge (13 Feb 2012) - example/x11: Build only when sys/un.h is found by configure The example can't be built on systems without AF_UNIX sockets. Daniel Stenberg (10 Feb 2012) - [Alexander Lamaison brought this change] Simplified sftp_read. Removed the total_read variable that originally must have tracked how much data had been written to the buffer. With non-blocking reads, we must return straight away once we have read data into the buffer so this variable served not purpose. I think it was still hanging around in case the initial processing of 'leftover' data meant we wrote to the buffer but this case, like the others, must return immediately. Now that it does, the last remaining need for the variable is gone. - [Alexander Lamaison brought this change] Cleaned up sftp_read and added more explanation. Replaced the gotos which were implementing the state machine with a switch statement which makes the states more explicit. - sftp_read: avoid data *and* EAGAIN Whenever we have data and is about to call a function that *MAY* return EAGAIN we must return the data now and wait to get called again. Our API only allows data *or* EAGAIN and we must never try to get both. Peter Stuge (2 Feb 2012) - Add a tcpip-forward example which demonstrates remote port forwarding - libssh2.h: Add missing prototype for libssh2_session_banner_set() - example/subsystem_netconf.c: Return error when read buffer is too small Also remove a little redundancy in the read loop condition. - example/subsystem_netconf.c: Add a missing newline in an error message - Fix undefined reference to _libssh_error in libgcrypt backend Commit 209de22299b4b58e582891dfba70f57e1e0492db introduced a function call to a non-existing function, and since then the libgcrypt backend has not been buildable. Version 1.4.0 (31 Jan 2012) Daniel Stenberg (31 Jan 2012) - RELEASE-NOTES: synced with 6bd584d29 for 1.4.0 - s/1.3.1/1.4.0 We're bumping the minor number - [Jernej Kovacic brought this change] libssh2_session_supported_algs: fix compiler warning - [Jernej Kovacic brought this change] session_supported_algs docs: added an example - [Gellule Xg brought this change] sftp-seek: clear EOF flag Set the EOF flag to False when calling seek64 to be able to get some data back on a following read - [Peter Krempa brought this change] userauth: Provide more informations if ssh pub key extraction fails If the function that extracts/computes the public key from a private key fails the errors it reports were masked by the function calling it. This patch modifies the key extraction function to return errors using _libssh_error() function. The error messages are tweaked to contain reference to the failed operaton in addition to the reason. * AUTHORS: - add my name * libgcrypt.c: _libssh2_pub_priv_keyfile(): - return a more verbose error using _libssh2_error() func. * openssl.c: - modify call graph of _libssh2_pub_priv_keyfile() to use _libssh2_error for error reporting(); * userauth.c: - tweak functions calling _libssh2_pub_priv_keyfile() not to shadow error messages - TODO: remove issues we (sort of) did already - ssh2_exec: skip error outputs for EAGAIN Since the example uses non-blocking mode, it will just flood the output with this "nonsense" error. Guenter Knauf (30 Nov 2011) - Some NetWare makefile tweaks. Daniel Stenberg (18 Nov 2011) - LIBSSH2_SFTP_PACKET_MAXLEN: increase to 80000 Some SFTP servers send SFTP packets larger than 40000. Since the limit is only present to avoid insane sizes anyway, we can easily bump it. The define was formerly in the public header libssh2_sftp.h but served no external purpose and was moved into the source dir. Bug: http://www.libssh2.org/mail/libssh2-devel-archive-2011-11/0004.shtml Reported by: Michael Harris Alexander Lamaison (18 Nov 2011) - [Peter Krempa brought this change] knownhost_check(): Don't dereference ext if NULL is passed Documentation for libssh2_knownhost_checkp() and related functions states that the last argument is filled with data if non-NULL. "knownhost if set to non-NULL, it must be a pointer to a 'struct libssh2_knownhost' pointer that gets filled in to point to info about a known host that matches or partially matches." In this function ext is dereferenced even if set to NULL, causing segfault in applications not needing the extra data. Daniel Stenberg (11 Nov 2011) - [Peter Krempa brought this change] knownhost_add: Avoid dereferencing uninitialized memory on error path. In function knownhost_add, memory is alocated for a new entry. If normal alocation is used, memory is not initialized to 0 right after, but a check is done to verify if correct key type is passed. This test is done BEFORE setting the memory to null, and on the error path function free_host() is called, that tries to dereference unititialized memory, resulting into a glibc abort(). * knownhost.c - knownhost_add(): - move typemask check before alloc - windows build: add define to avoid compiler warning A recent mingw compiler has started to complain on "#warning Please include winsock2.h before windows.h" unless the magic define is set first. Reported by: Vincent Torri Bug: http://www.libssh2.org/mail/libssh2-devel-archive-2011-10/0064.shtml Henrik Nordstrom (31 Oct 2011) - [Vincent Torri brought this change] Correct Windows include file name case, simplifying cross-compilation When cross compiling to Windows, libssh2.h include Windows header files with upper case filenames : BaseTsd.h and WinSock2.h. These files have lowercase names with mingw-w64 (iirc, it's the same with mingw). And as on Windows, being lowercase or uppercase does not matter. Daniel Stenberg (25 Oct 2011) - [Jernej Kovacic brought this change] libssh2_session_supported_algs: added - [Kamil Dudka brought this change] example/sftp_RW_nonblock: do not ignore LIBSSH2_ERROR_EAGAIN Bug: https://bugzilla.redhat.com/745420 Peter Stuge (5 Oct 2011) - example/ssh2_agent: Print host key fingerprint before authentication Also moves the comment about not being authenticated to before the agent authentication takes place, so that it better matches the code. Daniel Stenberg (29 Sep 2011) - OpenSSL EVP: fix threaded use of structs Make sure we don't clear or reset static structs after first init so that they work fine even when used from multiple threads. Init the structs in the global init. Help and assistance by: John Engstrom Fixes #229 (again) - openssl: don't init static structs differently make_ctr_evp() is changed to take a struct pointer, and then each _libssh2_EVP_aes_[keylen]_ctr function is made to pass in their own static struct Reported by: John Engstrom Fixes #229 Guenter Knauf (27 Sep 2011) - Removed obsolete include path. Daniel Stenberg (21 Sep 2011) - read_state: clear the state variable better Set read_state back to idle before trying to send anything so that if the state somehow is wrongly set. Also, avoid such a case of confusion by resetting the read_state when an sftp handle is closed. - sftp_read: remove leftover fprintf Reported by: Alexander Lamaison - sftp.h: fix the #ifdef to prevent multiple inclusions - sftp_read: use a state variable to avoid bad writes When a channel_write call has gotten an EAGAIN back, we try harder to continue the same write in the subsequent invoke. - window_size: explicit adjustments only Removed the automatic window_size adjustments from _libssh2_channel_read() and instead all channel readers must now make sure to enlarge the window sizes properly themselves. libssh2_channel_read_ex() - the public function, now grows the window size according to the requested buffer size. Applications can still opt to grow the window more on demand. Larger windows tend to give higher performance. sftp_read() now uses the read-ahead logic to figure out a window_size. - libssh2.h: bump the default window size to 256K - libssh2_userauth_keyboard_interactive.3: fix man warning It seemed to occur due to the excessive line length - [Mikhail Gusarov brought this change] Add missing .gitignore entries - [Mikhail Gusarov brought this change] Add manpage syntax checker to 'check' target In virtually every libssh2 release Debian's lintian catches syntax errors in manpages. Prevent it by checking manpages as a part of testsuite. - libssh2_banner_set.3: fix nroff syntax mistake Guenter Knauf (10 Sep 2011) - Use predefined resource compiler macro. - Added casts to silent compiler warnings. - Fixed uint64_t printf. - Fixed macro function signatures. - NetWare makefile tweaks. - Removed unused var. - Added 2 samples not mentioned. - Dont build x11 sample with MinGW. - Fixed executable file description. - Removed unused var. - Kill stupid gcc 3.x uninitialized warning. - Build all examples. - More MinGW makefile tweaks. Renamed *.mingw makefiles to GNUmakefile since GNU make picks these up automatically, and therefore win32/Makefile removed. - Removed forgotten WINSOCK_VERSION defines. Daniel Stenberg (9 Sep 2011) - libssh2_session_startup(3) => libssh2_session_handshake(3) Propagate for the current function in docs and examples. libssh2_session_startup() is deprecated. - libssh2_banner_set => libssh2_session_banner_get Marked the old function as deprecated. Added the new name in the correct name space with the same arguments and functionality. - new function: libssh2_session_banner_get Returns the banner from the server handshake Fixes #226 - libssh2.h: bump version to 1.4.0 for new function(s) - remove embedded CVS/svn tags - [liuzl brought this change] API add:libssh2_sftp_get_channel Return the channel of sftp, then caller can control the channel's behavior. Signed-off-by: liuzl - _libssh2_channel_read: react on errors from receive_window_adjust Previously the function would ignore all errors except for EAGAIN. - sftp_read: extend and clarify the documentation - sftp_read: cap the read ahead maximum amount Now we only go up to LIBSSH2_CHANNEL_WINDOW_DEFAULT*30 bytes SFTP read ahead, which currently equals 64K*30 == 1966080 bytes. - _libssh2_channel_read: fix non-blocking window adjusting If EAGAIN is returned when adjusting the receive window, we must not read from the transport directly until we've finished the adjusting. Guenter Knauf (8 Sep 2011) - Fix for systems which need sys/select.h. - The files were not gone but renamed ... Daniel Stenberg (6 Sep 2011) - sftp_read: added documenting comment Taken from some recent email conversations I added some descriptions of the logic in sftp_read() to aid readers. - 1.3.1: start the work Version 1.3.0 (6 Sep 2011) Daniel Stenberg (6 Sep 2011) - Makefile.am: the Makefile.win32 files are gone - RELEASE-NOTES: updated for 1.3.0 - sftp_read: a short read is not end of file A returned READ packet that is short will now only reduce the offset. This is a temporary fix as it is slightly better than the previous approach but still not very good. - [liuzl brought this change] _libssh2_packet_add: adjust window size when truncating When receiving more data than what the window size allows on a particular channel, make sure that the window size is adjusted in that case too. Previously it would only adjust the window in the non-error case. Guenter Knauf (29 Aug 2011) - Silent compiler warning with MinGW64. - Fixed link to native Win32 awk tool. - Renamed MinGW makefiles. - Some MinGW makefile tweaks. Enable build without GNU tools and with MinGW64 compiler. - Fixed aes_ctr_do_cipher() signature. Daniel Stenberg (26 Aug 2011) - [liuzl brought this change] libssh2_sftp_seek64: flush packetlist and buffered data When seeking to a new position, flush the packetlist and buffered data to prevent already received or pending data to wrongly get used when sftp-reading from the new offset within the file. - sftp_read: advance offset correctly for buffered copies In the case where a read packet has been received from the server, but the entire contents couldn't be copied to the user-buffer, the data is instead buffered and copied to the user's buffer in the next invocation of sftp_read(). When that "extra" copy is made, the 'offset' pointer was not advanced accordingly. The biggest impact of this flaw was that the 'already' variable at the top of the function that figures out how much data "ahead" that has already been asked for would slowly go more and more out of sync, which could lead to the file not being read all the way to the end. This problem was most noticable in cases where the application would only try to read the exact file size amount, like curl does. In the examples libssh2 provides the sftp read function is most often called with a fixed size large buffer and then the bug would not appear as easily. This bug was introduced in the SFTP rewrite in 1.2.8. Bug: http://curl.haxx.se/mail/lib-2011-08/0305.html http://www.libssh2.org/mail/libssh2-devel-archive-2011-08/0085.shtml - wrap some long lines < 80 columns - LIBSSH2_RECV: fix typo, use the RECV_FD macro - subsystem_netconf.c: fix compiler warnings - [Henrik Nordstrom brought this change] Custom callbacks for performing low level socket I/O - version bump: start working towards 1.3.0 Version 1.2.9 (16 Aug 2011) Daniel Stenberg (16 Aug 2011) - RELEASE-NOTES: synced with 95d69d3a81261 - [Henrik Nordstrom brought this change] Document prototypes for macro defined functions - [Henrik Nordstrom brought this change] Avoid reuse after free when closing X11 channels - _libssh2_channel_write: handle window_size == 0 better When about to send data on the channel and the window size is 0, we must not just return 0 if the transport_read() function returned EAGAIN as it then causes a busy-loop. Bug: http://libssh2.org/mail/libssh2-devel-archive-2011-08/0011.shtml - gettimeofday: fix name space pollution For systems without its own gettimeofday() implementation, we still must not provide one outside our namespace. Reported by: Bill Segall Dan Fandrich (5 Aug 2011) - libssh2.pc.in: Fixed spelling in pkgconfig file Peter Stuge (17 Jul 2011) - example/subsystem_netconf.c: Add missing #include - example/subsystem_netconf.c: Discard ]]>]]> and return only XML response - example/subsystem_netconf.c: Fix uninitialized variable bug - example: Add subsystem_netconf.c This example demonstrates how to use libssh2 to send a request to the NETCONF subsystem available e.g. in JunOS. See also http://tools.ietf.org/html/draft-ietf-netconf-ssh-06 Daniel Stenberg (16 Jul 2011) - man page cleanups: non-existing functions need no man pages - libssh2_new_host_entry.3: removed This is just junk leftovers. - userauth_keyboard_interactive: fix buffer overflow Partly reverse 566894494b4972ae12 which was simplifying the code far too much and ended up overflowing a buffer within the LIBSSH2_SESSION struct. Back to allocating the buffer properly like it used to do. Bug: http://www.libssh2.org/mail/libssh2-devel-archive-2011-06/0032.shtml Reported by: Alfred Gebert - keyboard-interactive man page: cleaned up - [Alfred Gebert brought this change] _libssh2_recv(): handle ENOENT error as EAGAIN A sftp session failed with error "failure establishing ssh session" on Solaris and HP-UX. Sometimes the first recv() function call sets errno to ENOENT. In the man pages for recv of Solaris and HP-UX the error ENOENT is not documented. I tested Solaris SPARC and x86, HP-UX i64, AIX, Windows and Linux. - agent_list_identities: fix out of scope access An auto variable out of scope was being referenced and used. fixes #220 - _libssh2_wait_socket: fix timeouts for poll() uses - windows: inclusion fix include winsock2.h for all windows compilers - keyb-interactive: add the fixed buffer Belongs to commit 5668944 - code cleanup: don't use C99/c++ comments We aim for C89 compliance - keyb-interactive: allow zero length fields Allow zero length fields so they don't cause malloc(0) calls Avoid free()ing NULL pointers Avoid a malloc of a fixed 5 byte buffer. - libssh2_channel_process_startup.3: clean up Remove the references to the macro-fied shortcuts as they have their own individual man pages. Made the prototype different and more readable. - man page: fix .BR lines We don't use \fI etc on .BR lines - userauth_keyboard_interactive: skip code on zero length auth - libssh2_channel_forward_accept.3: mention how to get error Since this returns a pointer, libssh2_session_last_errno() must be used to get the actual error code and it wasn't that clear before. - timeout docs: mention they're added in 1.2.9 - sftp_write_sliding.c: indent fix Use the standard indenting and removed CVS leftover comment - [zl liu brought this change] sftp_write_sliding: send the complete file When reaching the end of file there can still be data left not sent. - [Douglas Masterson brought this change] session_startup: init state properly libssh2_session_startup() didn't set the state correctly so it could get confused. Fixes #218 - timeout: added man pages - BLOCK_ADJUST_ERRNO: move rc to right level We can't declare the variable within the block and use it in the final do-while() expression to be properly portable C89. - [Matt Lilley brought this change] adds a timeout to blocking calls Fixes bug #160 as per Daniel's suggestion Adds libssh2_session_set_timeout() and libssh2_session_get_timeout() - SCP: fix incorrect error code After an error occurs in libssh2_scp_recv() or libssh2_scp_send(), the function libssh2_session_last_error() would return LIBSSH2_ERROR_SOCKET_NONE on error. Bug: http://trac.libssh2.org/ticket/216 Patch by: "littlesavage" Fixes #216 Guenter Knauf (19 Apr 2011) - Updated default (recommended) dependency versions. Daniel Stenberg (17 Apr 2011) - libssh2_session_block_directions: fix mistake The last LIBSSH2_SESSION_BLOCK_INBOUND should be LIBSSH2_SESSION_BLOCK_OUTBOUND And I shortened the short description Reported by: "drswinghead" - msvcproj: added libs and debug stuff Added libraries needed to link whether using openssl dynamically or statically Added LIBSSH2DEBUG define to debug versions to enable tracing URL: http://trac.libssh2.org/ticket/215 Patch by: Mark Smith - sftp_write: clean offsets on error When an error has occurred on FXP_WRITE, we must make sure that the offset, sent offset and acked counter are reset properly. - example/.gitignore: ignore built binaries - sftp_write: flush the packetlist on error When an error occurs during write, flush the entire list of pending outgoing SFTP packets. - keepalive: add first basic man pages Someone on IRC pointed out that we don't have these documented so I wrote up a first set based on the information in the wiki: http://trac.libssh2.org/wiki/KeepAlive - scp_write_nonblock.c: remove pointless check libssh2_channel_write() cannot return a value that is larger than the input length value Mikhail Gusarov (9 Apr 2011) - s/\.NF/.nf/ to fix wrong macro name caught by man --warnings Daniel Stenberg (6 Apr 2011) - version: bump to 1.2.9_dev Also update the copyright year range to include 2011 - configure: fix $VERSION Stop using the $VERSION variable as it seems to be magically used by autoconfig itself and thus gets set to the value set in AC_INIT() without us wanting that. $LIBSSH2VER is now the libssh2 version as detected. Reported by: Paul Howarth Bug: http://www.libssh2.org/mail/libssh2-devel-archive-2011-04/0008.shtml - maketgz: use git2news.pl by the correct name Version 1.2.8 (4 Apr 2011) Daniel Stenberg (4 Apr 2011) - RELEASE-NOTES: synced with fabf1a45ee - NEWS: auto-generated from git Starting now, the NEWS file is generated from git using the git2news.pl script. This makes it always accurate and up-to-date, even for daily snapshots etc. - sftp_write: handle FXP_WRITE errors When an sftp server returns an error back on write, make sure the function bails out and returns the proper error. - configure: stop using the deprecated AM_INIT_AUTOMAKE syntax Alexander Lamaison (13 Mar 2011) - Support unlimited number of host names in a single line of the known_hosts file. Previously the code assumed either a single host name or a hostname,ip-address pair. However, according to the spec [1], there can be any number of comma separated host names or IP addresses. [1] http://www.openbsd.org/cgi-bin/man.cgi?query=sshd&sektion=8 Daniel Stenberg (26 Feb 2011) - libssh2_knownhost_readfile.3: clarify return value This function returns the number of parsed hosts on success, not just zero as previously documented. Peter Stuge (26 Feb 2011) - Don't save allocated packet size until it has actually been allocated The allocated packet size is internal state which needs to match reality in order to avoid problems. This commit fixes #211. Daniel Stenberg (21 Feb 2011) - [Alfred Gebert brought this change] session_startup: manage server data before server identification Fix the bug that libssh2 could not connect if the sftp server sends data before sending the version string. http://tools.ietf.org/html/rfc4253#section-4.2 "The server MAY send other lines of data before sending the version string. Each line SHOULD be terminated by a Carriage Return and Line Feed. Such lines MUST NOT begin with "SSH-", and SHOULD be encoded in ISO-10646 UTF-8 [RFC3629] (language is not specified). Clients MUST be able to process such lines." - [Alfred Gebert brought this change] fullpacket: decompression only after init The buffer for the decompression (remote.comp_abstract) is initialised in time when it is needed. With this fix decompression is disabled when the buffer (remote.comp_abstract) is not initialised. Bug: http://trac.libssh2.org/ticket/200 - _libssh2_channel_read: store last error When the transport layer returns EAGAIN this function didn't call _libssh2_error() which made the last_error not get set. - sftp_write: clarified the comment header - sftp_read: avoid wrapping counter to insanity As pointed out in bug #206, if a second invoke of libssh2_sftp_read() would shrink the buffer size, libssh2 would go nuts and send out read requests like crazy. This was due to an unsigned variable turning "negative" by some wrong math, and that value would be the amount of data attempt to pre-buffer! Bug: http://trac.libssh2.org/ticket/206 - sftp_packet_read: use 32bit variables for 32bit data - libssh2_sftp_stat_ex.3: cleaned up, extended Removed the macros from it as they have their own man pages. Added the LIBSSH2_SFTP_ATTRIBUTES struct in here for easier reference. - sftp_readdir: return error if buffer is too small If asked to read data into a buffer and the buffer is too small to hold the data, this function now returns an error instead of as previously just copy as much as fits. - sftp_symlink: return error if receive buffer too small and clean up some variable type mismatches Discussion: http://www.libssh2.org/mail/libssh2-devel-archive-2011-01/0001.shtml - docs: clarify what happens with a too small buffer This flaw is subject to change, but I figured it might be valuable to users of existing code to know how it works. - channel_request_pty_size: fix reqPTY_state The state variable isn't properly set so every other call to the function fails! Bug: http://libssh2.org/mail/libssh2-devel-archive-2010-12/0096.shtml Reported by: Steve Legg - data size: cleanup Fix 64bit warnings by using (s)size_t and dedicated uint32_t types more. - [Pierre Joye brought this change] ssize_t: proper typedef with MSVC compilers As discussed on the mailing list, it was wrong for win64 and using the VC-provided type is the safest approach instead of second- guessing which one it should be. Guenter Knauf (22 Dec 2010) - Updated OpenSSL version. - Expanded tabs to spaces. Peter Stuge (21 Dec 2010) - [Joey Degges brought this change] _libssh2_ntohu64: fix conversion from network bytes to uint64 Cast individual bytes to uint64 to avoid overflow in arithmetic. Daniel Stenberg (20 Dec 2010) - libssh2_userauth_list: language fix "faily" is not a good English word, and I also cleaned up some other minor mistakes - crypto: unify the generic functions Added crypto.h that is the unified header to include when using crypto functionality. It should be the only header that needs to adapt to the underlying crypto library in use. It provides the set of prototypes that are library agnostic. - [Mark Smith brought this change] userauth: derive publickey from private Pass a NULL pointer for the publickey parameter of libssh2_userauth_publickey_fromfile and libssh2_userauth_hostbased_fromfile functions. In this case, the functions recompute the public key from the private key file data. This is work done by Jean-Louis CHARTON , then adapted by Mark Smith and slightly edited further by me Daniel. WARNING: this does leave the feature NOT WORKING when libssh2 is built to use libgcrypt instead of OpenSSL simply due to lack of implementation. - ssh2_echo: Value stored to 'exitcode' is never read - _libssh2_packet_add: fix SSH_MSG_DEBUG weirdness I believe I may have caused this weird typo style error when I cleaned up this function a while ago. Corrected now. - uint32: more longs converted to proper types I also moved the MAC struct over to the mac.h header file and made sure that the users of that struct include that file. - SFTP: more types to uint32_t The 'num_names' field in the SSH_FXP_NAME response is an unsigned 32bit value so we make sure to treat it like that. - SFTP: request_ids are uint32_t I went over the code and made sure we use uint32_t all over for the request_id data. It is an unsigned 32bit value on the wire. - SFTP: store request_id separately in packets By using a new separate struct for incoming SFTP packets and not sharing the generic packet struct, we can get rid of an unused field and add a new one dedicated for holding the request_id for the incoming package. As sftp_packet_ask() is called fairly often, a "mere" integer comparison is MUCH faster than the previous memcmp() of (typically) 5 bytes. - libssh2_sftp_open_ex: man page extended and cleaned up I added the missing documentation for the 'flags' argument. - SFTP: unify the READ/WRITE chunk structs - SFTP: fix memory leaks Make sure that we cleanup remainders when the handle is closed and when the subsystem is shutdown. Existing flaw: if a single handle sends packets that haven't been replied to yet at the time when the handle is closed, those packets will arrive later and end up in the generic packet brigade queue and they will remain in there until flushed. They will use unnecessary memory, make things slower and they will ruin the SFTP handling if the request_id counter ever wraps (highly unlikely to every happen). - sftp_close_handle: packet list is generic Fix comment, simplify the loop logic - sftp_read: pipeline reads The SFTP read function now does transfers the same way the SFTP write function was made to recently: it creates a list of many outgoing FXP_READ packets that each asks for a small data chunk. The code then tries to keep sending read request while collecting the acks for the previous requests and returns the received data. - sftp_write: removed unused variable - _libssh2_channel_close: don't call transport read if disconnected The loop that waits for remote.close to get set may end up looping forever since session->socket_state gets set to LIBSSH2_SOCKET_DISCONNECTED by the packet_add() function called from the transport_read() function and after having been set to LIBSSH2_SOCKET_DISCONNECTED, the transport_read() function will only return 0. Bug: http://trac.libssh2.org/ticket/198 - libssh2_sftp_seek64: new man page Split off libssh2_sftp_seek64 from the libssh2_sftp_seek man page, and mentioned that we consider the latter deprecated. Also added a mention about the dangers of doing seek during writing or reading. - sftp_seek: fix The new SFTP write code caused a regression as the seek function no longer worked as it didn't set the write position properly. It should be noted that seeking is STRONGLY PROHIBITED during upload, as the upload magic uses two different offset positions and the multiple outstanding packets etc make them sensitive to change in the midst of operations. This functionality was just verified with the new example code sftp_append. This bug was filed as bug #202: Bug: http://trac.libssh2.org/ticket/202 - sftp_append: new example doing SFTP append - MAX_SFTP_OUTGOING_SIZE: 30000 I ran SFTP upload tests against localhost. It showed that to make the app reach really good speeds, I needed to do a little code tweak and change MAX_SFTP_OUTGOING_SIZE from 4000 to 30000. The tests I did before with the high latency tests didn't show any real difference whatever I had that size set to. This number is the size in bytes that libssh2 cuts off the large input buffer and sends off as an individual sftp packet. - sftp_write_sliding.c: new example This is an example that is very similar to sftp_write_nonblock.c, with the exception that this uses 1 - a larger upload buffer 2 - a sliding buffer mechnism to allow the app to keep sending lots of data to libssh2 without having to first drain the buffer. These are two key issues to make libssh2 SFTP uploads really perform well at this point in time. - cpp: s/#elsif/#elif This looks like a typo as #elsif is not really C... - _libssh2_channel_write: revert channel_write() use The attempts made to have _libssh2_channel_write() accept larger pieces of data and split up the data by itself into 32700 byte chunks and pass them on to channel_write() in a loop as a way to do faster operations on larger data blocks was a failed attempt. The reason why it is difficult: The API only allows EAGAIN or a length to be returned. When looping over multiple blocks to get sent, one block can get sent and the next might not. And yet: when transport_send() has returned EAGAIN we must not call it again with new data until it has returned OK on the existing data it is still working on. This makes it a mess and we do get a much easier job by simply returning the bytes or EAGAIN at once, as in the EAGAIN case we can assume that we will be called with the same arguments again and transport_send() will be happy. Unfortunately, I think we take a small performance hit by not being able to do this. - ssh2_echo: new example This is a new example snippet. The code is largely based on ssh2_exec, and is written by Tommy Lindgren. I edited it into C90 compliance and to conform to libssh2 indent style and some more. - send_existing: return after send_existing When a piece of data is sent from the send_existing() function we must make the parent function return afterwards. Otherwise we risk that the parent function tries to send more data and ends up getting an EGAIN for that more data and since it can only return one return code it doesn't return info for the successfully sent data. As this change is a regression I now added a larger comment explaining why it has to work like this. - _libssh2_channel_write: count resent data as written In the logic that resends data that was kept for that purpose due to a previous EAGAIN, the data was not counted as sent causing badness. Peter Stuge (13 Nov 2010) - Use fprintf(stderr, ) instead of write(2, ) for debugging - session/transport: Correctly handle when _libssh2_send() returns -EAGAIN - src/agent.c: Simplify _libssh2_send() error checking ever so slightly Daniel Stenberg (12 Nov 2010) - send/recv: use _libssh2_recv and _libssh2_send now Starting now, we unconditionally use the internal replacement functions for send() and recv() - creatively named _libssh2_recv() and _libssh2_send(). On errors, these functions return the negative 'errno' value instead of the traditional -1. This design allows systems that have no "natural" errno support to not have to invent it. It also means that no code outside of these two transfer functions should use the errno variable. - channel_write: move some logic to _libssh2_channel_write Some checks are better done in _libssh2_channel_write just once per write instead of in channel_write() since the looping will call the latter function multiple times per _libssh2_channel_write() invoke. - sftp_write: handle "left over" acked data The SFTP handle struct now buffers number of acked bytes that haven't yet been returned. The way this is used is as following: 1. sftp_write() gets called with a buffer of let say size 32000. We split 32000 into 8 smaller packets and send them off one by one. One of them gets acked before the function returns so 4000 is returned. 2. sftp_write() gets called again a short while after the previous one, now with a much smaller size passed in to the function. Lets say 8000. In the mean-time, all of the remaining packets from the previous call have been acked (7*4000 = 28000). This function then returns 8000 as all data passed in are already sent and it can't return any more than what it got passed in. But we have 28000 bytes acked. We now store the remaining 20000 in the handle->u.file.acked struct field to add up in the next call. 3. sftp_write() gets called again, and now there's a backlogged 20000 bytes to return as fine and that will get skipped from the beginning of the buffer that is passed in. - sftp_write: polished and simplified Removed unnecessary struct fields and state changes within the function. Made the loop that checks for ACKs only check chunks that were fully sent. - SCP: on failure, show the numerical error reason By calling libssh2_session_last_errno() - SFTP: provide the numerical error reason on failure - SCP: clean up failure treatment When SCP send or recv fails, it gets a special message from the server with a warning or error message included. We have no current API to expose that message but the foundation is there. Removed unnecessary use of session struct fields. - sftp_write: enlarge buffer to perform better - packets: code cleanup I added size checks in several places. I fixed the code flow to be easier to read in some places. I removed unnecessary zeroing of structs. I removed unused struct fields. - LIBSSH2_CALLBACK_MACERROR: clarify return code use - _libssh2_userauth_publickey: avoid shadowing - packet: avoid shadowing global symbols - sftp_readdir: avoid shadowing - shadowing: don't shadow the global compress - _libssh2_packet_add: turn ifs into a single switch - _libssh2_packet_add: check SSH_MSG_GLOBAL_REQUEST packet - _libssh2_packet_add: SSH_MSG_DEBUG length checks Verify lengths before using them. Read always_display from the correct index. Don't copy stuff around just to provide zero-termination of the strings. - _libssh2_packet_add: SSH_MSG_IGNORE skip memmove There's no promise of a zero termination of the data in the callback so no longer perform ugly operation in order to provide it. - _libssh2_packet_add: SSH_MSG_DISCONNECT length checks Verify lengths before trying to read data. - indent: break lines at 80 columns - SSH_MSG_CHANNEL_OPEN_FAILURE: used defined values We don't like magic numbers in the code. Now the acceptable failure codes sent in the SSH_MSG_CHANNEL_OPEN_FAILURE message are added as defined values in the private header file. - sftp_write: don't return EAGAIN if no EAGAIN was received This function now only returns EAGAIN if a lower layer actually returned EAGAIN to it. If nothing was acked and no EAGAIN was received, it will now instead return 0. - _libssh2_wait_socket: detect nothing-to-wait-for If _libssh2_wait_socket() gets called but there's no direction set to wait for, this causes a "hang". This code now detects this situation, set a 1 second timeout instead and outputs a debug output about it. - decomp: remove the free_dest argument Since the decompress function ALWAYS returns allocated memory we get a lot simpler code by removing the ability to return data unallocated. - decomp: cleaned off old compression stuff I cleared off legacy code from when the compression and decompression functions were a single unified function. Makes the code easier to read too. - [TJ Saunders brought this change] decomp: increase decompression buffer sizes - [TJ Saunders brought this change] zlib: Add debug tracing of zlib errors - sftp_packet_read: handle partial reads of the length field SFTP packets come as [32 bit length][payload] and the code didn't previously handle that the initial 32 bit field was read only partially when it was read. - [Jasmeet Bagga brought this change] kex_agree_hostkey: fix NULL pointer derefence While setting up the session, ssh tries to determine the type of encryption method it can use for the session. This requires looking at the keys offered by the remote host and comparing these with the methods supported by libssh2 (rsa & dss). To do this there is an iteration over the array containing the methods supported by libssh2. If there is no agreement on the type of encryption we come to the 3rd entry of the hostkeyp array. Here hostkeyp is valid but *hostkep is NULL. Thus when we dereference that in (*hostkeyp)->name there is a crash - _libssh2_transport_send: remove dead assignment 'data' isn't accessed beyond this point so there's no need to assign it. - scp_recv: remove dead assignment Instead of assigning a variable we won't read, we now use the more explicit (void) prefix. - sftp_write: removed superfluous assignment - bugfix: avoid use of uninitialized value - sftp_packet_require: propagate error codes better There were some chances that they would cause -1 to get returned by public functions and as we're hunting down all such occurances and since the underlying functions do return valuable information the code now passes back proper return codes better. - [Alfred Gebert brought this change] fix memory leaks (two times cipher_data) for each sftp session - libssh2_userauth_authenticated: make it work as documented The man page clearly says it returns 1 for "already authenticated" but the code said non-zero. I changed the code to use 1 now, as that is also non-zero but it gets the benefit that it now matches the documentation. Using 1 instead of non-zero is better for two reasons: 1. We have the opportunity to introduce other return codes in the future for things like error and what not. 2. We don't expose the internal bitmask variable value. - userauth_keyboard_interactive: fix indent - [Alfred Gebert brought this change] fix memory leak in userauth_keyboard_interactive() First I wanted to free the memory in session_free() but then I had still memory leaks because in my test case the function userauth_keyboard_interactive() is called twice. It is called twice perhaps because the server has this authentication methods available: publickey,gssapi-with-mic,keyboard-interactive The keyboard-interactive method is successful. - dist: include sftp.h in dist archives Simon Josefsson (27 Oct 2010) - Update header to match new function prototype, see c48840ba88. Daniel Stenberg (26 Oct 2010) - bugfixes: the transport rearrange left some subtle flaws now gone - libssh2_userauth_publickey_fromfile_ex.3: cleaned up looks - libssh2_userauth_publickey: add man page I found an undocumented public function and we can't have it like that. The description here is incomplete, but should serve as a template to allow filling in... - libssh2_sftp_write.3: added blurb about the "write ahead" Documented the new SFTP write concept - sftp_close_handle: free any trailing write chunks - _libssh2_channel_write: fix warnings - SFTP: bufgix, move more sftp stuff to sftp.h The sftp_write function shouldn't assume that the buffer pointer will be the same in subsequent calls, even if it assumes that the data already passed in before haven't changed. The sftp structs are now moved to sftp.h (which I forgot to add before) - SFTP: use multiple outgoing packets when writing sftp_write was rewritten to split up outgoing data into multiple packets and deal with the acks in a more asynchronous manner. This is meant to help overcome latency and round-trip problems with the SFTP protocol. - TODO: implemented a lot of the ideas now - _libssh2_channel_write: removed 32500 size limit Neither _libssh2_channel_write nor sftp_write now have the 32500 size limit anymore and instead the channel writing function now has its own logic to send data in multiple calls until everything is sent. - send_existing: don't tell parent to return when drained That will just cause unnecessary code execution. - _libssh2_channel_write: general code cleanup simplified the function and removed some unused struct fields - _libssh2_transport_send: replaces _libssh2_transport_write The new function takes two data areas, combines them and sends them as a single SSH packet. This allows several functions to allocate and copy less data. I also found and fixed a mixed up use of the compression function arguments that I introduced in my rewrite in a recent commit. - scp_write_nonblock: use select() instead of busyloop Make this example nicer by not busylooping. - send_existing: clear olen when the data is sent off - _libssh2_transport_write: allow 256 extra bytes around the packet - _libssh2_transport_write: remade to send without malloc - compress: compression disabled by default We now allow libssh2_session_flag() to enable compression with a new flag and I added documentation for the previous LIBSSH2_FLAG_SIGPIPE flag which I wasn't really aware of! - comp: split the compress function It is now made into two separate compress and decompress functions. In preparation for upcoming further modficications. Dan Fandrich (20 Oct 2010) - Added header file to allow compiling in older environments Daniel Stenberg (20 Oct 2010) - TODO: add a possible new API for SFTP transfers - TODO: "New Transport API" added - TODO: add buffering plans Simon Josefsson (13 Oct 2010) - Mention libssh2_channel_get_exit_signal and give kudos. - [Tommy Lindgren brought this change] Add libssh2_channel_get_exit_signal man page. Signed-off-by: Simon Josefsson - [Tommy Lindgren brought this change] Add libssh2_channel_get_exit_signal. Signed-off-by: Simon Josefsson - Add libssh2_free man page and fix typo. - Add libssh2_free. Daniel Stenberg (11 Oct 2010) - scp_recv: improved treatment of channel_read() returning zero As a zero return code from channel_read() is not an error we must make sure that the SCP functions deal with that properly. channel_read() always returns 0 if the channel is EOFed already so we check for EOF after 0-reads to be able to return error properly. - libssh2_session_methods.3: detail what can be asked for - compression: send zlib before none As the list of algorithms in a preferred order we should send zlib before none to increase the chances that the server will let us do compression. - compress: faster check, better return codes In the transport functions we avoid a strcmp() now and just check a boolean instead. The compress/decompress function's return code is now acknowledged and used as actual return code in case of failures. - libssh2_session_handshake: replaces libssh2_session_startup() The function libssh2_session_startup() is now considered deprecated due to the portability issue with the socket argument. libssh2_session_handshake() is the name of the replacement. - libssh2_socket_t: now externally visible In preparation for upcominig changes, the libssh2_socket_t type is now typedef'ed in the public header. - _libssh2_transport_drain: removed This function proved not to be used nor useful. - _libssh2_channel_write: don't iterate over transport writes When a call to _libssh2_transport_write() succeeds, we must return from _libssh2_channel_write() to allow the caller to provide the next chunk of data. We cannot move on to send the next piece of data that may already have been provided in this same function call, as we risk getting EAGAIN for that and we can't return information both about sent data as well as EAGAIN. So, by returning short now, the caller will call this function again with new data to send. - _libssh2_transport_write: updated documentation blurb - _libssh2_transport_write: remove fprintf remainder Mistake from previous debugging - session: improved errors Replaced -1/SOCKET_NONE errors with appropriate error defines instead. Made the verbose trace output during banner receiving less annoying for non-blocking sessions. - crypt_init: use correct error define - _libssh2_error: hide EAGAIN for non-blocking sessions In an attempt to make the trace output less cluttered for non-blocking sessions the error function now avoids calling the debug function if the error is the EAGAIN and the session is non-blocking. - agent: use better error defines - comp_method_zlib_init: use correct error defines - transport: better error codes LIBSSH2_SOCKET_NONE (-1) should no longer be used as error code as it is (too) generic and we should instead use specific and dedicated error codes to better describe the error. - channel: return code and _libssh2_error cleanup Made sure that all transport_write() call failures get _libssh2_error called. - _libssh2_channel_write: limit to 32700 bytes The well known and used ssh server Dropbear has a maximum SSH packet length at 32768 by default. Since the libssh2 design current have a fixed one-to-one mapping from channel_write() to the packet size created by transport_write() the previous limit of 32768 in the channel layer caused the transport layer to create larger packets than 32768 at times which Dropbear rejected forcibly (by closing the connection). The long term fix is of course to remove the hard relation between the outgoing SSH packet size and what the input length argument is in the transport_write() function call. - libssh.h: add more dedicated error codes - SCP: allow file names with bytes > 126 When parsing the SCP protocol and verifying that the data looks like a valid file name, byte values over 126 must not be consider illegal since UTF-8 file names will use such codes. Reported by: Uli Zappe Bug: http://www.libssh2.org/mail/libssh2-devel-archive-2010-08/0112.shtml Dan Fandrich (25 Aug 2010) - Document the three sftp stat constants Guenter Knauf (18 Aug 2010) - Fixed Win32 makefile which was now broken at resource build. - It is sufficient to pipe stderr to NUL to get rid of the nasty messages. - [Author: Guenter Knauf brought this change] Removed Win32 ifdef completely for sys/uio.h. No idea why we had this ifdef at all but MSVC, MingW32, Watcom and Borland all have no sys/uio.h header; so if there's another Win32 compiler which needs it then it should be added explicitely instead of this negative list. - New files should also be added to Makefile.am. Otherwise they will never be included with release and snapshot tarballs ... Daniel Stenberg (18 Aug 2010) - version: bump to 1.2.8_DEV Version 1.2.7 (17 Aug 2010) Daniel Stenberg (17 Aug 2010) - release: updated to hold 1.2.7 info Guenter Knauf (17 Aug 2010) - Use the new libssh2.rc file. - Added resource file for libssh2.dll (shamelessly stolen from libcurl). - Updated Win32 MSVC dependencies versions. - Added include for sys/select.h to get fd.set on some platforms. - Added Watcom makefile borrowed from libcurl. This makefile compiles already all files fine for static lib, but needs final touch when I have OpenSSL fully working with shared libs and Watcom. - Added copyright define to libssh2.h and use it for binary builds. - Moved version defines up in order to include from .rc file. Blocked rest of header with ifndef so its possible to let the rc compiler only use the version defines. - Some minor makefile tweaks. Daniel Stenberg (2 Aug 2010) - example: treat the libssh2_channel_read() return code properly A short read is not an error. Only negative values are errors! - libssh2_wait_socket: reset error code to "leak" EAGAIN less Since libssh2 often sets LIBSSH2_ERROR_EAGAIN internally before _libssh2_wait_socket is called, we can decrease some amount of confusion in user programs by resetting the error code in this function to reduce the risk of EAGAIN being stored as error when a blocking function returns. - _libssh2_wait_socket: poll needs milliseconds As reported on the mailing list, the code path using poll() should multiple seconds with 1000 to get milliseconds, not divide! Reported by: Jan Van Boghout - typedef: make ssize_t get typedef without LIBSSH2_WIN32 The condition around the ssize_t typedef depended on both LIBSSH2_WIN32 *and* _MSC_VER being defined when it should be enough to depend on _MSC_VER only. It also makes it nicer so libssh2-using code builds fine without having custom defines. - [John Little brought this change] session_free: free more data to avoid memory leaks - channel_free: ignore problems with channel_close() As was pointed out in bug #182, we must not return failure from _libssh2_channel_free() when _libssh2_channel_close() returns an error that isn't EAGAIN. It can effectively cause the function to never go through, like it did now in the case where the socket was actually closed but socket_state still said LIBSSH2_SOCKET_CONNECTED. I consider this fix the right thing as it now also survives other errors, even if making sure socket_state isn't lying is also a good idea. - publickey_list_free: no return value from a void function Fixed a compiler warning I introduced previously when checking input arguments more. I also added a check for the other pointer to avoid NULL pointer dereferences. - [Lars Nordin brought this change] openssl: make use of the EVP interface Make use of the EVP interface for the AES-funktion. Using this method supports the use of different ENGINES in OpenSSL for the AES function (and the direct call to the AES_encrypt should not be used according to openssl.org) Peter Stuge (23 Jun 2010) - [Tor Arntsen brought this change] Don't overflow MD5 server hostkey Use SHA_DIGEST_LENGTH and MD5_DIGEST_LENGTH in memcpy instead of hardcoded values. An incorrect value was used for MD5. - Fix message length bugs in libssh2_debug() There was a buffer overflow waiting to happen when a debug message was longer than 1536 bytes. Thanks to Daniel who spotted that there was a problem with the message length passed to a trace handler also after commit 0f0652a3093111fc7dac0205fdcf8d02bf16e89f. - Make libssh2_debug() create a correctly terminated string Also use FILE *stderr rather than fd 2, which can very well be something completely different. Daniel Stenberg (23 Jun 2010) - [TJ Saunders brought this change] handshake: Compression enabled at the wrong time In KEXINIT messages, the client and server agree on, among other things, whether to use compression. This method agreement occurs in src/kex.c's kex_agree_methods() function. However, if compression is enabled (either client->server, server->client, or both), then the compression layer is initialized in kex_agree_methods() -- before NEWKEYS has been received. Instead, the initialization of the compression layer should happen after NEWKEYS has been received. This looks to occur insrc/kex.c's diffie_hellman_sha1(), which even has the comment: /* The first key exchange has been performed, switch to active crypt/comp/mac mode */ There, after NEWKEYS is received, the cipher and mac algorithms are initialized, and that is where the compression should be initialized as well. The current implementation fails if server->client compression is enabled because most server implementations follow OpenSSH's lead, where compression is initialized after NEWKEYS. Since the server initializes compression after NEWKEYS, but libssh2 initializes compression after KEXINIT (i.e. before NEWKEYS), they are out of sync. Reported in bug report #180 - [TJ Saunders brought this change] userauth_hostbased_fromfile: packet length too short The packet length calculated in src/userauth.c's userauth_hostbased_fromfile() function is too short by 4 bytes; it forgets to add four bytes for the length of the hostname. This causes hostbased authentication to fail, since the server will read junk data. verified against proftpd's mod_sftp module - _libssh2_userauth_publickey: reject method names longer than the data This functions get the method length by looking at the first 32 bit of data, and I now made it not accept method lengths that are longer than the whole data set is, as given in the dedicated function argument. This was detected when the function was given bogus public key data as an ascii string, which caused the first 32bits to create a HUGE number. - NULL resistance: make more public functions survive NULL pointer input Sending in NULL as the primary pointer is now dealt with by more public functions. I also narrowed the userauth.c code somewhat to stay within 80 columns better. - agent: make libssh2_agent_userauth() work blocking properly previously it would always work in a non-blocking manner Peter Stuge (17 Jun 2010) - Fix underscore typo for 64-bit printf format specifiers on Windows Commit 49ddf447ff4bd80285f926eac0115f4e595f9425 was missing underscores. Daniel Stenberg (16 Jun 2010) - libssh2_session_callback_set: extended the man page - [John brought this change] LIBSSH2_DEBUG: macro uses incorrect function variable The LIBSSH2_DEBUG macro, defined in libssh2_priv.h, incorrectly uses the function variable ssh_msg_disconnect when it should use ssh_msg_debug. This shows that the LIBSSH2_CALLBACK_DEBUG callback never has worked... - warning: fix a compiler warning 'pointer differs in signedness' As reported in bug #177 - portability: introduce LIBSSH2_INT64_T_FORMAT for 64bit printf()s As pointed out in bug #177, some of the Windows compilers use %I64 to output 64 bit variables with the printf family. - debug: avoid sending NULL to sprintf %s Via the _libssh2_debug() macro/function. Pointed out by john in bug report - sftp docs: show macro on macro page, only function on function page The individual man pages for macros now show the full convenience macro as defined, and then the man page for the actual function only shows the function. - code police: make the code use less than 80 columns - libssh2_channel_write_ex: remove macros, added wording on buffer size - libssh2_sftp_write: document buffer size and changed some ordering - libssh2_channel_write_stderr: show how the macro is defined - libssh2_channel_write: show how the macro is defined - SFTP: limit write() to not produce overly large packets sftp_write() now limits how much data it gets at a time even more than before. Since this function creates a complete outgoing packet based on what gets passed to it, it is crucial that it doesn't create too large packets. With this method, there's also no longer any problem to use very large buffers in your application and feed that to libssh2. I've done numerous tests now with uploading data over SFTP using 100K buffers and I've had no problems with that. - scp_write_nonblock: add transfer time info Using the same timing logic and output format as sftp_write_nonblock allows us to very easily run benchmarks on SCP vs SFTP uploads using libssh2. - sftp_write_nonblock: select() on socket, use *BIG* buffer, time transfer The select() is just to make it nicer so that it doesn't crazy-loop on EAGAIN. The buffer size thing is mostly to verify that this really work as supposed. Transfer timing is just a minor thing, but it can just as well be there and help us time and work on performance easier using out of the box examples. - agent: use _libssh2_error() when returning errors As pointed out in bug report #173, this module basically never used _libssh2_error() which made it work inconstently with other parts of the libssh2 code base. This is my first take at making this code more in line with the rest. - inputchecks: make lots of API functions check for NULL pointers If an application accidentally provides a NULL handle pointer to the channel or sftp public functions, they now return an error instead of segfaulting. - libssh2_channel_eof: clarify that it returns negative on errors - SFTP: keep the sftp error code as 32 bit 'last_errno' holds to the error code from the SFTP protocol and since that is 32 bits on the wire there's no point in using a long for this internally which is larger on some platforms. - agent: make the code better deal with unexpected code flows agent->ops gets initialized by the libssh2_agent_connect() call but we need to make sure that we don't segfault even if a bad sequence of function calls is used. Alexander Lamaison (10 Jun 2010) - Better handling of invalid key files. Passing an invalid public key to libssh2_userauth_publickey_fromfile_ex triggered an assertion. Replaced this with a runtime check that rejects obviously invalid key data. Daniel Stenberg (10 Jun 2010) - version: we start working on 1.2.7 now Version 1.2.6 (10 Jun 2010) Daniel Stenberg (10 Jun 2010) - NEWS: add the 1.2.6 release details - RELEASE-NOTES: 1.2.6 details added Guenter Knauf (10 Jun 2010) - fixed libssh2.dsw to use the generated libssh2.dsp; removed old *.dsp files. - moved MSVC strdup define to libssh2_config.h which we include already. - added missing source files to src/NMakefile. Daniel Stenberg (8 Jun 2010) - libssh2_poll: refer to poll(3) and select(3) instead - example: fix strdup() for MSVC compiles MSVC has a _strdup() that we better use. This was reported in bug - SFTP: fail init SFTP if session isn't authenticated Alexander Lamaison filed bug #172 (http://trac.libssh2.org/ticket/172), and pointed out that SFTP init would do bad if the session isn't yet authenticated at the time of the call, so we now check for this situation and returns an error if detected. Calling sftp_init() at this point is bad usage to start with. - direct_tcpip: bring back inclusion of libssh2_config.h In order to increase portability of this example, I'm bringing the inclusion of libssh2_config.h back, and I also added an require that header for this example to compile. I also made all code lines fit within 80 columns. Guenter Knauf (3 Jun 2010) - cast away a warning. - moved CRT_SECURE_NO_DEPRECATE define up so its defined before the winsock headers are included. - fixed platform detection for MingW32 test makefile. - MingW32 has gettimeofday() implemented, so proper ifdef this function here. - removed MSVC ifdef since seems we can use __int64 still with latest headers. - changed copyright notice for MinW32 and NetWare binaries. - cleaned up MSVC ifdefs which where spreaded over 3 places. - added uint8_t typedef for NetWare CLIB platform. - if the function declaration gets changed the header should be changed too. - this is MSVC specific and doesnt apply for all Win32 compilers; the uint8_t typedef clashes with MingW32 headers. - updated MingW32 makefiles for latest dependency lib versions. - updated NetWare makefiles for latest dependency lib versions. Dan Fandrich (30 May 2010) - Fixed compiling with libgcrypt A change of parameter types from unsigned long to size_t was missed in the prototype in libgcrypt.h Daniel Stenberg (28 May 2010) - statvfs: use libssh2_sftp_statvfs only, no "_ex" As the long-term goal is to get rid of the extensive set of macros from the API we can just as well start small by not adding new macros when we add new functions. Therefore we let the function be libssh2_sftp_statvfs() plainly without using an _ex suffix. I also made it use size_t instead of unsigned int for the string length as that too is a long-term goal for the API. - [Grubsky Grigory brought this change] DSP: output lib name typo - [Grubsky Grigory brought this change] win32: provide a uint8_t typedef for better building on windows - agent: win32: fix bad _libssh2_store_str call As pointed out by Grubsky Grigory , I made a mistake when I added the _libssh2_store_str() call before and I made a slightly different patch than what he suggested. Based purely on taste. Peter Stuge (24 May 2010) - [Joey Degges brought this change] Add libssh2_sftp_statvfs() and libssh2_sftp_fstatvfs() These can be used to get file system statistics from servers that support the statvfs@openssh.com and fstatvfs@openssh.com extensions. Alexander Lamaison (22 May 2010) - [Jose Baars brought this change] VMS specific: make sure final release can be installed over daily build - [Jose Baars brought this change] VMS: small improvement to the man2help utilities Peter Stuge (22 May 2010) - [Joey Degges brought this change] libssh2_exit and libssh2_sftp_readdir man page fixes Daniel Stenberg (21 May 2010) - spelling: s/sue/use Alexander Lamaison (21 May 2010) - Change magic port number for generic knownhost check. libssh2_knownhost_checkp took 0 as a magic port number that indicated a 'generic' check should be performed. However, 0 is a valid port number in its own right so this commit changes the magic value to any negative int. Mikhail Gusarov (5 May 2010) - Add re-discovered copyright holders to COPYING - Restoring copyright statements from pre-git era Eli Fant has contributed fragmenting SFTP requests - Restoring my copyright statements from pre-git era keyboard_interactive, 'exit-status' information packet, non-atomic read/write under FreeBSD, multi-channel operation bugfixes. Daniel Stenberg (3 May 2010) - pedantic: make the code C90 clean Peter Stuge (3 May 2010) - Do proper keyboard-interactive user dialog in the sftp.c example Daniel Stenberg (3 May 2010) - added to tarball: libssh2_knownhost_checkp.3 - knownhost: support [host]:port in knownhost file OpenSSH has ways to add hosts to the knownhosts file that include a specific port number which makes the key associated with only that specific host+port pair. libssh2 previously did not support this, and I was forced to add a new function to the API to properly expose this ability to applications: libssh2_knownhost_checkp() To *add* such hosts to the knownhosts file, you make sure to pass on the host name in that manner to the libssh2_knownhost_addc() function. - init/exit: mention these were added in 1.2.5 - libssh2_knownhost_check docs: correct the prototype - examples: avoid use of uninitialized variable 'sock' - KEX: stop pretending we negotiate language There was some stub-like parts of an implementation for implementing kex language negotiation that caused clang-analyzer to warn and as it did nothing I've now removed the dead code. - Uninitialized argument - sftpdir: removed dead assignment - Makefile.am: include the VMS-specific config header as well - [Jose Baars brought this change] Add VMS specific libssh2_config.h - fix Value stored to 's' is never read warning and moved variable declaration of s to be more local - kexinit: simplify the code and avoid scan-build warning Previously it would say "Value stored to 's' is never read" due fourth increment of 's'. Alexander Lamaison (28 Apr 2010) - Removed unecessary brackets. - Changed sftp_attrsize macro to a static function. Daniel Stenberg (28 Apr 2010) - release: include the VMS-specific files - sftp_attrsize: protect the macro argument with proper parentheses - ssh2_agent: avoid using 'session' uninitialized on failures - examples: remove assignments of variable rc that's never used - publickey_init: remove useless variable increment - hostkey_method_ssh_rsa_init: remove useless variable increment - packet_x11_open: removed useless variable increment and made the declaration of a variable more local - packet_queue_listener: removed useless variable increment and made the declaration of a variable more local - sftp_read: move a read_responses array to where its used I find that this increases readability since the array is used only in the function call just immediately below and nowhere else. - sftp_readdir: turn a small array static const and move it - sftp_attrsize: converted function to a macro This way, the macro can evaluate a static number at compile time for two out of four uses, and it probably runs faster for the other two cases too. - sftp_open: deal with short channel_write calls This was an old TODO that just wasn't done before. If channel_write returns short, that is not an error. - sftp_open: clean up, better check of input data The clang-analyzer report made it look into this function and I've went through it to remove a potential use of an uninitialized variable and I also added some validation of input data received from the server. In general, lots of more code in this file need to validate the input before assuming it is correct: there are servers out there that have bugs or just have another idea of how to do the SFTP protocol. - bugfix: avoid using the socket if it failed to create one - bugfix: potential use of NULL pointer - libssh2_userauth_password_ex: clarify errors somewhat The errors mentioned in this man page are possible return codes but not necessarily the only return codes that this can return. Also reformatted the typ prototypes somewhat. - examples: fixed and made them more similar The channel read/write functions can return 0 in legitimate cases without it being an error, and we need to loop properly if they return short. - [Jose Baars brought this change] VMS port of libssh2; changes in the libssh2 common code - Makefile: added the two news headers userauth.h and session.h - cleanup: prefer the internal functions To get the blocking vs non-blocking to work as smooth as possible and behave better internally, we avoid using the external interfaces when calling functions internally. Renamed a few internal functions to use _libssh2 prefix when not being private within a file, and removed the libssh2_ for one that was private within the file. - session_free: remove dead code - libssh2_publickey_init: fixed to work better non-blocking This was triggered by a clang-analyzer complaint that turned out to be valid, and it made me dig deeper and fix some generic non- blocking problems I disovered in the code. While cleaning this up, I moved session-specific stuff over to a new session.h header from the libssh2_priv.h header. - channel: reduce duplicated free and returns Simplified the code by trying to free data and return on a single spot. - channel: make variables more local By making 'data' and 'data_len' more local in several places in this file it will be easier to spot how they are used and we'll get less risks to accidentally do bad things with them. Mikhail Gusarov (24 Apr 2010) - Fix typos in manpages, catched by Lintian Daniel Stenberg (24 Apr 2010) - channel_request_pty: simplify the code clang-analyzer pointed out how 'data' could be accessed as a NULL pointer if the wrong state was set, and while I don't see that happen in real-life the code flow is easier to read and follow by moving the LIBSSH2_FREE() call into the block that is supposed to deal with the data pointer anyway. - libssh2_channel_process_startup: simplify the code clang-analyzer pointed out how 'data' could be accessed as a NULL pointer if the wrong state was set, and while I don't see that happen in real-life the code flow is easier to read and follow by moving the LIBSSH2_FREE() call into the block that is supposed to deal with the data pointer anyway. - sftp_close_handle: add precation to not access NULL pointer clang-analyzer pointed this out as a "Pass-by-value argument in function call is undefined" but while I can't see exactly how this can ever happen in reality I think a little check for safety isn't such a bad thing here. - scp_write_nonblock: Value stored to 'nread' is never read - scp_write: Value stored to 'ptr' is never read - scp_write_nonblock: Value stored to 'ptr' is never read - sftp_mkdir: less silly output but show failures - [Jose Baars brought this change] VMS port of libssh2 including VMS specific build procedures - two variable types changes, made lines less than 80 columns The two variable type changes are only to match type variable fields actually read from the binary protocol. - remove check for negative padding_length It was silly, since it is read as an unsigned char... - hostkey_method_ssh_dss_init: Value stored to 's' is never read - libssh2_banner_set: avoid unnecessary increment and explain code - agent_transact_unix: remove unused variable - remove two unnecessary increments - more code converted to use _libssh2_store_*() - libssh2_publickey_list_fetch: removed unused variables - libssh2_publickey_init: remove unused variables - libssh2_scp_send64: added to API to provide large file transfers The previously existing libssh2_scp_send_ex() function has no way to send files that are larger than 'size_t' which on 32bit systems mean 4GB. This new API uses a libssh2_int64_t type and should thus on most modern systems be able to send enormous files. - sftp_init: remove unused variables and assignments - libssh2_knownhost_check: Value stored to 'keylen' is never read - hostkey: fix compiler warning - remove unused variable - data types: convert more to use size_t and uint32_t - channel: variable type cleanups - cleanups: better binary packet gen, size_t fixes and PACKET_* removal I'll introduce a new internal function set named _libssh2_store_u32 _libssh2_store_u64 _libssh2_store_str That can be used all through the library to build binary outgoing packets. Using these instead of the current approach removes hundreds of lines from the library while at the same time greatly enhances readability. I've not yet fully converted everything to use these functions. I've converted LOTS of 'unsigned long' to 'size_t' where data/string lengths are dealt with internally. This is The Right Thing and it will help us make the transition to our size_t-polished API later on as well. I'm removing the PACKET_* error codes. They were originally introduced as a set of separate error codes from the transport layer, but having its own set of errors turned out to be very awkward and they were then converted into a set of #defines that simply maps them to the global libssh2 error codes instead. Now, I'l take the next logical step and simply replace the PACKET_* defines with the actual LIBSSH2_ERROR_* defines. It will increase readability and decrease confusion. I also separated packet stuff into its own packet.h header file. - clarified the return code - rename libssh2_error to the correct _libssh2_error We reserve ^libssh2_ for public symbols and we use _libssh2 as prefix for internal ones. I fixed the intendation of all these edits with emacs afterwards, which then changed it slightly more than just _libssh2_error() expressions but I didn't see any obvious problems. - data type cleanup: made lots of code use size_t etc A lot of code used 'unsigned long' and the likes when it should rather just use plain 'int' or use size_t for data lengths. - wait_socket: make c89 compliant and use two fd_sets for select() - sftp_readdir: always zero terminate, detail the return code I also added a description for the 'longentry' field which was previously undocumented! - sftp_readdir: simplified and bugfixed This function no longer has any special purpose code for the single entry case, as it was pointless. The previous code would overflow the buffers with an off-by-one in case the file name or longentry data fields received from the server were exactly as long as the buffer provided to libssh2_sftp_readdir_ex. We now make sure that libssh2_sftp_readdir_ex() ALWAYS zero terminate the buffers it fills in. The function no longer calls the libssh2_* function again, but properly uses the internal sftp_* instead. - channel/transport: we now drain the outgoing send buffer when we ignore EAGAIN When we ignore the EAGAIN from the transport layer within channel_write, we now drain the outgoing transport layer buffer so that remainders in that won't cause any problems in the next invoke of _libssh2_transport_write() - channel_write: if data has been sent, don't return EAGAIN When sending data in a loop, we must not return EAGAIN if we managed to send data in an earlier round. This was reported in bug #126 => http://libssh2.stuge.se/ticket/126 Simon Josefsson (14 Apr 2010) - Fix OpenSSL AES-128-CTR detection. Patch from Paul Howarth . Daniel Stenberg (13 Apr 2010) - version in header file now says 1.2.6-DEV - 1.2.6: clean the RELEASE-NOTES for next release round - NEWS: add the stuff from the version 1.2.5 RELEASE-NOTES Version 1.2.5 (13 Apr 2010) Daniel Stenberg (13 Apr 2010) - channel_close: no longer wait for the SSH_MSG_CHANNEL_CLOSE message As the packet may simply not arrive we cannot have the close function wait for it unconditionally. - less code duplication in the poll vs select code flows libssh2_keepalive_send and libssh2_session_block_directions are now used outside of the #ifdef blocks. - make it C90 compliant - updated with all changes and bugs since 1.2.4 - Added LIBSSH2_SFTP_S_IS***() macros and updated docs libssh2_sftp_fstat_ex.3 is now extended quite a lot to describe a lot of the struct and the bits it uses and how to test for them. - sftp_init() deal with _libssh2_channel_write() returns short When _libssh2_channel_write() is asked to send off 9 bytes, the code needs to deal with the situation where less than 9 bytes were sent off and prepare to send the remaining piece at a later time. - handle a NULL password as if it was "" libssh2_userauth_publickey_fromfile_ex() takes a "passphrase" but didn't deal with it being set to NULL. - Reduce used window sizes by factor 10 As reported in bug report #166 http://libssh2.stuge.se/ticket/166 by 'ptjm', the maximum window size must be less crazy for libssh2 to do better with more server implementations. I did not do any testing to see how this changes raw SCP performance, but the maximum window size is still almost 4MB. This also has the upside that libssh2 will use less memory. Peter Stuge (28 Mar 2010) - Correctly clear blocking flag after sending multipart packet commit 7317edab61d2179febc38a2c2c4da0b951d74cbc cleared the outbound blocking bit when send_existing() returned PACKET_NONE and *ret=0, as opposed to before even calling send_existing(), but because *ret=1 when sending parts 2..n of an existing packet, the bit would only be cleared when calling libssh2_transport_write() for a new packet. Clear the direction flag after the final part of a packet has been sent. Daniel Stenberg (24 Mar 2010) - Added man page for libssh2_knownhost_addc() Added mention in libssh2_knownhost_add() docs that libssh2_knownhost_addc() is the preferred function now. - at next soname bump remove libssh2_knownhost_add() - ignore TAGS ("make tags" makes them) - fix memory leak we must not assign the pointer a NULL since it keeps allocated data and at least parts of an error string - fixed the pattern for avoiding the poll check added some comments about known problems with poll on darwin - avoid checking for poll on some systems darwin and interix are known to have broken poll implementations so we skip the check on those and thus have them use select unconditionally - ignore libssh2.dsp Simon Josefsson (23 Mar 2010) - Fix logic in "on-the-fly" crypto init. - Make sure keepalive is working even when poll is used. - [Paul Querna brought this change] Use poll when available on blocking API. Signed-off-by: Simon Josefsson Peter Stuge (20 Mar 2010) - Fix speling Daniel Stenberg (19 Mar 2010) - fix NULL dereference when window adjusting a non-existing channel Suyog Jadhav pointed out that when receiving a window adjust to a channel not found, the code would reference a NULL pointer. Now it will instead output a message about that fact. Simon Josefsson (19 Mar 2010) - Fix build problem. - Eat our own dog food, call libssh2_init and libssh2_exit in the examples. - Fix init/exit logic. Add self-test of it. Daniel Stenberg (19 Mar 2010) - fix typo Simon Josefsson (19 Mar 2010) - Add man page for libssh2_init and libssh2_exit. Fix libssh2_exit prototype. - Shorten constant a bit. More documentation. - Fix namespace pollution. - Add global init/exit points, to do crypto initialization in one place. By Lars Nordin. Daniel Stenberg (14 Mar 2010) - libssh2 is released under the Modifed BSD license, not GPL Alexander Lamaison (14 Mar 2010) - Add libssh2_knownhost_addc to handle comments. Comments in known_hosts file were not handle properly. They were parsed as part of the key causing key matching to return a mismatch if the entry had a comment. This adds a new API function that takes an optional comment and changes libssh2_knownhost_readline to parse the comment as pass it to the new function. Fixes #164. - Fix gettimeofday to compile with Visual C++ 6. Reported by Steven Van Ingelgem. Simon Josefsson (10 Mar 2010) - Add. - keepalive.c: Fix libssh2_error usage. - Fix typo in last commit. - Tidy up build option notice. - Add entry about keep alive stuff. - Add keep-alive support. Alexander Lamaison (7 Mar 2010) - Untabify. - Fix memory leak in libssh2_knownhost_add. Daniel Stenberg (6 Mar 2010) - change 'int' to 'libssh2_socket_t' in the public API for sockets - reduce code duplication and return underlying error better - acknowledge when _libssh2_packet_requirev() returns error when _libssh2_packet_requirev() returns an error when waiting for SSH_MSG_USERAUTH_SUCCESS or SSH_MSG_USERAUTH_FAILURE, it is an error and it should be treated as such - wrap long lines - polished the phrasing in two error strings - silence picky compiler warnings - silence picky compiler warnings - removed libssh2_error()'s forth argument libssh2_error() no longer allocates a string and only accepts a const error string. I also made a lot of functions use the construct of return libssh2_error(...) instead of having one call to libssh2_error() and then a separate return call. In several of those cases I then also changed the former -1 return code to a more detailed one - something that I think will not change behaviors anywhere but it's worth keeping an eye open for any such. - repaired --enable-debug Simon Josefsson (1 Mar 2010) - Make ./configure output a summary of build options. Daniel Stenberg (1 Mar 2010) - let the err_msg in the session struct be const too Simon Josefsson (1 Mar 2010) - Revert #ifdef change that pulled in AES-CTR code when explicitly disabled. Daniel Stenberg (1 Mar 2010) - fix #ifdefs - make function match the new proto Simon Josefsson (1 Mar 2010) - Improve AES-CTR check. Daniel Stenberg (1 Mar 2010) - use const to silence a bazillion warnings Simon Josefsson (1 Mar 2010) - Use AES-CTR from OpenSSL when available. Reported by Lars Nordin . - Make it possible to disable DSA. Patch from Lars Nordin . Peter Stuge (1 Mar 2010) - Send and receive channel EOF before sending SSH_MSG_CHANNEL_CLOSE Sending SSH_MSG_CHANNEL_CLOSE without channel EOF is explicitly allowed in RFC 4254, but some non-conforming servers will hang or time out when the channel is closed before EOF. Other common clients send and receive EOF before closing, there are no drawbacks, and some servers need it to work correctly. Alexander Lamaison (26 Feb 2010) - Style improvements to knownhost error handling. Made improvements as suggested by Peter Stuge: http://www.libssh2.org/mail/libssh2-devel-archive-2010-02/0161.shtml. - Call libssh2_error for every knownhost API failure. The libssh2 API calls should set the last error code and a message when returning a failure by calling libssh2_error. This changeset adds these calls to the libssh2_knownhost_* API as well as libssh2_base64_decode. This change also makes libssh2_error into a function rather than a macro. Its implementation is moved to misc.c. This function returns the error code passed to it allowing callers to return the error value directly without duplicating the error code. - Fix LIBSSH2_ALLOC checks. These appear to be cut-and paste errors where the wrong variable is checked for NULLness after calling LIBSSH2_ALLOC. Simon Josefsson (23 Feb 2010) - Silence compiler warning. - Make it portable; test uses = for string comparison (not ==). Indent. Alexander Lamaison (22 Feb 2010) - libssh2_knownhost_del: fix write to freed memory. When removing a known host, libssh2_knownhost_del would remove the node from the linked list, free its memory and then overwrite the struct parameter (which indicated which node to remove) with 0. However, this struct is actually allocated within the just-freed node meaning we're writing to freed memory. This made Windows very upset. The fix is simply to overwrite the struct first before freeing the memory. Daniel Stenberg (21 Feb 2010) - show more verbose error when SCP send fails - libssh2_socket_t is done, a library-free function is needed - clarify that this frees all data associated with a session - improved error handling - add missing libssh2_error() calls To make sure the public API is functional and that the BLOCK_ADJUST_ERRNO() macro works correctly we MUST make sure to call libssh2_error() when we return errors. - fix memory leak in userauth_keyboard_interactive() Mr anonymous in bug #125 pointed out that the userauth_keyboard_interactive() function does in fact assign the same pointer a second time to a new allocated buffer without properly freeing the previous one, which caused a memory leak. - added missing error codes To allow the libssh2_session_last_error() function to work as documented, userauth_password() now better makes sure to call libssh2_error() everywhere before it returns error. Pointed out by mr anonymous in bug #128 Peter Stuge (16 Feb 2010) - Fix resource and memory leaks in examples as reported by cppcheck Thanks to Ettl Martin for the report and patch. This fixes #132 Daniel Stenberg (15 Feb 2010) - mention the new man pages for macros - added man pages for API macros all #defined macros in the public headers are considered to be part of the API and I've generated individual man pages for each of them to A) make it easier to figure out what each function/macro actually is for so that automated lookups work better and for B) make sure we have all public functions document (both macros and functions) to make it easier for us to work away from all the macros in a future release. - Committed the patch by Yoichi Iwaki in bug #2929647 Committed the patch by Yoichi Iwaki in bug #2929647, which fixed a memory leak when an 'outbuf' was still allocated when a session was freed. - free "outbuf" when killing a session Fix memoary leak: if there was an "output" still allocated when a session was torn down it needs to be freed in session_free() Patch by Yoichi Iwaki in bug #2929647 - the working version name is now 1.2.5_DEV Version 1.2.4 (13 Feb 2010) Daniel Stenberg (13 Feb 2010) - updated info for 1.2.4 Dan Fandrich (10 Feb 2010) - Allow compiling with OpenSSL when AES isn't available. Peter Stuge (9 Feb 2010) - [Dave McCaldon brought this change] Fix Tru64 socklen_t compile issue with example/direct_tcpip.c Building libssh2-1.2.3 on Tru64 fails at line 48 and 166 because socklen_t isn't defined on Tru64 unless _POSIX_PII_SOCKET is defined. This patch updates configure.ac to add -D_POSIX_PII_SOCKET when building on Tru64 platform(s). - [Dave McCaldon brought this change] Resolve compile issues on Solaris x64 and UltraSPARC Solaris builds of libssh2-1.2.3 failed on both x64 and UltraSPARC platforms because of two problems: 1) src/agent.c:145 sun is a reserved word when using the SUNWspro compiler 2) example/direct_tcpip.c:84 INADDR_NONE is not defined Daniel Stenberg (3 Feb 2010) - towards 1.2.4 now Version 1.2.3 (3 Feb 2010) Daniel Stenberg (3 Feb 2010) - Version 1.2.3 (February 3, 2010) - fix building out of source tree by proving better include path when building out of source tree, we provide -I$(top_builddir)/example since the libssh2_config.h gets generated in that dir Peter Stuge (1 Feb 2010) - [Sofian Brabez brought this change] Replace : in hexdump with " " (two spaces) - Detect when the forwarded connection is closed in example/direct_tcpip.c - Fix example/direct_tcpip.c to work also on WIN32 read() and write() are no good for WIN32 sockets, use recv() and send(). - Ignore libssh2_config.h.in and stamp-h2 in example/ and remove .cvsignore - Simplify WIN32 ifdefs in example/direct_tcpip.c to allow standalone compile - Always #define INVALID_SOCKET -1 in libssh2_priv.h when not on win32 Fix broken builds since commit abd9bd0bbe631efeada1f54552c70b54e1c490c1 for all non-win32 platforms. - Include hmac-md5 and hmac-md5-96 only if crypto backend supports MD5 - Use LIBSSH2_HOSTKEY_HASH_SHA1 instead of _MD5 in examples and tests MD5 support is optional and may not always be available, while SHA1 is both required and recommended. - Update mailing list address in configure.ac to @cool.haxx.se - Make example/direct_tcpip.c compile for win32 One warning from FD_SET() remains, it is also in some other examples. - Correctly check for an invalid socket in session_startup() - Small documentation fix after Dave's _USERAUTH_FAILURE improvement - [Dave McCaldon brought this change] Handle SSH_MSG_USERAUTH_FAILURE for password and kbd-int authentication Neither libssh2_userauth_password_ex() nor libssh2_userauth_keyboard_interactive_ex() would return a login failure error if the server responded with a SSH_MSG_USERAUTH_FAILURE, instead you would see whatever previous error had occurred, typically LIBSSH2_ERROR_EAGAIN. This patch changes error code -18 to LIBSSH2_ERROR_AUTHENTICATION_FAILED and makes LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED an alias for LIBSSH2_ERROR_AUTHENTICATION_FAILED. In addition, new logic in userauth_password() properly handles SSH_MSG_USERAUTH_FAILURE and both this function and userauth_keyboard_interactive() now properly return LIBSSH2_ERROR_AUTHENTICATION_FAILED. Simon Josefsson (28 Jan 2010) - Fix. - Also deal with GLOBAL_REQUEST keep-alives. - Make OpenSSH-style keepalive work against libssh2 clients. Daniel Stenberg (27 Jan 2010) - clarified Peter Stuge (26 Jan 2010) - [Dave McCaldon brought this change] Fix trace context lookup in libssh2_debug() The trace context is actually a bitmask so that tracing output can be controlled by setting a bitmask using libssh2_trace(). However, the logic in libssh2_debug() that converted the context to a string was using the context value as an array index. Because the code used a bounds check on the array, there was never a danger of a crash, but you would certainly either get the wrong string, or "unknown". This patch adds a lookup that iterates over the context strings and uses it's index to check for the corresponding bit in the context. - Fix typo in RELEASE-NOTES Daniel Stenberg (20 Jan 2010) - updated for 1.2.3 with all the stuff I found in the log - ignore more generated files - [Dave McCaldon brought this change] Pass user context through libssh2_trace_sethandler() to callback The libssh2_trace_sethandler() call allows the user to handle the output of libssh2 rather than having it written to stderr. This patch updates libssh2_trace_sethandler() to allow a user-defined void* context value to be passed back to the output handler. - [Dave McCaldon brought this change] Add libssh2_trace_sethandler() to the API (even more) - [Dave McCaldon brought this change] Add libssh2_trace_sethandler() to the API - cleanup includes We now produce a local libssh2_config.h file in this dir for the examples to use so I cleaned up the include path at the same time. - generate a libssh2_config.h in the example dir buildconf copies the template to example/ and configure makes sure to generate a proper file from it and the direct_tcpip.c example is the first one to use it - to make sure it builds fine on more paltforms Simon Josefsson (13 Jan 2010) - Remove redundant #includes and reorder sys/types.h include. Daniel Stenberg (10 Jan 2010) - avoid a free(NULL) Simon Josefsson (7 Jan 2010) - Make it simpler to get more debug info. Daiki Ueno (1 Jan 2010) - Simplify the commit 63457dfa using type cast from size_t * to ulong *. Alexander Lamaison (30 Dec 2009) - Fixed memory leak in userauth_publickey(). userauth_publickey_fromfile() reads the key from a file using file_read_publickey() which returns two allocated strings, the decoded key and the key method (such as "ssh-dss"). The latter can be derived from the former but returning both avoids a later allocation while doing so. Older versions of userauth_publickey_fromfile() used this method string directly but when userauth_publickey() was factored out of userauth_publickey_fromfile() it derived the method from the key itself. This resulted in the method being allocated twice. This fix, which maintains the optimisation that avoids an extra allocation, changes userauth_publickey() so it doesn't allocate and derive the method when userauth_pblc_method already has a value. Signed-off-by: Alexander Lamaison Daiki Ueno (25 Dec 2009) - Fix the return value description of libssh2_knownhost_free(). - Fix compiler warnings for size_t pointers on 32-bit Windows. - Define INVALID_SOCKET and use it instead of SOCKET_BAD. Revert the part of previous commit that defines SOCKET_BAD library wide. - Use libssh2_socket_t in the ssh-agent stuff. Define a portability macro SOCKET_BAD which means "invalid socket". - Mark/unmark connection to Pageant is open/close. - Add test to check if the socket is connected. Peter Stuge (24 Dec 2009) - Add libssh2.pc to top-level .gitignore - Fix publickey authentication regression Commit 70b199f47659a74b8778c528beccf893843e5ecb introduced a parsing bug in file_read_publickey() which made the algorithm name contain an extra trailing space character, breaking all publickey authentication. - Add a direct-tcpip example which shows local port forwarding - Add session parameter and LIBSSH2_TRACE_SOCKET to libssh2_trace(3) man page - Add TODO: Expose error messages sent by the server Daiki Ueno (23 Dec 2009) - Fix doc comments. - Add man pages for ssh-agent API. - Don't request userauthlist after authentication. Simon Josefsson (21 Dec 2009) - Add. - [Daiki Ueno brought this change] Add an example to use ssh-agent API. Signed-off-by: Simon Josefsson - [Daiki Ueno brought this change] Add ssh-agent API. Signed-off-by: Simon Josefsson - [Daiki Ueno brought this change] Add callback-based API for publickey auth. Signed-off-by: Simon Josefsson - Move examples from example/simple to example/. - Move examples from example/simple to example/. Daniel Stenberg (17 Dec 2009) - _libssh2_list_insert() fixed to work While this is code not currently in use, it is part of the generic linked list code and since I found the error I thought I'd better fix it since we might bring in this function into the code one day. Simon Josefsson (16 Dec 2009) - Silence compiler warnings. Based on patch by Kamil Dudka in . - [Kamil Dudka brought this change] libgcrypt: simplify code of _libssh2_dsa_sha1_sign Signed-off-by: Simon Josefsson - [Kamil Dudka brought this change] libgcrypt: follow-up for ssh-dss padding fix Signed-off-by: Simon Josefsson Dan Fandrich (15 Dec 2009) - Check for the right environment variable in the test app Simon Josefsson (14 Dec 2009) - Silence warning about unused function parameter. Reported by Steven Van Ingelgem . Daniel Stenberg (10 Dec 2009) - avoid returning data to memory already freed In case of failure we must make sure that the data we return doesn't point to a memory area already freed. Reported anonymously in the bug report #2910103. Peter Stuge (8 Dec 2009) - Use LIBSSH2_TRACE_* internally and remove redundant LIBSSH2_DBG_* - Add LIBSSH2_TRACE_SOCKET context for tracing send() and recv() Helpful in debugging the -39 errors. - Another transport layer fix for bogus -39 (LIBSSH2_ERROR_BAD_USE) errors Commit 683aa0f6b52fb1014873c961709102b5006372fc made send_existing() send more than just the second part of a packet when the kernel did not accept the full packet, but the function still overlooked the SSH protocol overhead in each packet, often 48 bytes. If only the last few bytes of a packet remained, then the packet would erroneously be considered completely sent, and the next call to write more data in the session would return a -39 error. Daniel Stenberg (6 Dec 2009) - move local variable to be more localized - fixed some indent mistakes Peter Stuge (6 Dec 2009) - Fix padding in ssh-dss signature blob encoding DSA signatures consist of two 160-bit integers called r and s. In ssh-dss signature blobs r and s are stored directly after each other in binary representation, making up a 320-bit (40 byte) string. (See RFC4253 p14.) The crypto wrappers in libssh2 would either pack r and s incorrectly, or fail, when at least one integer was small enough to be stored in 19 bytes or less. The patch ensures that r and s are always stored as two 160 bit numbers. - Don't always clear write direction blocking flag When libssh2_transport_write() is called to continue sending a partially sent packet the write direction flag must not be cleared until the previous packet has been completely sent, or the app would hang if the packet still isn't sent completely, since select() gets called by the internal blocking emulation layer in libssh2 but would then not be watching the socket for writability. Clear the flag only once processing of previous packet data is complete and a new packet is about to be prepared. Alexander Lamaison (24 Nov 2009) - Detabify. - [Daniel Stenberg brought this change] Fixed memory leak in sftp_fstat(). Simon Josefsson (17 Nov 2009) - Mark date of 1.2.2 release. - Merge branch 'master' of ssh://git.stuge.se/var/lib/git/libssh2 Version 1.2.2 (16 Nov 2009) Daniel Stenberg (16 Nov 2009) - prepared for 1.2.2 Simon Josefsson (16 Nov 2009) - Improve NEWS items. - Support AES-Counter ciphers. - Silence compiler warning. Reported by Steven Van Ingelgem in . - Mention libssh2-style.el. - Use memmove instead of memcpy on overlapping memory areas. Reported by Bob Alexander in . - Add. - Protect against crash on too small SSH_MSG_IGNORE packets. Reported by Bob Alexander in . - add copyright line PK0]٪!!aclocal/libmcrypt.m4nu[dnl Autoconf macros for libmcrypt dnl $id$ # This script detects libmcrypt version and defines # LIBMCRYPT_CFLAGS, LIBMCRYPT_LIBS # and LIBMCRYPT24 or LIBMCRYPT22 depending on libmcrypt version # found. # Modified for LIBMCRYPT -- nmav # Configure paths for LIBGCRYPT # Shamelessly stolen from the one of XDELTA by Owen Taylor # Werner Koch 99-12-09 dnl AM_PATH_LIBMCRYPT([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]]) dnl Test for libmcrypt, and define LIBMCRYPT_CFLAGS and LIBMCRYPT_LIBS dnl AC_DEFUN([AM_PATH_LIBMCRYPT], [dnl dnl Get the cflags and libraries from the libmcrypt-config script dnl AC_ARG_WITH(libmcrypt-prefix, [ --with-libmcrypt-prefix=PFX Prefix where libmcrypt is installed (optional)], libmcrypt_config_prefix="$withval", libmcrypt_config_prefix="") if test x$libmcrypt_config_prefix != x ; then libmcrypt_config_args="$libmcrypt_config_args --prefix=$libmcrypt_config_prefix" if test x${LIBMCRYPT_CONFIG+set} != xset ; then LIBMCRYPT_CONFIG=$libmcrypt_config_prefix/bin/libmcrypt-config fi fi AC_PATH_PROG(LIBMCRYPT_CONFIG, libmcrypt-config, no) min_libmcrypt_version=ifelse([$1], ,2.4.0,$1) AC_MSG_CHECKING(for libmcrypt - version >= $min_libmcrypt_version) no_libmcrypt="" if test "$LIBMCRYPT_CONFIG" = "no" ; then dnl libmcrypt-config was not found (pre 2.4.11 versions) dnl Try to detect libmcrypt version AC_TRY_RUN([ #include #include #include #include int main () { #if MCRYPT_API_VERSION <= 19991015 /* version 2.2 */ return 0; #else /* version 2.4 */ return 1; #endif /* 19991015 */ } ], libmcrypt_config_version="2.2.0" if test x$libmcrypt_config_prefix != x ; then TTLIBS="-L${libmcrypt_config_prefix}/libs" TTINCLUDE="-I${libmcrypt_config_prefix}/include" fi LIBMCRYPT_CFLAGS="${TTINCLUDE}" LIBMCRYPT_LIBS="${TTLIBS} -lmcrypt" AC_DEFINE(LIBMCRYPT22, 1, [have libmcrypt 2.2]) , libmcrypt_config_version="2.4.0" if test x$libmcrypt_config_prefix != x ; then TTLIBS="-L${libmcrypt_config_prefix}/libs" TTINCLUDE="-I${libmcrypt_config_prefix}/include" fi LIBMCRYPT_CFLAGS="${TTINCLUDE}" LIBMCRYPT_LIBS="${TTLIBS} -lmcrypt -lltdl ${LIBADD_DL}" AC_DEFINE(LIBMCRYPT24, 1, [have libmcrypt 2.4])) else dnl libmcrypt-config was found LIBMCRYPT_CFLAGS=`$LIBMCRYPT_CONFIG $libmcrypt_config_args --cflags` LIBMCRYPT_LIBS=`$LIBMCRYPT_CONFIG $libmcrypt_config_args --libs` libmcrypt_config_version=`$LIBMCRYPT_CONFIG $libmcrypt_config_args --version` AC_DEFINE(LIBMCRYPT24, 1, [have libmcrypt 2.4]) fi ac_save_CFLAGS="$CFLAGS" ac_save_LIBS="$LIBS" CFLAGS="$CFLAGS $LIBMCRYPT_CFLAGS" LIBS="$LIBS $LIBMCRYPT_LIBS" dnl dnl Now check if the installed libmcrypt is sufficiently new. Also sanity dnl checks the results of libmcrypt-config to some extent dnl rm -f conf.libmcrypttest AC_TRY_RUN([ #include #include #include #include #define TWO "2.2" int main () { #if MCRYPT_API_VERSION <= 20010201 #if MCRYPT_API_VERSION <= 19991015 /* version 2.2 */ int x = mcrypt_get_key_size(MCRYPT_TWOFISH_128); system ("touch conf.libmcrypttest"); if( strncmp( TWO, "$min_libmcrypt_version", strlen(TWO))) { printf("\n*** Requested libmcrypt %s, but LIBMCRYPT (%s)\n", "$min_libmcrypt_version", TWO ); printf("*** was found!\n"); return 1; } return 0; #else /* version 2.4 before 11 */ MCRYPT td = mcrypt_module_open("twofish", NULL, "cbc", NULL); system ("touch conf.libmcrypttest"); mcrypt_module_close(td); return 0; #endif /* 19991015 */ #else system ("touch conf.libmcrypttest"); if( strcmp( mcrypt_check_version(NULL), "$libmcrypt_config_version" ) ) { printf("\n*** 'libmcrypt-config --version' returned %s, but LIBMCRYPT (%s)\n", "$libmcrypt_config_version", mcrypt_check_version(NULL) ); printf("*** was found! If libmcrypt-config was correct, then it is best\n"); printf("*** to remove the old version of LIBMCRYPT. You may also be able to fix the error\n"); printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n"); printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n"); printf("*** required on your system.\n"); printf("*** If libmcrypt-config was wrong, set the environment variable LIBMCRYPT_CONFIG\n"); printf("*** to point to the correct copy of libmcrypt-config, and remove the file config.cache\n"); printf("*** before re-running configure\n"); } else if ( strcmp(mcrypt_check_version(NULL), LIBMCRYPT_VERSION ) ) { printf("\n*** LIBMCRYPT header file (version %s) does not match\n", LIBMCRYPT_VERSION); printf("*** library (version %s)\n", mcrypt_check_version(NULL) ); } else { if ( mcrypt_check_version( "$min_libmcrypt_version" ) ) { return 0; } else { printf("no\n*** An old version of LIBMCRYPT (%s) was found.\n", mcrypt_check_version(NULL) ); printf("*** You need a version of LIBMCRYPT newer than %s. The latest version of\n", "$min_libmcrypt_version" ); printf("*** LIBMCRYPT is always available from ftp://mcrypt.hellug.gr/pub/mcrypt.\n"); printf("*** \n"); printf("*** If you have already installed a sufficiently new version, this error\n"); printf("*** probably means that the wrong copy of the libmcrypt-config shell script is\n"); printf("*** being found. The easiest way to fix this is to remove the old version\n"); printf("*** of LIBMCRYPT, but you can also set the LIBMCRYPT_CONFIG environment to point to the\n"); printf("*** correct copy of libmcrypt-config. (In this case, you will have to\n"); printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n"); printf("*** so that the correct libraries are found at run-time)\n"); } } return 1; #endif /* 20010201 */ } ],, no_libmcrypt=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" if test "x$no_libmcrypt" = x ; then AC_MSG_RESULT(yes) ifelse([$2], , :, [$2]) else if test -f conf.libmcrypttest ; then : else AC_MSG_RESULT(no) fi if test -f conf.libmcrypttest ; then : else echo "*** Could not run libmcrypt test program, checking why..." CFLAGS="$CFLAGS $LIBMCRYPT_CFLAGS" LIBS="$LIBS $LIBMCRYPT_LIBS" AC_TRY_LINK([ #include #include #include #include ], [ #if MCRYPT_API_VERSION <= 20010201 #if MCRYPT_API_VERSION <= 19991015 /* version 2.2 */ int x = mcrypt_get_key_size(MCRYPT_TWOFISH_128); return 0; #else /* version 2.4 before 11 */ MCRYPT td = mcrypt_module_open("twofish", NULL, "cbc", NULL); mcrypt_module_close(td); return 0; #endif /* 19991015 */ #else return !!mcrypt_check_version(NULL); #endif /* 20010201 */ ], [ echo "*** The test program compiled, but did not run. This usually means" echo "*** that the run-time linker is not finding LIBMCRYPT or finding the wrong" echo "*** version of LIBMCRYPT. If it is not finding LIBMCRYPT, you'll need to set your" echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" echo "*** to the installed location Also, make sure you have run ldconfig if that" echo "*** is required on your system" echo "***" echo "*** If you have an old version installed, it is best to remove it, although" echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" echo "***" ], [ echo "*** The test program failed to compile or link. See the file config.log for the" echo "*** exact error that occured. This usually means LIBMCRYPT was incorrectly installed" echo "*** or that you have moved LIBMCRYPT since it was installed. In the latter case, you" echo "*** may want to edit the libmcrypt-config script: $LIBMCRYPT_CONFIG" ]) CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" fi LIBMCRYPT_CFLAGS="" LIBMCRYPT_LIBS="" ifelse([$3], , :, [$3]) fi rm -f conf.libmcrypttest AC_SUBST(LIBMCRYPT_CFLAGS) AC_SUBST(LIBMCRYPT_LIBS) ]) dnl *-*wedit:notab*-* Please keep this as the last line. PK0]l??doc/alt-krb5-libs/READMEnu[ Kerberos Version 5, Release 1.17 Release Notes The MIT Kerberos Team Copyright and Other Notices --------------------------- Copyright (C) 1985-2019 by the Massachusetts Institute of Technology and its contributors. All rights reserved. Please see the file named NOTICE for additional notices. Documentation ------------- Unified documentation for Kerberos V5 is available in both HTML and PDF formats. The table of contents of the HTML format documentation is at doc/html/index.html, and the PDF format documentation is in the doc/pdf directory. Additionally, you may find copies of the HTML format documentation online at http://web.mit.edu/kerberos/krb5-latest/doc/ for the most recent supported release, or at http://web.mit.edu/kerberos/krb5-devel/doc/ for the release under development. More information about Kerberos may be found at http://web.mit.edu/kerberos/ and at the MIT Kerberos Consortium web site http://kerberos.org/ Building and Installing Kerberos 5 ---------------------------------- Build documentation is in doc/html/build/index.html or doc/pdf/build.pdf. The installation guide is in doc/html/admin/install.html or doc/pdf/install.pdf. If you are attempting to build under Windows, please see the src/windows/README file. Reporting Bugs -------------- Please report any problems/bugs/comments by sending email to krb5-bugs@mit.edu. You may view bug reports by visiting http://krbdev.mit.edu/rt/ and using the "Guest Login" button. Please note that the web interface to our bug database is read-only for guests, and the primary way to interact with our bug database is via email. DES transition -------------- The Data Encryption Standard (DES) is widely recognized as weak. The krb5-1.7 release contains measures to encourage sites to migrate away from using single-DES cryptosystems. Among these is a configuration variable that enables "weak" enctypes, which defaults to "false" beginning with krb5-1.8. Major changes in 1.17 (2019-01-08) ---------------------------------- Administrator experience: * A new Kerberos database module using the Lightning Memory-Mapped Database library (LMDB) has been added. The LMDB KDB module should be more performant and more robust than the DB2 module, and may become the default module for new databases in a future release. * "kdb5_util dump" will no longer dump policy entries when specific principal names are requested. Developer experience: * The new krb5_get_etype_info() API can be used to retrieve enctype, salt, and string-to-key parameters from the KDC for a client principal. * The new GSS_KRB5_NT_ENTERPRISE_NAME name type allows enterprise principal names to be used with GSS-API functions. * KDC and kadmind modules which call com_err() will now write to the log file in a format more consistent with other log messages. * Programs which use large numbers of memory credential caches should perform better. Protocol evolution: * The SPAKE pre-authentication mechanism is now supported. This mechanism protects against password dictionary attacks without requiring any additional infrastructure such as certificates. SPAKE is enabled by default on clients, but must be manually enabled on the KDC for this release. * PKINIT freshness tokens are now supported. Freshness tokens can protect against scenarios where an attacker uses temporary access to a smart card to generate authentication requests for the future. * Password change operations now prefer TCP over UDP, to avoid spurious error messages about replays when a response packet is dropped. * The KDC now supports cross-realm S4U2Self requests when used with a third-party KDB module such as Samba's. The client code for cross-realm S4U2Self requests is also now more robust. User experience: * The new ktutil addent -f flag can be used to fetch salt information from the KDC for password-based keys. * The new kdestroy -p option can be used to destroy a credential cache within a collection by client principal name. * The Kerberos man page has been restored, and documents the environment variables that affect programs using the Kerberos library. Code quality: * Python test scripts now use Python 3. * Python test scripts now display markers in verbose output, making it easier to find where a failure occurred within the scripts. * The Windows build system has been simplified and updated to work with more recent versions of Visual Studio. A large volume of unused Windows-specific code has been removed. Visual Studio 2013 or later is now required. krb5-1.17 changes by ticket ID ------------------------------ 7905 Password changes can result in replay error 8202 memory ccache cursors are invalidated by initialize 8270 No logging when a non-root ksu with command fails authorization 8587 ktutil addent should be able to fetch etype-info2 for principal 8629 etype-info not included in hint list for REQUIRES_HW_AUTH principals 8630 Logging from KDC/kadmind plugin modules 8634 Trace log on k5tls load failure 8635 Fix a few German translation prepositions 8636 PKINIT certid option cannot handle leading zero 8641 Make public headers work with gcc -Wundef 8642 etype-info conflated for initial, final reply key enctype 8647 Add SPAKE preauth support 8648 Implement PKINIT freshness tokens 8650 Exit with status 0 from kadmind 8651 profile library may try to reread from special device files 8652 Report extended errors in kinit -k -t KDB: 8653 Include preauth name in trace output if possible 8654 Prevent fallback from SPAKE to encrypted timestamp 8655 Need per-realm client configuration to deny encrypted timestamp 8657 SPAKE support for Windows build 8659 SPAKE client asks for password before checking second-factor support 8661 ksu segfaults when argc == 0 8662 Windows README does not document MFC requirement 8663 TLS is not free on library unload 8664 Avoid simultaneous KDB/ulog locks in ulog_replay 8665 Display more extended errors in kdb5_util 8673 Improve error for kadmind -proponly without iprop 8674 Add LMDB KDB module 8677 Escape curly braces in def-check.pl regexes 8678 Don't specify MFC library in Leash build 8679 Fix Leash build error with recent Visual Studio 8680 Update kfw installer for VS2017, WiX 3.11.1 8682 Stop building CNS for Windows 8684 Fix option parsing on Windows 8685 Make plugin auto-registration work on Windows 8686 Process profile includedir in sorted order 8687 Repeated lookups of local computer name on Windows 8689 t_path.c build failure with NDEBUG 8690 Fix Windows strerror_r() implementation 8691 Use pkg.m4 macros 8692 Make docs build python3-compatible 8693 Resource leak in domain_fallback_realm() 8694 Add documentation on dictionary attacks 8695 Resource leak in krb5_524_conv_principal() 8696 Resource leak in krb5_425_conv_principal() 8697 Resource leak in krb5_gss_inquire_cred() 8698 Resource leak in aname_replacer() 8699 Resource leak in k5_os_hostaddr() 8700 Resource leak in krb5int_get_fq_local_hostname() 8702 Resource leak in kdb5_purge_mkeys() 8703 Resource leak in RPC UDP cache code 8704 Resource leak in read_secret_file() 8707 Resource leak in ulog_map() 8708 Incorrect error handling in OTP plugin 8709 Explicitly look for python2 in configure.in 8710 Convert Python tests to Python 3 8711 Use SHA-256 instead of MD5 for audit ticket IDs 8713 Zap copy of secret in RC4 string-to-key 8715 Make krb5kdc -p affect TCP ports 8716 Remove outdated note in krb5kdc man page 8718 krb5_get_credentials incorrectly matches user to user ticket 8719 Extend gss-sample timeout from 10s to 300s 8720 Don't include all MEMORY ccaches in collection 8721 Don't tag S4U2Proxy result creds as user-to-user 8722 Use a hash table for MEMORY ccache resolution 8723 Use PTHREAD_CFLAGS when testing for getpwnam_r() 8724 Add kdestroy -p option 8725 Update many documentation links to https 8726 Null deref on some invalid PKINIT identities 8727 Check strdup return in kadm5_get_config_params() 8728 doc: kswitch manual "see also" subsection typo 8729 Memory leak in gss_add_cred() creation case 8730 Add kvno option for user-to-user 8731 Document that DESTDIR must be an absolute path 8732 Fix name of .pdb file in ccapi/test/Makefile.in 8733 Multiple pkinit_identities semantics are unclear and perhaps not useful 8734 gss_add_cred() aliases memory when creating extended cred 8736 Check mech cred in gss_inquire_cred_by_mech() 8737 gss_add_cred() ignores desired_name if creating a new credential 8738 Use the term "replica KDC" in source and docs 8741 S4U2Self client code fails with no default realm 8742 Use "replica" in iprop settings 8743 Fix incorrect TRACE usages to use {str} 8744 KDC/kadmind may not follow master key change before purge_mkeys 8745 libss without readline can interfere with reading passwords 8746 Fix 64-bit Windows socket write error handling 8747 Allow referrals for cross-realm S4U2Self requests 8748 Add more constraints to S4U2Self processing 8749 Add PAC APIs which can include a client realm 8750 Resource leak in ktutil_add() 8751 Fix up kdb5_util documentation 8752 Don't dump policies if principals are specified 8753 Prevent SIGPIPE from socket writes on UNIX-likes 8754 Correct kpasswd_server description in krb5.conf(5) 8755 Bring back general kerberos man page 8756 Add GSS_KRB5_NT_ENTERPRISE_NAME name type 8757 Start S4U2Self realm lookup at server realm 8759 Resource leak in kadm5_randkey_principal_3() 8760 Retry KCM writes once on remote hangup 8762 Fix spelling of auth_to_local example 8763 Ignore password attributes for S4U2Self requests 8767 Remove incorrect KDC assertion 8768 Fix double-close in ksu get_authorized_princ_names 8769 Fix build issues with Solaris native compiler Acknowledgements ---------------- Past Sponsors of the MIT Kerberos Consortium: Apple Carnegie Mellon University Centrify Corporation Columbia University Cornell University The Department of Defense of the United States of America (DoD) Fidelity Investments Google Iowa State University MIT Michigan State University Microsoft MITRE Corporation Morgan-Stanley The National Aeronautics and Space Administration of the United States of America (NASA) Network Appliance (NetApp) Nippon Telephone and Telegraph (NTT) US Government Office of the National Coordinator for Health Information Technology (ONC) Oracle Pennsylvania State University Red Hat Stanford University TeamF1, Inc. The University of Alaska The University of Michigan The University of Pennsylvania Past and present members of the Kerberos Team at MIT: Danilo Almeida Jeffrey Altman Justin Anderson Richard Basch Mitch Berger Jay Berkenbilt Andrew Boardman Bill Bryant Steve Buckley Joe Calzaretta John Carr Mark Colan Don Davis Sarah Day Alexandra Ellwood Carlos Garay Dan Geer Nancy Gilman Matt Hancher Thomas Hardjono Sam Hartman Paul Hill Marc Horowitz Eva Jacobus Miroslav Jurisic Barry Jaspan Benjamin Kaduk Geoffrey King Kevin Koch John Kohl HaoQi Li Jonathan Lin Peter Litwack Scott McGuire Steve Miller Kevin Mitchell Cliff Neuman Paul Park Ezra Peisach Chris Provenzano Ken Raeburn Jon Rochlis Jeff Schiller Jen Selby Robert Silk Bill Sommerfeld Jennifer Steiner Ralph Swick Brad Thompson Harry Tsai Zhanna Tsitkova Ted Ts'o Marshall Vale Taylor Yu The following external contributors have provided code, patches, bug reports, suggestions, and valuable resources: Ian Abbott Brandon Allbery Russell Allbery Brian Almeida Michael B Allen Pooja Anil Heinz-Ado Arnolds Derek Atkins Mark Bannister David Bantz Alex Baule David Benjamin Thomas Bernard Adam Bernstein Arlene Berry Jeff Blaine Radoslav Bodo Sumit Bose Emmanuel Bouillon Isaac Boukris Philip Brown Samuel Cabrero Michael Calmer Andrea Campi Julien Chaffraix Puran Chand Ravi Channavajhala Srinivas Cheruku Leonardo Chiquitto Seemant Choudhary Howard Chu Andrea Cirulli Christopher D. Clausen Kevin Coffman Simon Cooper Sylvain Cortes Ian Crowther Arran Cudbard-Bell Jeff D'Angelo Nalin Dahyabhai Mark Davies Dennis Davis Alex Dehnert Mark Deneen Günther Deschner John Devitofranceschi Marc Dionne Roland Dowdeswell Dorian Ducournau Viktor Dukhovni Jason Edgecombe Mark Eichin Shawn M. Emery Douglas E. Engert Peter Eriksson Juha Erkkilä Gilles Espinasse Ronni Feldt Bill Fellows JC Ferguson Remi Ferrand Paul Fertser Fabiano Fidêncio William Fiveash Jacques Florent Ákos Frohner Sebastian Galiano Marcus Granado Dylan Gray Scott Grizzard Helmut Grohne Steve Grubb Philip Guenther Timo Gurr Dominic Hargreaves Robbie Harwood John Hascall Jakob Haufe Matthieu Hautreux Jochen Hein Paul B. Henson Jeff Hodges Christopher Hogan Love Hörnquist Åstrand Ken Hornstein Henry B. Hotz Luke Howard Jakub Hrozek Shumon Huque Jeffrey Hutzelman Sergey Ilinykh Wyllys Ingersoll Holger Isenberg Spencer Jackson Diogenes S. Jesus Pavel Jindra Brian Johannesmeyer Joel Johnson Alexander Karaivanov Anders Kaseorg Bar Katz Zentaro Kavanagh Mubashir Kazia W. Trevor King Patrik Kis Martin Kittel Matthew Krupcale Mikkel Kruse Reinhard Kugler Tomas Kuthan Pierre Labastie Chris Leick Volker Lendecke Jan iankko Lieskovsky Todd Lipcon Oliver Loch Chris Long Kevin Longfellow Frank Lonigro Jon Looney Nuno Lopes Todd Lubin Ryan Lynch Roland Mainz Sorin Manolache Andrei Maslennikov Michael Mattioli Nathaniel McCallum Greg McClement Cameron Meadors Alexey Melnikov Franklyn Mendez Markus Moeller Kyle Moffett Paul Moore Keiichi Mori Michael Morony Zbysek Mraz Edward Murrell Nikos Nikoleris Felipe Ortega Michael Osipov Andrej Ota Dmitri Pal Javier Palacios Tom Parker Ezra Peisach Alejandro Perez Zoran Pericic W. Michael Petullo Mark Phalan Sharwan Ram Brett Randall Jonathan Reams Jonathan Reed Robert Relyea Tony Reix Martin Rex Pat Riehecky Jason Rogers Matt Rogers Nate Rosenblum Solly Ross Mike Roszkowski Guillaume Rousse Joshua Schaeffer Andreas Schneider Paul Seyfert Tom Shaw Jim Shi Peter Shoults Richard Silverman Cel Skeggs Simo Sorce Michael Spang Michael Ströder Bjørn Tore Sund Joe Travaglini Tim Uglow Rathor Vipin Denis Vlasenko Jorgen Wahlsten Stef Walter Max (Weijun) Wang John Washington Stef Walter Xi Wang Nehal J Wani Kevin Wasserman Margaret Wasserman Marcus Watts Andreas Wiese Simon Wilkinson Nicolas Williams Ross Wilper Augustin Wolf David Woodhouse Tsu-Phong Wu Xu Qiang Neng Xue Zhaomo Yang Nickolai Zeldovich Bean Zhang Hanz van Zijst Gertjan Zwartjes The above is not an exhaustive list; many others have contributed in various ways to the MIT Kerberos development effort over the years. Other acknowledgments (for bug reports and patches) are in the doc/CHANGES file. PK0]l>doc/alt-krb5-libs/NOTICEnu[Copyright (C) 1985-2019 by the Massachusetts Institute of Technology. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Downloading of this software may constitute an export of cryptographic software from the United States of America that is subject to the United States Export Administration Regulations (EAR), 15 CFR 730-774. Additional laws or regulations may apply. It is the responsibility of the person or entity contemplating export to comply with all applicable export laws and regulations, including obtaining any required license from the U.S. government. The U.S. government prohibits export of encryption source code to certain countries and individuals, including, but not limited to, the countries of Cuba, Iran, North Korea, Sudan, Syria, and residents and nationals of those countries. Documentation components of this software distribution are licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. (https://creativecommons.org/licenses/by-sa/3.0/) Individual source code files are copyright MIT, Cygnus Support, Novell, OpenVision Technologies, Oracle, Red Hat, Sun Microsystems, FundsXpress, and others. Project Athena, Athena, Athena MUSE, Discuss, Hesiod, Kerberos, Moira, and Zephyr are trademarks of the Massachusetts Institute of Technology (MIT). No commercial use of these trademarks may be made without prior written permission of MIT. "Commercial use" means use of a name in a product or other for-profit manner. It does NOT prevent a commercial firm from referring to the MIT trademarks in order to convey information (although in doing so, recognition of their trademark status should be given). ====================================================================== The following copyright and permission notice applies to the OpenVision Kerberos Administration system located in "kadmin/create", "kadmin/dbutil", "kadmin/passwd", "kadmin/server", "lib/kadm5", and portions of "lib/rpc": Copyright, OpenVision Technologies, Inc., 1993-1996, All Rights Reserved WARNING: Retrieving the OpenVision Kerberos Administration system source code, as described below, indicates your acceptance of the following terms. If you do not agree to the following terms, do not retrieve the OpenVision Kerberos administration system. You may freely use and distribute the Source Code and Object Code compiled from it, with or without modification, but this Source Code is provided to you "AS IS" EXCLUSIVE OF ANY WARRANTY, INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, OR ANY OTHER WARRANTY, WHETHER EXPRESS OR IMPLIED. IN NO EVENT WILL OPENVISION HAVE ANY LIABILITY FOR ANY LOST PROFITS, LOSS OF DATA OR COSTS OF PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THIS AGREEMENT, INCLUDING, WITHOUT LIMITATION, THOSE RESULTING FROM THE USE OF THE SOURCE CODE, OR THE FAILURE OF THE SOURCE CODE TO PERFORM, OR FOR ANY OTHER REASON. OpenVision retains all copyrights in the donated Source Code. OpenVision also retains copyright to derivative works of the Source Code, whether created by OpenVision or by a third party. The OpenVision copyright notice must be preserved if derivative works are made based on the donated Source Code. OpenVision Technologies, Inc. has donated this Kerberos Administration system to MIT for inclusion in the standard Kerberos 5 distribution. This donation underscores our commitment to continuing Kerberos technology development and our gratitude for the valuable work which has been performed by MIT and the Kerberos community. ====================================================================== Portions contributed by Matt Crawford "crawdad@fnal.gov" were work performed at Fermi National Accelerator Laboratory, which is operated by Universities Research Association, Inc., under contract DE-AC02-76CHO3000 with the U.S. Department of Energy. ====================================================================== Portions of "src/lib/crypto" have the following copyright: Copyright (C) 1998 by the FundsXpress, INC. All rights reserved. Export of this software from the United States of America may require a specific license from the United States Government. It is the responsibility of any person or organization contemplating export to obtain such a license before exporting. WITHIN THAT CONSTRAINT, permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of FundsXpress. not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. FundsXpress makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ====================================================================== The implementation of the AES encryption algorithm in "src/lib/crypto/builtin/aes" has the following copyright: Copyright (C) 2001, Dr Brian Gladman "brg@gladman.uk.net", Worcester, UK. All rights reserved. LICENSE TERMS The free distribution and use of this software in both source and binary form is allowed (with or without changes) provided that: 1. distributions of this source code include the above copyright notice, this list of conditions and the following disclaimer; 2. distributions in binary form include the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other associated materials; 3. the copyright holder's name is not used to endorse products built using this software without specific written permission. DISCLAIMER This software is provided 'as is' with no explcit or implied warranties in respect of any properties, including, but not limited to, correctness and fitness for purpose. ====================================================================== Portions contributed by Red Hat, including the pre-authentication plug-in framework and the NSS crypto implementation, contain the following copyright: Copyright (C) 2006 Red Hat, Inc. Portions copyright (C) 2006 Massachusetts Institute of Technology All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Red Hat, Inc., nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== The bundled verto source code is subject to the following license: Copyright 2011 Red Hat, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ====================================================================== The MS-KKDCP client implementation has the following copyright: Copyright 2013,2014 Red Hat, Inc. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== The implementations of GSSAPI mechglue in GSSAPI-SPNEGO in "src/lib/gssapi", including the following files: lib/gssapi/generic/gssapi_err_generic.et lib/gssapi/mechglue/g_accept_sec_context.c lib/gssapi/mechglue/g_acquire_cred.c lib/gssapi/mechglue/g_canon_name.c lib/gssapi/mechglue/g_compare_name.c lib/gssapi/mechglue/g_context_time.c lib/gssapi/mechglue/g_delete_sec_context.c lib/gssapi/mechglue/g_dsp_name.c lib/gssapi/mechglue/g_dsp_status.c lib/gssapi/mechglue/g_dup_name.c lib/gssapi/mechglue/g_exp_sec_context.c lib/gssapi/mechglue/g_export_name.c lib/gssapi/mechglue/g_glue.c lib/gssapi/mechglue/g_imp_name.c lib/gssapi/mechglue/g_imp_sec_context.c lib/gssapi/mechglue/g_init_sec_context.c lib/gssapi/mechglue/g_initialize.c lib/gssapi/mechglue/g_inquire_context.c lib/gssapi/mechglue/g_inquire_cred.c lib/gssapi/mechglue/g_inquire_names.c lib/gssapi/mechglue/g_process_context.c lib/gssapi/mechglue/g_rel_buffer.c lib/gssapi/mechglue/g_rel_cred.c lib/gssapi/mechglue/g_rel_name.c lib/gssapi/mechglue/g_rel_oid_set.c lib/gssapi/mechglue/g_seal.c lib/gssapi/mechglue/g_sign.c lib/gssapi/mechglue/g_store_cred.c lib/gssapi/mechglue/g_unseal.c lib/gssapi/mechglue/g_userok.c lib/gssapi/mechglue/g_utils.c lib/gssapi/mechglue/g_verify.c lib/gssapi/mechglue/gssd_pname_to_uid.c lib/gssapi/mechglue/mglueP.h lib/gssapi/mechglue/oid_ops.c lib/gssapi/spnego/gssapiP_spnego.h lib/gssapi/spnego/spnego_mech.c and the initial implementation of incremental propagation, including the following new or changed files: include/iprop_hdr.h kadmin/server/ipropd_svc.c lib/kdb/iprop.x lib/kdb/kdb_convert.c lib/kdb/kdb_log.c lib/kdb/kdb_log.h lib/krb5/error_tables/kdb5_err.et kprop/kpropd_rpc.c kprop/kproplog.c are subject to the following license: Copyright (C) 2004 Sun Microsystems, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ====================================================================== Kerberos V5 includes documentation and software developed at the University of California at Berkeley, which includes this copyright notice: Copyright (C) 1983 Regents of the University of California. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== Portions contributed by Novell, Inc., including the LDAP database backend, are subject to the following license: Copyright (C) 2004-2005, Novell, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * The copyright holder's name is not used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== Portions funded by Sandia National Laboratory and developed by the University of Michigan's Center for Information Technology Integration, including the PKINIT implementation, are subject to the following license: COPYRIGHT (C) 2006-2007 THE REGENTS OF THE UNIVERSITY OF MICHIGAN ALL RIGHTS RESERVED Permission is granted to use, copy, create derivative works and redistribute this software and such derivative works for any purpose, so long as the name of The University of Michigan is not used in any advertising or publicity pertaining to the use of distribution of this software without specific, written prior authorization. If the above copyright notice or any other identification of the University of Michigan is included in any copy of any portion of this software, then the disclaimer below must also be included. THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. ====================================================================== The pkcs11.h file included in the PKINIT code has the following license: Copyright 2006 g10 Code GmbH Copyright 2006 Andreas Jellinghaus This file is free software; as a special exception the author gives unlimited permission to copy and/or distribute it, with or without modifications, as long as this notice is preserved. This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY, to the extent permitted by law; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ====================================================================== Portions contributed by Apple Inc. are subject to the following license: Copyright 2004-2008 Apple Inc. All Rights Reserved. Export of this software from the United States of America may require a specific license from the United States Government. It is the responsibility of any person or organization contemplating export to obtain such a license before exporting. WITHIN THAT CONSTRAINT, permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Apple Inc. not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. Apple Inc. makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ====================================================================== The implementations of UTF-8 string handling in src/util/support and src/lib/krb5/unicode are subject to the following copyright and permission notice: The OpenLDAP Public License Version 2.8, 17 August 2003 Redistribution and use of this software and associated documentation ("Software"), with or without modification, are permitted provided that the following conditions are met: 1. Redistributions in source form must retain copyright statements and notices, 2. Redistributions in binary form must reproduce applicable copyright statements and notices, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution, and 3. Redistributions must contain a verbatim copy of this document. The OpenLDAP Foundation may revise this license from time to time. Each revision is distinguished by a version number. You may use this Software under terms of this license revision or under the terms of any subsequent revision of the license. THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S) OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The names of the authors and copyright holders must not be used in advertising or otherwise to promote the sale, use or other dealing in this Software without specific, written prior permission. Title to copyright in this Software shall at all times remain with copyright holders. OpenLDAP is a registered trademark of the OpenLDAP Foundation. Copyright 1999-2003 The OpenLDAP Foundation, Redwood City, California, USA. All Rights Reserved. Permission to copy and distribute verbatim copies of this document is granted. ====================================================================== Marked test programs in src/lib/krb5/krb have the following copyright: Copyright (C) 2006 Kungliga Tekniska Högskola (Royal Institute of Technology, Stockholm, Sweden). All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of KTH nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== The KCM Mach RPC definition file used on macOS has the following copyright: Copyright (C) 2009 Kungliga Tekniska Högskola (Royal Institute of Technology, Stockholm, Sweden). All rights reserved. Portions Copyright (C) 2009 Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the Institute nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== Portions of the RPC implementation in src/lib/rpc and src/include/gssrpc have the following copyright and permission notice: Copyright (C) 2010, Oracle America, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the "Oracle America, Inc." nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== Copyright (C) 2006,2007,2009 NTT (Nippon Telegraph and Telephone Corporation). All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer as the first lines of this file unmodified. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY NTT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NTT BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== Copyright 2000 by Carnegie Mellon University All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Carnegie Mellon University not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ====================================================================== Copyright (C) 2002 Naval Research Laboratory (NRL/CCS) Permission to use, copy, modify and distribute this software and its documentation is hereby granted, provided that both the copyright notice and this permission notice appear in all copies of the software, derivative works or modified versions, and any portions thereof. NRL ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION AND DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. ====================================================================== Copyright (C) 1991, 1992, 1994 by Cygnus Support. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. Cygnus Support makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ====================================================================== Copyright (C) 2006 Secure Endpoints Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ====================================================================== Portions of the implementation of the Fortuna-like PRNG are subject to the following notice: Copyright (C) 2005 Marko Kreen All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Copyright (C) 1994 by the University of Southern California EXPORT OF THIS SOFTWARE from the United States of America may require a specific license from the United States Government. It is the responsibility of any person or organization contemplating export to obtain such a license before exporting. WITHIN THAT CONSTRAINT, permission to copy, modify, and distribute this software and its documentation in source and binary forms is hereby granted, provided that any documentation or other materials related to such distribution or use acknowledge that the software was developed by the University of Southern California. DISCLAIMER OF WARRANTY. THIS SOFTWARE IS PROVIDED "AS IS". The University of Southern California MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. By way of example, but not limitation, the University of Southern California MAKES NO REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE. The University of Southern California shall not be held liable for any liability nor for any direct, indirect, or consequential damages with respect to any claim by the user or distributor of the ksu software. ====================================================================== Copyright (C) 1995 The President and Fellows of Harvard University This code is derived from software contributed to Harvard by Jeremy Rassen. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the University of California, Berkeley and its contributors. 4. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== Copyright (C) 2008 by the Massachusetts Institute of Technology. Copyright 1995 by Richard P. Basch. All Rights Reserved. Copyright 1995 by Lehman Brothers, Inc. All Rights Reserved. Export of this software from the United States of America may require a specific license from the United States Government. It is the responsibility of any person or organization contemplating export to obtain such a license before exporting. WITHIN THAT CONSTRAINT, permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Richard P. Basch, Lehman Brothers and M.I.T. not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. Richard P. Basch, Lehman Brothers and M.I.T. make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ====================================================================== The following notice applies to "src/lib/krb5/krb/strptime.c" and "src/include/k5-queue.h". Copyright (C) 1997, 1998 The NetBSD Foundation, Inc. All rights reserved. This code was contributed to The NetBSD Foundation by Klaus Klein. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the NetBSD Foundation, Inc. and its contributors. 4. Neither the name of The NetBSD Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== The following notice applies to Unicode library files in "src/lib/krb5/unicode": Copyright 1997, 1998, 1999 Computing Research Labs, New Mexico State University Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ====================================================================== The following notice applies to "src/util/support/strlcpy.c": Copyright (C) 1998 Todd C. Miller "Todd.Miller@courtesan.com" Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ====================================================================== The following notice applies to "src/util/profile/argv_parse.c" and "src/util/profile/argv_parse.h": Copyright 1999 by Theodore Ts'o. Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THEODORE TS'O (THE AUTHOR) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. (Isn't it sick that the U.S. culture of lawsuit-happy lawyers requires this kind of disclaimer?) ====================================================================== The following notice applies to SWIG-generated code in "src/util/profile/profile_tcl.c": Copyright (C) 1999-2000, The University of Chicago This file may be freely redistributed without license or fee provided this copyright message remains intact. ====================================================================== The following notice applies to portiions of "src/lib/rpc" and "src/include/gssrpc": Copyright (C) 2000 The Regents of the University of Michigan. All rights reserved. Copyright (C) 2000 Dug Song "dugsong@UMICH.EDU". All rights reserved, all wrongs reversed. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== Implementations of the MD4 algorithm are subject to the following notice: Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. License to copy and use this software is granted provided that it is identified as the "RSA Data Security, Inc. MD4 Message Digest Algorithm" in all material mentioning or referencing this software or this function. License is also granted to make and use derivative works provided that such works are identified as "derived from the RSA Data Security, Inc. MD4 Message Digest Algorithm" in all material mentioning or referencing the derived work. RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided "as is" without express or implied warranty of any kind. These notices must be retained in any copies of any part of this documentation and/or software. ====================================================================== Implementations of the MD5 algorithm are subject to the following notice: Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. License to copy and use this software is granted provided that it is identified as the "RSA Data Security, Inc. MD5 Message- Digest Algorithm" in all material mentioning or referencing this software or this function. License is also granted to make and use derivative works provided that such works are identified as "derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing the derived work. RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided "as is" without express or implied warranty of any kind. These notices must be retained in any copies of any part of this documentation and/or software. ====================================================================== The following notice applies to "src/lib/crypto/crypto_tests/t_mddriver.c": Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All rights reserved. RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided "as is" without express or implied warranty of any kind. These notices must be retained in any copies of any part of this documentation and/or software. ====================================================================== Portions of "src/lib/krb5" are subject to the following notice: Copyright (C) 1994 CyberSAFE Corporation. Copyright 1990,1991,2007,2008 by the Massachusetts Institute of Technology. All Rights Reserved. Export of this software from the United States of America may require a specific license from the United States Government. It is the responsibility of any person or organization contemplating export to obtain such a license before exporting. WITHIN THAT CONSTRAINT, permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of M.I.T. not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. Furthermore if you modify this software you must label your software as modified software and not distribute it in such a fashion that it might be confused with the original M.I.T. software. Neither M.I.T., the Open Computing Security Group, nor CyberSAFE Corporation make any representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ====================================================================== Portions contributed by PADL Software are subject to the following license: Copyright (c) 2011, PADL Software Pty Ltd. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of PADL Software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== The bundled libev source code is subject to the following license: All files in libev are Copyright (C)2007,2008,2009 Marc Alexander Lehmann. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Alternatively, the contents of this package may be used under the terms of the GNU General Public License ("GPL") version 2 or any later version, in which case the provisions of the GPL are applicable instead of the above. If you wish to allow the use of your version of this package only under the terms of the GPL and not to allow others to use your version of this file under the BSD license, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the GPL in this and the other files of this package. If you do not delete the provisions above, a recipient may use your version of this file under either the BSD or the GPL. ====================================================================== Files copied from the Intel AESNI Sample Library are subject to the following license: Copyright (C) 2010, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== The following notice applies to "src/ccapi/common/win/OldCC/autolock.hxx": Copyright (C) 1998 by Danilo Almeida. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== The following notice applies to portions of "src/plugins/preauth/spake/edwards25519.c" and "src/plugins/preauth/spake/edwards25519_tables.h": The MIT License (MIT) Copyright (c) 2015-2016 the fiat-crypto authors (see the AUTHORS file). Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ====================================================================== The following notice applies to portions of "src/plugins/preauth/spake/edwards25519.c": Copyright (c) 2015-2016, Google Inc. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. PK0]qM$locale/en_US/LC_MESSAGES/mit-krb5.monu[$,8`9Project-Id-Version: mit-krb5 1.10-prerelease Report-Msgid-Bugs-To: POT-Creation-Date: 2011-06-06 00:34-0400 PO-Revision-Date: 2011-06-06 00:35-0400 Last-Translator: Greg Hudson Language-Team: English MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); PK0]6  !locale/de/LC_MESSAGES/mit-krb5.monu[89 s()Ge23֚# .A Zfz ͛ "6Oh{  Ŝ|:#0+4`(r á Ρۡ) !.I` {&â(0Mc s*ʣ! @ N[o̤&= Vdyץ40H+y'ͦߦ 5M%@Z+uG8"@@A"é-*.Ykp٪ 7:r"Ϋ"6Yv ' ǬԬ,J-\)˭-2.?a !ڮm:j"!ȯB%-:S2)!($M=h#ʱ+CZw"!ɲ ! $.S+fdz!ܳ'&@[%lB%մ* &>G)+ε00+)\8A8I \fŷ).)>(h%Ҹ0(Ir,/0B*]4 ޺ =Ҽdpj!(Ⱦ-)I)d +#/!E1g. 2<',d'&9*.Y%p!*..#K1o&7';(>dF* *4'_" '(47:l/.C=J"/$B!^ !-"Or!)$+./*^3*0,*F1q*,2+.(Z1-)6 2D(w.,4.10`1)4'"&J&q++,*0H2y.+%0--^(1,*,?(l')!& (0*Y/-*6 'Dl56)42%g!OUU2t+*& Ef"+<)f1!'*%,Rm&)%4R/r# # ()CR)&"% %0(V'+<::9uB&/!H#j.4!()&R$y))$1*Kv%&& .26a%!!G1_($$7OXv#'"*'@]#x$6,5;b +*A%l('-(:/V0%0!Vx-%6*#Nm&*!1ShG-4"P"s '" / No %;J!] <LFLc##  # D!e 8#*0Nl">P![r; 07h}&C+1) [|-&/ :)C m$w#':W,t!4Mi0" ):J"#67;Vj./.)'-Q)()0$Z ()5)C@V% +^ $i2($15A w*0 76&T{!(,I g8*":V+k#,'Th 3/1$J o"|$&"%!4Vhx!$"6J^r&:Nbv)<Obu  3FYl4 % '0,X"$$) . )M &w  '   & B \ o  -    %* P :m ! '   *$ O  j   #    '# 'K  s , P M M%m".Fb~21"T,l2&<!\:~'$,38P " Y6E3= q!(-?m4*'QD+5$!F&X36519k'~(+1-2K~0"!< O ]kr!,5 D)R$|"!;Rq'$0'L$t%'(CLT)! &" I Y i   d!{!!!2!!* "*4"_"q""""/"/"&'#N#d#%#(#;#$ $6$&P$4w$"$ $)$%%@%]%w%%% % %%%&& &G&`&z&& &&&&'7'5P'"'.''3'9%("_("((B(P(,H)&u)')))")+*!=*)_*/*+**#*-#+Q+n++++++0,4,$O,t, ,,,2,-0-2K-3~-!-$-7-'1.Y.9v....O/UR/H/R/RD0=0?031FI1[1]15J2-2-2*2(3'03/X3'3+3(34#4=4Q4,d4*4*44&5"-5 P5\5#|5(5 5%5&55#6;Y6)696&6. 7O7`7z7777B7,848S8g8 8A8D8;.90j999(9)9)99(:4b:::$::/;B;(W;%;+;#;;?<4N<7<(<7<\=yy=b=cV>1>?>9,?"f?%?'?/?"@9*@Bd@@-@'@6ALAjA7AAA(A #BDB+aBB BB#BBC C$TZ$Z&Z2Z([;["O[Nr[1[[\%\I8\9\7\+\ ](],/]!\]~]"])]-]"^"8^7[^^8^!^(_$7_"\_'_?_5_`":` ]`+k`````` a3aSa ka a'aa2a1b#Lb$pb(b%bb2b#)c#Mc,qccccc!c d2)d\d?ld%d"d"de+eHeZezeeeeeee+f./f'^ff4ff f&f%g2>g*qg"gg1g2h6h%Ih&ohh'h!hh,i,Kixi6iiiij-jEjcj/wj/j=j$k:kUkok tkkkkk.k l*lHlfl&l5l&l m(m*Dm,om-m,mm n1nHn![n[}nnno#,oPo6cooo$o&op6/pfppNpp7p2qEq4cqq#q#q5qH-rvr,rrrr3r $s1sFsZszs sssss.s# t#Dtht&~tt t ttu u)uDAuuuuuuu-uv4vKv$`v0v0v/vw6wNwDmwJwwx#x3xOxcxvx*x x=x+y=3yqz)zWz {%+{+Q{!}{"{({#{|!|,?|@l|=|&|}.}E}X},x}+}}}~~/~8~ S~$t~~-~~~1!M'o32(J"s*6*#B$` !""2U#t!2҂#")FpӃ##%GmÄ%%D\tŅ 6So$Ć"( 'I)q,ׇ# ;\wƈ.$-Rj׉ ),=j   ۊ&/@Us/+Ӌ#"# Fg#Ì!!"@c"}%##A\o)% -D(`!Ǐߏ">a'|!Ɛ"/(Xvӑ$ &+!R"t-#Œ+'=V)lѓ!+"Mp!'є$"6 Vw  ȕ4T"q"%ݖ,$*(O'x,͗ &&?f*ʘ  #"C*f˙"#:%^"'Ԛ-)Jt!)Ǜ#,Jh:!*%L"r!+$$<!V)x#$ߞ !%!G#i%(ܟ -0N"Π:#5Y1w$ǡ+ #90])Т!1Skϣ %D`"}67פ6/F9v!˥!1Sm  Ҧ?ݦ#;ب71%iH<ة#)M+`( &)F^q  ܫ *06İB ><_3  *-61dɲ$- ?7$wBZ#M q8 ܴ&"5U4d ʵ2Jg~Ӷ;#T%x #( ?';g3׸-"#Pt*(Ϻ(-!gOT Y+e'0PD?ս)$7/\) !ľ)?+P|!.̿ ''1O " ,"+6 b;8)2<oC*J^C"M'?5<']dH@ 'L,t2)L0K!|(!% ?`1o$1K@DB#B'/$W|&5!";'LUt=7,@Nm2J<Y</?GC!  %*4_#8A8.W$,3$C1h%=AD@S;]+sxeH'&!610h0.#83P""B>>[3E,'.T -N)"L7bF$2C9&},D7CN-MLX[4NJ8/0 76'^!rAB<0VHF"0+S-C*,.Ix/(+&-Bp''%'7".Z03/8/W5106 /W670-'6U2.;7*-b319405e5.91:0l00060/g57309+j62-6.1e/1--'/U',./ 492n0<)19$k=`NBW7?~>P[2AE!=gC(G?Z8.N_Q>V2G5z>70'*X=?3/5ae-?@52v76NlgD:DTA1P $^9"BQ#NuL^0p=9G=a79&(,O.|).1(6_</#BB+3PN6*'+_ 1  / ) / =M  1 & 4 E& 3l D & + (8 0a 4 - 5 V+ ; Q )%:3`);?7:Br07.,MDz#-5Aw%#S5QP*+/M)f-- :4Z TJ JV*0/+5%aH%"%+/Q#82/!Q!j1*3Wsu"#(7(`%!4*&-Q)L09''a(9*(LSqjE}A8I.eS2@ '\ $ F * ;!JW! !+! !/!1"?I"^"5"8#?W#>#Q#8($ a$!$/$$;$A/%*q%%@%)%"&!;&-]&4&[&4'&Q'Bx'A','*(H((e(2(3(6(2,)=_),))))*0*H*2Z*&*$*<*=+DT++!+R+/(,'X,@,,<E-G-,-0-8(.Ia.+.:.2/ E/DP/!/2//0#0):0d080300 1..1L]1191!1*!2L28g222$2 23763n3333*33: 4/H4 x4(4 4-4,42)5(\5+5)555( 6,56+b6(6666677/7C7W7k77777777 8838G8[8o88888888 9929E9X9k9~9999999::):<:O:b:u:?: :6:8 ;CB;);4;3;$<><(Q<.z<*<!<5<,=H=)b=4="=$=4 >B>>(>->)>-?&0?RW?2?=?@7;@4s@@ @@A*AJA _A!A0A3A B9BoOBlB,,C+YC%C)C%C#CD?:D"zD(DDDE!EAEA\E@EEAE0AFrF"FFF:F+ G?LG*G'GTG-4HRbHHH3HIbJ{JTqKEK. L;L SL_L&xLLL2LBM&QM!xM*MEM; N(GN|pN@N;.O jO6vOAO)O:P$TPMyPKPOQHcQQ=Q<RE@RDR(RPRES`SCS-SSTT4TOTcTtT|T*TT(T0T-UVHU UU7U7V+IV uV$V.V V W-'W-UW-W,W:WX.8X!gXX#X!X&XWYWmY8Y YZ2Z/FZ2vZZZZZ[$[\\0\1E\w\0\B\]]8]N]_]Cw]4]*]^5^@M^3^E^_'_ @_7a_=_)_2`84`+m`$`"```(aAaUa!ga#a!a-aa"b!igOg)g"h0?hNph(h'h)i:iZi>oiai.j8?j&xj j"jjFj"/kFRkAkAk7l=UlJl8l.mMFmmm$mrmgnvnqoxo[vpAp5q]Jqbq` r@lrLr8r83s0ls3s<s.tC=t3t tttu9u2Xu4u7u9u22v ev2qv%v8v w/wB@wLw?w7x?Hx3xPx y! yBy#YyH}ySyzK"znz1z/zUzZA{M{J{5|>|IE|(|0|G|C1}u}-}*}}B ~M~-b~(~)~&~ U&?|?,I)lseoiՁ>?Z~Kق)%0O07+QEg;̄4Y=%B݅ &=*d-)6 0/=%m$/҇=@Ws'b͈008ac594'n)G*?3Gsw03(d,3ڌM;\l9?X!v+)Ď$ 4"Lqo5 -!LO)@Ɛ-M59/ۑ 3#!W(y%/ȒUN'4Εpt->yŞU?*!,2VB%*0762n R`jtQߢQ1f,H`[hĤ6F:}G526Gi8,=UQ!7YrY?;(/d /'֩**)?T3)Ȫ(<*XO?ӫ:5N1+MC0 t" Aŭ&1.6`0-Ȯ;72-j16ʯ<$>ac\Ű#",F9s+ٱ:'-GUB#!2.T@![A^B3!0R+jʵ۵ (!;JF4Ͷ2+R ~<EƷ: IG 4Ǹ2/3C1w"0̹7'5?]9#׺NJ&[(!ͻ!&+8RT%") GSf'#ǽ7/#/S*%9Ծ?0N/<46!7X6#  --Awo@@(Bi6(M ,Z10R"rYJ- x'N 41=f;fG>_1J <Jh5$.#8P5o+)52P' U%{ +$C^8tHGU>1$0]Sz":S i-K,H+b#(*, '()P"z.MBL-!5+:a",!1@G-g06:%8-^'#,:?@!!8:*U8P+ 63V(((#A=]=8=#P&t83/'Wt%(#92])!.#$.H'w#+))I#s$% !A)^/*,/+@0l#'2'D6a""%(&-T2m%$%);&e&23:8s'!+??%]0/&&B#i4(),H"u#'%# 0.!_&%' #(;>d-%!/6(f$'(+"3N!3'#2$-W;1#/%GmB%4)%.O:~),++<h-"#!$F1_1'6*=-h6$$&>)[)#& &4&[(A8-&4T0+-'!<^|0%6*J$c% "*-1K}'::.T)6$'=3e)761$h$"+KE"8%-S3r ' #*2N('')&$*K'v/7,,3#`):');Qq<11 O.p0&.6&-]296p+!$8=5v;8A!%cEFL:cC!$ ) %H n ( + 5 ' 4 P  i Tv smBagIqYz*A fpOt/3OO`\ /R>opQ2_Y/C-WDJUMtKeD[>+Hl3# VeZM4'I y,"Tqp#ygN?hS&D+v(] ""   >Dz_d 0"B'|l<}bT8cn b%,x0/3B }S!{ok%1!^-' d{d[@I9  #7]G kZzC)[<Czo(oxs^a.91"!af3[QUL+;W!{rP xVF+*,`m&6WA|cDw+W.fj:_=SGo= b_ )RB~7-p40Zfaq~`nM am iu6'4z89;GGvJ  t> 6YtPjgcS/, Q4q=$wUs[Cz2\6(Eghek# -pBuA.9c7bDe8o   \j"?i IK($,dcHZ/~h*0/Cq%3`+hq!V&-]PA;|<Tv:MH *%.O61PtF-NG|5+h:X&XtTLi!0mR -irXG&&'NHb;l^}SD+Y66nVFw9L s)NYyi 'f'o}dK4lJ9]56`|,TXLR2E75cH1U&1U&#Mw \$Q\a v(LKEkm1f($?W$C@tr}IFV_{?L~<X2Wj "5y0yEJu~Z@je}MgwFneVkl8u!_Oghe#r@258u.v{GvAN,7sx]Kxld3z)w!uS8bi@^)I;%;(s/ X%LcJW:?.[~#k@ nnIvT_.^:*3COxr;*43\Yf%)E*=,:YpZEA5K|{R SEP*Q^`8l?n TjJU }7 H9X'Zpy(`<|=q4$>NA=5^a]1B>2 74~ wN) mi02rk{U8bFj<>y#? FrHRuh=J"$-0P)O[]Ksxdg\BmVRQP7@M 125:< %Q$. -n do not reverse-resolve -K shows keytab entry keys -a displays the address list -d shows the submitted authorization data types -f shows credentials flags -s sets exit status based on valid tgt existence -t shows keytab entry timestamps Attribute flags Failed passwd attempt Key data Last failed auth Last successful auth Length Maximum renewable life Maximum ticket life Modification time Modified where Modifying principal Password expiration Password history Password history KVNO Password last changed Password policy Password policy switch Principal Principal expiration TL data [-lockoutduration time] [-maxlife time] [-minlife time] [-minlength length] [-minclasses number] [-history number] [-maxfailure number] [-failurecountinterval time] [-allowedkeysalts keysalts] [-randkey|-nokey] [-x db_princ_args]* [-expire expdate] [-pwexpire pwexpdate] [-maxlife maxtixlife] [-kvno kvno] [-policy policy] [-clearpolicy] [-pw password] [-maxrenewlife maxrenewlife] [-e keysaltlist] [{+|-}attribute] [-x db_princ_args]* [-expire expdate] [-pwexpire pwexpdate] [-maxlife maxtixlife] [-kvno kvno] [-policy policy] [-clearpolicy] [-maxrenewlife maxrenewlife] [-unlock] [{+|-}attribute] allow_postdated allow_forwardable allow_tgs_req allow_renewable allow_proxiable allow_dup_skey allow_tix requires_preauth requires_hwauth needchange allow_svr password_changing_service ok_as_delegate ok_to_auth_as_delegate no_auth_data_required where, [-x db_princ_args]* - any number of database specific arguments. Look at each database documentation for supported arguments (Default is credentials cache) -A destroy all credential caches in collection -A do not include addresses -A shows content of all credential caches -C canonicalize -E client is enterprise principal name -F not forwardable -P not proxiable -R renew -S service -T armor credential cache -V shows the Kerberos version and exits -V verbose -X [=] -a include addresses -c Kerberos 5 cache name -c specifies credentials cache -c specify name of credentials cache -e shows the encryption type -f forwardable -i use default client keytab (with -k) -i uses default client keytab if no name given -k specifies keytab -k use keytab -l lifetime -l lists credential caches in collection -n anonymous -p proxiable -p specify name of principal -q quiet mode -r renewable lifetime -s start time -t filename of keytab to use -v validate Addresses: Addresses: (none) Attributes changed : %d Entry block size : %u First serial # : First serial # : None First time stamp : %s First time stamp : None Last serial # : Last serial # : None Last time stamp : %s Last time stamp : None Log state : Log version # : %u Number of entries : %u Update committed : %s Update operation : Update principal : %s Update serial # : %u Update size : %u Update time stamp : %s Update time stamp : None [-A admin_server] [-F kerberos_db_file ] [-p kdb5_util_pathname] [-k keytab] [-S sname] [-U for_user [-P]] [-x db_args]* [-P port] [-a acl_file] attributes are: for client %s options are: options for credential caches: options for keytabs: service1 service2 ... update_princ_encryption [-f] [-n] [-v] [princ-pattern] purge_mkeys [-f] [-n] [-v] where, [-x db_args]* - any number of database specific arguments. Look at each database documentation for supported arguments (type 'yes' to confirm)? Couldn't allocate memory Kerberos update log (%s) Usage: %s [-h] [-v] [-v] [-e num] %s -R Usage: %s [-r realm] [-f file] [-d] [-P port] [-s srvtab] slave_host Usage: %s [-r realm] [-s srvtab] [-dS] [-f slave_file] %s successful in remotely using an unsecure (non-encrypted) channel. in remotely using an unsecure (non-encrypted) channel. GSS-API error strings complete. %s while unparsing name Full resync, error returned from master KDC. KDC cannot initialize. Not enough memory [does not exist]$Id$%.*s%s%s. Please try again. %d is an invalid KVNO value%d key(s) purged. %d key(s) would be purged. %s Cannot decode status %d%s changed %s constructing AP-REQ armor%s contains invalid filename%s does not have correct permissions for %s, %s aborted%s is an invalid enctype%s load terminated%s option not supported%s option value missing%s returned a bad exit status (%d)%s version %s %s while %s, aborting %s while decrypting FAST reply%s while handling ap-request armor%s while opening ACL file %s%s while storing credentials%s(%d): %s %s(%d): ignoring trash at end of line: %s, aborting%s. Please try again.%s: %s restore failed %s: %s while %s, aborting %s: %s while initializing context, aborting %s: %s, aborting %s: %s, attempting to retrieve default realm %s: Cannot initialize. Not enough memory %s: Could not map log %s: Invalid password %s: KDC cannot initialize. Not enough memory %s: Keytab %s does not exist. %s: No entry for principal %s exists in keytab %s %s: No entry for principal %s with kvno %d exists in keytab %s %s: Password incorrect %s: Password incorrect while %s %s: Principal %s does not exist. %s: Standard input does not appear to be a network socket. (Not run from inetd, and missing the -S option?) %s: There is only one entry for principal %s in keytab %s %s: Unknown iprop dump version %d %s: can't read dump header in %s %s: cannot construct kdb5 util dump string too long; out of memory%s: cannot get memory for realm list %s: cannot initialize realm %s - see log file for details %s: cannot initialize realm %s. Not enough memory %s: cannot initialize. Not enough memory %s: cannot parse <%s> %s: constrained delegation failed%s: create IPROP svc (PROG=%d, VERS=%d) %s: dump header bad in %s %s: dump version %s can only be loaded with the -update flag %s: error processing line %d of %s %s: error writing to %s %s: error writing to %s device %s: exec failed: %s%s: fork failed: %s%s: getclhoststr failed%s: invalid arguments %s: invalid restrictions: %s%s: kvno = %d %s: kvno = %d, keytab entry valid %s: line %d too long, truncated%s: must specify entry to delete %s: must specify keytab to read %s: must specify keytab to write %s: must specify the srvtab to read %s: out of memory %s: out of memory recording principal names%s: parser lost count! %s: pclose(popen) failed: %s%s: popen failed: %s%s: regular expression error: %s %s: regular expression match error: %s %s: server handle is NULL%s: setup_gss_names failed%s: starting... %s: syntax error at line %d <%10s...>%s: tagged data list inconsistency for %s (counted %d, stored %d) %s: unable to canonicalize principal %s: unable to figure out a principal name %s: unable to get default realm %s: unable to get kiprop host based service name for realm %s %s: usage: %s [-t] [-k] [-e] %s: warning - logging entry syntax error %s: writing srvtabs is no longer supported %s; Incoming SerialNo=%lu; Outgoing SerialNo=%lu%s; Incoming SerialNo=%lu; Outgoing SerialNo=N/A%spreauth required but hint list is empty%u principals processed: %u updated, %u already current %u principals processed: %u would be updated, %u already current '%s' is an invalid option '%s' more recent than '%s'.'%s' not found: '%s' value missing(Expired)(No string attributes.) (principal %s)(type 'yes' to confirm)? ** Database '%s' destroyed. ** Database of '%s' destroyed. -Z option is mutually exclusive with -z. -norandkey option only valid for kadmin.local -z option is mutually exclusive with -Z. ... CONSTRAINED-DELEGATION s4u-client=%s... PROTOCOL-TRANSITION s4u-client=%s0 is an invalid KVNO value: %s is an invalid enctype: %s is an invalid mkeyVNOA credential was invalidA later token has already been processedA parameter was malformedA required input parameter could not be readA required input parameter could not be writtenA required output parameter could not be writtenA service is not available that is required to process the requestA supplied name was of an unsupported typeA token had an invalid Message Integrity Check (MIC)A token had an invalid signatureA token was invalidAD types: ALERTAPI structure version specified by application is no longer supported (to fix, recompile application against current KADM5 API header files and libraries)API structure version specified by application is unknown to libraries (to fix, obtain current KADM5 API header files and libraries and recompile application)API version specified by application is no longer supported by libraries (to fix, update application to adhere to current API version and recompile)API version specified by application is no longer supported by server (to fix, update application to adhere to current API version and recompile)API version specified by application is unknown to libraries (to fix, obtain current KADM5 API header files and libraries and recompile application)API version specified by application is unknown to server (to fix, obtain and install newest KADM5 Admin Server)ASN.1 bad return from gmtimeASN.1 badly-formatted encodingASN.1 encoding ended unexpectedlyASN.1 failed call to system time libraryASN.1 identifier doesn't match expected valueASN.1 length doesn't match expected valueASN.1 missing expected EOCASN.1 non-constructed indefinite encodingASN.1 object omitted in sequenceASN.1 parse errorASN.1 structure is missing a required fieldASN.1 type numbers are inconsistentASN.1 unexpected field numberASN.1 value too largeAS_REQ (%s) %s: %s: %s for %s%s%sAS_REQ (%s) %s: ISSUE: authtime %d, %s, %s for %sAS_REQ : handle_authdata (%d)Acceptor and Initiator share no checksum typesAccess deniedAccount %s: authorization for %s for execution of Account %s: authorization for %s for execution of %s failed Account %s: authorization for %s successful Account %s: authorization of %s failed Add Additional pre-authentication requiredAdmin ServicesAdmin server does not support -nokey while creating "%s" Administration credentials NOT DESTROYED. All Tickets DisallowedAll keys for principal "%s" removed. All keys in use, nothing purged. Allowed key/salt types: %s Alternative authentication method requiredAn expected per-message token was not receivedAn invalid name was suppliedAn invalid status code was suppliedAn unsupported critical FAST option was requestedAn unsupported mechanism was requestedAnonymous pkinit without DH public value not supported.Ap-request armor not permitted with TGSAre you sure you want to delete the policy "%s"? (yes/no): Are you sure you want to delete the principal "%s"? (yes/no): Are you sure you want to rename the principal "%s" to "%s"? (yes/no): Attempt to lock database twiceAttempt to unlock database when not lockedAttempt to use incomplete security contextAttribute removed from principal "%s". Attribute set for principal "%s". Attributes:Auth context must contain local addressAuth context must contain remote addressAuthenticated %s Authenticated to Kerberos v5 Authenticating as principal %s with default keytab. Authenticating as principal %s with existing credentials. Authenticating as principal %s with keytab %s. Authenticating as principal %s with password. Authenticating as principal %s with password; anonymous requested. Authentication attempt failed: %s, GSS-API error strings are:Authentication errorAuthentication failed. Authenticator has no subkeyBad SAM flags in obtain_sam_padataBad application version was sent (via sendauth)Bad checksum in SAM challengeBad database creation flagsBad direction for converting keysBad encryption typeBad format in credentials cacheBad format in keytabBad krb5 admin server hostnameBad lifetime value %s Bad lifetime value (%s hours?) Bad lock modeBad magic number for FAST requestBad magic number for FAST responseBad magic number for GSSAPI OIDBad magic number for GSSAPI QUEUEBad magic number for fast armored requestBad magic number for iakerb_ctx_id_tBad magic number for krb5_address structureBad magic number for krb5_alt_method structureBad magic number for krb5_ap_rep structureBad magic number for krb5_ap_rep_enc_part structureBad magic number for krb5_ap_req structureBad magic number for krb5_auth_context structureBad magic number for krb5_authdata structureBad magic number for krb5_authdata_contextBad magic number for krb5_authenticator structureBad magic number for krb5_ccache structureBad magic number for krb5_checksum structureBad magic number for krb5_checksum_entry structureBad magic number for krb5_context structureBad magic number for krb5_cred structureBad magic number for krb5_cred_enc_part structureBad magic number for krb5_cred_info structureBad magic number for krb5_creds structureBad magic number for krb5_cryptosystem_entry structureBad magic number for krb5_cs_table_entry structureBad magic number for krb5_data structureBad magic number for krb5_db_context structureBad magic number for krb5_enc_data structureBad magic number for krb5_enc_kdc_rep_part structureBad magic number for krb5_enc_sam_response_encBad magic number for krb5_enc_tkt_part structureBad magic number for krb5_encrypt_block structureBad magic number for krb5_error structureBad magic number for krb5_etype_info_entry structureBad magic number for krb5_gss_cred_id_tBad magic number for krb5_gss_ctx_id_tBad magic number for krb5_gss_enc_descBad magic number for krb5_kdc_rep structureBad magic number for krb5_kdc_req structureBad magic number for krb5_keyblock structureBad magic number for krb5_keytab structureBad magic number for krb5_keytab_entry structureBad magic number for krb5_last_req_entry structureBad magic number for krb5_os_context structureBad magic number for krb5_pa_data structureBad magic number for krb5_preauth_opsBad magic number for krb5_predicted_sam_responseBad magic number for krb5_principal structureBad magic number for krb5_priv structureBad magic number for krb5_priv_enc_part structureBad magic number for krb5_pwd_data structureBad magic number for krb5_rcache structureBad magic number for krb5_response structureBad magic number for krb5_safe structureBad magic number for krb5_sam_challengeBad magic number for krb5_sam_challenge_2Bad magic number for krb5_sam_keyBad magic number for krb5_sam_responseBad magic number for krb5_sam_response 2Bad magic number for krb5_ticket structureBad magic number for krb5_tkt_authent structureBad magic number for krb5_transited structureBad magic number for passwd_phrase_elementBad principal name while trying to forward credentialsBad response (during sendauth exchange)Bad sendauth version was sentBad start time value %s Bind DN entry '%s' missing in LDAP password file '%s'Buffer is the wrong sizeBusy signal received from master, backoff for %d secs CRITICALCalling iprop_get_updates_1 (sno=%u sec=%u usec=%u) Can not fetch master key (error: %s).Can't create new subsidiary cacheCan't create new subsidiary cache because default cache is already a subsidiaryCan't create new subsidiary cache because default cache is not a directory collectionCan't destroy replay cache: %sCan't find client principal %s in cache collectionCan't find username for uid %luCan't open/find Kerberos configuration fileCan't open/find lname translation databaseCan't open/find realm translation fileCan't read from replay cache: %sCan't verify certificateCan't write to replay cache: %sCannot %s replay cache file %s: %sCannot %s replay cache: %sCannot bind server socket on %sCannot bind to LDAP server '%s' as '%s': %sCannot bind to LDAP server '%s' with SASL mechanism '%s': %sCannot build GSSAPI auth namesCannot change keytab with keytab iterators activeCannot change protected principalCannot contact any KDC for realm '%.*s'Cannot contact any KDC for requested realmCannot convert V5 address informationCannot convert V5 keyblockCannot convert V5 principalCannot create LDAP handle for '%s': %sCannot create RPC service: %s; continuingCannot create TCP server socket on %sCannot create cert chain: %sCannot decrypt ticket for %s using keytab key for %sCannot determine realm for hostCannot determine realm for numeric host addressCannot enable SO_REUSEADDR on fd %dCannot find KDC for realm "%.*s"Cannot find KDC for requested realmCannot find active master keyCannot find key for %s kvno %d in keytabCannot find key for %s kvno %d in keytab (request ticket server %s)Cannot find master key record in databaseCannot find ticket for requested realmCannot find/read stored master keyCannot fstat replay cache file %s: %sCannot initialize GSSAPI service nameCannot listen on TCP server socket on %sCannot lock databaseCannot open LDAP password file '%s': %sCannot read passwordCannot register RPC service: %s; continuingCannot request packet info for udp socket address %s port %dCannot resolve address of admin server "%s" for realm "%s"Cannot resolve network address for KDC in requested realmCannot resolve network address for admin server in requested realmCannot reuse passwordCannot set GSSAPI authentication namesCannot set up KDB keytabCannot sync replay cache file: %sCannot write to specified key tableCcache function not supported: not implementedCcache function not supported: read-only ccache typeCertificate mismatchChallenge for Activcard mechanismChallenge for Digital Pathways mechanismChallenge for Enhanced S/Key mechanismChallenge for Enigma Logic mechanismChallenge for Security Dynamics mechanismChallenge for Traditional S/Key mechanismChallenge from authentication serverChanging uid to %s (%ld) Checksum must be includedClient '%s' not found in Kerberos databaseClient name mismatchClient not found in Kerberos databaseClient not trustedClient not yet valid - try again laterClient or server has a null keyClient's entry in database has expiredClient's key is encrypted in an old master keyClient/server realm mismatch in initial ticket requestClients credentials have been revokedClock skew too greatClock skew too great in KDC replyCommunication failure with serverConditional dump is an undocumented option for use only for iprop dumpsConfiguration file does not specify default realmConnection from %sConnection to server already initializedConnection to server not initializedContext is already fully establishedConversion to service principal undefined for name typeCorrupt Corrupt header log, exiting Corrupt update entry Could not allocate principal name Could not create temp keytab file name.Could not dup the inetd socket: %sCould not find %s plugin module named '%s'Could not get a tgt for Could not open /dev/null: %sCould not open iprop ulog Couldn't allocate gss_buffer_t dataCouldn't open log file %s: %s Couldn't read database_name Couldn't reinitialize ulog file %s Creating new master key for master key principal '%s' Credential cache directory %s does not existCredential cache directory %s exists but is not a directoryCredential cache has no TGTCredential cache is emptyCredential cache name malformedCredential usage type is unknownCredentials cache I/O operation failed (%s)Credentials cache I/O operation failed XXXCredentials cache file '%s' not foundCredentials cache keyring '%s' not foundCredentials cache permissions incorrectCredentials cache type is already registered.Credentials for server have been revokedCryptosystem internal errorCurrent password's minimum life has not expiredDES key has bad parityDES key is a weak keyDN information missingDN is out of the realm subtreeDatabase error! Required KADM5 principal missingDatabase format errorDatabase has already been initializedDatabase has not been initializedDatabase inconsistency detectedDatabase is locked or in use--try again laterDatabase library failed to initializeDatabase load process for full propagation completed. Database module does not match KDC versionDatabase not currently opened!Database not initializedDatabase not lockedDatabase propagation to %s: SUCCEEDED Database read errorDatabase record is incomplete or corruptedDatabase store errorDatabase type not supportedDatabase was modified during readDecoding out of dataDecrypt integrity check failedDefault realm not setDelete Delete Failed: One or more Principals associated with the Ticket PolicyDeleting KDC database of '%s', are you sure? Deleting KDC database stored in '%s', are you sure? Digest in certificate not acceptedDigest in signed-data not acceptedEMERGENCYERROR returned by master KDC, bailing. ERROR returned by master, bailing Empty passwords are not allowedEncoding too largeEncrypted Challenge used outside of FAST tunnelEncryption type %s not permittedEncryption type not permittedEnctype %d has no PRFEnd of credential cache reachedEnd of key table reachedEnd-of-file on replay cache I/OEnter #Enter DN of Kerberos container: Enter OTP Token ValueEnter it againEnter new passwordEnter password for principal "%s"Entry already exists in databaseEntry data decode failure Entry for principal %s with kvno %d removed from keytab %s. Entry for principal %s with kvno %d, encryption type %s added to keytab %s. ErrorError creating file %sError in krb5_auth_con_ini: %sError in krb5_auth_con_setaddrs: %sError in krb5_auth_con_setflags: %sError in krb5_copy_prinicpal: %sError in krb5_kt_resolve: %sError in krb5_recvauth: %sError processing LDAP DB params:Error reading '%s' attribute: %sError reading LDAP server params:Error reading container object: Error reading kerberos container location from krb5.confError reading password from stash: Error reading service object password fileError reading ticket policy. Error reading ticket policy: Error text from client: %s Error text from server: %s Error writing to credentials cacheError writing to key tableError: bad entry - %s in %s file, CMD_PATH contains no paths Error: bad entry - %s in %s file, must be either full path or just the cmd name Error: bad entry - %s in %s file, since %s is just the cmd name, CMD_PATH must be defined Error: bad path %s in CMD_PATH for %s must start with '/' Error: not found -> Etype (skey, tkt): %s, Expecting FX_ERROR pa-data inside FAST containerExpiration date: %s Exponential backoff Extra arguments (starting with "%s"). FAST protected pre-authentication required but not supported by KDCFAST req_checksum invalid; request modifiedFAST response missing finish message in KDC replyFailed getting object referencesFailed password attempts: %d Failed to convert the password to hexadecimalFailed to open file %s: %sFailed to reconfigure network, exitingFailed to write service object password to fileFailure!Field is too long for this implementationFlags: %sFull propagation transfer finished. Full propagation transfer started. Full resync error from master Full resync invalid result from master Full resync needed Full resync permission denied Full resync request granted Full resync request granted.Full resync, invalid return from master KDC.Full resync, permission denied.GSS-API (or Kerberos) errorGeneric error (see e-text)Generic preauthentication failureGeneric remote error: %s Generic unknown RC errorGeneric unknown RC/IO errorGeneric update log errorGoodbye Got incremental updates (sno=%u sec=%u usec=%u) Got signal to request exitGot signal to resetHW authentication requiredHistory entry contains no key dataHostname cannot be canonicalizedHostname in SERVICE-NAME string could not be canonicalizedI/O error in replay cache i/o codeIllegal character in component nameIllegal configuration parameter for local KADM5 clientIllegal configuration parameter for remote KADM5 clientIllegal cross-realm ticketIllegal policy nameIllegal principal nameIllegal use of wildcardIllegal version number for KRB5_TL_ACTKVNO %d Illegal version number for KRB5_TL_MKEY_AUX %d Improper format of Kerberos configuration fileImproper format of realm translation fileImproper format of translation database entryInappropriate type of checksum in messageInconsistent key purposeIncorrect channel bindings were suppliedIncorrect message directionIncorrect net addressIncorrect passwordIncorrect password in encrypted challengeIncorrect sequence number in messageIncremental propagation enabled Incremental updates: %d updates / %lu usIncremental updates: %d updates / %lu us Initial Ticket response appears to be Version 4 errorInitial password requiredInitialized cache Initializing database '%s' for realm '%s', master key name '%s' Initializing database for realm '%s' Initializing kadm5 as client %s Insecure file mode for replay cache file %sInsecure mkstemp() file mode for replay cache file %s; try running this program with umask 077Insufficient access to lock databaseInsufficient access to perform requested operationInsufficient authorization for operationInsufficient permission to lock fileInsufficient space to return complete informationInsufficient system space to store replay informationInternal credentials cache errorInternal error parsing X509_user_identity Internal error: command %s did not get resolved Invalid !Invalid KDC option combination (library internal error)Invalid Kerberos container DNInvalid UID in persistent keyring nameInvalid UTF-8 stringInvalid auxillary attributesInvalid certificateInvalid credential was suppliedInvalid date specification "%s". Invalid encryption type in SAM challengeInvalid enctype for setv4keyInvalid field length in tokenInvalid field mask for operationInvalid flag for file lock modeInvalid format of Kerberos lifetime or clock skew stringInvalid kdb lock modeInvalid key generation parameters from KDCInvalid key/salt tuplesInvalid keyring anchor nameInvalid message typeInvalid message type specified for encodingInvalid module specifierInvalid module specifier %sInvalid number of character classesInvalid password history countInvalid password lengthInvalid pkinit packet: octet string expectedInvalid policy nameInvalid principal nameInvalid signatureInvalid tokenInvalid token was suppliedIprop not enabled It is important that you NOT FORGET this password. KADM5 administration database lock file missingKCM daemon reply too bigKDB module requires -update argumentKDC ServicesKDC can't fulfill requested optionKDC has no support for checksum typeKDC has no support for encryption typeKDC has no support for padata typeKDC has no support for transited typeKDC is synchronized with master. KDC name mismatchKDC not trustedKDC policy rejects requestKDC policy rejects transited pathKDC reply did not match expectationsKDC returned error string: %.*sKRB5 error code 101KRB5 error code 102KRB5 error code 103KRB5 error code 104KRB5 error code 105KRB5 error code 106KRB5 error code 107KRB5 error code 108KRB5 error code 109KRB5 error code 110KRB5 error code 111KRB5 error code 112KRB5 error code 113KRB5 error code 114KRB5 error code 115KRB5 error code 116KRB5 error code 117KRB5 error code 118KRB5 error code 119KRB5 error code 120KRB5 error code 121KRB5 error code 122KRB5 error code 123KRB5 error code 124KRB5 error code 125KRB5 error code 126KRB5 error code 127KRB5 error code 30KRB5 error code 53KRB5 error code 54KRB5 error code 55KRB5 error code 56KRB5 error code 57KRB5 error code 58KRB5 error code 59KRB5 error code 82KRB5 error code 83KRB5 error code 84KRB5 error code 87KRB5 error code 88KRB5 error code 89KRB5 error code 90KRB5 error code 91KRB5 error code 92KRB5 error code 94KRB5 error code 95KRB5 error code 96KRB5 error code 97KRB5 error code 98KRB5 error code 99KVNO of new master key does not match expected valueKVNO: %d KVNO: %d, Enctype: %s, Active on: %s KVNO: %d, Enctype: %s, Active on: %s * KVNO: %d, Enctype: %s, No activate time set Kerberos 4 is no longer supported Kerberos Container create FAILED: %sKerberos Container delete FAILED: %sKerberos Container information is missingKerberos V4 errorKerberos V5 magic number tableKerberos container location not specifiedKerberos database constraints violatedKerberos database internal errorKerberos library initialization failureKerberos password for %s: Kerberos service unknownKerberos version 4 support is disabledKey for "%s" randomized. Key for %s (hex): Key parameters not acceptedKey size in database is invalidKey size is incompatible with encryption typeKey table entry not foundKey table file '%s' not foundKey table name malformedKey table type is already registered.Key version is not availableKey version number for principal in key table is incorrectKeytab %s is nonexistent or emptyKeytab contains no suitable keys for %sKeytab name too longKilling fullprop child (%d) Kpropd sent database size %d, expecting %dLDAP bind dn value missingLDAP bind password value missingLast modified: %s (%s) Last password change: %s Last successful authentication: %s Leaving uid as %s (%ld) Loading random dataLoading random data Local host name could not be determinedLooping detected inside krb5_get_in_tktMKey: vno %d Mach RPC error communicating with KCM daemonMake sure that you have removed the old principal from all ACLs before reusing. Make sure that you have removed this principal from all ACLs before reusing. Malformed reply from KCM daemonMalformed representation of principalMalformed request errorMaster key does not match databaseMatching credential not foundMaximum Renewable LifeMaximum Ticket LifeMaximum password failures before lockout: %lu Maximum password life: %ld Maximum renewable life: %s Maximum ticket life: %s Mechanism is incorrectMessage context invalidMessage is a replayMessage out of orderMessage replay detection requires rcache parameterMessage size is incompatible with encryption typeMessage stream modifiedMinimum connections required per server is 2Minimum number of password character classes: %ld Minimum password length: %ld Minimum password life: %ld Miscellaneous RPC error: %s, %sMiscellaneous failureMismatched enctypes for setkey3Missing checksum in SAM challengeMissing parameters in krb5.conf required for kadmin clientMissing required ACL file configurationMissing required realm configurationMultiple values for single or folded enctypeMutual authentication failedNOTICE: no policy specified for %s; assigning "default" New passwordNew password (again)New password cannot be zero lengthNew password does not have enough character classes. The character classes are: - lower-case letters, - upper-case letters, - digits, - punctuation, and - all other characters (e.g., control characters). Please choose a password with at least %d character classes.New password is too short. Please choose a password which is at least %d characters long.New password was found in a dictionary of possible passwords and therefore may be easily guessed. Please choose another password. See the kpasswd man page for help in choosing a good password.New password was used previously. Please choose a different password.New passwords do not match - password not changed. No @ in SERVICE-NAME name stringNo DUP_SKEY requestsNo ErrorNo KCM server foundNo Kerberos credentials availableNo TGT-based requestsNo acceptable KDF offeredNo administration server found for realmNo armor key but FAST armored request presentNo authorization data requiredNo context has been establishedNo credentials cache foundNo credentials found with supported encryption typesNo credentials supplied to library routineNo credentials were suppliedNo credentials were supplied, or the credentials were unavailable or inaccessibleNo default realm set; cannot initialize KDBNo dictionary file specified, continuing without one.No errorNo key table entry found for %sNo key table entry found matching %sNo keys in keytabNo local name found for principal nameNo matching key in entryNo matching key in entry having a permitted enctypeNo more memory to allocate (in credentials cache code)No more memory to allocate (in replay cache I/O code)No more memory to allocate (in replay cache code)No password given No pkinit_anchors supplied for realm %sNo pkinit_identity supplied for realm %sNo principal in keytab matches desired nameNo realms configured correctly for pkinit supportNo such entry in the databaseNo supported encryption types (config file error?)No supported tokensNo ticket granting ticketNo translation available for requested principalNo user identity options specifiedNot ForwardableNot PostdateableNot a hexadecimal passwordNot enough memory Not proxiableNot renewableNoticeNumber of keys: %d Number of old keys kept: %ld OK as DelegateOK, deleting database '%s'... OK, deleting database of '%s'... OK, doing nothing. OK, purging unused master keys from '%s'... OTP Challenge:OTP Token PINOTP value doesn't match any token formatsOld keys for principal "%s" purged. One of -c or -p must be specified Only one -I option allowed Only one -c option allowed Only one -c or -p option allowed Only one -t option allowed. Only one armor_ccache Only one of -a and -A allowed Only one of -f and -F allowed Only one of -p and -P allowed Only one of -t and -i allowed Operation failed for unspecified reasonOperation requires ``add'' privilegeOperation requires ``change-password'' privilegeOperation requires ``delete'' privilegeOperation requires ``get'' privilegeOperation requires ``list'' privilegeOperation requires ``modify'' privilegeOperation requires ``set-key'' privilegeOption -P (constrained delegation) requires keytab to be specified Option -P (constrained delegation) requires option -U (protocol transition) Options -u and -S are mutually exclusive Out of memory PA Option %s = %s PRF input too longPROCESS_TGS: failed lineage checkPacket was replayed in wrong directionPass phrase forPasswd ServicesPassword Change requiredPassword Changing ServicePassword cannot be changed because it was changed too recently. Please wait until %s before you change it. If you need to change your password before then, contact your system security administrator.Password change failedPassword change rejectedPassword changed. Password corruptPassword does not contain enough character classesPassword expiration date: %s Password expired. You must change it now.Password failure count reset interval: %s Password for "%s"Password for "%s" changed. Password for %.1000sPassword for %sPassword has expiredPassword history principal key version mismatchPassword incorrect while getting initial ticketPassword is in the password dictionaryPassword is too shortPassword lockout duration: %s Password may not match principal namePassword may not match user information.Password minimum life is greater than password maximum lifePassword mismatchPassword not changed.Password read interruptedPermission denied in replay cache codePkinit request not signed, but client not anonymous.Please choose from the following: Please enter new master key.... Plugin does not support interface versionPlugin does not support the operationPlugin module name not foundPolicy "%s" not deleted. Policy does not existPolicy is in usePolicy rejects transited pathPolicy: %s Policy: %s%s Preauth module %s: %sPreauthentication failedPreauthentication requiredPremature EOF found for database file!Principal "%s" deleted. Principal "%s" modified. Principal "%s" not deleted Principal "%s" not renamed Principal "%s" renamed to "%s". Principal %s has realm presentPrincipal %s is missing required realmPrincipal Container ReferencePrincipal add failed: %sPrincipal delete failed (trying to replace entry): %sPrincipal does not belong to realmPrincipal does not belong to the default realmPrincipal does not existPrincipal has multiple entries in Kerberos databasePrincipal in credential cache does not match desired namePrincipal or policy already existsPrincipal or policy does not existPrincipal: %s Principals whose keys WOULD BE re-encrypted to master key vno %u: Principals whose keys are being re-encrypted to master key vno %u if necessary: Program called an obsolete, deleted functionProgram lacks support for address typeProgram lacks support for checksum typeProgram lacks support for encryption typeProgram lacks support for key typeProgrammer error! Bad API structure versionProgrammer error! Bad API versionProgrammer error! Bad Admin server handleProgrammer error! Illegal tagged data list typeProtocol transition with delegation allowedProtocol version mismatchPublic key encryption not supportedPurging the following master key(s) from %s: RPC svc_freeargs failed (%s)RPC svc_getargs failed (%s)RPC svc_sendreply failed (%s)RPC unknown request: %d (%s)RSA-MD5 supportedRandom number generator could not be seededRe-encrypt all keys not using master key vno %u?Re-enter password for "%s"Re-enter password for principal "%s"Realm Delete FAILED: %sRealm NameRealm information not availableRealm not local to KDCReceived token ID does not match expected token IDReinitialized the ulog. Reinitializing iprop because get updates failed Rejected connection from unauthorized principal %sRejected connection from unauthorized principal %s Replay cache I/O operation failedReplay cache name parse/format errorReplay cache type does not support non-volatile storageReplay cache type is already registeredReplay cache type is unknownReply has wrong form of session key for anonymous requestRequest did not supply a ticketRequest is a replayRequest missing second ticketRequest ticket server %s found in keytab but does not match server principal %sRequest ticket server %s kvno %d enctype %s found in keytab but cannot decrypt ticketRequest ticket server %s kvno %d found in keytab but not with enctype %sRequest ticket server %s kvno %d not found in keytab; keytab is likely out of dateRequest ticket server %s kvno %d not found in keytab; ticket is likely out of dateRequest ticket server %s not found in keytab (ticket kvno %d)Request: %s, %.*s%s, %s, client=%.*s%s, service=%.*s%s, addr=%sRequest: %s, %s, %s, client=%s, service=%s, addr=%sRequest: %s, spawned resync process %d, client=%s, service=%s, addr=%sRequest: kadm5_init, %.*s%s, %s, client=%.*s%s, service=%.*s%s, addr=%s, vers=%d, flavor=%dRequest: kadm5_rename_principal, %.*s%s to %.*s%s, %s, client=%.*s%s, service=%.*s%s, addr=%sRequested effective lifetime is negative or too shortRequested encryption type not used in messageRequested lucid context version not supportedRequested principal and ticket don't matchRequested protocol version not supportedRequested server and ticket don't matchRequesting ticket can't get forwardable ticketsRequired parameters in kdc.conf missingRequired preauthentication key not suppliedResponse too big for UDP, retry with TCPRevocation status unavailableRevocation status unknownRevoked certificateSAM AuthenticationSPNEGO acceptor did not return a valid tokenSPNEGO acceptor did not select a mechanismSPNEGO cannot find mechanisms to negotiateSPNEGO failed to acquire credsSPNEGO failed to negotiate a mechanismSTRING-UID-NAME contains nondigitsSearchScopeSeeding random number generatorSequence number in token is corruptServer %s not found in Kerberos databaseServer errorServer not found in Kerberos databaseServer not yet valid - try again laterServer principal %s does not match any keys in keytabServer principal %s does not match request ticket server %sServer principal valid for user2user onlyServer rejected authentication (during sendauth exchange)Server's entry in database has expiredServer's key is encrypted in an old master keyService DisabledService key not availableService not respondingService password length is zeroSleeping %d seconds to re-initialize kadm5 (RPC ERROR) Sleeping %d seconds to re-initialize kadm5 (krb5kdc not running?) Stable Still waiting for full resync Stored credentials Stored master key is corruptedStored master key is not currentStoring of acceptor credentials is not supported by the mechanismStoring of non-default credentials is not supported by the mechanismSubsidiary cache path %s filename does not begin with "tkt"Subsidiary cache path %s has no parent directorySubtreeSuccessSupplied data not handled by this pluginTCP client %s wants %lu bytes, cap is %luTCP socket fd number %d (for %s) too highTGS_REQ %s: %s: authtime %d, %s for %s, 2nd tkt client %sTGS_REQ (%s) %s: %s: authtime %d, %s%s %s for %s%s%sTGS_REQ : handle_authdata (%d)TGS_REQ: SESSION KEY or MUTUALTGS_REQ: UNKNOWN SERVER: server='%s'TGS_REQ: issuing TGT %sTGS_REQ: issuing alternate TGTTGT has been revokedTemporary stash file already exists: %s.The IAKERB proxy could not find a KDCThe KDC did not respond to the IAKERB proxyThe KDC should discard this requestThe context has expiredThe given credential's usage does not match the requested usageThe operation is forbidden by local security policyThe operation is forbidden by the local security policyThe operation or option is not availableThe operation or option is not available or unsupportedThe password can only be changed once a day.The password can only be changed every %d days.The password must be different from the previous password.The password must be different from the previous %d passwords.The password must contain at least %d character.The password must contain at least %d characters.The password must include numbers or symbols. Don't include any part of your name in the password.The provided name was not mechanism specific (MN)The quality-of-protection (QOP) requested could not be providedThe quality-of-protection requested could not be providedThe referenced context has expiredThe referenced credential has expiredThe referenced credentials have expiredThe requested credential element already existsThe routine completed successfullyThe routine must be called again to complete its functionThe salt type of the specified principal does not support renamingThe ticket isn't for usThe token was a duplicate of an earlier tokenThe token's validity period has expiredThere is only one master key which can not be purged. This Krb5 library has expiredThis application has expiredThis will delete the policy object '%s', are you sure? Ticket Policy Name missingTicket Policy Object DN missingTicket Policy Object information missingTicket cache %cNOT%c destroyed! Ticket cache NOT destroyed! Ticket cache: %s:%s Default principal: %s Ticket expiredTicket flagsTicket has invalid flag setTicket is ineligible for postdatingTicket modified in KDC replyTicket not yet validTicket/authenticator don't matchToken header is malformed or corruptToken is missing dataToken was reflectedToo few argumentsToo much string mapping dataTracing unsupportedTry a more complex password, or contact your administrator.UID does not resolve to usernameUnable to access Kerberos databaseUnable to decrypt latest master key with the provided master key Unable to find requested database typeUnable to find requested database type: %sUnable to get default realmUnable to identify user Unable to identify user from password file Unable to initialize Kerberos Unable to initialize preauth contextUnable to initialize signal handlers in pid %dUnable to load requested database module '%s': plugin symbol 'kdb_function_table' not foundUnable to map log file %s Unable to map log! Unable to read Kerberos containerUnable to read RealmUnable to reinitialize main loopUnauthorized request: %s, %.*s%s, client=%.*s%s, service=%.*s%s, addr=%sUnauthorized request: %s, client=%s, service=%s, addr=%sUnauthorized request: kadm5_rename_principal, %.*s%s to %.*s%s, client=%.*s%s, service=%.*s%s, addr=%sUnkeyed checksum used in fast_reqUnknown %s (field = %d)Unknown FAST armor type %dUnknown Key table typeUnknown credential cache typeUnknown keyring collection versionUnknown quality of protection specifiedUnknown signature type in tokenUnknown state: %d Unrecognized ACL operation '%c' in %sUnspecified GSS failure. Minor code may provide more informationUnspecified password quality failureUnstable Unsupported argument "%s" for db2Unsupported credentials cache format version numberUnsupported encryption typeUnsupported key table format version numberUnsupported preauthentication typeUnsupported replay cache format version numberUnsupported salt typeUnsupported type while processing '%s' Unsupported version in database entryUpdate Entry Update log conversion errorUpdate log dump : Update log is corruptUpdate log is unstableUsage: %s [-A] [-q] [-c cache_name] Usage: %s [-e] [-V] [[-c] [-l] [-A] [-d] [-f] [-s] [-a [-n]]] [-k [-t] [-K]] [name] Usage: %s [-r realm] [-p principal] [-q query] [clnt|local args] clnt args: [-s admin_server[:port]] [[-c ccache]|[-k [-t keytab]]]|[-n] local args: [-x db_args]* [-d dbname] [-e "enc:salt ..."] [-m] where, [-x db_args]* - any number of database specific arguments. Look at each database documentation for supported arguments Usage: %s [target user] [-n principal] [-c source cachename] [-k] [-D] [-r time] [-pf] [-l lifetime] [-zZ] [-q] [-e command [args... ] ] [-a [args... ] ] Usage: %s dbpathname realmnameUsage: %s {-c cache_name | -p principal} Usage: kadmind [-x db_args]* [-r realm] [-m] [-nofork] [-port port-number] [-proponly] [-p path-to-kdb5_util] [-F dump-file] [-K path-to-kprop] [-P pid_file] where, [-x db_args]* - any number of database specific arguments. Look at each database documentation for supported arguments Usage: kdb5_ldap_util [-D user_dn [-w passwd]] [-H ldapuri] cmd [cmd_options] create [-subtrees subtree_dn_list] [-sscope search_scope] [-containerref container_reference_dn] [-m|-P password|-sf stashfilename] [-k mkeytype] [-kv mkeyVNO] [-s] [-maxtktlife max_ticket_life] [-maxrenewlife max_renewable_ticket_life] [ticket_flags] [-r realm] modify [-subtrees subtree_dn_list] [-sscope search_scope] [-containerref container_reference_dn] [-maxtktlife max_ticket_life] [-maxrenewlife max_renewable_ticket_life] [ticket_flags] [-r realm] view [-r realm] destroy [-f] [-r realm] list stashsrvpw [-f filename] service_dn create_policy [-r realm] [-maxtktlife max_ticket_life] [-maxrenewlife max_renewable_ticket_life] [ticket_flags] policy modify_policy [-r realm] [-maxtktlife max_ticket_life] [-maxrenewlife max_renewable_ticket_life] [ticket_flags] policy view_policy [-r realm] policy destroy_policy [-r realm] [-force] policy list_policy [-r realm] Usage: kdb5_util [-x db_args]* [-r realm] [-d dbname] [-k mkeytype] [-M mkeyname] [-kv mkeyVNO] [-sf stashfilename] [-m] cmd [cmd_options] create [-s] destroy [-f] stash [-f keyfile] dump [-old|-ov|-b6|-b7|-r13|-r18] [-verbose] [-mkey_convert] [-new_mkey_file mkey_file] [-rev] [-recurse] [filename [princs...]] load [-old|-ov|-b6|-b7|-r13|-r18] [-verbose] [-update] filename ark [-e etype_list] principal add_mkey [-e etype] [-s] use_mkey kvno [time] list_mkeys Usage: ktadd [-k[eytab] keytab] [-q] [-e keysaltlist] [-norandkey] [principal | -glob princ-exp] [...] Usage: ktadd [-k[eytab] keytab] [-q] [-e keysaltlist] [principal | -glob princ-exp] [...] Usage: ktremove [-k[eytab] keytab] [-q] principal [kvno|"all"|"old"] User modification failed: %sUser to user requiredUsing default cache: %s Using existing cache: %s Using existing stashed keys to update stash file. Using keytab: %s Using new cache: %s Using principal: %s Using specified cache: %s Using specified input cache: %s V5 realm name longer than V4 maximumValidation errorVendor:WARNING! Cannot find dictionary file %s, continuing without one.WARNING! Forged/garbled request: %d, claimed client = %.*s%s, server = %.*s%s, addr = %sWARNING! Forged/garbled request: %s, claimed client = %.*s%s, server = %.*s%s, addr = %sWARNING: Your password may be exposed if you enter it here and are logged WARNING: Your password may be exposed if you enter it here and are logged WARNING: no policy specified for %s; defaulting to no policy WARNING: policy "%s" does not exist Waiting for %d seconds before checking for updates again WarningWarning! Multiple DES-CBC-CRC keys for principal %s; skipping duplicates. Warning! No DES-CBC-CRC key for principal %s, cannot generate OV-compatible record; skipping Warning: Your password will expire in %d days on %sWarning: Your password will expire in %d hour%s on %sWarning: Your password will expire in less than one hour on %sWarning: couldn't stash master key. Warning: proceeding without master keyWhen determining client principal name from keytabWhen resolving the default client keytabWhen unparsing nameWhile converting enctype to stringWill purge all unused master keys stored in the '%s' principal, are you sure? Would purge the following master key(s) from %s: Wrong principal in requestWrong protocol versionXDR encoding errorYou must type a password. Passwords must be at least one character long. You will be prompted for a new database Master Password. You will be prompted for the database Master Password. Your uid doesn't match your passwd entry?! [never][none]accepted RPC connection on socket %d from %saccount %s: authorization failed addent: Error reading key. addent: Illegal character in key. adding new master key to master principalallocating buffer for new TCP session from %sallocating database file name '%s'allocating krb5_get_init_creds_optap-request armor for something other than the local TGSap-request armor without subkeyasprintf could not allocate enough memory to hold outputassuming IPv6 socket accepts IPv4audit plugin %s failed to open. error=%iauthdata %s failed to initialize: %sauthdata (%s) handling failure: %sauthenticated client: %s (etype == %s) authentication attempt failed: %s, RPC authentication flavor %dbad realm transit path from '%s' to '%s' via '%.*s%s'bad service principal %.*s%sbroken address (type %d length %d)calling errorcannot allocate storage for connection infocannot create io eventcannot match size tokenscannot parse policy (%d read) cannot read extra datacannot read key datacannot read key size and versioncannot read key type and lengthcannot read name stringcannot read principal attributescannot read tagged data contentscannot read tagged data type and lengthcannot save eventcannot set listening tcp socket on %s non-blockingcannot specify keysaltlist when not changing key change_password: Not enough memory change_password: missing db argumentchange_password: missing keysaltlist argchange_password: missing password argchanging passwordcheck_rpcsec_auth: failed inquire_context, stat=%uchpw request from %s for %.*s%s: %schpw: Couldn't open admin keytab %sclient and server principal names must matchclosing ccacheclosing down fd %dcommencing operationcomposing ccache namecomputing parameters for databasecontainer reference value: converting glob pattern '%s' to regular expressioncopying db argscould not complete roll-back, error deleting Kerberos Containercould not parse date-time string '%s'could not sync ulog header to diskcouldn't set up routing socket: %screated policy %s creating %d worker processescreating PID filecreating host service principalcreating invocationcreating worker processescurrent privileges:database lockeddebugdeleting database '%s'deleting database of '%s'descriptor %d closed but still in svc_fdsetdetermining master key used for principal '%s'disabling SO_LINGER on TCP socket on %sdo_iprop failed. does not have any appropriate tickets in the cache. dropping %s fd %d from %serror %lderror allocating tcp dispatch private!error compiling converted regexp '%s'error constructing KRB_ERR_FIELD_TOOLONG error! %serror re-encrypting key for principal '%s'file descriptor number %d too highfinished, exitingfor container reference while creating realm '%s'for container reference while modifying realm '%s'for local servicesfor subtree while creating realm '%s'for subtree while modifying realm '%s'get_updates error from master get_updates invalid result from master get_updates master busy; backoff get_updates permission denied get_updates, error returned from master KDC.get_updates, invalid return from master KDC.get_updates, permission denied.getaddrinfo(%s): Cannot determine canonical hostname. getaddrinfo: %s getting config parametersgetting initial credentialsgetting initial ticketgetting local addressesgetting principal from ccachegetting random seedgetting string representation of principal namegss_to_krb5_name: failed display_name status %dhome directory name `%s' too long, can't search for .k5login home directory path for %s too long host/domain name not foundin krb5_auth_con_setaddrsinfoinitializinginitializing ACL fileinitializing kerberos libraryinitializing networkinvalid enctype %sinvalid search scope while creating realm '%s'iprop_full_resync call failediprop_get_updates call failedkadm5 initialization failed! kadm5 initialization succeeded kadm5_hook %s failed postcommit %s: %skerberos principal is already linked to a ldap objectkeyfile (%s) is not a regular file: %skeytab specified, forcing -k kpropd: Full resync needed.ksu: couldn't set environment variable %s ksu: couldn't set environment variable HOME ksu: couldn't set environment variable SHELL ksu: couldn't set environment variable USER ksu: initgroups failed. ksu: permission denied (shell). ksu: unknown login %s ksu: who are you? ldap object is already kerberizedlink information can not be set/updated as the kerberos principal belongs to an ldap objectlistening on fd %d: rpc %slistening on fd %d: tcp %slistening on fd %d: udp %s%smalformed credential cache name %s mapping update logmaster key stash file needs updating, command abortingmaster keylist not initializedmissing principal nameno IPV6_V6ONLY socket option supportno data available for host/domain nameno sockets set up?nonce modified in FAST response: KDC response modifiednot checking transit pathopening default ccacheoperation can not continue, more than one entry with principal name "%s" foundparsing client namepassword quality module %s rejected password for %s: %sperforming %s dumppktinfo says local addr is %splugin symbol 'kdb_function_table' lookup failed: %splugin unavailable: %spreauth %s failed to initialize: %spreauth %s failed to setup loop: %sprincipal name %s too long for internal buffer space program name too long - quitting to avoid triggering system logging bugsrcache not owned by %dread %d from routing socket but msglen is %dreading update log headerready realm object value: rename of temporary keyfile (%s) to (%s) failed: %srenew until renewing credentialsresolving ccache %sresolving default client keytabresolving keytab %sroutine errorrouting socket is fd %dservice name unknownservice not availableset up %d socketssetpw request from %s by %.*s%s for %.*s%s: %ssetsockopt(%d,IPV6_V6ONLY,1) failedsetsockopt(%d,IPV6_V6ONLY,1) workedsetting up network...short read (%d/%d) from routing socketshort reply write %d vs %d shutting downsignal %d received in supervisorsignaled from serversignalled from serverskipping: %s spawning daemon processspecified for search scope while modifying information of realm '%s'standard inputstartingsubtree value: successsupplementary info codeterminal name %s too long there must be one master key currently activeticket policy object value: ticket policy object: too many connectionstrying to process principal databaseulog_replay failed (%s), updates not registered ulog_replay failed (%s), updates not registered.unable to decode stored principal key data (%s)unable to find plugin [%s]: %sunable to load DLL [%s]unable to load plugin [%s]: %sunexpected error checking transit from '%s' to '%s' via '%.*s%s': %sunhandled routing message type %d, will reconfigure just for the fun of itunknown addrtype %dunknown code 0x%xunknown failureunknown getaddrinfo failureunknown option '%s'unknown option: %sunknown record type "%s" unprintable address (type %d, error %d %s)updating: %s usage: %s (-key | -password) -p principal -k kvno -e enctype usage: %s [-C] [-u] [-c ccache] [-e etype] usage: %s [-x db_args]* [-d dbpathname] [-r dbrealmname] [-R replaycachename] [-m] [-k masterenctype] [-M masterkeyname] [-p port] [-P pid_file] [-n] [-w numworkers] [/] where, [-x db_args]* - Any number of database specific arguments. Look at each database module documentation for supported arguments usage: %s [principal] usage: add_principal [options] principal usage: change_password [-randkey] [-keepold] [-e keysaltlist] [-pw password] principal usage: del_string principal key usage: delete_policy [-force] policy usage: delete_principal [-force] principal usage: get_policies [expression] usage: get_policy [-terse] policy usage: get_principal [-terse] principal usage: get_principals [expression] usage: get_privs usage: get_strings principal usage: modify_principal [options] principal usage: purgekeys [-all|-keepkvno oldest_kvno_to_keep] principal usage: rename_principal [-force] old_principal new_principal usage: set_string principal key value usage; %s [options] policy validating credentialsvariable missing }waiting for a kprop connection waitpid() failed to wait for doit() (%d %s) when creating default server principal namewhen gathering parameterswhen initializing cache %swhen parsing name %swhen unparsing namewhile %swhile accepting connectionwhile adding entries to databasewhile adding entries to the databasewhile adding key to keytabwhile adding master key entry to the databasewhile adding new entrywhile adding new master keywhile adding preauth optionwhile allocating args.kvnoswhile allocating dump_ok filenamewhile allocating filename for temp filewhile allocating filename for update_last_prop_filewhile allocating i_vectorwhile allocating key_datawhile allocating memorywhile allocating memory for the target ccache namewhile allocating temporary filename dumpwhile appending realm to principalwhile attempting setsockopt (SO_KEEPALIVE)while attempting to connect to master KDC ... retryingwhile attempting to retrieve default realmwhile authenticating to serverwhile binding listener socketwhile building TGS name for realm %swhile building principalwhile calculated master key saltwhile calculating master key saltwhile calling cc_filterwhile calling waitpidwhile canonicalizing new principalwhile canonicalizing old principalwhile canonicalizing principalwhile canonicalizing principal namewhile changing %s's keywhile changing password for "%s".while changing to target uid for destroying ccachewhile checking authorizationwhile checking if stdin is a socketwhile clearing DISALLOW_ALL_TIX for "%s".while clearing the value of %swhile closing ccachewhile closing ccache %swhile closing databasewhile closing keytabwhile closing previous databasewhile connecting to serverwhile constructing my service realmwhile converting %s to new master keywhile converting etypewhile converting local addresswhile converting server addresswhile copying cache %s to %swhile copying client principalwhile creating 'last_prop' file, '%s'while creating 'ok' file, '%s'while creating PID filewhile creating databasewhile creating database '%s'while creating keytab namewhile creating main loopwhile creating new master keywhile creating new target ccachewhile creating policywhile creating policy "%s".while creating policy objectwhile creating principal %swhile creating realm '%s'while creating socketwhile creating sub-command argumentswhile creating temporary cachewhile creating tgt for local realmwhile creating update logwhile decoding database size from clientwhile decoding error packet from clientwhile decoding error response from serverwhile decoding ticket for %swhile decrypting ticket for %swhile deleting attribute from principal "%s"while deleting bad database %swhile deleting entry %dwhile deleting entry from keytabwhile deleting policy "%s"while deleting principal "%s"while destroying cachewhile destroying cache %swhile destroying policy objectwhile detaching from ttywhile determining local service principal namewhile determining target ccache namewhile dispatching (tcp)while dispatching (udp)while downgrading lock on '%s'while encoding database sizewhile ending keytab scanwhile erasing target cachewhile expanding expression "%s".while fetching master key %s for realm %swhile fetching master keys list for realm %swhile fetching principal %swhile finding master keys in usewhile finding mkeywhile finishing ticket retrievalwhile forkingwhile formatting master principal namewhile formatting parsed principal name for '%s'while freeing ktlistwhile freeing principal entrywhile generating new ccachewhile generating part of the target ccache namewhile getting "%s".while getting attributes for principal "%s"while getting client principal namewhile getting context for realm %swhile getting credentials for %swhile getting current timewhile getting default ccachewhile getting default client keytabwhile getting default keytabwhile getting default realmwhile getting enctype descriptionwhile getting initial credentialswhile getting initial credentials while getting keytab namewhile getting local socket addresswhile getting master key listwhile getting master key principal %swhile getting name of target ccachewhile getting policy info.while getting principal info.while getting source cachewhile getting timewhile getting time of daywhile initializingwhile initializing %s interfacewhile initializing %s interface, retryingwhile initializing Kerberos 5 librarywhile initializing Kerberos codewhile initializing SAMwhile initializing databasewhile initializing database for realm %swhile initializing error handlingwhile initializing i_vectorwhile initializing krb5while initializing krb5 librarywhile initializing krb5_contextwhile initializing ldap handlewhile initializing lookaside cachewhile initializing networkwhile initializing random key generatorwhile initializing routing socketwhile initializing server listwhile initializing signal handlerswhile initializing target cachewhile initializing the Kerberos admin interfacewhile initializing update logwhile listing ccache collectionwhile listing credential cacheswhile listing policy objectswhile listing realmswhile loading audit plugin module(s)while locking 'ok' file, '%s'while looking for credentials cache %swhile looking up active kvno listwhile looking up active master keywhile looking up active version of master keywhile looking up mkey aux data listwhile looking up the Kerberos configurationwhile making newly loaded database livewhile mapping update logwhile modifying "%s".while modifying information of realm '%s'while modifying policy "%s".while modifying policy objectwhile obtaining socketwhile opening %swhile opening %s for writingwhile opening ccachewhile opening credential cache %swhile opening credentials cache %swhile opening databasewhile opening database file, '%s'while opening default credentials cachewhile opening default keytabwhile parsing -add principal name %swhile parsing admin principal namewhile parsing command argumentswhile parsing command arguments while parsing keysalts %swhile parsing name %swhile parsing new principal namewhile parsing old principal namewhile parsing optionswhile parsing principalwhile parsing principal namewhile parsing principal name %swhile parsing temporary namewhile permanently locking databasewhile providing time specificationwhile purging keys for principal "%s"while randomizing key for "%s".while reading Kerberos container informationwhile reading cache name from ccachewhile reading information of policy '%s'while reading information of realm '%s'while reading kerberos container informationwhile reading keytab "%s"while reading ldap configurationwhile reading ldap parameterswhile reading master keywhile reading master key from keyboardwhile reading new master keywhile reading new master key from keyboardwhile reading new password.while reading passwordwhile reading password for "%s".while reading password for '%s' while reading realm informationwhile reading response from serverwhile reading size of database from clientwhile reading srvtab "%s"while reading update log headerwhile receiving from networkwhile reclaiming root uidwhile reinitializing update logwhile renaming %s to %swhile renaming dump file into placewhile renaming principal "%s" to "%s"while resetting target ccache namewhile resolving %swhile resolving ccache %swhile resolving kdb keytab for realm %swhile resolving keytabwhile resolving keytab %swhile restarting keytab scanwhile retreiving configuration parameterswhile retrieving "%s".while retrieving a ticketwhile retrieving creds from cachewhile retrieving highest kvno from keytabwhile retrieving ldap configurationwhile retrieving list.while retrieving master entrywhile retrieving policy "%s".while retrieving principalwhile retrieving principal namewhile retrieving privilegeswhile returning to source uid after finding best principalwhile saving principal %swhile scanning keytabwhile searching for ccache for %swhile seeding random number generatorwhile selecting the best principalwhile sending # of received byteswhile sending database block starting at %dwhile sending database sizewhile sending reply to %s/%s from %swhile setting '%s'='%s'while setting FAST ccachewhile setting SO_REUSEADDR optionwhile setting attribute on principal "%s"while setting changetimewhile setting client principal namewhile setting client principal realmwhile setting default realm namewhile setting default realm to %swhile setting euid to source userwhile setting server principal namewhile setting service object passwordwhile setting up KDB keytab for realm %swhile setting up enctype %dwhile setting up lib handlewhile setting up loggingwhile setting up master key namewhile setting up master key name %s for realm %swhile starting keytab scanwhile starting to retrieve ticketswhile storing %swhile storing credentialswhile storing keywhile switching back from target after authorization checkwhile switching to credential cachewhile switching to new ccachewhile switching to target for authorization checkwhile switching to target uidwhile temporarily ending keytab scanwhile transforming master key from passwordwhile trying to change password.while trying to close database filewhile trying to construct host service principalwhile trying to construct my service namewhile trying to exec %swhile trying to execv %swhile trying to fork %swhile trying to fork.while trying to lock '%s'while trying to malloc data_ok_fnwhile trying to open %swhile trying to stat %swhile unlocking '%s'while unlocking databasewhile unlocking database '%s'while unlocking locked databasewhile unparsing client namewhile unparsing principalwhile unparsing principal namewhile unparsing server namewhile unparsing ticket etypewhile unsetting IPV6_V6ONLY optionwhile updating actkvno data for master principal entrywhile updating mkey_aux data for master principal entrywhile updating principal '%s' key data in the databasewhile updating principal '%s' modification timewhile updating the master key principal modification timewhile verifying master keywhile verifying ticket for serverwhile viewing policywhile viewing policy '%s'while waiting for %swhile waiting to receive databasewhile writing keytab "%s"while writing update log headerworker %ld exited with status %dwould skip: %s would update: %s wrong oid yet no policy set! Contact your system security administrator.Project-Id-Version: mit-krb5 13.2 Report-Msgid-Bugs-To: krbdev@mit.edu POT-Creation-Date: 2015-05-06 14:59-0400 PO-Revision-Date: 2016-04-07 08:15+0200 Last-Translator: Chris Leick Language-Team: German Language: de MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=n != 1; -n löst nicht rückwärts auf. -K zeigt die Schlüssel der Schlüsseltabelleneinträge. -a zeigt die Adressliste. -d zeigt die übertragenen Autorisierungsdatentypen. t -f zeigt die Anmeldedatenschalter. -s setzt den Exit-Status auf Basis der Existenz eines gültigen TGTs. -t zeigt die Zeitstempel der Schlüsseltabelleneinträge. Attributschalter fehlgeschlagener Passwortversuch Schlüsseldaten letzte fehlgeschlagene Authentifizierung letzte erfolgreiche Authentifizierung Länge maximale verlängerbare Lebensdauer maximale Ticketlebensdauer Änderungszeit Geändert wobei tPrincipal wird geändert Ablauf des Passworts Passwortchronik Passwortchronik KVNO letzte Passwortänderung Passwortrichtlinie Passwortrichtlinienumschalter Principal Ablauf des Principals TL-Daten [-lockoutduration Dauer] [-maxlife Zeit] [-minlife Zeit] [-minlength Länge] [-minclasses Anzahl] [-history Nummer] [-maxfailure Anzahl] [-failurecountinterval Zeit] [-allowedkeysalts Schlüssel-Salts] [-randkey|-nokey] [-x DB-Principal-Argumente]* [-expire Ablaufdatum] [-pwexpire Passwortablaufdatum] [-maxlife maximale_Ticketlebensdauer] [-kvno KVNO] [-policy Richtlinie] [-clearpolicy] [-pw Passwort] [-maxrenewlife maximale_Dauer_bis_zum_Erneuern] [-e Schlüssel-Salt-Liste] [{+|-}Attribut] [-x DB-Principal-Argumente]* [-expire Ablaufdatum] [-pwexpire Passwortablaufdatum] [-maxlife maximale_Ticketlebensdauer] [-kvno KVNO] [-policy Richtlinie] [-clearpolicy] [-maxrenewlife maximale_Dauer_bis_zum_Erneuern] [-unlock] [{+|-}Attribut] allow_postdated allow_forwardable allow_tgs_req allow_renewable allow_proxiable allow_dup_skey allow_tix requires_preauth requires_hwauth needchange allow_svr password_changing_service ok_as_delegate ok_to_auth_as_delegate no_auth_data_required wobei [-x DB-Principal-Argumente]* - eine beliebige Zahl datenbankspezifischer Argumente ist. Die unterstützten Argumente finden Sie in der jeweiligen Datenbankdokumentation. (Voreinstellung ist Anmeldedatenzwischenspeicher) -A vernichtet alle Anmeldedatenzwischenspeicher in der Sammlung. -a bezieht Adressen nicht ein. -A zeigt den Inhalt aller Anmeldedatenzwischenspeicher an. -C bringt in Normalform -E Client ist der Principal-Name des Unternehmens -F nicht weiterleitbar -P Proxy nicht nutzbar -R erneuert -S Dienst -T gehärteter Anmeldedatenzwischenspeicher -V zeigt die Kerberos-Version und wird beendet. -V detaillierte Ausgabe -X [=] -a bezieht Adressen ein. -c Kerberos-5-Zwischenspeichername -c gibt den Anmeldedatenzwischenspeicher an -c gibt den Namen des Zwischenspeichers für Anmeldedaten an. -e zeigt den Verschlüsselungstyp. -f weiterleitbar -i verwendet die Standardschlüsseltabelle des Clients (mit -k). -i verwendet die Standardschlüsseltabelle des Clients, falls kein Name angegeben wurde. -k gibt die Schlüsseltabelle an. -k verwendet Schlüsseltabelle -l Lebensdauer -l listet gesammelte Anmeldedatenzwischenspeicher auf. -n anonym -p Proxy nutzbar -p gibt den Namen des Principals an. -q stiller Modus -r verlängerbare Lebensdauer -s Startzeit -t Dateiname der zu verwendenden Schlüsseltabelle -v überprüft Adressen: Adressen: (keine) geänderte Attribute: %d Blockgrößeneintrag: %u erste Seriennummer: erste Seriennummer: keine erster Zeitstempel: %s erster Zeitstempel: keiner letzte Seriennummer: letzte Seriennummer: keine letzter Zeitstempel: %s letzter Zeitstempel: keiner Protokollstatus: Protokollversion #: %u Anzahl der Einträge: %u Aktualisierung übergeben: %s Aktualisierungsaktion: Aktualisierung des Principals: %s Aktualisierung der Seriennummer: %u Größe der Aktualisierung: %u Zeitstempel der Aktualisierung: %s Zeitstempel der Aktualisierung: keiner [-A Serveradministrator] [-F Kerberos-Datenbankdatei ] [-p KDB5-Hilfswerkzeugpfadname] [-k Schlüsseltabelle] [-S Sname] [-U für_Benutzer [-P]] [-x Datenbankargumente]* [-P Port] [-a ACL-Datei] Es gibt folgende Attribute: für Client %s Es gibt folgende Optionen: Optionen für Anmeldedatenzwischenspeicher: Optionen für Schlüsseltabellen: Dienst1 Dienst2 … update_princ_encryption [-f] [-n] [-v] [Principal-Muster] purge_mkeys [-f] [-n] [-v] dabei sind [-x Datenbankargumente]* - eine beliebige Anzahl datenbankspezifischer Argumente. Welche Argumente unterstützt werden, finden Sie in der Dokumentation der jeweiligen Datenbank. (Geben Sie als Bestätigung »yes« ein) Speicher konnte nicht reserviert werden Kerberos-Aktualisierungsprotokoll (%s) Aufruf: %s [-h] [-v] [-v] [-e Zahl] %s -R Aufruf: %s [-r Realm] [-f Datei] [-d] [-P Port] [-s Dienstschlüsseltabelle] untergeordneter_Rechner Aufruf: %s [-r Realm] [-s Dienstschlüsseltabelle] [-dS] [-f untergeordnete_Datei] %s erfolgreich in der Ferne über einen unsicheren (unverschlüsselten) Kanal angemeldet sind. in der Ferne mittels eines unsicheren (unverschlüsselten) Kanals angemeldet sind. GSS-API-Fehlermeldungen vollständig%s beim Rückgängigmachen der Namensauswertung vollständiges erneutes Synchronisieren, das Master-KDC gab einen Fehler zurückKDC kann nicht initialisiert werden. Speicher reicht nicht aus [existiert nicht]$Id$%.*s%s%s. Bitte versuchen Sie es erneut. %d ist kein gültiger KVNO-Wert%d Schlüssel vollständig entfernt %d Schlüssel würde(n) vollständig entfernt. %s: Status %d kann nicht dekodiert werden%s geändert %s-Konstruktion von AP-REQ-Schutz%s enthält einen ungültigen Dateinamen.%s hat nicht die korrekten Rechte für %s, %s wird abgebrochen.%s ist ein ungültiger VerschlüsselungstypLaden von %s beendetOption %s wird nicht unterstütztWert der Option %s fehlt%s gab einen falschen Exit-Status (%d) zurück%s Version %s %s bei %s, wird abgebrochen %s beim Entschlüsseln der FAST-Antwort%s bei der Handhabung des »ap-request«-Schutzes%s beim Öffnen der ACL-Datei %s%s beim Speichern der Anmeldedaten%s(%d): %s %s(%d): Müll am Zeilenende wird ignoriert: %s, wird abgebrochen%s. Bitte versuchen Sie es erneut.%s: Wiederherstellen von %s fehlgeschlagen %s: %s bei %s, wird abgebrochen %s: %s beim Initialisieren des Kontextes, wird abgebrochen %s: %s, wird abgebrochen %s: %s, es wird versucht, den Standard-Realm abzufragen %s: Zu wenig Speicher zum Initialisieren %s: Protokolldatei konnte nicht abgebildet werden %s: ungültiges Passwort %s: KDC kann nicht initialisiert werden. Speicher reicht nicht aus %s: Schlüsseltabelle %s existiert nicht. %s: Für Principal %s existiert kein Eintrag in der Schlüsseltabelle %s. %s: Für den Principal %s mit der KVNO %d existiert kein Eintrag in der Schlüsseltabelle %s. %s: Passwort falsch %s: Passwort bei %s falsch %s: Principal %s existiert nicht. %s: Bei der Standardeingabe scheint es sich nicht um ein Netzwerk-Socket zu handeln (läuft nicht aus Inetd und die Option -S fehlt?). %s: Es gibt nur einen Eintrag für Principal %s in der Schlüsseltabelle %s. %s: unbekannte Iprop-Auszugsversion %d %s: Kopfzeilen des Auszugs in %s können nicht gelesen werden. Ausgabenzeichenkette des KDB5-Hilfswerkzeugs nicht konstruierbar, da zu lang; Speicher reicht nicht aus.%s: Die Ausgabezeichenkette des KDB5-Hilfswerkzeugs kann nicht erstellt werden, weil sie zu lang ist. Der Speicherplatz reicht nicht aus.%s: Speicher für die Realm-Liste kann nicht erlangt werden %s: Realm %s kann nicht initialisiert werden - Einzelheiten finden Sie in der Protokolldatei %s: Realm %s kann nicht initialisiert werden. Speicher reicht nicht aus %s: kann nicht initialisiert werden: Speicher reicht nicht aus. %s: <%s> kann nicht ausgewertet werden %s: eingeschränkte Abtretung fehlgeschlagen%s: IPROP-Dienst wird erstellt (PROG=%d, VERS=%d) %s: falsche Kopfzeilen des Auszugs in %s %s: Die Auszugsversion %s kann nur mit dem Schalter -update geladen werden. %s: Fehler beim Verarbeiten von Zeile %d von %s %s: Fehler beim Schreiben auf %s %s: Fehler beim Schreiben auf Gerät %s %s: exec fehlgeschlagen: %s%s: Verzweigen fehlgeschlagen: %s%s: getclhoststr fehlgeschlagen%s: ungültige Argumente %s: ungültige Beschränkung: %s%s: KVNO = %d %s: KVNO = %d, Schlüsseltabelleneintrag gültig %s: Zeile %d zu lang, wurde gekürzt%s: zu löschender Eintrag muss angegeben werden %s: Die Schlüsseltabelle, die gelesen werden soll, muss angegeben werden. %s: Die zu schreibende Schlüsseltabelle muss angegeben werden. %s: Die zu lesende Dienstschlüsseltabelle muss angegeben werden. %s: Speicherplatz reicht nicht aus %s: Speicher reicht nicht zur Aufzeichnung der Principal-Namen aus%s: Auswertungsprogramm verlor Anzahl! %s: pclose(popen) fehlgeschlagen: %s%s: popen fehlgeschlagen: %s%s: Fehler im regulären Ausdruck: %s %s: Fehler beim Abgleich mit regulärem Ausdruck: %s %s: Server-Identifikator ist NULL%s: setup_gss_names fehlgeschlagen%s: startet … %s: Syntaxfehler in Zeile %d <%10s …>%s: Unstimmigkeit in der markierten Datenliste für %s (%d gezählt, %d gespeichert) %s: Principal kann nicht in die normale Form gebracht werden %s: Es kann kein Principal-Name herausgefunden werden. %s: Standard-Realm kann nicht geholt werden %s: Kiprop-rechnerbasierter Dienstname für Realm %s kann nicht geholt werden %s: Aufruf: %s [-t] [-k] [-e] %s: Warnung – Syntaxfehler bei Protokolleintrag %s: Schreiben der Dienstschlüsseltabelle wird nicht länger unterstützt %s; eingehende Seriennummer=%lu; ausgehende Seriennummer=%lu%s; eingehende Seriennummer=%lu; ausgehende Seriennummer=N/A%spreauth benötigt, aber Hinweisliste ist leer%u Principals verarbeitet: %u aktualisiert, %u bereits aktuell %u Principals verarbeitet: %u würden aktualisiert, %u bereits aktuell »%s« ist keine gültige Option »%s« ist aktueller als »%s«.»%s« nicht gefunden: Wert »%s« fehlt(abgelaufen)(keine Zeichenkettenattribute) (Principal %s)(Geben Sie als Bestätigung »yes« ein)? ** Datenbank »%s« vernichtet ** Datenbank von »%s« vernichtet Die Optionen -Z und -z schließen sich gegenseitig aus. Die Option »-norandkey« ist nur für »kadmin.local« gültig. Die Optionen -z und -Z schließen sich gegenseitig aus. … EINHESCHRÄNKTE DELEGIERUNG s4u-client=%s… PROTOKOLLÜBERGANG s4u-client=%s0 ist kein gültiger KVNO-Wert: %s ist kein gültiger Verschlüsselungstyp: %s ist kein gültiger MkeyVNOEine der Anmeldedaten war ungültig.Es wurde bereits ein neueres Merkmal verarbeitet.Ein Parameter hatte eine falsche FormEin benötigter Eingabeparameter konnte nicht gelesen werden.Ein benötigter Eingabeparameter konnte nicht geschrieben werden.Ein erforderlicher Ausgabeparameter konnte nicht geschrieben werden.Ein Dienst, der zum Verarbeiten der Abfrage erforderlich ist, ist nicht verfügbar.Ein übergebener Name hatte einen nicht unterstützten Typ.Ein Merkmal hatte eine ungültige Meldungsintegritätsprüfung (Message Integrity Check/MIC).Ein Merkmal hatte eine ungültige Signatur.Ein Merkmal war ungültig.AD-TypenALARMDie von der Anwendung angegebene Version der API-Struktur wird nicht länger unterstützt. (Kompilieren Sie die Anwendung mit den aktuellen KADM5-API-Header-Dateien und -Bibliotheken, um dies zu beheben.)Die von der Anwendung angegebene Version der API-Struktur ist den Bibliotheken unbekannt. (Besorgen Sie sich die aktuellen KADM5-API-Header-Dateien und -Bibliotheken und kompilieren Sie die Anwendung neu, um dies zu beheben.)Die von der Anwendung angegebene Version der API-Struktur wird nicht länger von den Bibliotheken unterstützt. (Aktualisieren Sie die Anwendung, dass sie zu der aktuellen API-Version passt, und kompilieren Sie sie, um dies zu beheben.)Die von der Anwendung angegebene Version der API-Struktur wird nicht länger vom Server unterstützt. (Aktualisieren Sie die Anwendung, dass sie zu der aktuellen API-Version passt, und kompilieren Sie sie, um dies zu beheben.)Die von der Anwendung angegebenene API-Version ist den Bibliotheken unbekannt. (Besorgen Sie sich die aktuellen KADM5-API-Header-Dateien und -Bibliotheken und kompilieren Sie die Anwendung neu, um dies zu beheben.)Die von der Anwendung angegebene API-Version ist dem Server unbekannt. (Besorgen und installieren Sie sich den neuesten KADM5-Admin-Server, um dies zu beheben.)ASN.1 falscher Rückgabewert von Gmtimefehlerhaft formatierte ASN.1-KodierungASN.1-Kodierung endete unerwartetASN.1 beim Aufruf der Systemzeitbibliothek gescheitertASN.1-Bezeichner passt nicht zum erwarteten WertLänge von ASN.1 passt nicht zum erwarteten WertASN.1 fehlt erwartetes EOCnicht konstruierte unbestimmte ASN.1-KodierungASN.1-Objekt in Sequenz ausgelassenASN.1-Auswertungsfehlerein erforderliches Feld fehlt in der ASN.1-StrukturASN.1-Typnummern sind inkonsistentASN.1 unerwartete FeldnummerASN.1-Wert zu großAS_REQ (%s) %s: %s: %s für %s%s%sAS_REQ (%s) %s: PROBLEM: Authentifizierungszeit %d, %s, %s für %sAS_REQ: handle_authdata (%d)Abnehmer und Initiator haben keinen gemeinsamen PrüfsummentypZugriff verweigertKonto %s: Autorisierung für %s zum Ausführen von Konto %s: Autorisierung für %s zum Ausführen von %s fehlgeschlagen Konto %s: Autorisierung für %s erfolgreich Konto %s: Autorisierung von %s fehlgeschlagen Hinzufügen zusätzlich Vorauthentifizierung erforderlichAdministratordiensteDer Administrationsrechner unterstützt beim Erstellen von »%s« kein -nokey Verwaltungsanmeldedaten NICHT VERNICHTET keine Tickets erlaubtAlle Schlüssel für Principal »%s« wurden entfernt. Alle Schlüssel sind in Gebrauch, keiner wurde vollständig entfernt. erlaubte Schlüssel-/Salt-Typen: %s alternative Authentifizierungsmethode erforderlichEin erwartetes nachrichtenspezifisches Token wurde nicht empfangen.Ein ungültiger Name wurde übergeben.Ein ungültiger Statuscode wurde übergeben.Es wurde eine nicht unterstützte kritische FAST-Aktion angefordert.Ein nicht unterstützter Mechanismus wurde angefordert.Anonymes Pkinit wird nicht ohne öffentlichen DH-Wert unterstützt.»ap-request«-Schutz nicht mit TGS gestattetSind Sie sicher, dass Sie die Richtlinie »%s« löschen möchten? (yes/no): Sind Sie sicher, dass Sie den Principal »%s« löschen möchten? (yes/no): Sind Sie sicher, dass Sie den Principal »%s« in »%s« umbenennen möchten? (yes/no): Es wurde zweimal versucht, die Datenbank zu sperren.Es wurde versucht, die Datenbank zu entsperren, obwohl sie nicht gesperrt ist.Es wurde versucht, einen unvollständigen Sicherheitskontext zu verwenden.Attribut von Principal »%s« wurde gelöscht. Attribute für Principal »%s« wurden gesetzt. Attribute:Authentifizierungskontext muss lokale Adresse enthaltenAuthentifizierungskontext muss ferne Adresse enthaltenAuthentifiziert %s Authentifiziert für Kerberos v5 Authentifizierung als Principal %s mit Standardschlüsseltabelle Authentifizierung als Principal %s mit existierenden Anmeldedaten Authentifizierung als Principal %s mit Schlüsseltabelle %s Authentifizierung als Principal %s mit Passwort Authentifizierung als Principal %s mit Passwort; Anonymität erwünscht Authentifizierungsversuch fehlgeschlagen: %s, GSS-API-Fehlermeldungen:AuthentifizierungsfehlerAuthentifizierung fehlgeschlagen. Schlüsselziffer hat keinen Unterschlüsselfalsche SAM-Schalter in »obtain_sam_padata«Es wurde eine falsche Anwendungsversion (über Sendauth) verschicktfalsche Prüfsumme in der SAM-Aufforderungfalsche Schalter zum Erstellen der Datenbankfalsche Richtung zum Umwandeln von Schlüsselnfalscher Verschlüsselungstypfalsches Format im Anmeldedatenzwischenspeicherfalsches Format in der Schlüsseltabellefalscher Rechnername des KRB5-Admin-Serversfalscher Wert für die Lebensdauer %s falscher Wert für Lebensdauer (%s Stunden?) falscher Sperrmodusfalsche magische Zahl für FAST-Anfragefalsche magische Zahl für FAST-Antwortfalsche magische Zahl für GSSAPI OIDfalsche magische Zahl für GSSAPI QUEUEfalsche magische Zahl für per FAST geschützte Anfragefalsche magische Zahl für »iakerb_ctx_id_t«falsche magische Zahl für Krb5_address-Strukturfalsche magische Zahl für Krb5_alt_method-Strukturfalsche magische Zahl für Krb5_ap_rep-Strukturfalsche magische Zahl für Krb5_ap_rep_enc_part-Strukturfalsche magische Zahl für Krb5_ap_req-Strukturfalsche magische Zahl für Krb5_auth_context-Strukturfalsche magische Zahl für Krb5_authdata-Strukturfalsche magische Zahl für Krb5_authdata_contextfalsche magische Zahl für Krb5_authenticator-Strukturfalsche magische Zahl für Krb5_ccache-Strukturfalsche magische Zahl für Krb5_krb5_checksum-Strukturfalsche magische Zahl für Krb5_checksum_entry-Strukturfalsche magische Zahl für Krb5_context-Strukturfalsche magische Zahl für Krb5_cred-Strukturfalsche magische Zahl für Krb5_cred_enc_part-Strukturfalsche magische Zahl für Krb5_cred_info-Strukturfalsche magische Zahl für Krb5_creds-Strukturfalsche magische Zahl für Krb5_cryptosystem_entry-Strukturfalsche magische Zahl für Krb5_cs_table_entry-Strukturfalsche magische Zahl für Krb5_data-Strukturfalsche magische Zahl für Krb5_db_context-Strukturfalsche magische Zahl für Krb5_enc_data-Strukturfalsche magische Zahl für Krb5_enc_kdc_rep_part-Strukturfalsche magische Zahl für Krb5_enc_sam_response_encfalsche magische Zahl für Krb5_enc_tkt_part-Strukturfalsche magische Zahl für Krb5_encrypt_bloc-Strukturfalsche magische Zahl für Krb5_error-Strukturfalsche magische Zahl für Krb5_etype_info_entry-Strukturfalsche magische Zahl für »krb5_gss_cred_id_t«falsche magische Zahl für »krb5_gss_ctx_id_t«falsche magische Zahl für »krb5_gss_enc_desc«falsche magische Zahl für Krb5_kdc_rep-Strukturfalsche magische Zahl für Krb5_kdc_req-Strukturfalsche magische Zahl für Krb5_krb5_keyblock-Strukturfalsche magische Zahl für Krb5_keytab-Strukturfalsche magische Zahl für Krb5_keytab_entry-Strukturfalsche magische Zahl für Krb5_last_req_entry-Strukturfalsche magische Zahl für Krb5_os_context-Strukturfalsche magische Zahl für Krb5_pa_data-Strukturfalsche magische Zahl für Krb5_preauth_opsfalsche magische Zahl für Krb5_predicted_sam_responsefalsche magische Zahl für Krb5_principal-Strukturfalsche magische Zahl für Krb5_priv-Strukturfalsche magische Zahl für Krb5_priv_enc_part-Strukturfalsche magische Zahl für Krb5_pwd_data-Strukturfalsche magische Zahl für Krb5_rcache-Strukturfalsche magische Zahl für Krb5_response-Strukturfalsche magische Zahl für Krb5_safe-Strukturfalsche magische Zahl für Krb5_sam_challengefalsche magische Zahl für Krb5_sam_challenge_2falsche magische Zahl für Krb5_sam_keyfalsche magische Zahl für Krb5_sam_responsefalsche magische Zahl für Krb5_sam_response 2falsche magische Zahl für Krb5_ticket-Strukturfalsche magische Zahl für Krb5_tkt_authent-Strukturfalsche magische Zahl für Krb5_transited-Strukturfalsche magische Zahl für Passwd_phrase_elementfalscher Principal beim Versuch, Anmeldedaten weiterzuleitenfalsche Antwort (beim Sendauth-Austausch)Es wurde eine falsche Sendauth-Version verschicktfalscher Wert für die Startzeit %s Bind-DN-Eintrag »%s« fehlt in der LDAP-Passwortdatei »%s«Puffer hat die falsche GrößeVom Master wurde ein Signal empfangen, dass er ausgelastet ist, Zurückhaltung für %d Sekunden KRITISCH»iprop_get_updates_1()« wird aufgerufen (sno=%u sec=%u usec=%u) Hauptschlüssel kann nicht abgeholt werden (Fehler: %s)Der neue ergänzende Zwischenspeicher kann nicht erzeugt werdenDer neue ergänzende Zwischenspeicher kann nicht erstellt werden, da der Standardzwischenspeicher bereits eine Ergänzung ist.Der neue ergänzende Zwischenspeicher kann nicht erstellt werden, da der Standardzwischenspeicher keine Ansammlung von Verzeichnissen ist.Wiederholungszwischenspeicher kann nicht vernichtet werden: %sClient-Principal %s kann nicht in der Zwischenspeicheransammlung gefunden werdenZu UID %lu kann kein Benutzername gefunden werden.Kerberos-Konfigurationsdatei kann nicht geöffnet/gefunden werdendie Lname-Übersetzungsdatenbank kann nicht geöffnet/gefunden werdenRealm-Übersetzungsdatei kann nicht geöffnet/gefunden werdenaus dem Wiederholungszwischenspeicher kann nicht gelesen werden: %sZertifikat kann nicht überprüft werdenin Wiederholungszwischenspeicherdatei kann nicht geschrieben werden: %s%s der Wiederholungszwischenspeicherdatei %s nicht möglich: %s%s des Wiederholungszwischenspeichers nicht möglich: %sServer-Socket kann nicht an %s gebunden werdenmit LDAP-Server »%s« kann keine Verbindung als »%s« hergestellt werden: %smit LDAP-Server »%s« kann keine Verbindung mit SASL-Mechanismus »%s« hergestellt werden: %sGSS-API-Authentifizierungsnamen können nicht gebildet werden.Schlüsseltabelle mit aktiven Schlüsseltabelleniteratoren kann nicht geändert werdengeschützter Principal kann nicht geändert werdenfür Realm »%.*s« kann nicht KDC kontaktiert werdenFür den angeforderten Realm kann kein KDC kontaktiert werden.V5-Adressinformationen können nicht umgewandelt werdenV5-Schlüsselblock kann nicht umgewandelt werdenV5-Principal kann nicht umgewandelt werdenLDAP-Identifikator für »%s« kann nicht erstellt werden: %sRPC-Dienst kann nicht erstellt werden: %s; es wird fortgefahrenAuf %s kann kein TCP-Server-Socket erstellt werden.Zertifikatskette kann nicht erstellt werden: %sTicket für %s kann nicht mittels des Schlüsseltabellenschlüssels für %s entschlüsselt werdenRealm für Rechner kann nicht bestimmt werdenRealm für numerische Rechneradresse kann nicht bestimmt werdenSO_REUSEADDR kann nicht für Dateideskriptor %d aktiviert werdenKDC für Realm »%.*s« kann nicht gefunden werdenKDC für angeforderten Realm kann nicht gefunden werdenDer aktive Hauptschlüssel kann nicht gefunden werden.Schlüssel für %s-KNVO %d kann nicht in der Schlüsseltabelle gefunden werdenSchlüssel für %s-KNVO %d kann nicht in der Schlüsseltabelle gefunden werden (angefragter Ticketserver %s)Hauptschlüsseldatensatz kann nicht in der Datenbank gefunden werdenTicket für angeforderten Realm kann nicht gefunden werdenDer gespeicherte Hauptschlüssel kann nicht gefunden/gelesen werden.»fstat« für Antwortzwischenspeicherdatei %s nicht möglich: %sGSSAPI-Dienstname kann nicht initialisiert werdenAuf dem TCP-Server-Socket kann nicht auf eine Verbindung gewartet werden auf %s.Datenbank kann nicht gesperrt werdenLDAP-Passwortdatei »%s« kann nicht geöffnet werden: %sPasswort kann nicht gelesen werdenRPC-Dienst kann nicht registriert werden: %s; es wird fortgefahrenPaketinformation für UDP-Socket-Adresse %s, Port %d, kann nicht abgefragt werdenAdresse des Admin-Servers »%s« für Realm »%s« kann nicht ermittelt werdenNetzwerkadresse für KDC im angeforderten Realm kann nicht aufgelöst werdenDie Netzwerkadresse für den Admin-Server im angeforderten Realm kann nicht aufgelöst werden.Das Passwort kann nicht erneut verwendet werden.GSS-API-Authentifizierungsnamen können nicht gesetzt werden.Die KDB-Schlüsseltabelle kann nicht eingerichtet werden.Wiederholungszwischenspeicherdatei kann nicht synchronisiert werden: %sin angegebene Schlüsseltabelle kann nicht geschrieben werdenCcache-Funktion nicht unterstützt: nicht implementiertCcache-Funktion nicht unterstützt: Ccache-Typ nur lesbarZertifikat passt nichtAnforderung für Activcard-MechanismusAnforderung für Digital-Pathway-MechanismusAnforderung für erweiterten S/Key-MechanismusAnforderung für Enigma-Logic-MechanismusAnforderung für Security-Dynamics-MechanismusAnforderung für traditionellen S/Key-MechanismusAnforderung vom AuthentifizierungsserverUID wird zu %s (%ld) geändert Prüfsumme muss enthalten seinClient »%s« wurde nicht in der Kerberos-Datenbank gefundenClient-Name passt nichtClient nicht in der Kerberos-Datenbank gefundenClient nicht vertrauenswürdigClient noch nicht gültig – versuchen Sie es später noch einmalClient oder Server hat einen NullschlüsselEintrag des Clients in der Datenbank ist abgelaufenDer Schlüssel des Clients wurde mit einem alten Hauptschlüssel verschlüsselt.Client-/Server-Realm passen in der anfänglichen Ticketanfrage nicht zusammen.Anmeldedaten des Clients wurden widerrufenUhrzeitabweichung zu großZeitversatz in der KDC-Antwort zu großKommunikation mit dem Server fehlgeschlagenBedingter Auszug ist eine nicht dokumentierte Option, die nur für Iprop-Auszüge benutzt wird.Konfigurationsdatei gibt keinen Standard-Realm anVerbindung von %sVerbindung zum Server ist bereits initialisiertVerbindung zum Server nicht initialisiertKontext wurde bereits vollständig eingerichtetUmwandlung in Dienst-Principal für Namenstyp nicht definiertbeschädigt beschädigtes Kopfzeilenprotokoll, wird beendet beschädigter Aktualisierungseintrag Der Principal-Name konnte nicht reserviert werden. Temporärer Schlüsseltabellendateiname konnte nicht erstellt werden.Das Inetd-Socket konnte nicht dupliziert werden: %sDas Erweiterungsmodul %s namens »%s« konnte nicht gefunden werden.Es konnte kein TGT geholt werden für /dev/null konnte nicht geöffnet werden: %sIprop-Ulog kann nicht geöffnet werden. »gss_buffer_t«-Daten konnten reserviert werdenProtokolldatei %s konnte nicht geöffnet werden: %s »database_name« kann nicht gelesen werden Ulog-Datei %s konnte nicht neu initialisiert werden Es wird ein neuer Hauptschlüssel für den Hauptschlüssel-Principal »%s« erstellt. Anmeldedatenzwischenspeicherverzeichnis %s existiert nicht.Anmeldedatenzwischenspeicherverzeichnis %s existiert, ist jedoch kein VerzeichnisAnmeldedatenzwischenspeicher hat kein TGTAnmeldedatenzwischenspeicher ist leerfalsche Form des AnmeldedatenzwischenspeichernamensTyp des Anmeldedatenaufrufs ist unbekanntAnmeldedatenzwischenspeicher-E/A-Aktion fehlgeschlagen (%s)E/A-Aktion für Anmeldedatenzwischenspeicher fehlgeschlagen XXXAnmeldedatenzwischenspeicherdatei »%s« nicht gefundenSchlüsselbund %s des Anmeldedatenzwischenspeichers nicht gefundenAnmeldedatenzwischenspeicherrechte nicht korrektAnmeldedatenzwischenspeichertyp ist bereits registriertAnmeldedaten für den Server wurden widerrufeninterner Fehler des VerschlüsselungssystemsDie aktuell minimale Lebensdauer des Passworts ist nicht abgelaufen.DES-Schlüssel hat falsche ParitätDES-Schlüssel ist schwachDN-Information fehltDN liegt außerhalb ders Teilbaums des RealmsDatenbankfehler! Erforderlicher KADM5-Principal fehltfehlerhaftes DatenbankformatDatenbank wurde bereits initialisiertDatenbank wurde nicht initialisiertDatenbankinkonsistenz entdecktDatenbank ist gesperrt oder wird gerade benutzt – versuchen Sie es später wiederInitialisieren der Datenbankbibliothek fehlgeschlagenDer Datenbankladeprozess für eine vollständige Verbreitung ist abgeschlossen. Datenbankmodul passt nicht zur KDC-VersionDie Datenbank ist zur Zeit nicht geöffnet!Datenbank nicht initialisiertDatenbank nicht gesperrtDatenbankverbreitung auf %s: ERFOLGREICH Datenbank-LesefehlerDatensatz ist unvollständig oder beschädigtDatenbank-SpeicherfehlerDatenbanktyp nicht unterstütztDatenbank wurde während des Lesens geändertDekodieren außerhalb der DatenEntschlüsselungsintegritätsprüfung fehlgeschlagenStandard-Realm nicht gesetztLöschen Löschen fehlgeschlagen: Ein oder mehrere Principals gehören zur Ticket-Richtlinie.Sind Sie sicher, dass die KDC-Datenbank von »%s« gelöscht werden soll? Die in »%s« gespeicherte KDC-Datenbank wird gelöscht. Sind Sie sicher? Kurzfassung im Zertifikat nicht akzeptiertKurzfassung in signierten Daten nicht akzeptiertNOTFALLFEHLER vom Master-KDC zurückgegeben, Ausstieg FEHLER vom Master zurückgegeben, Ausstieg Leere Passwörter sind nicht erlaubt.Kodierung zu langverschlüsselte Aufforderung wurde außerhalb des FAST-Tunnels verwendetVerschlüsselungstyp %s nicht erlaubtVerschlüsselungstyp nicht erlaubtVerschlüsselungstyp %d hat keine PRFEnde des Anmeldedatenzwischenspeichers erreichtEnde der Schlüsseltabelle erreichtDateiende bei der E/A des WiederholungszwischenspeichersGeben Sie # einGeben Sie die den DN des Kerberos-Containers ein: Geben Sie den Wert des Einwegpasswort-Tokens anGeben Sie es erneut ein.Geben Sie ein neues Passwort ein.Geben Sie das Passwort für Principal »%s« ein.Eintrag existiert bereits in der DatenbankDekodieren der eingetragenen Daten fehlgeschlagen Der Eintrag für Principal %s mit KVNO %d wurde aus der Schlüsseltabelle %s entfernt. Der Eintrag für Principal %s mit KVNO %d und Verschlüsselungstyp %s wurde der Schlüsseltabelle %s hinzugefügt. FehlerFehler beim Erstellen der Datei %sFehler in »krb5_auth_con_ini«: %sFehler in »krb5_auth_con_setaddrs«: %sFehler in »krb5_auth_con_setflags«: %sFehler in »krb5_copy_prinicpal«: %sFehler in »krb5_kt_resolve«: %sFehler in »krb5_recvauth«: %sFehler beim Verarbeiten der LDAP-Datenbankparameter:Fehler beim Lesen des Attributs »%s«: %sFehler beim Lesen der LDAP-Server-Parameters:Fehler beim Lesen des Container-Objekts: Fehler beim Lesen des Kerberos-Container-Speicherorts aus der »krb5.conf«.Fehler beim Lesen des Passworts aus der Ablage: Fehler beim Lesen der Passwortdatei für das DienstobjektFehler beim Lesen der Ticket-RichtlinieFehler beim Lesen der Ticket-Richtlinie:Fehlermeldung vom Client: %s Fehlermeldung vom Server: %s Fehler beim Schreiben in den AnmeldedatenzwischenspeicherFehler beim Schreiben in SchlüsseltabelleFehler: falscher Eintrag – %s in Datei %s. CMD_PATH enthält keine Pfade. Fehler: falscher Eintrag – %s in Datei %s muss entweder ein vollständiger Pfad oder nur ein Befehlsname sein. Fehler: falscher Eintrag – %s in Datei %s. Da %s nur ein Befehlsname ist, muss CMD_PATH definiert sein. Fehler: falscher Pfad %s in CMD_PATH für %s muss mit »/« beginnen Fehler: nicht gefunden -> Etype (Skey, TKT): %s, Innerhalb des FAST-Containers wird »FX_ERROR pa-data« erwartet.Ablaufdatum: %s exponentieller Wartezyklus zusätzliche Argumente (beginnend mit »%s«) FAST-geschützte Vorauthentifizierung erforderlich, aber nicht vom KDC unterstütztFAST-»req_checksum« ungültig; Anfrage geändertDer FAST-Anwort fehlt die Beendigungsnachricht in der KDC-AnwortHolen von Objektbezügen fehlgeschlagenFehlgeschlagene Anmeldeversuche: %d Das Umwandeln des Passworts in Dezimalschreibweise ist fehlgeschlagen.Datei %s konnte nicht geöffnet werden: %sNeukonfiguration des Netzwerks fehlgeschlagen, wird beendetSchreiben des Passworts für das Dienstobjekt in eine Datei fehlgeschlagenFehlschlag!Feld ist für diese Implementierung zu langSchalter: %svollständige Verbreitungsübertragung beendet vollständige Verbreitungsübertragung gestartet Fehler beim vollständigen erneuten Synchronisieren vom Master Beim vollständigen erneuten Synchronisieren gab der Master ein ungültiges Ergebnis zurück. erneutes vollständiges Synchronisieren erforderlich vollständiges erneutes Synchronisieren nicht gestattet Anfrage nach vollständigem erneuten Synchronisieren genehmigt Anfrage nach vollständigem erneuten Synchronisieren genehmigtvollständiges erneutes Synchronisieren, ungültiger Rückgabewert vom Master-KDCvollständiges erneutes Synchronisieren, nicht gestattetGSS-API- (oder Kerberos-) Fehlerallgemeiner Fehler (siehe E-Text)allgemeiner Fehlschlag der Vorauthentifizierungallgemeiner ferner Fehler: %s allgemeiner unbekannter Wiederholungszwischenspeicherfehlerallgemeiner unbekannter Wiederholungszwischenspeicher-/E/A-Fehlerallgemeiner AktualisierungsprotokollfehlerAuf Wiedersehen inkrementelle Aktualisierungen erhalten (sno=%u sec=%u usec=%u) Signal zur Anfrage des Beendens empfangenSignal zum Zurücksetzen empfangenHW-Authentifizierung erforderlichChronikeintrag enthält keine SchlüsseldatenRechnername kann nicht in Normalform gebracht werdenRechnername in der Zeichenkette »SERVICE-NAME« konnte nicht in Normalform gebracht werdenE/A-Fehler im Wiederholungszwischenspeicher-E/A-Codeungültiges Zeichen in Komponentennamewiderrechtlicher Konfigurationsparameter für lokalen KADM5-Clientwiderrechtlicher Konfigurationsparameter für fernen KADM5-ClientWiderrechliches Realm-übergreifendes Ticketunzulässiger Richtliniennameunzulässiger Principal-Nameungültige Verwendung eines PlatzhaltersUngültige Versionsnummer für KRB5_TL_ACTKVNO %d Ungültige Versionsnummer für KRB5_TL_MKEY_AUX %d Format der Kerberos-Konfigurationsdatei ist ungeeignetFormat der Realm-Übersetzungsdatei ist ungeeignetFormat des Eintrags der Übersetzungsdatenbank ist ungeeignetungeeigneter Prüfsummentyp in der Nachrichtinkonstistenter SchlüsselzweckFalsche Kanalbindungen wurden übergeben.falsche Nachrichtenrichtungfalsche Netzwerkadressefalsches Passwortfalsches Passwort in verschlüsselter Aufforderungfalsche Sequenznummer in der Nachrichtinkrementelle Verbreitung aktiviert inkrementelle Aktualisierungen: %d Aktualisierungen / %lu usinkrementelle Aktualisierungen: %d Aktualisierungen / %lu us anfängliche Ticket-Antwort scheint ein Fehler der Version 4 zu seinErstpasswort erforderlichinitialisierter Zwischenspeicher Datenbank »%s« für Realm »%s« wird initialisiert, Hauptschlüsselname »%s« Datenbank für Realm »%s« wird initialisiert Kadm5 wird als Client %s initialisiert unsicherer Dateimodus für Wiederholungszwischenspeicherdatei %sunsicherer mkstemp()-Dateimodus für Antwortzwischenspeicherdatei %s; versuchen Sie, dieses Programm mit der Umask 077 auszuführenkeine ausreichenden Zugriffsrechte zum Sperren der DatenbankZugriffsrechte reichen nicht zur Durchführung der angeforderten Aktionunzureichende Berechtigung für diese Aktionkeine ausreichenden Rechte zum Sperren der DateiPlatz reicht nicht zur Rückgabe aller Informationen ausPlatz im System reicht nicht zum Speichern der Wiederholungsinformationeninterner Anmeldedatenzwischenspeicherfehlerinterner Fehler beim Auswerten von »X509_user_identity« Interner Fehler: Befehl %s wurde nicht aufgelöst ungültig!ungültige Kombination von KDC-Optionen (interner Bibliotheksfehler)ungültiger Kerberos-Container-DNungültige UID im beständigen Schlüsselbundnamenungültige UTF-8-Zeichenketteungültige Zusatzattributeungültiges Zertifikatungültige Anmeldedaten wurden übergebenungültige Datumsangabe »%s« ungültiger Verschlüsselungstyp in der SAM-Aufforderungwiderrechtlicher Verschlüsselungstyp für Setv4keyfalsche Feldlänge im Tokenungültige Feldmaske für Aktionungültiger Schalter für den Datei-Sperrmodusungültiges Format der Kerberos-Lebensdauer oder der Zeitversatzzeichenketteungültiger KDB-Sperrmodusungültige Parameter zum Erzeugen von Schlüsseln vom KDCungültige Schlüssel-/Salt-Tupelungültiger Schlüsselbundverankerungsnameungültiger Nachrichtentypder zum Kodieren angegebene Nachrichtentyp ist ungültigungültige Modulangabeungültiger Modulbezeichner %sungültige Anzahl von Zeichenklassenungültige Passwortchronikanzahlungültige Passwortlängeungültiges Pkinit-Paket: Achtbit-Zeichenkette erwartetungültiger Richtliniennameungültiger Principal-Nameungültige Signaturungültiges TokenEs wurde ein ungültiges Token übergeben.Iprop nicht aktiviert Es ist wichtig, dass Sie dieses Passwort NICHT VERGESSEN. Sperrdatei der KADM5-Verwaltungsdatenbank fehltAntwort des KCM-Daemons zu großKDB-Modul benötigt Argument »-update«KDC-DiensteKDC kann erforderliche Option nicht erfüllenKDC unterstützt diesen Prüfsummentyp nichtKDC unterstützt diesen Verschlüsselungstyp nichtKDC unterstützt diesen Padata-Typ nichtKDC unterstützt diesen Übergangstyp nichtKDC wurde mit dem Master synchronisiert. KDC-Name passt nichtKDC nicht vertrauenswürdigKDC-Richtlinie weist die Anfrage zurückKDC-Richtlinie verwirft durchgereichten PfadKDC-Antwort entsprach nicht den ErwartungenKDC gab eine Fehlermeldung zurück: %.*sKRB5-Fehlercode 101KRB5-Fehlercode 102KRB5-Fehlercode 103KRB5-Fehlercode 104KRB5-Fehlercode 105KRB5-Fehlercode 106KRB5-Fehlercode 107KRB5-Fehlercode 108KRB5-Fehlercode 109KRB5-Fehlercode 110KRB5-Fehlercode 111KRB5-Fehlercode 112KRB5-Fehlercode 113KRB5-Fehlercode 114KRB5-Fehlercode 115KRB5-Fehlercode 116KRB5-Fehlercode 117KRB5-Fehlercode 118KRB5-Fehlercode 119KRB5-Fehlercode 120KRB5-Fehlercode 121KRB5-Fehlercode 122KRB5-Fehlercode 123KRB5-Fehlercode 124KRB5-Fehlercode 125KRB5-Fehlercode 126KRB5-Fehlercode 127KRB5-Fehlercode 30KRB5-Fehlercode 53KRB5-Fehlercode 54KRB5-Fehlercode 55KRB5-Fehlercode 56KRB5-Fehlercode 57KRB5-Fehlercode 58KRB5-Fehlercode 59KRB5-Fehlercode 82KRB5-Fehlercode 83KRB5-Fehlercode 84KRB5-Fehlercode 87KRB5-Fehlercode 88KRB5-Fehlercode 89KRB5-Fehlercode 90KRB5-Fehlercode 91KRB5-Fehlercode 92KRB5-Fehlercode 94KRB5-Fehlercode 95KRB5-Fehlercode 96KRB5-Fehlercode 97KRB5-Fehlercode 98KRB5-Fehlercode 99KVNO des neuen Hauptschlüssels passt nicht zum erwarteten WertKVNO: %d KVNO: %d, Verschlüsselungstyp: %s, aktiviert auf: %s KVNO: %d, Verschlüsselungstyp: %s, aktiviert auf: %s * KVNO: %d, Verschlüsselungstyp: %s, keine Aktivierungszeit gesetzt Kerberos 4 wird nicht mehr unterstützt. Erstellen des Kerberos-Containers FEHLGESCHLAGEN: %sLöschen des Kerberos-Containers FEHLGESCHLAGEN: %sKerberos-Container-Information fehltKerberos-V4-FehlerTabelle magischer Zahlen von Kerberos V5Kerberos-Container-Speicherort nicht angegebenKerberos-Datenbankbeschränkungen verletztinterner Kerberos-DatenbankfehlerInitialisieren der Kerberos-Bibliothek fehlgeschlagenKerberos-Passwort für %s: Kerberos-Dienst unbekanntKerberos 4 Unterstützung ist deaktiviertEs wurde ein zufälliger Schlüssel für %s erzeugt Schlüssel für %s (hexadezimal): Schlüsselparameter nicht akzeptiertDie Schlüssellänge in der Datenbank ist ungültig,Schlüssellänge ist nicht mit dem Verschlüsselungstyp kompatibelSchlüsseltabelleneintrag nicht gefundenSchlüsseltabellendatei »%s« nicht gefundenfalsche Form des SchlüsseltabellennamensSchlüsseltabellentyp ist bereits registriertSchlüsselversion ist nicht verfügbarSchlüsselversionsnummer des Principals in der Schlüsseltabelle ist nicht korrektSchlüsseltabelle %s existiert nicht oder ist leerSchlüsseltabelle enthält keine passenden Schlüssel für %sSchlüsseltabellennamen zu langBeenden des Fullprop-Kindprozesses (%d) wird erzwungen Kpropd sendet Datenbankgröße %d, erwartet wurde %dLDAP-Bindungs-DN-Wert fehltLDAP-Bindungs-Passwortwert fehltzuletzt geändert: %s (%s) Letzte Passwortänderung: %s letzte erfolgreiche Authentifizierung: %s UID bleibt %s (%ld) Zufällige Daten werden geladen.Zufällige Daten werden geladen. lokaler Rechnername konnte nicht bestimmt werdenSchleife innerhalb von »krb5_get_in_tkt« entdecktMKey: vno %d Mach-RPC-Fehler beim der Kommunikation mit dem KCM-DaemonStellen Sie sicher, dass Sie den alten Principal aus allen ACLs entfernt haben, bevor Sie ihn erneut benutzen. Stellen Sie sicher, dass Sie diesen Principal aus allen ACLs entfernt haben, bevor Sie ihn erneut benutzen. Antwort des KCM-Daemons hat die falsche FormDarstellung des Principals in falscher FormFehler wegen Anfrage in falscher FormHauptschlüssel passt nicht zur Datenbankkeine passenden Anmeldedaten gefundenmaximale verlängerbare Lebensdauermaximale Ticketlebensdauermaximale Anzahl falscher Passworteingaben vor dem Sperren: %lu maximale Passwortlebensdauer: %ld maximale verlängerbare Lebensdauer: %s maximale Ticketlebensdauer: %s Mechanismus ist nicht korrektNachrichtenkontext ungültigNachricht ist eine WiederholungNachricht nicht in OrdnungErkennung der Antwortnachricht erfordert den Parameter »rcache«Nachrichtengröße ist nicht mit Verschlüsselungstyp kompatibelNachrichtendatenstrom geändertDie benötigte Mindestanzahl von Verbindungen pro Server ist zweiminimale Anzahl von Passwortzeichenklassen: %ld minimale Passwortlänge: %ld minimale Passwortlebensdauer: %ld sonstiger PRC-Fehler: %s, %ssonstiger Fehlschlagnicht zusammenpassende Verschlüsselungstypen für Setkey3fehlende Prüfsumme in der SAM-Aufforderungfür Kadmin-Client benötigte Parameter fehlen in »krb5.conf«erforderliche ACL-Dateikonfiguration fehlterforderliche Realm-Konfiguration fehltmehrere Werte für einzelnen Verschlüsselungstyp oder Verschlüsselungstyp mit Saltgegenseitige Authentifizierung fehlgeschlagenHINWEIS: Für %s wurde keine Richtlinie angegeben, es wird »default« zugewiesen neues Passwortneues Passwort (erneut)Das neue Passwort kann nicht die Länge Null haben.Das neue Passwort besteht aus zu wenigen Zeichenklassen. Die Zeichenklassen sind: - Kleinbuchstaben, - Großbuchstaben, - Ziffern, - Satzzeichen und - alle anderen Zeichen (z.B. Steuerzeichen). Bitte wählen Sie ein Passwort mit mindestens %d Zeichenklassen.Das neue Passwort ist zu kurz. Bitte wählen Sie ein Passwort, das mindestens %d Zeichen lang ist.Das neue Passwort wurde in einem Wörterbuch mit möglichen Passwörtern gefunden und kann daher leicht erraten werden. Bitte wählen Sie ein anderes Passwort. Hilfe bei der Wahl guter Passwörter finden Sie in der Handbuchseite von »kpasswd«.Das neue Passwort wurde zuvor schon benutzt. Bitte wählen Sie ein anderes Passwort.neue Passwörter stimmen nicht überein – Passwort nicht geändert keine @ in der Namenszeichenkette SERVICE-NAMEkeine DUP_SKEY-Anfragenkein FehlerKein KCM-Server gefundenkeine Kerberos-Anmeldedaten verfügbarkeine TGT-basierten Anfragenkein akzeptables KDF angebotenkein Administrationsserver für den Realm gefundenEs gibt keinen Schutzschlüssel aber eine FAST-geschützte Anfragekeine Autorisierungsdaten erforderlichEs wurde keine Kontext etabliert.kein Anmeldedatenzwischenspeicher gefundenkeine Anmeldedaten mit unterstützten Verschlüsselungstypen gefundender Bibliotheks-Routine wurden keine Anmeldedaten geliefertEs wurden keine Anmeldedaten übergeben.Es wurden keine Anmeldedaten übergeben oder die Anmeldedaten waren nicht verfügbar bzw. ein Zugriff darauf nicht möglich.kein Standard-Realm gesetzt; KDB kann nicht initialisiert werdenkeine Wörterbuchdatei angegeben, es wird ohne fortgefahrenkein FehlerFür %s wurde kein Schlüsseltabelleneintrag gefunden.Es wurde kein zu %s passender Schlüsseltabelleneintrag gefunden.keine Schlüssel in der SchlüsseltabelleFür den Principal-Namen wurde kein lokaler Name gefunden.kein passender Schlüssel im Eintragkein passender Schlüssel in einem Eintrag mit erlaubtem Verschlüsselungstypkein weiterer Speicher zu reservieren (im Anmeldedatenzwischenspeichercode)kein weiterer Speicher reservierbar (im Wiederholungszwischenspeicher-E/A-Code)kein Speicher mehr zu reservieren (im Wiederholungszwischenspeichercode)kein Passwort angegeben Für Realm %s werden keine »pkinit_anchors« bereitgestellt.Für Realm %s wird keine »pkinit_identity« bereitgestellt.Kein Principal in der Schlüsseltabelle passt zum gewünschten Namen.Für Pkinit-Unterstützung wurden keine Realms korrekt konfiguriert.kein derartiger Eintrag in der Datenbankkeine unterstützten Verschlüsselungstypen (Fehler in der Konfigurationsdatei?)keine unterstützten Tokenkein ticketgewährendes TicketFür den angeforderten Principal ist keine Übersetzung verfügbar.keine Optionen der Nutzeridentität angegebennicht weiterleitbarnicht vordatierbarkein hexadezimales PasswortSpeicher reicht nicht aus Proxy nicht nutzbarnicht erneuerbarHinweisAnzahl der Schlüssel: %d Anzahl aufbewahrter alter Schlüssel: %ld OK als VertreterOK, Datenbank »%s« wird gelöscht … OK, die Datenbank von »%s« wird gelöscht … Ok, es wird nichts getan. Ok, die nicht verwendeten Hauptschlüssel von »%s« werden vollständig entfernt … Anforderung des Einwegpassworts:Einwegpasswort-Token-PINWert des Einwegpassworts entspricht keinem Token-FormatAlte Schlüssel für Principal »%s« wurden entfernt. Entweder -c oder -p muss angegeben werden. Nur eine -I-Option ist erlaubt. Nur eine »-c«-Option ist erlaubt. Nur eine der Optionen -c oder -p ist erlaubt. Nur eine -t-Option ist erlaubt. nur ein gehärteter Ccache Nur eine der Optionen -a und -A ist erlaubt. Nur eine der Optionen -f und -F ist erlaubt. Nur eine der Optionen -p und -P ist erlaubt. Nur eine der Optionen -t und-i ist erlaubt. Aktion aus nicht näher beschriebenem Grund fehlgeschlagenAktion erfordert »add«-RechtAktion erfordert das »change-password«-RechtAktion erfordert »delete«-RechtAktion erfordert »get«-RechtAktion erfordert das »list«-RechtAktion erfordert »modify«-RechtAktion erfordert das »set-key«-RechtDie Option -P (eingeschränkte Abtretung) erfordert zur Angabe eine Schlüsseltabelle. Die Option -P (eingeschränkte Abtretung) erfordert die Option -U (Protokollübergang) Die Optionen -u und -S schließen sich gegenseitig aus. Speicherplatz reicht nicht aus. PA-Option %s = %s PRF-Eingabe zu langPROCESS_TGS: Abstammungsprüfung fehlgeschlagenPaket wurde in falscher Richtung erneut abgespieltPassphrase fürPasswortdienstePasswortänderung erforderlichPasswortänderungsdienstDas Passwort kann nicht geändert werden, da es erst vor kurzem geändert wurde. Bitte warten Sie bis %s, ehe Sie es ändern. Falls Sie es vorher ändern müssen, kontaktieren Sie Ihren Systemsicherheitsadministrator.Ändern des Passworts fehlgeschlagenPasswortänderung abgelehntPasswort geändert Passwort beschädigtDas Passwort enthält nicht genug Zeichenklassen.Passwortablaufdatum: %s Passwort abgelaufen. Sie müssen es nun ändern.Rücksetzintervall für zu viele falsch eingebene Passwörter: %s Passwort für »%s«Passwort von »%s« geändert Passwort für %.1000sPasswort für %sPasswort ist abgelaufenDie Passwortchronikschlüssel des Principals passen nicht zusammen.Passwort beim Holen des anfänglichen Tickets falschDas Passwort steht im Passwortwörterbuch.Das Passwort ist zu kurz.Passwortsperrdauer: %s Das Passwort darf nicht mit dem Principal-Namen übereinstimmen.Das Passwort darf keinen Anwenderdaten entsprechen.Die minimale Lebensdauer des Passworts ist größer als die maximale.Passwort stimmt nicht übereinPasswort nicht geändertLesen des Passworts unterbrochenZugriff im Wiederholungszwischenspeichercode verweigertPkinit-Anfrage nicht signiert, Client ist jedoch nicht anonymBitte wählen Sie aus dem Folgenden aus: Bitte geben Sie den neuen Hauptschlüssel ein … Erweiterung unterstützt nicht die SchnittstellenversionErweiterung unterstützt diese Aktion nichtErweiterungsmodulname nicht gefundenRichtlinie »%s« nicht gelöscht Richtlinie existiert nichtRichtlinie ist in BenutzungRichtlinie verwirft durchgereichten PfadRichtlinie: »%s« Richtlinie: %s%s Vorauthentifizierungsmodul %s: %sVorauthentifizierung fehlgeschlagenVorauthentifizierung erforderlichvorzeitiges EOF für Datenbankdatei gefunden!Principal »%s« gelöscht Principal »%s« wurde geändert. Principal »%s« nicht gelöscht Principal »%s« wurde nicht umbenannt. Principal »%s« wurde in »%s« umbenannt. Für Principal %s ist Realm vorhandenPrincipal %s fehlt erforderlicher RealmPrincipal-Container-BezugHinzufügen des Principals fehlgeschlagen: %sLöschen des Principals fehlgeschlagen (es wird versucht, den Eintrag zu ersetzen): %sPrincipal gehört nicht zum RealmPrincipal gehört nicht zum Standard-RealmPrincipal existiert nichtPrincipal hat in der Kerberos-Datenbank mehrere EinträgePrincipal im Anmeldedatenzwischenspeicher entspricht nicht dem gewünschten NamenPrincipal oder Richtlinie existiert bereitsPrincipal oder Richtlinie existiert nichtPrincipal: %s Principals, deren Schlüssel mit dem Hauptschlüssel VNO %u neu verschlüsselt WÜRDEN: Principals, deren Schlüssel mit dem Hauptschlüssel VNO %u neu verschlüsselt werden, falls nötig: Das Programm rief eine veraltete, gelöschte Funktion auf.Dem Programm fehlt es an der Unterstützung des Adresstyps.Dem Programm fehlt es an der Unterstützung für den Prüfsummentyp.Dem Programm fehlt es an der Unterstützung für den Verschlüsselungstyp.Dem Programm fehlt es an der Unterstützung für den Schlüsseltyp.Fehler des Programmierers! Falsche API-StrukturversionFehler des Programmierers! Falsche API-VersionFehler des Programmierers! Falscher Admin-Server-IdentifikatorFehler des Programmierers! Widerrechlicher Listentyp für gekennzeichnete DatenProtokollübergang mit Vertretung erlaubtProtokollversion passt nichtAsymetrische Verschlüsselung nicht unterstütztDer/Die folgende(n) Hauptschlüssel werden/wird von %s vollständig entfernt: RPC-»svc_freeargs« fehlgeschlagen (%s)RPC-»svc_getargs« fehlgeschlagen (%s)RPC-»svc_sendreply« fehlgeschlagen (%s)unbekannte PRC-Anfrage: %d (%s)RSA-MD5 unterstütztZufallszahlengenerator konnte kein Startwert zugewiesen werdenSollen alle Schlüssel neu verschlüsselt werden, die nicht die Hauptschlüssel-VNO %u verwenden?Geben Sie das Passwort für »%s« erneut ein.Geben Sie das Passwort für Principal »%s« erneut ein.Löschen des Realms FEHLGESCHLAGEN: %sRealm-NameRealm-Information nicht verfügbarRealm für KDC nicht lokalDie empfangene Token-Kennung passt nicht zur erwarteten Token-Kennung.Das Ulog wurde neu initialisiert. Iprop wird neu initialisiert, da Aktualisierungen fehlgeschlagen sind Zurückgewiesene Verbindung von nicht authorisiertem Principal %sZurückgewiesene Verbindung von nicht autorisiertem Principal %s Wiederholungszwischenspeicher-E/A-Aktion fehlgeschlagenAuswerte-/Formatfehler im WiederholungszwischenspeichernamensWiederholungszwischenspeichertyp unterstützt keinen beständigen SpeicherWiederholungszwischenspeichertyp ist bereits registriertWiederholungszwischenspeichertyp ist unbekanntAntwort hat die falsche Form des Sitzungschlüssels für eine anonyme AnfrageAnfrage lieferte kein TicketAnfrage ist eine WiederholungDer Anfrage fehlt das zweite Ticket.abgefragter Ticketserver %s wurde in der Schlüsseltabelle gefunden, er passte jedoch nicht zu Server-Principal %sAbgefragter Ticketserver %s KVNO %d mit Verschlüsselungstyp %s in der Schlüsseltabelle gefunden, Ticket kann jedoch nicht entschlüsselt werden.Abgefragter Ticketserver %s KVNO %d wurde in der Schlüsseltabelle gefunden, jedoch nicht mit Verschlüsselungstyp %s.Abgefragter Ticketserver %s KVNO %d wurde nicht in der Schlüsseltabelle gefunden; Schlüsseltabelle ist wahrscheinlich nicht mehr aktuell.Abgefragter Ticketserver %s KVNO %d wurde nicht in der Schlüsseltabelle gefunden; Ticket ist wahrscheinlich abgelaufen.Abgefragter Ticketserver %s wurde nicht in der Schlüsseltabelle gefunden (Ticket KVNO %d).Anfrage: %s, %.*s%s, %s, Client=%.*s%s, Dienst=%.*s%s, Adresse=%sAnfrage: %s, %s, %s, Client=%s, Dienst=%s, Adresse=%sAnfrage: %s, hervorgebrachter Neusynchronisationsprozess %d, Client=%s, Dienst=%s, Adresse=%sAnfrage: kadm5_init, %.*s%s, %s, Client=%.*s%s, Dienst=%.*s%s, Adresse=%s, Version=%d, Variante=%dAnfrage: kadm5_rename_principal, %.*s%s bis %.*s%s, %s, Client=%.*s%s, Dienst=%.*s%s, Adresse=%sDie angeforderte effektive Lebensdauer ist negativ oder zu kurz.Der angeforderte Verschlüsselungstyp wird in der Nachricht nicht verwendet.angeforderte »lucid«-Kontextversion nicht unterstütztangeforderter Principal und Ticket passen nicht zusammenangeforderte Protokollversion nicht unterstütztabgefragter Server und Ticket passen nicht zusammenanforderndes Ticket kann keine weiterleitbaren Tickets holenerforderliche Parameter in »kdc.conf« fehlenerforderlicher Vorauthentifizierungsschlüssel nicht bereitgestelltAntwort für UDP zu groß, erneuter Versuch mit TCPWiderrufsstatus nicht verfügbarWiderrufsstatus unbekanntwiderrufenes ZertifikatSAM-AuthentifizierungSPNEGO-Abnehmer hat kein gültiges Token zurückgeliefertSPNEGO-Abnehmer hat keinen Mechanismus ausgewähltSPNEGO kann keine Mechanismen zum Aushandeln finden.SPNEGO ist beim Beschaffen von Anmeldedaten gescheitertSPNEGO ist beim Aushandeln eines Mechanismus gescheitert.STRING-UID-NAME enthält etwas anderes als ZiffernSuchbereichStartwert des Zufallszahlengenerators wird erzeugtSequnznummer im Token ist beschädigtServer %s wurde nicht in der Kerberos-Datenbank gefundenServerfehlerServer nicht in der Kerberos-Datenbank gefundenServer noch nicht gültig – versuchen Sie es später noch einmalServer-Principal %s hat keinen passenden Schlüssel in der SchlüsseltabelleServer-Principal %s passt nicht zum abgefragten Ticketserver %sDer Server-Principal ist nur für »user2user« gültigServer wies Authentifizierung (beim Sendauth-Austausch) zurückEintrag des Servers in der Datenbank ist abgelaufenDer Schlüssel des Servers wurde mit einem alten Hauptschlüssel verschlüsselt.Dienst deaktiviertDienstschlüssel nicht verfügbarDienst antwortet nichtLänge des Dienstpassworts ist NullUm Kadm5 neu zu initialisieren, wird %d Sekunden gewartet (RPC-FEHLER). Um Kadm5 neu zu initialisieren, wird %d Sekunden gewartet (läuft Krb5kdc nicht?). stabil Es wird immer noch auf das vollständige erneute Synchronisieren gewartet. gespeicherte Anmeldedaten Der gespeicherte Hauptschlüssel ist beschädigt.gespeicherter Hauptschlüssel ist nicht aktuellDas Speichern von Abnehmeranmeldedaten wird nicht durch den Mechanismus unterstützt.Das Speichern von Nichtstandardanmeldedaten wird nicht durch den Mechanismus unterstützt.Dateiname des ergänzenden Zwischenspeicherpfads %s beginnt nicht mit »tkt«Ergänzender Zwischenspeicherpfad %s hat kein übergeordnetes Verzeichnis.TeilbaumErfolgDie bereitgestellten Daten werden nicht von dieser Erweiterung behandelt.TCP-Client %s will %lu Byte, Cap ist %luTCP-Socket-Deskriptornummer %d (für %s) zu hochTGS_REQ %s: %s: Authentifizierungszeit %d, %s für %s, 2. TKT-Client %sTGS_REQ (%s) %s: %s: Authentifizierungszeit %d, %s%s %s für %s%s%sTGS_REQ: handle_authdata (%d)TGS_REQ: SITZUNGSSCHLÜSSEL oder BEIDERSEITIGTGS_REQ: UNBEKANNTER SERVER: Server=»%s«TGS_REQ: TGT %s wird erstelltTGS_REQ: alternativer TGT wird erstelltTGT wurde widerrufenTemporäre Ablagedatei existiert bereits: %s.Der IAKERB-Proxy konnte kein KDC finden.Das KDC anwortete dem IAKERB-Proxy nicht.Das KDC sollte diese Anfrage verwerfenDer Kontext ist abgelaufen.Die Verwendung der angegebenen Anmeldedaten passt nicht zur angeforderten Verwendung.Die Aktion wird durch die lokale Sicherheitsrichtinie verboten.Die Aktion wird durch die lokale Sicherheitsrichtinie verboten.Die Aktion oder Option ist nicht verfügbar.Die Aktion oder Option ist nicht verfügbar oder wird nicht unterstützt.Das Passwort kann nur einmal täglich geändert werden.Das Passwort kann nur alle %d Tage geändert werden.Das Passwort muss sich vom vorhergehenden Passwort unterscheiden.Das Passwort muss sich von den vorhergehenden %d Passwörtern unterscheiden.Das Passwort muss mindestens %d Zeichen enthalten.Das Passwort muss mindestens %d Zeichen enthalten.Das Passwort muss Zahlen oder Symbole enthalten. Fügen Sie keinen Teil Ihres Namens in das Passwort ein.Der bereitgestellte Name war nicht mechanismusspezifisch (MN).Die Qualität des Schutzes (quality-of-protection/QOP) konnte nicht bereitgestellt werden.Die angeforderte Qualität des Schutzes konnte nicht bereitgestellt werden.Der referenzierte Kontext ist abgelaufen.Die referenzierten Anmeldedaten sind abgelaufen.Die referenzierten Anmeldedaten sind abgelaufen.Das angeforderte Anmeldedatenelement existiert bereits.Die Routine wurde erfolgreich abgeschlossenDie Routine muss erneut aufgerufen werden, um ihre Funktion zu vervollständigen.Der Salt-Typ des angegebenen Principals unterstützt kein Umbenennen.Das Ticket ist nicht für uns.Das Merkmal war ein Zweitexemplar eines früheren Merkmals.Die Gültigkeitsperiode des Merkmals ist abgelaufen.Es gibt nur einen einzigen Hauptschlüssel, der nicht vollständig entfernt werden kann. Diese Krb5-Bibliothek ist abgelaufen.Diese Anwendung ist abgelaufen.Dies wird das Richtlinienobjekt »%s« löschen, sind Sie sicher? Ticket-Richtlinienname fehltDN des Ticket-Richtlinienobjekts fehltTicket-Richtlinienobjekt-Information fehltTicketzwischenspeicher %cNICHT%c vernichtet! Ticketzwischenspeicher NICHT vernichtet! Ticketzwischenspeicher: %s:%s Standard-Principal: %s Ticket abgelaufenTicket-FlagsDas Ticket hat einen falsch gesetzten Schalter.Ticket ist zum Vordatieren ungeeignetTicket in der KDC-Antwort verändertTicket noch nicht gültigTicket/Schlüsselziffer passen nicht zueinanderToken-Kopfzeilen haben die falsche Form oder sind beschädigtdem Token fehlen DatenToken wurde zurückgeworfenzu wenige Argumentezu viele zeichenkettenabbildenden DatenVerfolgung nicht unterstütztVersuchen Sie es mit einem etwas komplexeren Passwort oder wenden Sie sich an Ihren Administrator.UID lässt sich nicht zu Benutzernamen ermittelnauf die Kerberos-Datenbank kann nicht zugegriffen werdenLetzter Hauptschlüssel kann nicht mit dem bereitgestellten Hauptschlüssel entschlüsselt werden. angeforderter Datenbanktyp kann nicht gefunden werdenangeforderter Datenbanktyp kann nicht gefunden werden. %sStandard-Realm kann nicht geholt werdenBenutzer kann nicht identifiziert werden Der Benutzer kann nicht anhand der Passwortdatei identifiziert werden. Kerberos kann nicht initialisiert werden Vorauthentifizierungskontext konnte nicht initialisiert werden.Signalbehandlungsprogramme in PID %d konnten nicht initialisiert werdenangefordertes Datenbankmodul »%s« kann nicht geladen werden: Erweiterungssymbol »kdb_function_table« nicht gefundenProtokolldatei %s kann nicht abgebildet werden Protokoll kann nicht abgebildet werden! Kerberos-Container kann nicht gelesen werdenRealm kann nicht gelesen werdenHauptschleife konnte nicht neu initialisiert werdenUnauthorisierte Anfrage: %s, %.*s%s, Client=%.*s%s, Dienst=%.*s%s, Adresse=%sunberechtigte Anfrage: %s, Client=%s, Dienst=%s, Adresse=%sUnauthorisierte Anfrage: kadm5_rename_principal, %.*s%s bis %.*s%s, Client=%.*s%s, Dienst=%.*s%s, Adresse=%sin fast_req wurde eine Prüfsumme ohne Schlüssel benutzt%s unbekannt (Feld = %d)unbekanntet FAST-Schutztyp %dunbekannter Schlüsseltabellentypunbekannter Anmeldedatenzwischenspeichertypunbekannte Schlüsselbundsammlungsversionunbekannte Schutzqualität angegebenunbekannter Signaturtyp im Tokenunbekannter Status: %d unbekannte ACL-Aktion »%c« in %snicht spezifizierter GSS-Fehlschlag. Möglicherweise stellt der untergeordnete Code weitere Informationen bereit.nicht näher angegebener Passwortqualitätsfehlschlaginstabil nicht unterstütztes Argument »%s« für DB2nicht unterstütztes Versionsnummernformat des Anmeldedatenzwischenspeichersnicht unterstützter Verschlüsselungstypnicht unterstütztes Versionsnummernformat der Schlüsseltabellenicht unterstützter Vorauthentifizierungstypnicht unterstütztes Versionsnummernformat des Wiederholungszwischenspeichersnicht unterstützter Salt-Typnicht unterstützter Typ bei der Verarbeitung von »%s« nicht unterstützte Version im DatenbankeintragAktualisierungseintrag Fehler beim Umwandeln des AktualisierungsprotokollsAktualisierungsprotokollauszug : Aktualisierungsprotokoll ist beschädigtAktualisierungsprotokoll ist instabilAufruf: %s [-A] [-q] [-c Zwischenspeichername] Aufruf: %s [-e] [-V] [[-c] [-l] [-A] [-d] [-f] [-s] [-a [-n]]] [-k [-t] [-K]] [Name] Aufruf: %s [-r Realm] [-p Principal] [-q Abfrage] [clnt|lokale Argumente] clnt Argumente: [-s Admin-Server[:Port]] [[-c Ccache]| [-k [-t Schlüsseltabelle]]]|[-n] lokale Argumente: [-x DB-Argumente]* [-d Datenbankname] [-e "enc:Salt …"] [-m] wobei [-x DB-Argumente]* - eine beliebige Anzahl datenbankspezifischer Argumente ist. Die unterstützten Argumente finden Sie in den jeweiligen Datenbankdokumentationen Aufruf: %s [Zielbenutzer] [-n Principal] [-c Quellenzwischenspeichername] [-k] [-D] [-r Zeit] [-pf] [-l Lebensdauer] [-zZ] [-q] [-e Befehl [Argumente …] ] [-a [Argumente …] ] Aufruf: %s Datenbankpfadname Realm-NameAufruf: %s {-c Zwischenspeichername | -p Principal} Aufruf: kadmind [-x Datenbankargumente]* [-r Realm] [-m] [-nofork] [-port Portummer] [-p Pfad_zum_KDB5-Hilfswerkzeug] [-F Auszugsdatei] [-K Pfad_zu_Kprop] [-P PID-Datei] dabei sind [-x Datenbankargumente]* - eine beliebige Anzahl datenbankspezifischer Argumente. Welche Argumente unterstützt werden, finden Sie in der Dokumentation der jeweiligen Datenbank. Aufruf: kdb5_ldap_util [-D Benutzer-DN [-w Passwort]] [-H LDAP-URI] cmd [Befehlsoptionen] create [-subtrees DN-Liste_Teilbäume] [-sscope Suchbereich] [-containerref Container-Bezug-DN] [-m|-P Passwort|-sf Ablagedateiname] [-k mkeytype] [-kv mkeyVNO] [-s] [-maxtktlife maximale_Ticketlebensdauer] [-maxrenewlife maximale_Dauer_bis_zum_Erneuern_des_Tickets] [Ticket_Flags] [-r Realm] modify [-subtrees DN-Liste_Teilbäume] [-sscope Suchbereich] [-containerref Container-Bezug-DN] [-maxtktlife maximale_Ticketlebensdauer] [-maxrenewlife maximale_Dauer_bis_zum_Erneuern_des_Tickets] [Ticket_Flags] [-r Realm] view [-r Realm] destroy [-f] [-r Realm] list stashsrvpw [-f Dateiname] Dienst-DN create_policy [-r Realm] [-maxtktlife maximale_Ticketlebensdauer] [-maxrenewlife maximale_Dauer_bis_zum_Erneuern_des_Tickets] [Ticket_Flags] Richtlinie modify_policy [-r Realm] [-maxtktlife maximale_Ticketlebensdauer] [-maxrenewlife maximale_Dauer_bis_zum_Erneuern_des_Tickets] [Ticket_Flags] Richtlinie view_policy [-r Realm] Richtlinie destroy_policy [-r Realm] [-force] Richtlinie list_policy [-r Realm] Aufruf: kdb5_util [-x Datenbankargumente]* [-r Realm] [-d Datenbankname] [-k Mkeytype] [-M Mkeyname] [-kv MkeyVNO] [-sf Ablagedateiname] [-m] Befehl [Befehlsoptionen] create [-s] destroy [-f] stash [-f Schlüsseldatei] dump [-old|-ov|-b6|-b7|-r13|-r18] [-verbose] [-mkey_convert] [-new_mkey_file mkey-Datei] [-rev] [-recurse] [Dateiname [Principals …]] load [-old|-ov|-b6|-b7|-r13|-r18] [-verbose] [-update] Dateiname ark [-e Etype-Liste] Principal add_mkey [-e Etype] [-s] use_mkey kvno [Zeit] list_mkeys Aufruf: ktadd [-k[eytab] Schlüsseltabelle] [-q] [-e Schlüssel-Salt-Liste] [-norandkey] [Principal | -glob Principal-Ausdruck] […] Aufruf: ktadd [-k[eytab] Schlüsseltabelle] [-q] [-e Schlüssel-Salt-Liste] [Principal | -glob Principal-Ausdruck] […] Aufruf: ktremove [-k[eytab] Schlüsseltabelle] [-q] Principal [kvno|»all«|»old«] Änderung des Benutzers fehlgeschlagen: %sBenutzer-zu-Benutzer erforderlichStandardzwischenspeicher wird verwendet: %s Existierender Zwischenspeicher wird verwendet: %s Zur Aktualisierung der Ablagedatei werden existierende gelagert Schlüssel verwendet. Schlüsseltabelle wird verwendet: %s Neuer Zwischenspeicher wird verwendet: %s verwendeter Principal: %s Angegebener Zwischenspeicher wird verwendet: %s Angegebener Eingabezwischenspeicher wird verwendet: %s V5-Realm-Name ist länger als die V4-MaximallängeÜberprüfungsfehlerAnbieter:WARNUNG! Wörterbuchdatei %s kann nicht gefunden werden, es wird ohne fortgefahrenWARNUNG! Gefälschte/verstümmelte Anfrage: %d, Client = %.*s%s, Server = %.*s%s, Adresse = %sWARNUNG! Gefälschte/verstümmelte Anfrage: %s, geforderter Client = %.*s%s, Server = %.*s%s, Adresse = %sWARNUNG: Ihr Passwort könnte offengelegt werden, falls Sie es hier eingeben und WARNUNG: Ihr Passwort könnte offengelegt werden, falls Sie es hier eingeben und WARNUNG: Für %s wurde keine Richtlinie angegeben, es wird die Vorgabe »keine Richtlinie« verwandt. WARNUNG: Richtlinie »%s« existiert nicht. vor der erneuten Prufung auf Aktualisierungen wird %d Sekunden gewartet WarnungWarnung! Mehrere DES-CBC-CRC-Schlüssel für Principal %s, Duplikate werden übersprungen. Warnung! Kein DES-CBC-CRC-Schlüssel für Principal %s, es kann kein OV-kompatibler Datensatz erzeugt werden, wird übersprungen Warnung: Ihr Passwort wird in %d Tagen am %s ablaufen.Warnung: Ihr Passwort wird in %d Stunden%s am %s ablaufen.Warnung: Ihr Passwort auf %s wird in weniger als einer Stunde ablaufen.Warnung: Hauptschlüssel kann nicht gelagert werden. Warnung: Es wird ohne Hauptschlüssel fortgefahrenbeim Bestimmen des Dienst-Principal-Namens anhand der Schlüsseltabellebeim Auflösen der Standardschlüsseltabelle des Clientsbeim Rückgängigmachen der Namensauswertungbeim Umwandeln des Verschlüsselungstyps in eine ZeichenketteSind Sie sicher, dass alle nicht verwendeten Hauptschlüssel, die für Principal »%s« gespeichert sind, vollständig entfernt werden sollen? Der/Die folgende(n) Hauptschlüssel würden/würde von %s vollständig entfernt: falscher Principal in der Anfragefalsche ProtokollversionXDR-VerschlüsselungsfehlerSie müssen ein Passwort eingeben. Passwörter müssen mindestens ein Zeichen lang sein. Sie werden nach einem neuen Datenbank-Master-Passwort gefragt. Sie werden nach dem Master-Passwort der Datenbank gefragt. Ihre UID passt nicht zu Ihrem Passworteintrag. [niemals][keins]akzeptierte PRC-Verbindung auf Socket %d von %sKonto %s: Autorisierung fehlgeschlagen addent: Fehler beim Lesen des Schlüssels addent: unerlaubtes Zeichen im Schlüssel dem Haupt-Principal wird ein neuer Hauptschlüssel hinzugefügtPuffer für neue TCP-Sitzung von %s wird reserviertDatenbankdateiname »%s« wird reserviertkrb5_get_init_creds_opt wird reserviert.»ap-request«-Schutz für etwas anderes als den lokalen TGS»ap-request«-Schutz ohne UnterschlüsselAsprintf konnte nicht genug Speicher reservieren, um die Ausgabe bereitzuhaltenes wird davon ausgegangen, dass das IPv6-Socket IPv4 akzeptiertÖffnen der Audit-Erweiterung %s fehlgeschlagen. Fehler=%iInitialisieren von »authdata« %s fehlgeschlagen: %sHandhabung von »authdata« %s fehlgeschlagen: %sAuthentifizierter Client: %s (etype == %s) Authentifizierungsversuche gescheitert: %s, PRC-Authentifizierungsvariante %dfalscher Realm-Übergangspfad von »%s« zu »%s« über »%.*s%s«falscher Dienst-Principal %.*s%skaputte Adresse (Typ %d Länge %d)AufruffehlerSpeicher für Verbindungsinformation kann nicht reserviert werdenE/A-Ereignis kann nicht erzeugt werdenGrößenmerkmale können nicht zugeordnet werden.Richtlinie kann nicht ausgewertet werden (%d gelesen) Zusätzliche Daten können nicht gelesen werden.Schlüsseldaten können nicht gelesen werden.Schlüssellänge und -version können nicht gelesen werden.Schlüsseltyp und -länge können nicht gelesen werden.Namenszeichenkette kann nicht gelesen werden.Principal-Attribute können nicht gelesen werden.Inhalt der markierten Daten kann nicht gelesen werden.Markierter Datentyp und Länge können nicht gelesen werden.Ereignis kann nicht gesichert werdenDas auf eine Verbindung wartende TCP-Socket kann nicht auf nicht-blockierendes %s gesetzt werden.Schlüssel-Salt-Liste kann nicht angegeben werden, wenn der Schlüssel nicht geändert wird change_password: zu wenig Speicher change_password: fehlendes Datenbankargumentchange_password: fehlendes Schlüssel-Salt-Listenargumentchange_password: fehlendes PasswortargumentPasswort wird geändert.check_rpcsec_auth: inquire_context fehlgeschlagen, Stat=%u»chpw«-Anfrage von %s für %.*s%s: %schpw«: Administratorschlüsseltabelle %s konnte nicht geöffnet werdenDie Principal-Namen von Client und Server müssen übereinstimmen.Ccache wird geschlossen.Dateideskriptor %d wird geschlossenAktion wird begonnenCcache-Name wird zusammengesetzt.Parameter für die Datenbank werden berechnet.Wert des Container-Bezugs: Platzhalter »%s« wird in einen regulären Ausdruck umgewandeltDatenbankargumente werden kopiertZurücksetzen kann nicht abgeschlossen werden, Fehler beim Löschen des Kerberos-Containers»date-time«-Zeichenkette »%s« konnte nicht ausgewertet werdenUlog-Kopfzeilen konnten nicht auf die Platte synchronisiert werdenRouting-Socket konnte nicht eingerichtet werden: %serstellte Richtlinie %s %d Arbeitsprozesse werden erzeugtPID-Datei wird erstelltPrincipal des Rechnerdienstes wird erstelltAufruf wird erstelltArbeitsprozesse werden erzeugtaktuelle Rechte:Datenbank gesperrtFehlersuchmeldungDatenbank »%s« wird gelöscht.Die Datenbank von »%s« wird gelöscht.Deskriptor %d geschlossen, aber immer noch in »svc_fdset«Hauptschlüssel, der für Principal »%s« benutzt wird, wird bestimmtSO_LINGER auf dem TCP-Socket auf %s wird deaktiviert»do_iprop« fehlgeschlagen hat keine geeigneten Tickets im Zwischenspeicher. %s Dateideskriptor %d von %s wird verworfenFehler %ldFehler beim Reservieren zum nicht öffentlichen TCP-Versand!Fehler beim Kompilieren des umgewandelten regulären Ausdrucks »%s«Fehler beim Erzeugen des KRB_ERR_FIELD_TOOLONG-Fehlers! %sFehler beim erneuten Verschlüsseln des Schlüssels für Principal »%s«Dateideskriptornummer %d zu hochfertig, wird beendetfür Container-Bezug beim Erstellen von Realm »%s«für Container-Bezug beim Ändern von Realm »%s«für lokale Dienstefür einen Teilbaum beim Erstellen von Realm »%s«für einen Teilbaum beim Ändern von Realm »%s«»get_updates«-Fehler vom Master ungültiges »get_updates«-Ergebnis vom Master »get_updates«-Master ausgelastet; hält sich zurück Zugriff bei »get_updates« verweigert Vom Master-KDC wurde ein »get_updates«-Fehler zurückgegeben.»get_updates«, ungültiger Rückgabewert vom Master-KDC»get_updates«, Zugriff verweigertgetaddrinfo(%s): Die Normalform des Rechnernamens kann nicht bestimmt werden. getaddrinfo: %s beim Holen der KonfigurationsparameterAnfängliche Anmeldedaten werden geholt.Anfängliches Ticket wird geholt.Lokale Adressen werden geholt.Principal wird vom Ccache geholt.Zufallsstartwert wird geholtPrincipal-Name wird im Klartext geholtgss_to_krb5_name: display_name fehlgeschlagen, Status %dName des Home-Verzeichnisses »%s« ist zu lang, Suche nach .k5login nicht möglich Home-Verzeichnispfad für %s zu lang Rechner/Domain-Name nicht gefundenin »krb5_auth_con_setaddrs«Informationwird initialisiertACL-Datei wird initialisiertKerberos-Bibliothek wird initialisiert.Netzwerk wird initialisiertungültiger Verschlüsselungstyp %sungültiger Suchbereich beim Erstellen von Realm »%s«Aufruf von »iprop_full_resync« fehlgeschlagenAufruf von »iprop_get_updates« fehlgeschlagenInitialisierung von Kadm5 fehlgeschlagen! Initialisieren von Kadm5 erfolgreich »kadm5_hook« %s ist beim Nach-Commit %s gescheitert: %sKerberos-Principal ist bereits mit einem LDAP-Objekt verknüpftSchlüsseldatei (%s) ist keine normale Datei: %sSchlüsseltabelle angegeben, -k wird erzwungen Kpropd: erneutes vollständiges Synchronisieren erforderlichksu: Umgebungsvariable %s kann nicht gesetzt werden ksu: Umgebungsvariable HOME kann nicht gesetzt werden ksu: Umgebungsvariable SHELL kann nicht gesetzt werden ksu: Umgebungsvariable USER kann nicht gesetzt werden ksu: »initgroups« fehlgeschlagen ksu: Zugriff verweigert (Shell) ksu: unbekannter Anmeldename %s ksu: Wer sind Sie? LDAP-Objekt ist bereits an Kerberos angepasstVerweisinformation kann nicht eingerichtet/aktualisiert werden, da der Kerberos-Principal zu einem LDAP-Objekt gehört.auf Dateideskriptor %d wird auf eine Verbindung gewartet: RPC %sauf Dateideskriptor %d wird auf eine Verbindung gewartet: TCP %sauf Dateideskriptor %d wird auf eine Verbindung gewartet: UDP %s%sfalsch gebildeter Anmeldedatenzwischenspeichername %s Aktualisierungsprotokoll wird abgebildetAblagedatei des Hauptschlüssels erfordert Aktualisierung, Befehl abgebrochenHauptschlüsselliste ist nicht initialisiertfehlender Principal-Namekeine Socket-Option für IPV6_V6ONLY unterstütztkeine Daten für Rechner/Domain-Namen verfügbarkeine Sockets eingerichtet?Nummer für einmaligen Gebrauch in der FAST-Anwort geändert: KDC-Anwort geändertÜbergangspfad wird nicht geprüftStandard-Ccache wird geöffnet.Die Aktion kann nicht fortfahren, da mehr als ein Principal namens »%s« gefunden wurde.Client-Name wird ausgewertet.Das Modul %s für Passwortqualität hat das Passwort für %s abgelehnt: %sAuszug von %s wird durchgeführtPktinfo sagt, die lokale Adresse sei %sNachschlagen des Erweiterungssymbols »kdb_function_table« fehlgeschlagen: %sErweiterung nicht verfügbar: %sInitialisieren von »preauth« %s fehlgeschlagen: %sEinrichten der Schleife von »preauth« %s fehlgeschlagen: %sPrincipal-Name %s für den internen Pufferbereich zu groß Programmname zu lang – wird beendet, um das Auslösen von Systemprotokollierungsfehlern zu vermeidenRcache gehört nicht %d%d vom Routing-Socket gelesen, Nachrichtenlänge ist jedoch %dAktualisierungsprotokollkopfzeilen werden gelesenbereit Wert des Realm-Objekts: Umbenennen von temporärer Schlüsseldatei (%s) in (%s) fehlgeschlagen: %serneuern bis Anmeldedaten werden erneuert.Ccache %s wird ermitteltStandardschlüsseltabelle des Clients wird ermittelt.Schlüsseltabelle wird ermittelt: %sFehler in einer RoutineDas Routing-Socket hat den Dateideskriptor %d.Dienstname unbekanntDienst nicht verfügbar%d Sockets werden eingerichtet»setpw«-Anfrage von %s durch %.*s%s für %.*s%s: %ssetsockopt(%d,IPV6_V6ONLY,1) fehlgeschlagensetsockopt(%d,IPV6_V6ONLY,1) funktioniertNetzwerk wird eingerichtet …ungenügende Daten (%d/%d) vom Routing-Socket gelesenungenügende Ausgabe der Antwort %d gegenüber %d wird heruntergefahrenÜberwachungsprogramm empfing Signal %dsignalisiert vom Serversignalisiert vom Serverwird übersprungen: %s Daemon-Prozess wird erzeugtangegeben für Suchbereich, während die Information für Realm »%s« geändert wirdStandardeingabestartetWert des Teilbaums: erfolgreichzusätzlicher InformationscodeTerminal-Name %s ist zu lang. ein Hauptschlüssel muss derzeit aktiv seinWert des Ticket-Richtlinienobjekts: Ticket-Richtlinienobjekt: zu viele Verbindungenes wird versucht, die Principal-Datenbank zu verarbeiten»ulog_replay« fehlgeschlagen (%s), Aktualisierungen nicht registriert »ulog_replay« fehlgeschlagen (%s), Aktualisierungen nicht registriertDie gespeicherten Schlüsseldaten des Principals (%s) konnten nicht dekodiert werden.Erweiterung [%s] konnte nicht gefunden werden: %sDLL [%s] konnte nicht geladen werdenErweiterung [%s] konnte nicht geladen werden: %sunerwarteter Fehler bei der Prüfung des Übergangs von »%s« zu »%s« über »%.*s%s«: %snicht behandelter Routing-Meldungstyp %d, es wird es nur zum Spaß neu konfiguriertunbekannter »addrtype« %dunbekannter Code 0x%xunbekannter Fehlschlagunbekannter Getaddrinfo-Fehlschlagunbekannte Option »%s«unbekannte Option: %sunbekannter Datensatztyp »%s« nicht druckbare Adresse (Typ %d Fehler %d %s)wird aktualisiert: %s Aufruf: %s (-key | -password) -p Principal -k KVNO -e Verschlüsselungstyp Aufruf: %s [-C] [-u] [-c Ccache] [-e Etype] Aufruf: %s [-x Datenbankargumente]* [-d Datenbankpfadname] [-r Datenbank-Realm-Name] [-m] [-k Hauptverschlüsselungstyp] [-M Hauptschlüsselname] [-p Port] [-P PID-Datei] [-n] [-w Arbeitsprozessanzahl] [/] dabei sind [-x Datenbankargumente]* - eine beliebige Anzahl datenbankspezifischer Argumente. Welche Argumente unterstützt werden, finden Sie in der Dokumentation der jeweiligen Datenbank. Aufruf: %s [Principal] Aufruf: add_principal [Optionen] Principal Aufruf: change_password [-randkey] [-keepold] [-e Schlüssel-Salt-Liste] [-pw Passwort] Principal Aufruf: del_string Principal Schlüssel Aufruf: delete_policy [-force] Richtlinie Aufruf: delete_principal [-force] Principal Aufruf: get_policies [Ausdruck] Aufruf: get_policy [-terse] Richtlinie Aufruf: get_principal [-terse] Principal Aufruf: get_principals [Ausdruck] Aufruf: get_privs Aufruf: get_strings Principal Aufruf: modify_principal [Optionen] Principal Aufruf: purgekeys [-all|-keepkvno älteste_KVNO_die_behalten_wird] Principal Aufruf: rename_principal [-force] alter_Principal neuer_Principal Aufruf: set_string Principal Schlüssel Wert Aufruf: %s [Optionen] Richtlinie Anmeldedaten werden geprüft.Variable fehlt }warten auf Kprop-Verbindung waitpid() schlug beim Warten auf doit() fehl (%d %s) wenn der Standard-Principal-Name des Servers erstellt wirdbeim Zusammenstellen der Parameterbeim Initialisieren des Zwischenspeichers %swenn der Name %s ausgewertet wirdbeim Rückgängigmachen der Auswertung des Namensbei %sbeim Akzeptieren der Verbindungbeim Hinzufügen von Einträgen zur Datenbankbeim Hinzufügen von Einträgen in die Datenbankbeim Hinzufügen des Schlüssels zur Schlüsseltabellebeim Hinzufügen des Hauptschlüsseleintrags zur Datenbankbeim Hinzufügen eines neuen Eintragsbeim Hinzufügen eines neuen Hauptschlüsselsbeim Hinzufügen der Option »preauth«beim Reservieren von »args.kvnos«beim Reservieren des »dump_ok«-Dateinamensbeim Reservieren des Dateinamens für die temporäre Dateibeim Reservieren des Dateinamens für »update_last_prop_file«beim Reservieren von »i_vector«beim Reservieren von »key_data«bei Reservieren von Speicherbeim Reservieren von Speicher für den Ziel-Ccache-Namenbeim Reservieren des temporären Dateinamenspeicherauszugsbeim Anhängen des Realms an den Principalbeim Versuch, »setsockopt« auszuführen (SO_KEEPALIVE)beim Versuch, eine Verbindung zum Master-KDC aufzubauen … wird erneut versuchtbeim Versuch, den Standard-Realm abzufragenbeim Authentifizieren am Serverbeim Anbinden an das auf Verbindung wartende Socketbeim Bilden des TGS-Namens für Realm %sbeim Erstellen des Principalsbeim Berechnen des Hauptschlüssel-Saltsbeim Berechnen des Hauptschlüssel-Saltsbeim Aufruf von »cc_filter«beim Aufruf von »waitpid«während der neue Principal in die normale Form gebracht wirdwährend der alte Principal in die normale Form gebracht wirdwährend der Principal in die normale Form gebracht wirdwährend der Principal-Name in die normale Form gebracht wirdbeim Ändern des Schlüssels von %sbeim Ändern des Passworts von »%s«.beim Ändern der Ziel-UID für das Zerstören von Ccachebeim Prüfen der Autorisierungbeim Prüfen, ob die Standardeingabe ein Socket istbeim Löschen von DISALLOW_ALL_TIX für »%s«.beim Leeren des Werts von %sbeim Schließen des Ccachesbeim Schließen von Ccache %sbeim Schließen der Datenbankbeim Schließen der Schlüsseltabellebeim Schließen der vorherigen Datenbankbeim Verbinden mit dem Serverbeim Erstellen meines Dienst-Realmsbeim Umwandeln von %s in den neuen Hauptschlüsselbei der Etype-Umwandlungbeim Umwandeln der lokalen Socket-Adressebeim Umwandeln der Server-Adressebeim Kopieren des Zwischenspeichers %s nach %sbeim Kopieren des Client-Principalsbeim Erstellen der Datei »last_prop«, »%s«beim Erstellen der Datei »ok«, »%s«beim Erstellen der PID-Dateibeim Erstellen der Datenbankbeim Erstellen der Datenbank »%s«beim Erstellen des Schlüsseltabellennamensbeim Erzeugen der Hauptschleifebeim Erstellen des neuen Hauptschlüsselsbei Erstellen von neuem Ziel-Ccachebeim Erstellen der Richtliniebeim Erstellen der Richtlinie »%s«beim Erstellen des Richtlinienobjektsbeim Erstellen des Principals %sbeim Erstellen von Realm »%s«beim Erstellen eines Socketsbeim Erstellen von Unterbefehlsargumentenbei Erstellen des temporären Zwischenspeichersbeim Erstellen des TGTs für lokalen Realmbeim Erstellen des Aktualisierungsprotokollsbeim Dekodieren der Datenbankgröße vom Clientbeim Dekodieren des Fehlerpakets vom Clientbeim Aufschlüsseln der Fehlerantwort vom Serverbeim Dekodieren des Tickets für %sbeim Entschlüsseln des Tickets für %sbeim Löschen eines Attributs von Principal »%s«beim Löschen der falschen Datenbank %sbeim Löschen von Eintrag %dbeim Löschen eines Eintrags aus der Schlüsseltabellebei Löschen der Richtlinie »%s«beim Löschen von Principal »%s«beim Zerstören des Zwischenspeichersbeim Zerstören des Zwischenspeichers %sbeim Zerstören des Richtlinienobjektsbeim Lösen vom Terminalbeim Bestimmen des lokalen Dienst-Principal-Namensbeim Bestimmen des Ziel-Ccache-Namensbeim Versenden (TCP)beim Versenden (UDP)beim Downgrade der Sperre auf »%s«beim Aufbereiten der Datenbankgrößebeim Beenden des Schlüsseltabellen-Scansbei Löschen des Zielzwischenspeichersbeim Expandieren des Ausdrucks »%s«.beim Abholen des Hauptschlüssels %s für Realm %sbeim Abholen der Hauptschlüsselliste für Realm %sbeim Abrufen von Principal %sbei der Suche nach den gerade verwendeten Hauptschlüsselnbeim Suchen nach Mkeywährend das Abfragem der Tickets endetbeim Erzeugen eines Kindprozessesbeim Formatieren des Haupt-Principal-Namensbeim Formatieren des ausgewerteten Principal-Namens für »%s«beim Freigeben von »ktlist«beim Freigeben des Principal-Eintragsbeim Erstellen von neuem Ccachebeim Erzeugen eines Teils des Ziel-Ccache-Namensbeim Holen von »%s«.beim Holen von Attributen für Principal »%s«beim Holen des Client-Principal-Namensbeim Holen des Kontextes für Realm %sbeim Holen der Anmeldedaten für %sbeim Holen der aktuellen Zeitbeim Holen des Standard-Ccachesbeim Holen der Standardschlüsseltabelle des Clientsbeim Holen der Standardschlüsseltabellebeim Holen des Standard-Realmsbeim Holen des Verschlüsselungsbeschreibungbeim Holen der Anfangsanmeldedatenbeim Holen der Anfangsanmeldedaten beim Holen des Schlüsseltabellennamensbeim Holen der lokalen Socket-Adressebeim Holen der Hauptschlüssellistebeim Holen des Hauptschlüssels von Principal %sbeim Holen des Ziel-Ccache-Namensbeim Holen der Richtlinieninformation.beim Holen der Principal-Information.beim Holen des Quellenzwischenspeichersbeim Holen der Zeitbeim Holen der Tageszeitbei der Initialisierungbeim Initialisieren der Schnittstelle %sbeim Initialisieren der Schnittstelle %s, wird erneut versuchtbeim Initialisieren der Kerberos-5-Bibliothekbeim Initialisieren von Kerberos-Codebeim Initialisieren des SAMsbeim Initialisieren der Datenbankbeim Initialisieren der Datenbank für Realm %sbeim Initialisieren der Fehlerbehandlungbeim Initialisieren von »i_vector«beim Initialisieren von Krb5beim Initialisieren der Krb5-Bibliothekbeim Initialisieren von »krb5_context«beim Initialisieren des LDAP-Identifikatorsbeim Initialisieren des Lookaside-Zwischenspeichersbeim Initialisieren des Netzwerksbeim Initialisieren des Zufallsschlüsselgeneratorsbeim Initialisieren des Routing-Socketsbeim Initialisieren der Serverlistebeim Initialisieren des Signalbehandlungsprogrammsbeim Initialisieren des Zielzwischenspeichersbeim Initialisieren der Kerberos-Administrationsoberflächebeim Initialisieren des Aktualisierungsprotokollsbeim Aufführen der Ccache-Sammlungbeim Auflisten der Anmeldedatenzwischenspeicherbeim Auflisten der Richtlinienobjektebeim Auflisten der Realmsbeim Laden des/der Auditerweiterungsmoduls/Auditerweiterungsmodulebeim Sperren der Datei »ok«, »%s«beim Suchen nach dem Anmeldedatenzwischenspeicher %sbeim Nachschlagen der Liste aktiver KVNOsbeim Nachschlagen des aktiven Hauptschlüsselsbeim Nachschlagen der aktiven Version des Hauptschlüsselsbeim Nachschlagen der Mkey-Aux-Datenlistebeim Nachschlagen der Kerberos-Konfigurationbeim Aktivieren der neu geladenen Datenbankbeim Abbilden des Aktualisierungsprotokollsbeim Ändern von »%s«.beim Ändern der Information von Realm »%s«beim Ändern der Richtlinie »%s«beim Ändern des Richtlinienobjektsbeim Erlangen des Socketsbeim Öffnen von %sbeim Öffnen von %s zum Schreibenbeim Öffnen des Ccachesbeim Öffnen des Anmeldedatenzwischenspeichers %sbeim Öffnen des Anmeldedatenzwischenspeichers %sbeim Öffnen der Datenbankbeim Öffnen der Datenbankdatei, »%s«beim Öffnen des Standardanmeldedatenzwischenspeichersbeim Öffnen der Standardschlüsseltabellebeim Auswerten von »-add Principal-Name %s«beim Auswerten des Principal-Namens des Administratorsbeim Auswerten von Befehlsargumentenbeim Auswerten der Befehlsargumente beim Auswerten der Schlüssel-Salts %sbeim Auswerten des Namens %sbeim Auswerten des neuen Principal-Namensbeim Auswerten des alten Principal-Namensbeim Auswerten der Optionenbeim Auswerten des Principalsbeim Auswerten des Principal-Namensbeim Auswerten des Principal-Namens %sbeim Auswertens des temporären Namensbeim dauerhaften Sperren der Datenbankbeim Bereitstellen der Zeitspezifikationbeim vollständigen Löschen der Schlüssel für Principal »%s«beim Erzeugen eines zufälligen Schlüssels für »%s«.beim Lesen der Kerberos-Container-Informationbeim Lesen des Zwischenspeichernamens aus dem Ccachebeim Lesen der Information der Richtlinie »%s«beim Lesen der Information von Realm »%s«beim Lesen der Kerberos-Container-Informationbeim Lesen der Schlüsseltabelle »%s«beim Lesen der LDAP-Konfigurationbeim Lesen der LDAP-Parameterbeim Lesen des Hauptschlüsselsbeim Lesen des Hauptschlüssels von der Tastaturbeim Lesen des neuen Hauptschlüsselsbeim Lesen des neuen Hauptschlüssels von der Tastaturbeim Lesen des neuen Passworts.beim Lesen des Passwortsbeim Lesen des Passworts von »%s«.beim Lesen des Passworts für »%s« beim Lesen der Realm-Informationbeim Lesen der Antwort vom Serversbeim Lesen der Datenbankgröße vom Clientbeim Lesen der Dienstschlüsseltabelle »%s«beim Lesen der Aktualisierungsprotokollkopfzeilenbeim Empfangen vom Netzwerkbeim erneuten Beanspruchen der Root-UIDbeim erneuten Initialisieren des Aktualisierungsprotokollsbeim Umbenennen von %s in %swährend das Umbenennen der Auszugsdateien Gestalt annimmtbeim Umbenennen von Principal »%s« in »%s«beim Zurücksetzen des Ziel-Ccache-Namensbeim Auflösen von %sbeim Ermitteln des Ccaches %sbeim Ermitteln der KDB-Schlüsseltabelle für Realm %sbeim Ermitteln der Schlüsseltabellebeim Ermitteln der Schlüsseltabelle %sbei der Wiederaufnahme des Schlüsseltabellen-Scansbeim Abfragen der Konfigurationsparameterbeim Abfragen von »%s«.beim Abfragen eines Ticketsbeim Abfragen der Anmeldedaten aus dem Zwischenspeicherbeim Abfragen der höchsten KVNO der Schlüsseltabellebeim Abfragen der LDAP-Konfigurationbeim Abfragen der Liste.beim Abfragen des Haupteintragsbeim Abfragen der Richtlinie »%s«.beim Abfragen des Principalsbeim Abfragen des Principal-Namensbeim Abfragen von Rechtenbei der Rückkehr zur Quell-UID, nachdem der beste Principal gefunden wurdebeim Speichern von Principal %sbeim Scannen der Schlüsseltabellebeim Suchen nach Ccache für %sbeim Erzeugen des Startwerts des Zufallszahlengeneratorsbeim Auswählen des besten Principalsbeim Senden n empfangener Bytebeim Senden des Datenbankblocks, der bei %d beginntbeim Senden der Datenbankgrößebeim Senden der Antwort zu %s/%s von %sbeim Setzen von »%s«=»%s«beim Setzen des FAST-Ccachesbeim Setzen der Option SO_REUSEADDRbeim Setzen eines Attributes für Principal »%s«beim Setzen der Änderungszeit der Dateibeim Setzen des Client-Principal-Namensbeim Setzen des Client-Principal-Realmsbeim Einstellen des Standard-Realm-Namensbeim Setzen des Standard-Realms auf %sbeim Setzen der EUID auf dem Quellbenutzerbeim Setzen des Server-Principal-Namensbeim Setzen des Passworts für das Dienstobjektbeim Einrichten der KDB-Schlüsseltabelle für Realm %sbeim Einrichten des Verschlüsselungstyps %dein Einrichten der BibliotheksIdentifikatorsbeim Einrichten der Protokollierungbeim Einrichten des Hauptschlüsselnamensbeim Einrichten des Hauptschlüsselnamens %s für Realm %sbeim Start des Schlüsseltabellen-Scanswährend das Abfragen der Tickets beginntbeim Speichern von %sbeim Speichern der Anmeldedatenbeim Speichern des Schlüsselsbeim Zurückwechsel vom Ziel nach der Autorisierungsprüfungbeim Wechsel auf den Anmeldedatenzwischenspeicherbeim Wechsel zum neuen Ccachebeim Wechsel des Ziels der Autorisierungsprüfungbeim Umschalten auf die Ziel-UIDbeim Unterbrechen des Schlüsseltabellen-Scansbeim Umwandeln des Hauptschlüssels vom Passwortbeim Versuch, das Passwort zu ändern.beim Versuch, die Datenbankdatei zu schließenbeim Versuch, den Rechnerdienst-Principal zu erstellenbeim Versuch, meinen Dienstnamen zu erstellenbeim Versuch, %s auszuführenbeim Versuch von »execv %s«beim Versuch, einen Kindprozess von %s zu erzeugenbeim Versuch zu verzweigen.beim Versuch, »%s« zu sperrenbeim Versuch, Speicher für »data_ok_fn« zu reservierenbeim Versuch, %s zu öffnenbeim Versuch, »stat« für %s auszuführenbeim Aufheben der Sperre »%s«beim Aufheben der Datenbanksperrebeim Entsperren von Datenbank »%s«beim Entsperren der Datenbankbeim Rückgängigmachen des Auswertens des Client-Namensbeim Rückgängigmachen der Auswertung des Principalsbeim Rückgängigmachen des Auswertens des Principal-Namensbeim Rückgängigmachen des Auswertens des Server-Namensbeim Rückgängigmachen der Auswertung des »etype«s des Ticketsbeim Entfernen der Option IPV6_V6ONLYbeim Aktualisieren der Actkvno-Daten für den Haupt-Principal-Eintragbeim Aktualisieren der Mkey-Aux-Daten für den Haupt-Principal-Eintragbeim Aktualisieren der Schlüsseldaten von Principal »%s« in der Datenbankbeim Aktualisieren der Änderungszeit von Principal »%s«beim Aktulisieren der Änderungszeit des Hauptschlüssel-Principalsbeim Prüfen des Hauptschlüsselsbeim Prüfen des Tickets für Serverbeim Betrachten der Richtliniebeim Betrachten der Richtlinie »%s«beim Warten auf %sbeim Warten auf den Erhalt der Datenbankbeim Schreiben der Schlüsseltabelle »%s«beim Schreiben der AktualisierungsprotokollkopfzeilenArbeitsprozess %ld endete mit Status %dwürde übersprungen: %s würde aktualisiert: %s falsche OID noch keine Richtlinie gesetzt! Kontaktieren Sie Ihren SystemsicherheitsadministratorPK0]l>licenses/alt-krb5-libs/LICENSEnu[Copyright (C) 1985-2019 by the Massachusetts Institute of Technology. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Downloading of this software may constitute an export of cryptographic software from the United States of America that is subject to the United States Export Administration Regulations (EAR), 15 CFR 730-774. Additional laws or regulations may apply. It is the responsibility of the person or entity contemplating export to comply with all applicable export laws and regulations, including obtaining any required license from the U.S. government. The U.S. government prohibits export of encryption source code to certain countries and individuals, including, but not limited to, the countries of Cuba, Iran, North Korea, Sudan, Syria, and residents and nationals of those countries. Documentation components of this software distribution are licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. (https://creativecommons.org/licenses/by-sa/3.0/) Individual source code files are copyright MIT, Cygnus Support, Novell, OpenVision Technologies, Oracle, Red Hat, Sun Microsystems, FundsXpress, and others. Project Athena, Athena, Athena MUSE, Discuss, Hesiod, Kerberos, Moira, and Zephyr are trademarks of the Massachusetts Institute of Technology (MIT). No commercial use of these trademarks may be made without prior written permission of MIT. "Commercial use" means use of a name in a product or other for-profit manner. It does NOT prevent a commercial firm from referring to the MIT trademarks in order to convey information (although in doing so, recognition of their trademark status should be given). ====================================================================== The following copyright and permission notice applies to the OpenVision Kerberos Administration system located in "kadmin/create", "kadmin/dbutil", "kadmin/passwd", "kadmin/server", "lib/kadm5", and portions of "lib/rpc": Copyright, OpenVision Technologies, Inc., 1993-1996, All Rights Reserved WARNING: Retrieving the OpenVision Kerberos Administration system source code, as described below, indicates your acceptance of the following terms. If you do not agree to the following terms, do not retrieve the OpenVision Kerberos administration system. You may freely use and distribute the Source Code and Object Code compiled from it, with or without modification, but this Source Code is provided to you "AS IS" EXCLUSIVE OF ANY WARRANTY, INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, OR ANY OTHER WARRANTY, WHETHER EXPRESS OR IMPLIED. IN NO EVENT WILL OPENVISION HAVE ANY LIABILITY FOR ANY LOST PROFITS, LOSS OF DATA OR COSTS OF PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THIS AGREEMENT, INCLUDING, WITHOUT LIMITATION, THOSE RESULTING FROM THE USE OF THE SOURCE CODE, OR THE FAILURE OF THE SOURCE CODE TO PERFORM, OR FOR ANY OTHER REASON. OpenVision retains all copyrights in the donated Source Code. OpenVision also retains copyright to derivative works of the Source Code, whether created by OpenVision or by a third party. The OpenVision copyright notice must be preserved if derivative works are made based on the donated Source Code. OpenVision Technologies, Inc. has donated this Kerberos Administration system to MIT for inclusion in the standard Kerberos 5 distribution. This donation underscores our commitment to continuing Kerberos technology development and our gratitude for the valuable work which has been performed by MIT and the Kerberos community. ====================================================================== Portions contributed by Matt Crawford "crawdad@fnal.gov" were work performed at Fermi National Accelerator Laboratory, which is operated by Universities Research Association, Inc., under contract DE-AC02-76CHO3000 with the U.S. Department of Energy. ====================================================================== Portions of "src/lib/crypto" have the following copyright: Copyright (C) 1998 by the FundsXpress, INC. All rights reserved. Export of this software from the United States of America may require a specific license from the United States Government. It is the responsibility of any person or organization contemplating export to obtain such a license before exporting. WITHIN THAT CONSTRAINT, permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of FundsXpress. not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. FundsXpress makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ====================================================================== The implementation of the AES encryption algorithm in "src/lib/crypto/builtin/aes" has the following copyright: Copyright (C) 2001, Dr Brian Gladman "brg@gladman.uk.net", Worcester, UK. All rights reserved. LICENSE TERMS The free distribution and use of this software in both source and binary form is allowed (with or without changes) provided that: 1. distributions of this source code include the above copyright notice, this list of conditions and the following disclaimer; 2. distributions in binary form include the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other associated materials; 3. the copyright holder's name is not used to endorse products built using this software without specific written permission. DISCLAIMER This software is provided 'as is' with no explcit or implied warranties in respect of any properties, including, but not limited to, correctness and fitness for purpose. ====================================================================== Portions contributed by Red Hat, including the pre-authentication plug-in framework and the NSS crypto implementation, contain the following copyright: Copyright (C) 2006 Red Hat, Inc. Portions copyright (C) 2006 Massachusetts Institute of Technology All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Red Hat, Inc., nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== The bundled verto source code is subject to the following license: Copyright 2011 Red Hat, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ====================================================================== The MS-KKDCP client implementation has the following copyright: Copyright 2013,2014 Red Hat, Inc. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== The implementations of GSSAPI mechglue in GSSAPI-SPNEGO in "src/lib/gssapi", including the following files: lib/gssapi/generic/gssapi_err_generic.et lib/gssapi/mechglue/g_accept_sec_context.c lib/gssapi/mechglue/g_acquire_cred.c lib/gssapi/mechglue/g_canon_name.c lib/gssapi/mechglue/g_compare_name.c lib/gssapi/mechglue/g_context_time.c lib/gssapi/mechglue/g_delete_sec_context.c lib/gssapi/mechglue/g_dsp_name.c lib/gssapi/mechglue/g_dsp_status.c lib/gssapi/mechglue/g_dup_name.c lib/gssapi/mechglue/g_exp_sec_context.c lib/gssapi/mechglue/g_export_name.c lib/gssapi/mechglue/g_glue.c lib/gssapi/mechglue/g_imp_name.c lib/gssapi/mechglue/g_imp_sec_context.c lib/gssapi/mechglue/g_init_sec_context.c lib/gssapi/mechglue/g_initialize.c lib/gssapi/mechglue/g_inquire_context.c lib/gssapi/mechglue/g_inquire_cred.c lib/gssapi/mechglue/g_inquire_names.c lib/gssapi/mechglue/g_process_context.c lib/gssapi/mechglue/g_rel_buffer.c lib/gssapi/mechglue/g_rel_cred.c lib/gssapi/mechglue/g_rel_name.c lib/gssapi/mechglue/g_rel_oid_set.c lib/gssapi/mechglue/g_seal.c lib/gssapi/mechglue/g_sign.c lib/gssapi/mechglue/g_store_cred.c lib/gssapi/mechglue/g_unseal.c lib/gssapi/mechglue/g_userok.c lib/gssapi/mechglue/g_utils.c lib/gssapi/mechglue/g_verify.c lib/gssapi/mechglue/gssd_pname_to_uid.c lib/gssapi/mechglue/mglueP.h lib/gssapi/mechglue/oid_ops.c lib/gssapi/spnego/gssapiP_spnego.h lib/gssapi/spnego/spnego_mech.c and the initial implementation of incremental propagation, including the following new or changed files: include/iprop_hdr.h kadmin/server/ipropd_svc.c lib/kdb/iprop.x lib/kdb/kdb_convert.c lib/kdb/kdb_log.c lib/kdb/kdb_log.h lib/krb5/error_tables/kdb5_err.et kprop/kpropd_rpc.c kprop/kproplog.c are subject to the following license: Copyright (C) 2004 Sun Microsystems, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ====================================================================== Kerberos V5 includes documentation and software developed at the University of California at Berkeley, which includes this copyright notice: Copyright (C) 1983 Regents of the University of California. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== Portions contributed by Novell, Inc., including the LDAP database backend, are subject to the following license: Copyright (C) 2004-2005, Novell, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * The copyright holder's name is not used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== Portions funded by Sandia National Laboratory and developed by the University of Michigan's Center for Information Technology Integration, including the PKINIT implementation, are subject to the following license: COPYRIGHT (C) 2006-2007 THE REGENTS OF THE UNIVERSITY OF MICHIGAN ALL RIGHTS RESERVED Permission is granted to use, copy, create derivative works and redistribute this software and such derivative works for any purpose, so long as the name of The University of Michigan is not used in any advertising or publicity pertaining to the use of distribution of this software without specific, written prior authorization. If the above copyright notice or any other identification of the University of Michigan is included in any copy of any portion of this software, then the disclaimer below must also be included. THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. ====================================================================== The pkcs11.h file included in the PKINIT code has the following license: Copyright 2006 g10 Code GmbH Copyright 2006 Andreas Jellinghaus This file is free software; as a special exception the author gives unlimited permission to copy and/or distribute it, with or without modifications, as long as this notice is preserved. This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY, to the extent permitted by law; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ====================================================================== Portions contributed by Apple Inc. are subject to the following license: Copyright 2004-2008 Apple Inc. All Rights Reserved. Export of this software from the United States of America may require a specific license from the United States Government. It is the responsibility of any person or organization contemplating export to obtain such a license before exporting. WITHIN THAT CONSTRAINT, permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Apple Inc. not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. Apple Inc. makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ====================================================================== The implementations of UTF-8 string handling in src/util/support and src/lib/krb5/unicode are subject to the following copyright and permission notice: The OpenLDAP Public License Version 2.8, 17 August 2003 Redistribution and use of this software and associated documentation ("Software"), with or without modification, are permitted provided that the following conditions are met: 1. Redistributions in source form must retain copyright statements and notices, 2. Redistributions in binary form must reproduce applicable copyright statements and notices, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution, and 3. Redistributions must contain a verbatim copy of this document. The OpenLDAP Foundation may revise this license from time to time. Each revision is distinguished by a version number. You may use this Software under terms of this license revision or under the terms of any subsequent revision of the license. THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S) OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The names of the authors and copyright holders must not be used in advertising or otherwise to promote the sale, use or other dealing in this Software without specific, written prior permission. Title to copyright in this Software shall at all times remain with copyright holders. OpenLDAP is a registered trademark of the OpenLDAP Foundation. Copyright 1999-2003 The OpenLDAP Foundation, Redwood City, California, USA. All Rights Reserved. Permission to copy and distribute verbatim copies of this document is granted. ====================================================================== Marked test programs in src/lib/krb5/krb have the following copyright: Copyright (C) 2006 Kungliga Tekniska Högskola (Royal Institute of Technology, Stockholm, Sweden). All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of KTH nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== The KCM Mach RPC definition file used on macOS has the following copyright: Copyright (C) 2009 Kungliga Tekniska Högskola (Royal Institute of Technology, Stockholm, Sweden). All rights reserved. Portions Copyright (C) 2009 Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the Institute nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== Portions of the RPC implementation in src/lib/rpc and src/include/gssrpc have the following copyright and permission notice: Copyright (C) 2010, Oracle America, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the "Oracle America, Inc." nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== Copyright (C) 2006,2007,2009 NTT (Nippon Telegraph and Telephone Corporation). All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer as the first lines of this file unmodified. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY NTT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NTT BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== Copyright 2000 by Carnegie Mellon University All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Carnegie Mellon University not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ====================================================================== Copyright (C) 2002 Naval Research Laboratory (NRL/CCS) Permission to use, copy, modify and distribute this software and its documentation is hereby granted, provided that both the copyright notice and this permission notice appear in all copies of the software, derivative works or modified versions, and any portions thereof. NRL ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION AND DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. ====================================================================== Copyright (C) 1991, 1992, 1994 by Cygnus Support. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. Cygnus Support makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ====================================================================== Copyright (C) 2006 Secure Endpoints Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ====================================================================== Portions of the implementation of the Fortuna-like PRNG are subject to the following notice: Copyright (C) 2005 Marko Kreen All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Copyright (C) 1994 by the University of Southern California EXPORT OF THIS SOFTWARE from the United States of America may require a specific license from the United States Government. It is the responsibility of any person or organization contemplating export to obtain such a license before exporting. WITHIN THAT CONSTRAINT, permission to copy, modify, and distribute this software and its documentation in source and binary forms is hereby granted, provided that any documentation or other materials related to such distribution or use acknowledge that the software was developed by the University of Southern California. DISCLAIMER OF WARRANTY. THIS SOFTWARE IS PROVIDED "AS IS". The University of Southern California MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. By way of example, but not limitation, the University of Southern California MAKES NO REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE. The University of Southern California shall not be held liable for any liability nor for any direct, indirect, or consequential damages with respect to any claim by the user or distributor of the ksu software. ====================================================================== Copyright (C) 1995 The President and Fellows of Harvard University This code is derived from software contributed to Harvard by Jeremy Rassen. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the University of California, Berkeley and its contributors. 4. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== Copyright (C) 2008 by the Massachusetts Institute of Technology. Copyright 1995 by Richard P. Basch. All Rights Reserved. Copyright 1995 by Lehman Brothers, Inc. All Rights Reserved. Export of this software from the United States of America may require a specific license from the United States Government. It is the responsibility of any person or organization contemplating export to obtain such a license before exporting. WITHIN THAT CONSTRAINT, permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Richard P. Basch, Lehman Brothers and M.I.T. not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. Richard P. Basch, Lehman Brothers and M.I.T. make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ====================================================================== The following notice applies to "src/lib/krb5/krb/strptime.c" and "src/include/k5-queue.h". Copyright (C) 1997, 1998 The NetBSD Foundation, Inc. All rights reserved. This code was contributed to The NetBSD Foundation by Klaus Klein. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the NetBSD Foundation, Inc. and its contributors. 4. Neither the name of The NetBSD Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== The following notice applies to Unicode library files in "src/lib/krb5/unicode": Copyright 1997, 1998, 1999 Computing Research Labs, New Mexico State University Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ====================================================================== The following notice applies to "src/util/support/strlcpy.c": Copyright (C) 1998 Todd C. Miller "Todd.Miller@courtesan.com" Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ====================================================================== The following notice applies to "src/util/profile/argv_parse.c" and "src/util/profile/argv_parse.h": Copyright 1999 by Theodore Ts'o. Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THEODORE TS'O (THE AUTHOR) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. (Isn't it sick that the U.S. culture of lawsuit-happy lawyers requires this kind of disclaimer?) ====================================================================== The following notice applies to SWIG-generated code in "src/util/profile/profile_tcl.c": Copyright (C) 1999-2000, The University of Chicago This file may be freely redistributed without license or fee provided this copyright message remains intact. ====================================================================== The following notice applies to portiions of "src/lib/rpc" and "src/include/gssrpc": Copyright (C) 2000 The Regents of the University of Michigan. All rights reserved. Copyright (C) 2000 Dug Song "dugsong@UMICH.EDU". All rights reserved, all wrongs reversed. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== Implementations of the MD4 algorithm are subject to the following notice: Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. License to copy and use this software is granted provided that it is identified as the "RSA Data Security, Inc. MD4 Message Digest Algorithm" in all material mentioning or referencing this software or this function. License is also granted to make and use derivative works provided that such works are identified as "derived from the RSA Data Security, Inc. MD4 Message Digest Algorithm" in all material mentioning or referencing the derived work. RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided "as is" without express or implied warranty of any kind. These notices must be retained in any copies of any part of this documentation and/or software. ====================================================================== Implementations of the MD5 algorithm are subject to the following notice: Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. License to copy and use this software is granted provided that it is identified as the "RSA Data Security, Inc. MD5 Message- Digest Algorithm" in all material mentioning or referencing this software or this function. License is also granted to make and use derivative works provided that such works are identified as "derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing the derived work. RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided "as is" without express or implied warranty of any kind. These notices must be retained in any copies of any part of this documentation and/or software. ====================================================================== The following notice applies to "src/lib/crypto/crypto_tests/t_mddriver.c": Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All rights reserved. RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided "as is" without express or implied warranty of any kind. These notices must be retained in any copies of any part of this documentation and/or software. ====================================================================== Portions of "src/lib/krb5" are subject to the following notice: Copyright (C) 1994 CyberSAFE Corporation. Copyright 1990,1991,2007,2008 by the Massachusetts Institute of Technology. All Rights Reserved. Export of this software from the United States of America may require a specific license from the United States Government. It is the responsibility of any person or organization contemplating export to obtain such a license before exporting. WITHIN THAT CONSTRAINT, permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of M.I.T. not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. Furthermore if you modify this software you must label your software as modified software and not distribute it in such a fashion that it might be confused with the original M.I.T. software. Neither M.I.T., the Open Computing Security Group, nor CyberSAFE Corporation make any representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ====================================================================== Portions contributed by PADL Software are subject to the following license: Copyright (c) 2011, PADL Software Pty Ltd. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of PADL Software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== The bundled libev source code is subject to the following license: All files in libev are Copyright (C)2007,2008,2009 Marc Alexander Lehmann. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Alternatively, the contents of this package may be used under the terms of the GNU General Public License ("GPL") version 2 or any later version, in which case the provisions of the GPL are applicable instead of the above. If you wish to allow the use of your version of this package only under the terms of the GPL and not to allow others to use your version of this file under the BSD license, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the GPL in this and the other files of this package. If you do not delete the provisions above, a recipient may use your version of this file under either the BSD or the GPL. ====================================================================== Files copied from the Intel AESNI Sample Library are subject to the following license: Copyright (C) 2010, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== The following notice applies to "src/ccapi/common/win/OldCC/autolock.hxx": Copyright (C) 1998 by Danilo Almeida. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ====================================================================== The following notice applies to portions of "src/plugins/preauth/spake/edwards25519.c" and "src/plugins/preauth/spake/edwards25519_tables.h": The MIT License (MIT) Copyright (c) 2015-2016 the fiat-crypto authors (see the AUTHORS file). Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ====================================================================== The following notice applies to portions of "src/plugins/preauth/spake/edwards25519.c": Copyright (c) 2015-2016, Google Inc. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. PK0]F~man/man5/krb5.conf.5nu[.\" Man page generated from reStructuredText. . .TH "KRB5.CONF" "5" " " "1.17" "MIT Kerberos" .SH NAME krb5.conf \- Kerberos configuration file . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp The krb5.conf file contains Kerberos configuration information, including the locations of KDCs and admin servers for the Kerberos realms of interest, defaults for the current realm and for Kerberos applications, and mappings of hostnames onto Kerberos realms. Normally, you should install your krb5.conf file in the directory \fB/etc\fP\&. You can override the default location by setting the environment variable \fBKRB5_CONFIG\fP\&. Multiple colon\-separated filenames may be specified in \fBKRB5_CONFIG\fP; all files which are present will be read. Starting in release 1.14, directory names can also be specified in \fBKRB5_CONFIG\fP; all files within the directory whose names consist solely of alphanumeric characters, dashes, or underscores will be read. .SH STRUCTURE .sp The krb5.conf file is set up in the style of a Windows INI file. Lines beginning with \(aq#\(aq or \(aq;\(aq (possibly after initial whitespace) are ignored as comments. Sections are headed by the section name, in square brackets. Each section may contain zero or more relations, of the form: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C foo = bar .ft P .fi .UNINDENT .UNINDENT .sp or: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C fubar = { foo = bar baz = quux } .ft P .fi .UNINDENT .UNINDENT .sp Placing a \(aq*\(aq at the end of a line indicates that this is the \fIfinal\fP value for the tag. This means that neither the remainder of this configuration file nor any other configuration file will be checked for any other values for this tag. .sp For example, if you have the following lines: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C foo = bar* foo = baz .ft P .fi .UNINDENT .UNINDENT .sp then the second value of \fBfoo\fP (\fBbaz\fP) would never be read. .sp The krb5.conf file can include other files using either of the following directives at the beginning of a line: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C include FILENAME includedir DIRNAME .ft P .fi .UNINDENT .UNINDENT .sp \fIFILENAME\fP or \fIDIRNAME\fP should be an absolute path. The named file or directory must exist and be readable. Including a directory includes all files within the directory whose names consist solely of alphanumeric characters, dashes, or underscores. Starting in release 1.15, files with names ending in ".conf" are also included, unless the name begins with ".". Included profile files are syntactically independent of their parents, so each included file must begin with a section header. Starting in release 1.17, files are read in alphanumeric order; in previous releases, they may be read in any order. .sp The krb5.conf file can specify that configuration should be obtained from a loadable module, rather than the file itself, using the following directive at the beginning of a line before any section headers: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C module MODULEPATH:RESIDUAL .ft P .fi .UNINDENT .UNINDENT .sp \fIMODULEPATH\fP may be relative to the library path of the krb5 installation, or it may be an absolute path. \fIRESIDUAL\fP is provided to the module at initialization time. If krb5.conf uses a module directive, kdc.conf(5) should also use one if it exists. .SH SECTIONS .sp The krb5.conf file may contain the following sections: .TS center; |l|l|. _ T{ \fI\%[libdefaults]\fP T} T{ Settings used by the Kerberos V5 library T} _ T{ \fI\%[realms]\fP T} T{ Realm\-specific contact information and settings T} _ T{ \fI\%[domain_realm]\fP T} T{ Maps server hostnames to Kerberos realms T} _ T{ \fI\%[capaths]\fP T} T{ Authentication paths for non\-hierarchical cross\-realm T} _ T{ \fI\%[appdefaults]\fP T} T{ Settings used by some Kerberos V5 applications T} _ T{ \fI\%[plugins]\fP T} T{ Controls plugin module registration T} _ .TE .sp Additionally, krb5.conf may include any of the relations described in kdc.conf(5), but it is not a recommended practice. .SS [libdefaults] .sp The libdefaults section may contain any of the following relations: .INDENT 0.0 .TP \fBallow_weak_crypto\fP If this flag is set to false, then weak encryption types (as noted in Encryption_types in kdc.conf(5)) will be filtered out of the lists \fBdefault_tgs_enctypes\fP, \fBdefault_tkt_enctypes\fP, and \fBpermitted_enctypes\fP\&. The default value for this tag is false, which may cause authentication failures in existing Kerberos infrastructures that do not support strong crypto. Users in affected environments should set this tag to true until their infrastructure adopts stronger ciphers. .TP \fBap_req_checksum_type\fP An integer which specifies the type of AP\-REQ checksum to use in authenticators. This variable should be unset so the appropriate checksum for the encryption key in use will be used. This can be set if backward compatibility requires a specific checksum type. See the \fBkdc_req_checksum_type\fP configuration option for the possible values and their meanings. .TP \fBcanonicalize\fP If this flag is set to true, initial ticket requests to the KDC will request canonicalization of the client principal name, and answers with different client principals than the requested principal will be accepted. The default value is false. .TP \fBccache_type\fP This parameter determines the format of credential cache types created by kinit(1) or other programs. The default value is 4, which represents the most current format. Smaller values can be used for compatibility with very old implementations of Kerberos which interact with credential caches on the same host. .TP \fBclockskew\fP Sets the maximum allowable amount of clockskew in seconds that the library will tolerate before assuming that a Kerberos message is invalid. The default value is 300 seconds, or five minutes. .sp The clockskew setting is also used when evaluating ticket start and expiration times. For example, tickets that have reached their expiration time can still be used (and renewed if they are renewable tickets) if they have been expired for a shorter duration than the \fBclockskew\fP setting. .TP \fBdefault_ccache_name\fP This relation specifies the name of the default credential cache. The default is \fBFILE:/tmp/krb5cc_%{uid}\fP\&. This relation is subject to parameter expansion (see below). New in release 1.11. .TP \fBdefault_client_keytab_name\fP This relation specifies the name of the default keytab for obtaining client credentials. The default is \fBFILE:/opt/alt/krb5/usr/var/krb5/user/%{euid}/client.keytab\fP\&. This relation is subject to parameter expansion (see below). New in release 1.11. .TP \fBdefault_keytab_name\fP This relation specifies the default keytab name to be used by application servers such as sshd. The default is \fBFILE:/etc/krb5.keytab\fP\&. This relation is subject to parameter expansion (see below). .TP \fBdefault_realm\fP Identifies the default Kerberos realm for the client. Set its value to your Kerberos realm. If this value is not set, then a realm must be specified with every Kerberos principal when invoking programs such as kinit(1)\&. .TP \fBdefault_tgs_enctypes\fP Identifies the supported list of session key encryption types that the client should request when making a TGS\-REQ, in order of preference from highest to lowest. The list may be delimited with commas or whitespace. See Encryption_types in kdc.conf(5) for a list of the accepted values for this tag. The default value is \fBaes256\-cts\-hmac\-sha1\-96 aes128\-cts\-hmac\-sha1\-96 aes256\-cts\-hmac\-sha384\-192 aes128\-cts\-hmac\-sha256\-128 des3\-cbc\-sha1 arcfour\-hmac\-md5 camellia256\-cts\-cmac camellia128\-cts\-cmac des\-cbc\-crc des\-cbc\-md5 des\-cbc\-md4\fP, but single\-DES encryption types will be implicitly removed from this list if the value of \fBallow_weak_crypto\fP is false. .sp Do not set this unless required for specific backward compatibility purposes; stale values of this setting can prevent clients from taking advantage of new stronger enctypes when the libraries are upgraded. .TP \fBdefault_tkt_enctypes\fP Identifies the supported list of session key encryption types that the client should request when making an AS\-REQ, in order of preference from highest to lowest. The format is the same as for default_tgs_enctypes. The default value for this tag is \fBaes256\-cts\-hmac\-sha1\-96 aes128\-cts\-hmac\-sha1\-96 aes256\-cts\-hmac\-sha384\-192 aes128\-cts\-hmac\-sha256\-128 des3\-cbc\-sha1 arcfour\-hmac\-md5 camellia256\-cts\-cmac camellia128\-cts\-cmac des\-cbc\-crc des\-cbc\-md5 des\-cbc\-md4\fP, but single\-DES encryption types will be implicitly removed from this list if the value of \fBallow_weak_crypto\fP is false. .sp Do not set this unless required for specific backward compatibility purposes; stale values of this setting can prevent clients from taking advantage of new stronger enctypes when the libraries are upgraded. .TP \fBdns_canonicalize_hostname\fP Indicate whether name lookups will be used to canonicalize hostnames for use in service principal names. Setting this flag to false can improve security by reducing reliance on DNS, but means that short hostnames will not be canonicalized to fully\-qualified hostnames. The default value is true. .TP \fBdns_lookup_kdc\fP Indicate whether DNS SRV records should be used to locate the KDCs and other servers for a realm, if they are not listed in the krb5.conf information for the realm. (Note that the admin_server entry must be in the krb5.conf realm information in order to contact kadmind, because the DNS implementation for kadmin is incomplete.) .sp Enabling this option does open up a type of denial\-of\-service attack, if someone spoofs the DNS records and redirects you to another server. However, it\(aqs no worse than a denial of service, because that fake KDC will be unable to decode anything you send it (besides the initial ticket request, which has no encrypted data), and anything the fake KDC sends will not be trusted without verification using some secret that it won\(aqt know. .TP \fBdns_uri_lookup\fP Indicate whether DNS URI records should be used to locate the KDCs and other servers for a realm, if they are not listed in the krb5.conf information for the realm. SRV records are used as a fallback if no URI records were found. The default value is true. New in release 1.15. .TP \fBerr_fmt\fP This relation allows for custom error message formatting. If a value is set, error messages will be formatted by substituting a normal error message for %M and an error code for %C in the value. .TP \fBextra_addresses\fP This allows a computer to use multiple local addresses, in order to allow Kerberos to work in a network that uses NATs while still using address\-restricted tickets. The addresses should be in a comma\-separated list. This option has no effect if \fBnoaddresses\fP is true. .TP \fBforwardable\fP If this flag is true, initial tickets will be forwardable by default, if allowed by the KDC. The default value is false. .TP \fBignore_acceptor_hostname\fP When accepting GSSAPI or krb5 security contexts for host\-based service principals, ignore any hostname passed by the calling application, and allow clients to authenticate to any service principal in the keytab matching the service name and realm name (if given). This option can improve the administrative flexibility of server applications on multihomed hosts, but could compromise the security of virtual hosting environments. The default value is false. New in release 1.10. .TP \fBk5login_authoritative\fP If this flag is true, principals must be listed in a local user\(aqs k5login file to be granted login access, if a \&.k5login(5) file exists. If this flag is false, a principal may still be granted login access through other mechanisms even if a k5login file exists but does not list the principal. The default value is true. .TP \fBk5login_directory\fP If set, the library will look for a local user\(aqs k5login file within the named directory, with a filename corresponding to the local username. If not set, the library will look for k5login files in the user\(aqs home directory, with the filename .k5login. For security reasons, .k5login files must be owned by the local user or by root. .TP \fBkcm_mach_service\fP On macOS only, determines the name of the bootstrap service used to contact the KCM daemon for the KCM credential cache type. If the value is \fB\-\fP, Mach RPC will not be used to contact the KCM daemon. The default value is \fBorg.h5l.kcm\fP\&. .TP \fBkcm_socket\fP Determines the path to the Unix domain socket used to access the KCM daemon for the KCM credential cache type. If the value is \fB\-\fP, Unix domain sockets will not be used to contact the KCM daemon. The default value is \fB/var/run/.heim_org.h5l.kcm\-socket\fP\&. .TP \fBkdc_default_options\fP Default KDC options (Xored for multiple values) when requesting initial tickets. By default it is set to 0x00000010 (KDC_OPT_RENEWABLE_OK). .TP \fBkdc_timesync\fP Accepted values for this relation are 1 or 0. If it is nonzero, client machines will compute the difference between their time and the time returned by the KDC in the timestamps in the tickets and use this value to correct for an inaccurate system clock when requesting service tickets or authenticating to services. This corrective factor is only used by the Kerberos library; it is not used to change the system clock. The default value is 1. .TP \fBkdc_req_checksum_type\fP An integer which specifies the type of checksum to use for the KDC requests, for compatibility with very old KDC implementations. This value is only used for DES keys; other keys use the preferred checksum type for those keys. .sp The possible values and their meanings are as follows. .TS center; |l|l|. _ T{ 1 T} T{ CRC32 T} _ T{ 2 T} T{ RSA MD4 T} _ T{ 3 T} T{ RSA MD4 DES T} _ T{ 4 T} T{ DES CBC T} _ T{ 7 T} T{ RSA MD5 T} _ T{ 8 T} T{ RSA MD5 DES T} _ T{ 9 T} T{ NIST SHA T} _ T{ 12 T} T{ HMAC SHA1 DES3 T} _ T{ \-138 T} T{ Microsoft MD5 HMAC checksum type T} _ .TE .TP \fBnoaddresses\fP If this flag is true, requests for initial tickets will not be made with address restrictions set, allowing the tickets to be used across NATs. The default value is true. .TP \fBpermitted_enctypes\fP Identifies all encryption types that are permitted for use in session key encryption. The default value for this tag is \fBaes256\-cts\-hmac\-sha1\-96 aes128\-cts\-hmac\-sha1\-96 aes256\-cts\-hmac\-sha384\-192 aes128\-cts\-hmac\-sha256\-128 des3\-cbc\-sha1 arcfour\-hmac\-md5 camellia256\-cts\-cmac camellia128\-cts\-cmac des\-cbc\-crc des\-cbc\-md5 des\-cbc\-md4\fP, but single\-DES encryption types will be implicitly removed from this list if the value of \fBallow_weak_crypto\fP is false. .TP \fBplugin_base_dir\fP If set, determines the base directory where krb5 plugins are located. The default value is the \fBkrb5/plugins\fP subdirectory of the krb5 library directory. This relation is subject to parameter expansion (see below) in release 1.17 and later. .TP \fBpreferred_preauth_types\fP This allows you to set the preferred preauthentication types which the client will attempt before others which may be advertised by a KDC. The default value for this setting is "17, 16, 15, 14", which forces libkrb5 to attempt to use PKINIT if it is supported. .TP \fBproxiable\fP If this flag is true, initial tickets will be proxiable by default, if allowed by the KDC. The default value is false. .TP \fBrdns\fP If this flag is true, reverse name lookup will be used in addition to forward name lookup to canonicalizing hostnames for use in service principal names. If \fBdns_canonicalize_hostname\fP is set to false, this flag has no effect. The default value is true. .TP \fBrealm_try_domains\fP Indicate whether a host\(aqs domain components should be used to determine the Kerberos realm of the host. The value of this variable is an integer: \-1 means not to search, 0 means to try the host\(aqs domain itself, 1 means to also try the domain\(aqs immediate parent, and so forth. The library\(aqs usual mechanism for locating Kerberos realms is used to determine whether a domain is a valid realm, which may involve consulting DNS if \fBdns_lookup_kdc\fP is set. The default is not to search domain components. .TP \fBrenew_lifetime\fP (duration string.) Sets the default renewable lifetime for initial ticket requests. The default value is 0. .TP \fBsafe_checksum_type\fP An integer which specifies the type of checksum to use for the KRB\-SAFE requests. By default it is set to 8 (RSA MD5 DES). For compatibility with applications linked against DCE version 1.1 or earlier Kerberos libraries, use a value of 3 to use the RSA MD4 DES instead. This field is ignored when its value is incompatible with the session key type. See the \fBkdc_req_checksum_type\fP configuration option for the possible values and their meanings. .TP \fBspake_preauth_groups\fP A whitespace or comma\-separated list of words which specifies the groups allowed for SPAKE preauthentication. The possible values are: .TS center; |l|l|. _ T{ edwards25519 T} T{ Edwards25519 curve (\fI\%RFC 7748\fP) T} _ T{ P\-256 T} T{ NIST P\-256 curve (\fI\%RFC 5480\fP) T} _ T{ P\-384 T} T{ NIST P\-384 curve (\fI\%RFC 5480\fP) T} _ T{ P\-521 T} T{ NIST P\-521 curve (\fI\%RFC 5480\fP) T} _ .TE .sp The default value for the client is \fBedwards25519\fP\&. The default value for the KDC is empty. New in release 1.17. .TP \fBticket_lifetime\fP (duration string.) Sets the default lifetime for initial ticket requests. The default value is 1 day. .TP \fBudp_preference_limit\fP When sending a message to the KDC, the library will try using TCP before UDP if the size of the message is above \fBudp_preference_limit\fP\&. If the message is smaller than \fBudp_preference_limit\fP, then UDP will be tried before TCP. Regardless of the size, both protocols will be tried if the first attempt fails. .TP \fBverify_ap_req_nofail\fP If this flag is true, then an attempt to verify initial credentials will fail if the client machine does not have a keytab. The default value is false. .UNINDENT .SS [realms] .sp Each tag in the [realms] section of the file is the name of a Kerberos realm. The value of the tag is a subsection with relations that define the properties of that particular realm. For each realm, the following tags may be specified in the realm\(aqs subsection: .INDENT 0.0 .TP \fBadmin_server\fP Identifies the host where the administration server is running. Typically, this is the master Kerberos server. This tag must be given a value in order to communicate with the kadmind(8) server for the realm. .TP \fBauth_to_local\fP This tag allows you to set a general rule for mapping principal names to local user names. It will be used if there is not an explicit mapping for the principal name that is being translated. The possible values are: .INDENT 7.0 .TP \fBRULE:\fP\fIexp\fP The local name will be formulated from \fIexp\fP\&. .sp The format for \fIexp\fP is \fB[\fP\fIn\fP\fB:\fP\fIstring\fP\fB](\fP\fIregexp\fP\fB)s/\fP\fIpattern\fP\fB/\fP\fIreplacement\fP\fB/g\fP\&. The integer \fIn\fP indicates how many components the target principal should have. If this matches, then a string will be formed from \fIstring\fP, substituting the realm of the principal for \fB$0\fP and the \fIn\fP\(aqth component of the principal for \fB$n\fP (e.g., if the principal was \fBjohndoe/admin\fP then \fB[2:$2$1foo]\fP would result in the string \fBadminjohndoefoo\fP). If this string matches \fIregexp\fP, then the \fBs//[g]\fP substitution command will be run over the string. The optional \fBg\fP will cause the substitution to be global over the \fIstring\fP, instead of replacing only the first match in the \fIstring\fP\&. .TP \fBDEFAULT\fP The principal name will be used as the local user name. If the principal has more than one component or is not in the default realm, this rule is not applicable and the conversion will fail. .UNINDENT .sp For example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C [realms] ATHENA.MIT.EDU = { auth_to_local = RULE:[2:$1](johndoe)s/^.*$/guest/ auth_to_local = RULE:[2:$1;$2](^.*;admin$)s/;admin$// auth_to_local = RULE:[2:$2](^.*;root)s/^.*$/root/ auth_to_local = DEFAULT } .ft P .fi .UNINDENT .UNINDENT .sp would result in any principal without \fBroot\fP or \fBadmin\fP as the second component to be translated with the default rule. A principal with a second component of \fBadmin\fP will become its first component. \fBroot\fP will be used as the local name for any principal with a second component of \fBroot\fP\&. The exception to these two rules are any principals \fBjohndoe/*\fP, which will always get the local name \fBguest\fP\&. .TP \fBauth_to_local_names\fP This subsection allows you to set explicit mappings from principal names to local user names. The tag is the mapping name, and the value is the corresponding local user name. .TP \fBdefault_domain\fP This tag specifies the domain used to expand hostnames when translating Kerberos 4 service principals to Kerberos 5 principals (for example, when converting \fBrcmd.hostname\fP to \fBhost/hostname.domain\fP). .TP \fBdisable_encrypted_timestamp\fP If this flag is true, the client will not perform encrypted timestamp preauthentication if requested by the KDC. Setting this flag can help to prevent dictionary attacks by active attackers, if the realm\(aqs KDCs support SPAKE preauthentication or if initial authentication always uses another mechanism or always uses FAST. This flag persists across client referrals during initial authentication. This flag does not prevent the KDC from offering encrypted timestamp. New in release 1.17. .TP \fBhttp_anchors\fP When KDCs and kpasswd servers are accessed through HTTPS proxies, this tag can be used to specify the location of the CA certificate which should be trusted to issue the certificate for a proxy server. If left unspecified, the system\-wide default set of CA certificates is used. .sp The syntax for values is similar to that of values for the \fBpkinit_anchors\fP tag: .sp \fBFILE:\fP \fIfilename\fP .sp \fIfilename\fP is assumed to be the name of an OpenSSL\-style ca\-bundle file. .sp \fBDIR:\fP \fIdirname\fP .sp \fIdirname\fP is assumed to be an directory which contains CA certificates. All files in the directory will be examined; if they contain certificates (in PEM format), they will be used. .sp \fBENV:\fP \fIenvvar\fP .sp \fIenvvar\fP specifies the name of an environment variable which has been set to a value conforming to one of the previous values. For example, \fBENV:X509_PROXY_CA\fP, where environment variable \fBX509_PROXY_CA\fP has been set to \fBFILE:/tmp/my_proxy.pem\fP\&. .TP \fBkdc\fP The name or address of a host running a KDC for that realm. An optional port number, separated from the hostname by a colon, may be included. If the name or address contains colons (for example, if it is an IPv6 address), enclose it in square brackets to distinguish the colon from a port separator. For your computer to be able to communicate with the KDC for each realm, this tag must be given a value in each realm subsection in the configuration file, or there must be DNS SRV records specifying the KDCs. .TP \fBkpasswd_server\fP Points to the server where all the password changes are performed. If there is no such entry, DNS will be queried (unless forbidden by \fBdns_lookup_kdc\fP). Finally, port 464 on the \fBadmin_server\fP host will be tried. .TP \fBmaster_kdc\fP Identifies the master KDC(s). Currently, this tag is used in only one case: If an attempt to get credentials fails because of an invalid password, the client software will attempt to contact the master KDC, in case the user\(aqs password has just been changed, and the updated database has not been propagated to the replica servers yet. .TP \fBv4_instance_convert\fP This subsection allows the administrator to configure exceptions to the \fBdefault_domain\fP mapping rule. It contains V4 instances (the tag name) which should be translated to some specific hostname (the tag value) as the second component in a Kerberos V5 principal name. .TP \fBv4_realm\fP This relation is used by the krb524 library routines when converting a V5 principal name to a V4 principal name. It is used when the V4 realm name and the V5 realm name are not the same, but still share the same principal names and passwords. The tag value is the Kerberos V4 realm name. .UNINDENT .SS [domain_realm] .sp The [domain_realm] section provides a translation from a domain name or hostname to a Kerberos realm name. The tag name can be a host name or domain name, where domain names are indicated by a prefix of a period (\fB\&.\fP). The value of the relation is the Kerberos realm name for that particular host or domain. A host name relation implicitly provides the corresponding domain name relation, unless an explicit domain name relation is provided. The Kerberos realm may be identified either in the \fI\%realms\fP section or using DNS SRV records. Host names and domain names should be in lower case. For example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C [domain_realm] crash.mit.edu = TEST.ATHENA.MIT.EDU .dev.mit.edu = TEST.ATHENA.MIT.EDU mit.edu = ATHENA.MIT.EDU .ft P .fi .UNINDENT .UNINDENT .sp maps the host with the name \fBcrash.mit.edu\fP into the \fBTEST.ATHENA.MIT.EDU\fP realm. The second entry maps all hosts under the domain \fBdev.mit.edu\fP into the \fBTEST.ATHENA.MIT.EDU\fP realm, but not the host with the name \fBdev.mit.edu\fP\&. That host is matched by the third entry, which maps the host \fBmit.edu\fP and all hosts under the domain \fBmit.edu\fP that do not match a preceding rule into the realm \fBATHENA.MIT.EDU\fP\&. .sp If no translation entry applies to a hostname used for a service principal for a service ticket request, the library will try to get a referral to the appropriate realm from the client realm\(aqs KDC. If that does not succeed, the host\(aqs realm is considered to be the hostname\(aqs domain portion converted to uppercase, unless the \fBrealm_try_domains\fP setting in [libdefaults] causes a different parent domain to be used. .SS [capaths] .sp In order to perform direct (non\-hierarchical) cross\-realm authentication, configuration is needed to determine the authentication paths between realms. .sp A client will use this section to find the authentication path between its realm and the realm of the server. The server will use this section to verify the authentication path used by the client, by checking the transited field of the received ticket. .sp There is a tag for each participating client realm, and each tag has subtags for each of the server realms. The value of the subtags is an intermediate realm which may participate in the cross\-realm authentication. The subtags may be repeated if there is more then one intermediate realm. A value of "." means that the two realms share keys directly, and no intermediate realms should be allowed to participate. .sp Only those entries which will be needed on the client or the server need to be present. A client needs a tag for its local realm with subtags for all the realms of servers it will need to authenticate to. A server needs a tag for each realm of the clients it will serve, with a subtag of the server realm. .sp For example, \fBANL.GOV\fP, \fBPNL.GOV\fP, and \fBNERSC.GOV\fP all wish to use the \fBES.NET\fP realm as an intermediate realm. ANL has a sub realm of \fBTEST.ANL.GOV\fP which will authenticate with \fBNERSC.GOV\fP but not \fBPNL.GOV\fP\&. The [capaths] section for \fBANL.GOV\fP systems would look like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C [capaths] ANL.GOV = { TEST.ANL.GOV = . PNL.GOV = ES.NET NERSC.GOV = ES.NET ES.NET = . } TEST.ANL.GOV = { ANL.GOV = . } PNL.GOV = { ANL.GOV = ES.NET } NERSC.GOV = { ANL.GOV = ES.NET } ES.NET = { ANL.GOV = . } .ft P .fi .UNINDENT .UNINDENT .sp The [capaths] section of the configuration file used on \fBNERSC.GOV\fP systems would look like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C [capaths] NERSC.GOV = { ANL.GOV = ES.NET TEST.ANL.GOV = ES.NET TEST.ANL.GOV = ANL.GOV PNL.GOV = ES.NET ES.NET = . } ANL.GOV = { NERSC.GOV = ES.NET } PNL.GOV = { NERSC.GOV = ES.NET } ES.NET = { NERSC.GOV = . } TEST.ANL.GOV = { NERSC.GOV = ANL.GOV NERSC.GOV = ES.NET } .ft P .fi .UNINDENT .UNINDENT .sp When a subtag is used more than once within a tag, clients will use the order of values to determine the path. The order of values is not important to servers. .SS [appdefaults] .sp Each tag in the [appdefaults] section names a Kerberos V5 application or an option that is used by some Kerberos V5 application[s]. The value of the tag defines the default behaviors for that application. .sp For example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C [appdefaults] telnet = { ATHENA.MIT.EDU = { option1 = false } } telnet = { option1 = true option2 = true } ATHENA.MIT.EDU = { option2 = false } option2 = true .ft P .fi .UNINDENT .UNINDENT .sp The above four ways of specifying the value of an option are shown in order of decreasing precedence. In this example, if telnet is running in the realm EXAMPLE.COM, it should, by default, have option1 and option2 set to true. However, a telnet program in the realm \fBATHENA.MIT.EDU\fP should have \fBoption1\fP set to false and \fBoption2\fP set to true. Any other programs in ATHENA.MIT.EDU should have \fBoption2\fP set to false by default. Any programs running in other realms should have \fBoption2\fP set to true. .sp The list of specifiable options for each application may be found in that application\(aqs man pages. The application defaults specified here are overridden by those specified in the \fI\%realms\fP section. .SS [plugins] .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .IP \(bu 2 \fI\%pwqual\fP interface .IP \(bu 2 \fI\%kadm5_hook\fP interface .IP \(bu 2 \fI\%clpreauth\fP and \fI\%kdcpreauth\fP interfaces .UNINDENT .UNINDENT .UNINDENT .sp Tags in the [plugins] section can be used to register dynamic plugin modules and to turn modules on and off. Not every krb5 pluggable interface uses the [plugins] section; the ones that do are documented here. .sp New in release 1.9. .sp Each pluggable interface corresponds to a subsection of [plugins]. All subsections support the same tags: .INDENT 0.0 .TP \fBdisable\fP This tag may have multiple values. If there are values for this tag, then the named modules will be disabled for the pluggable interface. .TP \fBenable_only\fP This tag may have multiple values. If there are values for this tag, then only the named modules will be enabled for the pluggable interface. .TP \fBmodule\fP This tag may have multiple values. Each value is a string of the form \fBmodulename:pathname\fP, which causes the shared object located at \fIpathname\fP to be registered as a dynamic module named \fImodulename\fP for the pluggable interface. If \fIpathname\fP is not an absolute path, it will be treated as relative to the \fBplugin_base_dir\fP value from \fI\%[libdefaults]\fP\&. .UNINDENT .sp For pluggable interfaces where module order matters, modules registered with a \fBmodule\fP tag normally come first, in the order they are registered, followed by built\-in modules in the order they are documented below. If \fBenable_only\fP tags are used, then the order of those tags overrides the normal module order. .sp The following subsections are currently supported within the [plugins] section: .SS ccselect interface .sp The ccselect subsection controls modules for credential cache selection within a cache collection. In addition to any registered dynamic modules, the following built\-in modules exist (and may be disabled with the disable tag): .INDENT 0.0 .TP \fBk5identity\fP Uses a .k5identity file in the user\(aqs home directory to select a client principal .TP \fBrealm\fP Uses the service realm to guess an appropriate cache from the collection .TP \fBhostname\fP If the service principal is host\-based, uses the service hostname to guess an appropriate cache from the collection .UNINDENT .SS pwqual interface .sp The pwqual subsection controls modules for the password quality interface, which is used to reject weak passwords when passwords are changed. The following built\-in modules exist for this interface: .INDENT 0.0 .TP \fBdict\fP Checks against the realm dictionary file .TP \fBempty\fP Rejects empty passwords .TP \fBhesiod\fP Checks against user information stored in Hesiod (only if Kerberos was built with Hesiod support) .TP \fBprinc\fP Checks against components of the principal name .UNINDENT .SS kadm5_hook interface .sp The kadm5_hook interface provides plugins with information on principal creation, modification, password changes and deletion. This interface can be used to write a plugin to synchronize MIT Kerberos with another database such as Active Directory. No plugins are built in for this interface. .SS kadm5_auth interface .sp The kadm5_auth section (introduced in release 1.16) controls modules for the kadmin authorization interface, which determines whether a client principal is allowed to perform a kadmin operation. The following built\-in modules exist for this interface: .INDENT 0.0 .TP \fBacl\fP This module reads the kadm5.acl(5) file, and authorizes operations which are allowed according to the rules in the file. .TP \fBself\fP This module authorizes self\-service operations including password changes, creation of new random keys, fetching the client\(aqs principal record or string attributes, and fetching the policy record associated with the client principal. .UNINDENT .SS clpreauth and kdcpreauth interfaces .sp The clpreauth and kdcpreauth interfaces allow plugin modules to provide client and KDC preauthentication mechanisms. The following built\-in modules exist for these interfaces: .INDENT 0.0 .TP \fBpkinit\fP This module implements the PKINIT preauthentication mechanism. .TP \fBencrypted_challenge\fP This module implements the encrypted challenge FAST factor. .TP \fBencrypted_timestamp\fP This module implements the encrypted timestamp mechanism. .UNINDENT .SS hostrealm interface .sp The hostrealm section (introduced in release 1.12) controls modules for the host\-to\-realm interface, which affects the local mapping of hostnames to realm names and the choice of default realm. The following built\-in modules exist for this interface: .INDENT 0.0 .TP \fBprofile\fP This module consults the [domain_realm] section of the profile for authoritative host\-to\-realm mappings, and the \fBdefault_realm\fP variable for the default realm. .TP \fBdns\fP This module looks for DNS records for fallback host\-to\-realm mappings and the default realm. It only operates if the \fBdns_lookup_realm\fP variable is set to true. .TP \fBdomain\fP This module applies heuristics for fallback host\-to\-realm mappings. It implements the \fBrealm_try_domains\fP variable, and uses the uppercased parent domain of the hostname if that does not produce a result. .UNINDENT .SS localauth interface .sp The localauth section (introduced in release 1.12) controls modules for the local authorization interface, which affects the relationship between Kerberos principals and local system accounts. The following built\-in modules exist for this interface: .INDENT 0.0 .TP \fBdefault\fP This module implements the \fBDEFAULT\fP type for \fBauth_to_local\fP values. .TP \fBrule\fP This module implements the \fBRULE\fP type for \fBauth_to_local\fP values. .TP \fBnames\fP This module looks for an \fBauth_to_local_names\fP mapping for the principal name. .TP \fBauth_to_local\fP This module processes \fBauth_to_local\fP values in the default realm\(aqs section, and applies the default method if no \fBauth_to_local\fP values exist. .TP \fBk5login\fP This module authorizes a principal to a local account according to the account\(aqs \&.k5login(5) file. .TP \fBan2ln\fP This module authorizes a principal to a local account if the principal name maps to the local account name. .UNINDENT .SS certauth interface .sp The certauth section (introduced in release 1.16) controls modules for the certificate authorization interface, which determines whether a certificate is allowed to preauthenticate a user via PKINIT. The following built\-in modules exist for this interface: .INDENT 0.0 .TP \fBpkinit_san\fP This module authorizes the certificate if it contains a PKINIT Subject Alternative Name for the requested client principal, or a Microsoft UPN SAN matching the principal if \fBpkinit_allow_upn\fP is set to true for the realm. .TP \fBpkinit_eku\fP This module rejects the certificate if it does not contain an Extended Key Usage attribute consistent with the \fBpkinit_eku_checking\fP value for the realm. .TP \fBdbmatch\fP This module authorizes or rejects the certificate according to whether it matches the \fBpkinit_cert_match\fP string attribute on the client principal, if that attribute is present. .UNINDENT .SH PKINIT OPTIONS .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 The following are PKINIT\-specific options. These values may be specified in [libdefaults] as global defaults, or within a realm\-specific subsection of [libdefaults], or may be specified as realm\-specific values in the [realms] section. A realm\-specific value overrides, not adds to, a generic [libdefaults] specification. The search order is: .UNINDENT .UNINDENT .INDENT 0.0 .IP 1. 3 realm\-specific subsection of [libdefaults]: .INDENT 3.0 .INDENT 3.5 .sp .nf .ft C [libdefaults] EXAMPLE.COM = { pkinit_anchors = FILE:/usr/local/example.com.crt } .ft P .fi .UNINDENT .UNINDENT .IP 2. 3 realm\-specific value in the [realms] section: .INDENT 3.0 .INDENT 3.5 .sp .nf .ft C [realms] OTHERREALM.ORG = { pkinit_anchors = FILE:/usr/local/otherrealm.org.crt } .ft P .fi .UNINDENT .UNINDENT .IP 3. 3 generic value in the [libdefaults] section: .INDENT 3.0 .INDENT 3.5 .sp .nf .ft C [libdefaults] pkinit_anchors = DIR:/usr/local/generic_trusted_cas/ .ft P .fi .UNINDENT .UNINDENT .UNINDENT .SS Specifying PKINIT identity information .sp The syntax for specifying Public Key identity, trust, and revocation information for PKINIT is as follows: .INDENT 0.0 .TP \fBFILE:\fP\fIfilename\fP[\fB,\fP\fIkeyfilename\fP] This option has context\-specific behavior. .sp In \fBpkinit_identity\fP or \fBpkinit_identities\fP, \fIfilename\fP specifies the name of a PEM\-format file containing the user\(aqs certificate. If \fIkeyfilename\fP is not specified, the user\(aqs private key is expected to be in \fIfilename\fP as well. Otherwise, \fIkeyfilename\fP is the name of the file containing the private key. .sp In \fBpkinit_anchors\fP or \fBpkinit_pool\fP, \fIfilename\fP is assumed to be the name of an OpenSSL\-style ca\-bundle file. .TP \fBDIR:\fP\fIdirname\fP This option has context\-specific behavior. .sp In \fBpkinit_identity\fP or \fBpkinit_identities\fP, \fIdirname\fP specifies a directory with files named \fB*.crt\fP and \fB*.key\fP where the first part of the file name is the same for matching pairs of certificate and private key files. When a file with a name ending with \fB\&.crt\fP is found, a matching file ending with \fB\&.key\fP is assumed to contain the private key. If no such file is found, then the certificate in the \fB\&.crt\fP is not used. .sp In \fBpkinit_anchors\fP or \fBpkinit_pool\fP, \fIdirname\fP is assumed to be an OpenSSL\-style hashed CA directory where each CA cert is stored in a file named \fBhash\-of\-ca\-cert.#\fP\&. This infrastructure is encouraged, but all files in the directory will be examined and if they contain certificates (in PEM format), they will be used. .sp In \fBpkinit_revoke\fP, \fIdirname\fP is assumed to be an OpenSSL\-style hashed CA directory where each revocation list is stored in a file named \fBhash\-of\-ca\-cert.r#\fP\&. This infrastructure is encouraged, but all files in the directory will be examined and if they contain a revocation list (in PEM format), they will be used. .TP \fBPKCS12:\fP\fIfilename\fP \fIfilename\fP is the name of a PKCS #12 format file, containing the user\(aqs certificate and private key. .TP \fBPKCS11:\fP[\fBmodule_name=\fP]\fImodname\fP[\fB:slotid=\fP\fIslot\-id\fP][\fB:token=\fP\fItoken\-label\fP][\fB:certid=\fP\fIcert\-id\fP][\fB:certlabel=\fP\fIcert\-label\fP] All keyword/values are optional. \fImodname\fP specifies the location of a library implementing PKCS #11. If a value is encountered with no keyword, it is assumed to be the \fImodname\fP\&. If no module\-name is specified, the default is \fBopensc\-pkcs11.so\fP\&. \fBslotid=\fP and/or \fBtoken=\fP may be specified to force the use of a particular smard card reader or token if there is more than one available. \fBcertid=\fP and/or \fBcertlabel=\fP may be specified to force the selection of a particular certificate on the device. See the \fBpkinit_cert_match\fP configuration option for more ways to select a particular certificate to use for PKINIT. .TP \fBENV:\fP\fIenvvar\fP \fIenvvar\fP specifies the name of an environment variable which has been set to a value conforming to one of the previous values. For example, \fBENV:X509_PROXY\fP, where environment variable \fBX509_PROXY\fP has been set to \fBFILE:/tmp/my_proxy.pem\fP\&. .UNINDENT .SS PKINIT krb5.conf options .INDENT 0.0 .TP \fBpkinit_anchors\fP Specifies the location of trusted anchor (root) certificates which the client trusts to sign KDC certificates. This option may be specified multiple times. These values from the config file are not used if the user specifies X509_anchors on the command line. .TP \fBpkinit_cert_match\fP Specifies matching rules that the client certificate must match before it is used to attempt PKINIT authentication. If a user has multiple certificates available (on a smart card, or via other media), there must be exactly one certificate chosen before attempting PKINIT authentication. This option may be specified multiple times. All the available certificates are checked against each rule in order until there is a match of exactly one certificate. .sp The Subject and Issuer comparison strings are the \fI\%RFC 2253\fP string representations from the certificate Subject DN and Issuer DN values. .sp The syntax of the matching rules is: .INDENT 7.0 .INDENT 3.5 [\fIrelation\-operator\fP]\fIcomponent\-rule\fP ... .UNINDENT .UNINDENT .sp where: .INDENT 7.0 .TP .B \fIrelation\-operator\fP can be either \fB&&\fP, meaning all component rules must match, or \fB||\fP, meaning only one component rule must match. The default is \fB&&\fP\&. .TP .B \fIcomponent\-rule\fP can be one of the following. Note that there is no punctuation or whitespace between component rules. .INDENT 7.0 .INDENT 3.5 .nf \fB\fP\fIregular\-expression\fP \fB\fP\fIregular\-expression\fP \fB\fP\fIregular\-expression\fP \fB\fP\fIextended\-key\-usage\-list\fP \fB\fP\fIkey\-usage\-list\fP .fi .sp .UNINDENT .UNINDENT .sp \fIextended\-key\-usage\-list\fP is a comma\-separated list of required Extended Key Usage values. All values in the list must be present in the certificate. Extended Key Usage values can be: .INDENT 7.0 .IP \(bu 2 pkinit .IP \(bu 2 msScLogin .IP \(bu 2 clientAuth .IP \(bu 2 emailProtection .UNINDENT .sp \fIkey\-usage\-list\fP is a comma\-separated list of required Key Usage values. All values in the list must be present in the certificate. Key Usage values can be: .INDENT 7.0 .IP \(bu 2 digitalSignature .IP \(bu 2 keyEncipherment .UNINDENT .UNINDENT .sp Examples: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C pkinit_cert_match = ||.*DoE.*.*@EXAMPLE.COM pkinit_cert_match = &&msScLogin,clientAuth.*DoE.* pkinit_cert_match = msScLogin,clientAuthdigitalSignature .ft P .fi .UNINDENT .UNINDENT .TP \fBpkinit_eku_checking\fP This option specifies what Extended Key Usage value the KDC certificate presented to the client must contain. (Note that if the KDC certificate has the pkinit SubjectAlternativeName encoded as the Kerberos TGS name, EKU checking is not necessary since the issuing CA has certified this as a KDC certificate.) The values recognized in the krb5.conf file are: .INDENT 7.0 .TP \fBkpKDC\fP This is the default value and specifies that the KDC must have the id\-pkinit\-KPKdc EKU as defined in \fI\%RFC 4556\fP\&. .TP \fBkpServerAuth\fP If \fBkpServerAuth\fP is specified, a KDC certificate with the id\-kp\-serverAuth EKU will be accepted. This key usage value is used in most commercially issued server certificates. .TP \fBnone\fP If \fBnone\fP is specified, then the KDC certificate will not be checked to verify it has an acceptable EKU. The use of this option is not recommended. .UNINDENT .TP \fBpkinit_dh_min_bits\fP Specifies the size of the Diffie\-Hellman key the client will attempt to use. The acceptable values are 1024, 2048, and 4096. The default is 2048. .TP \fBpkinit_identities\fP Specifies the location(s) to be used to find the user\(aqs X.509 identity information. If this option is specified multiple times, the first valid value is used; this can be used to specify an environment variable (with \fBENV:\fP\fIenvvar\fP) followed by a default value. Note that these values are not used if the user specifies \fBX509_user_identity\fP on the command line. .TP \fBpkinit_kdc_hostname\fP The presense of this option indicates that the client is willing to accept a KDC certificate with a dNSName SAN (Subject Alternative Name) rather than requiring the id\-pkinit\-san as defined in \fI\%RFC 4556\fP\&. This option may be specified multiple times. Its value should contain the acceptable hostname for the KDC (as contained in its certificate). .TP \fBpkinit_pool\fP Specifies the location of intermediate certificates which may be used by the client to complete the trust chain between a KDC certificate and a trusted anchor. This option may be specified multiple times. .TP \fBpkinit_require_crl_checking\fP The default certificate verification process will always check the available revocation information to see if a certificate has been revoked. If a match is found for the certificate in a CRL, verification fails. If the certificate being verified is not listed in a CRL, or there is no CRL present for its issuing CA, and \fBpkinit_require_crl_checking\fP is false, then verification succeeds. .sp However, if \fBpkinit_require_crl_checking\fP is true and there is no CRL information available for the issuing CA, then verification fails. .sp \fBpkinit_require_crl_checking\fP should be set to true if the policy is such that up\-to\-date CRLs must be present for every CA. .TP \fBpkinit_revoke\fP Specifies the location of Certificate Revocation List (CRL) information to be used by the client when verifying the validity of the KDC certificate presented. This option may be specified multiple times. .UNINDENT .SH PARAMETER EXPANSION .sp Starting with release 1.11, several variables, such as \fBdefault_keytab_name\fP, allow parameters to be expanded. Valid parameters are: .INDENT 0.0 .INDENT 3.5 .TS center; |l|l|. _ T{ %{TEMP} T} T{ Temporary directory T} _ T{ %{uid} T} T{ Unix real UID or Windows SID T} _ T{ %{euid} T} T{ Unix effective user ID or Windows SID T} _ T{ %{USERID} T} T{ Same as %{uid} T} _ T{ %{null} T} T{ Empty string T} _ T{ %{LIBDIR} T} T{ Installation library directory T} _ T{ %{BINDIR} T} T{ Installation binary directory T} _ T{ %{SBINDIR} T} T{ Installation admin binary directory T} _ T{ %{username} T} T{ (Unix) Username of effective user ID T} _ T{ %{APPDATA} T} T{ (Windows) Roaming application data for current user T} _ T{ %{COMMON_APPDATA} T} T{ (Windows) Application data for all users T} _ T{ %{LOCAL_APPDATA} T} T{ (Windows) Local application data for current user T} _ T{ %{SYSTEM} T} T{ (Windows) Windows system folder T} _ T{ %{WINDOWS} T} T{ (Windows) Windows folder T} _ T{ %{USERCONFIG} T} T{ (Windows) Per\-user MIT krb5 config file directory T} _ T{ %{COMMONCONFIG} T} T{ (Windows) Common MIT krb5 config file directory T} _ .TE .UNINDENT .UNINDENT .SH SAMPLE KRB5.CONF FILE .sp Here is an example of a generic krb5.conf file: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C [libdefaults] default_realm = ATHENA.MIT.EDU dns_lookup_kdc = true dns_lookup_realm = false [realms] ATHENA.MIT.EDU = { kdc = kerberos.mit.edu kdc = kerberos\-1.mit.edu kdc = kerberos\-2.mit.edu admin_server = kerberos.mit.edu master_kdc = kerberos.mit.edu } EXAMPLE.COM = { kdc = kerberos.example.com kdc = kerberos\-1.example.com admin_server = kerberos.example.com } [domain_realm] mit.edu = ATHENA.MIT.EDU [capaths] ATHENA.MIT.EDU = { EXAMPLE.COM = . } EXAMPLE.COM = { ATHENA.MIT.EDU = . } .ft P .fi .UNINDENT .UNINDENT .SH FILES .sp \fB/etc/krb5.conf\fP .SH SEE ALSO .sp syslog(3) .SH AUTHOR MIT .SH COPYRIGHT 1985-2019, MIT .\" Generated by docutils manpage writer. . PK0]3d  man/man5/k5login.5nu[.\" Man page generated from reStructuredText. . .TH "K5LOGIN" "5" " " "1.17" "MIT Kerberos" .SH NAME k5login \- Kerberos V5 acl file for host access . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH DESCRIPTION .sp The .k5login file, which resides in a user\(aqs home directory, contains a list of the Kerberos principals. Anyone with valid tickets for a principal in the file is allowed host access with the UID of the user in whose home directory the file resides. One common use is to place a .k5login file in root\(aqs home directory, thereby granting system administrators remote root access to the host via Kerberos. .SH EXAMPLES .sp Suppose the user \fBalice\fP had a .k5login file in her home directory containing just the following line: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C bob@FOOBAR.ORG .ft P .fi .UNINDENT .UNINDENT .sp This would allow \fBbob\fP to use Kerberos network applications, such as ssh(1), to access \fBalice\fP\(aqs account, using \fBbob\fP\(aqs Kerberos tickets. In a default configuration (with \fBk5login_authoritative\fP set to true in krb5.conf(5)), this .k5login file would not let \fBalice\fP use those network applications to access her account, since she is not listed! With no .k5login file, or with \fBk5login_authoritative\fP set to false, a default rule would permit the principal \fBalice\fP in the machine\(aqs default realm to access the \fBalice\fP account. .sp Let us further suppose that \fBalice\fP is a system administrator. Alice and the other system administrators would have their principals in root\(aqs .k5login file on each host: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C alice@BLEEP.COM joeadmin/root@BLEEP.COM .ft P .fi .UNINDENT .UNINDENT .sp This would allow either system administrator to log in to these hosts using their Kerberos tickets instead of having to type the root password. Note that because \fBbob\fP retains the Kerberos tickets for his own principal, \fBbob@FOOBAR.ORG\fP, he would not have any of the privileges that require \fBalice\fP\(aqs tickets, such as root access to any of the site\(aqs hosts, or the ability to change \fBalice\fP\(aqs password. .SH SEE ALSO .sp kerberos(1) .SH AUTHOR MIT .SH COPYRIGHT 1985-2019, MIT .\" Generated by docutils manpage writer. . PK0]ք man/man5/k5identity.5nu[.\" Man page generated from reStructuredText. . .TH "K5IDENTITY" "5" " " "1.17" "MIT Kerberos" .SH NAME k5identity \- Kerberos V5 client principal selection rules . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH DESCRIPTION .sp The .k5identity file, which resides in a user\(aqs home directory, contains a list of rules for selecting a client principals based on the server being accessed. These rules are used to choose a credential cache within the cache collection when possible. .sp Blank lines and lines beginning with \fB#\fP are ignored. Each line has the form: .INDENT 0.0 .INDENT 3.5 \fIprincipal\fP \fIfield\fP=\fIvalue\fP ... .UNINDENT .UNINDENT .sp If the server principal meets all of the field constraints, then principal is chosen as the client principal. The following fields are recognized: .INDENT 0.0 .TP \fBrealm\fP If the realm of the server principal is known, it is matched against \fIvalue\fP, which may be a pattern using shell wildcards. For host\-based server principals, the realm will generally only be known if there is a domain_realm section in krb5.conf(5) with a mapping for the hostname. .TP \fBservice\fP If the server principal is a host\-based principal, its service component is matched against \fIvalue\fP, which may be a pattern using shell wildcards. .TP \fBhost\fP If the server principal is a host\-based principal, its hostname component is converted to lower case and matched against \fIvalue\fP, which may be a pattern using shell wildcards. .sp If the server principal matches the constraints of multiple lines in the .k5identity file, the principal from the first matching line is used. If no line matches, credentials will be selected some other way, such as the realm heuristic or the current primary cache. .UNINDENT .SH EXAMPLE .sp The following example .k5identity file selects the client principal \fBalice@KRBTEST.COM\fP if the server principal is within that realm, the principal \fBalice/root@EXAMPLE.COM\fP if the server host is within a servers subdomain, and the principal \fBalice/mail@EXAMPLE.COM\fP when accessing the IMAP service on \fBmail.example.com\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C alice@KRBTEST.COM realm=KRBTEST.COM alice/root@EXAMPLE.COM host=*.servers.example.com alice/mail@EXAMPLE.COM host=mail.example.com service=imap .ft P .fi .UNINDENT .UNINDENT .SH SEE ALSO .sp kerberos(1), krb5.conf(5) .SH AUTHOR MIT .SH COPYRIGHT 1985-2019, MIT .\" Generated by docutils manpage writer. . PK0]tSXman/man5/.k5login.5nu[.so man5/k5login.5 PK0]Hman/man5/.k5identity.5nu[.so man5/k5identity.5 PK0]/fman/man7/kerberos.7nu[.\" Man page generated from reStructuredText. . .TH "KERBEROS" "7" " " "1.17" "MIT Kerberos" .SH NAME kerberos \- Overview of using Kerberos . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH DESCRIPTION .sp The Kerberos system authenticates individual users in a network environment. After authenticating yourself to Kerberos, you can use Kerberos\-enabled programs without having to present passwords or certificates to those programs. .sp If you receive the following response from kinit(1): .sp kinit: Client not found in Kerberos database while getting initial credentials .sp you haven\(aqt been registered as a Kerberos user. See your system administrator. .sp A Kerberos name usually contains three parts. The first is the \fBprimary\fP, which is usually a user\(aqs or service\(aqs name. The second is the \fBinstance\fP, which in the case of a user is usually null. Some users may have privileged instances, however, such as \fBroot\fP or \fBadmin\fP\&. In the case of a service, the instance is the fully qualified name of the machine on which it runs; i.e. there can be an ssh service running on the machine ABC (\fI\%ssh/ABC@REALM\fP), which is different from the ssh service running on the machine XYZ (\fI\%ssh/XYZ@REALM\fP). The third part of a Kerberos name is the \fBrealm\fP\&. The realm corresponds to the Kerberos service providing authentication for the principal. Realms are conventionally all\-uppercase, and often match the end of hostnames in the realm (for instance, host01.example.com might be in realm EXAMPLE.COM). .sp When writing a Kerberos name, the principal name is separated from the instance (if not null) by a slash, and the realm (if not the local realm) follows, preceded by an "@" sign. The following are examples of valid Kerberos names: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C david jennifer/admin joeuser@BLEEP.COM cbrown/root@FUBAR.ORG .ft P .fi .UNINDENT .UNINDENT .sp When you authenticate yourself with Kerberos you get an initial Kerberos \fBticket\fP\&. (A Kerberos ticket is an encrypted protocol message that provides authentication.) Kerberos uses this ticket for network utilities such as ssh. The ticket transactions are done transparently, so you don\(aqt have to worry about their management. .sp Note, however, that tickets expire. Administrators may configure more privileged tickets, such as those with service or instance of \fBroot\fP or \fBadmin\fP, to expire in a few minutes, while tickets that carry more ordinary privileges may be good for several hours or a day. If your login session extends beyond the time limit, you will have to re\-authenticate yourself to Kerberos to get new tickets using the kinit(1) command. .sp Some tickets are \fBrenewable\fP beyond their initial lifetime. This means that \fBkinit \-R\fP can extend their lifetime without requiring you to re\-authenticate. .sp If you wish to delete your local tickets, use the kdestroy(1) command. .sp Kerberos tickets can be forwarded. In order to forward tickets, you must request \fBforwardable\fP tickets when you kinit. Once you have forwardable tickets, most Kerberos programs have a command line option to forward them to the remote host. This can be useful for, e.g., running kinit on your local machine and then sshing into another to do work. Note that this should not be done on untrusted machines since they will then have your tickets. .SH ENVIRONMENT VARIABLES .sp Several environment variables affect the operation of Kerberos\-enabled programs. These include: .INDENT 0.0 .TP \fBKRB5CCNAME\fP Default name for the credentials cache file, in the form \fITYPE\fP:\fIresidual\fP\&. The type of the default cache may determine the availability of a cache collection. \fBFILE\fP is not a collection type; \fBKEYRING\fP, \fBDIR\fP, and \fBKCM\fP are. .sp If not set, the value of \fBdefault_ccache_name\fP from configuration files (see \fBKRB5_CONFIG\fP) will be used. If that is also not set, the default \fItype\fP is \fBFILE\fP, and the \fIresidual\fP is the path /tmp/krb5cc_*uid*, where \fIuid\fP is the decimal user ID of the user. .TP \fBKRB5_KTNAME\fP Specifies the location of the default keytab file, in the form \fITYPE\fP:\fIresidual\fP\&. If no \fItype\fP is present, the \fBFILE\fP type is assumed and \fIresidual\fP is the pathname of the keytab file. If unset, \fBFILE:/etc/krb5.keytab\fP will be used. .TP \fBKRB5_CONFIG\fP Specifies the location of the Kerberos configuration file. The default is \fB/etc\fP\fB/krb5.conf\fP\&. Multiple filenames can be specified, separated by a colon; all files which are present will be read. .TP \fBKRB5_KDC_PROFILE\fP Specifies the location of the KDC configuration file, which contains additional configuration directives for the Key Distribution Center daemon and associated programs. The default is \fB/opt/alt/krb5/usr/var\fP\fB/krb5kdc\fP\fB/kdc.conf\fP\&. .TP \fBKRB5RCACHETYPE\fP Specifies the default type of replay cache to use for servers. Valid types include \fBdfl\fP for the normal file type and \fBnone\fP for no replay cache. The default is \fBdfl\fP\&. .TP \fBKRB5RCACHEDIR\fP Specifies the default directory for replay caches used by servers. The default is the value of the \fBTMPDIR\fP environment variable, or \fB/var/tmp\fP if \fBTMPDIR\fP is not set. .TP \fBKRB5_TRACE\fP Specifies a filename to write trace log output to. Trace logs can help illuminate decisions made internally by the Kerberos libraries. For example, \fBenv KRB5_TRACE=/dev/stderr kinit\fP would send tracing information for kinit(1) to \fB/dev/stderr\fP\&. The default is not to write trace log output anywhere. .TP \fBKRB5_CLIENT_KTNAME\fP Default client keytab file name. If unset, \fBFILE:/opt/alt/krb5/usr/var/krb5/user/%{euid}/client.keytab\fP will be used). .TP \fBKPROP_PORT\fP kprop(8) port to use. Defaults to 754. .UNINDENT .sp Most environment variables are disabled for certain programs, such as login system programs and setuid programs, which are designed to be secure when run within an untrusted process environment. .SH SEE ALSO .sp kdestroy(1), kinit(1), klist(1), kswitch(1), kpasswd(1), ksu(1), krb5.conf(5), kdc.conf(5), kadmin(1), kadmind(8), kdb5_util(8), krb5kdc(8) .SH BUGS .SH AUTHORS .nf Steve Miller, MIT Project Athena/Digital Equipment Corporation Clifford Neuman, MIT Project Athena Greg Hudson, MIT Kerberos Consortium Robbie Harwood, Red Hat, Inc. .fi .sp .SH HISTORY .sp The MIT Kerberos 5 implementation was developed at MIT, with contributions from many outside parties. It is currently maintained by the MIT Kerberos Consortium. .SH RESTRICTIONS .sp Copyright 1985, 1986, 1989\-1996, 2002, 2011, 2018 Masachusetts Institute of Technology .SH AUTHOR MIT .SH COPYRIGHT 1985-2019, MIT .\" Generated by docutils manpage writer. . PK0]<ފdoc/nghttp2/README.rstnu[nghttp2 - HTTP/2 C Library ========================== This is an implementation of the Hypertext Transfer Protocol version 2 in C. The framing layer of HTTP/2 is implemented as a reusable C library. On top of that, we have implemented an HTTP/2 client, server and proxy. We have also developed load test and benchmarking tools for HTTP/2. An HPACK encoder and decoder are available as a public API. Development Status ------------------ nghttp2 was originally developed based on `RFC 7540 `_ HTTP/2 and `RFC 7541 `_ HPACK - Header Compression for HTTP/2. Now we are updating our code to implement `RFC 9113 `_. The nghttp2 code base was forked from the spdylay (https://github.com/tatsuhiro-t/spdylay) project. Public Test Server ------------------ The following endpoints are available to try out our nghttp2 implementation. * https://nghttp2.org/ (TLS + ALPN and HTTP/3) This endpoint supports ``h2`` and ``http/1.1`` via ALPN and requires TLSv1.2 for HTTP/2 connection. It also supports HTTP/3. * http://nghttp2.org/ (HTTP Upgrade and HTTP/2 Direct) ``h2c`` and ``http/1.1``. Requirements ------------ The following package is required to build the libnghttp2 library: * pkg-config >= 0.20 To build the documentation, you need to install: * sphinx (http://sphinx-doc.org/) If you need libnghttp2 (C library) only, then the above packages are all you need. Use ``--enable-lib-only`` to ensure that only libnghttp2 is built. This avoids potential build error related to building bundled applications. To build and run the application programs (``nghttp``, ``nghttpd``, ``nghttpx`` and ``h2load``) in the ``src`` directory, the following packages are required: * OpenSSL >= 1.1.1; or wolfSSL >= 5.7.0; or LibreSSL >= 3.8.1; or aws-lc >= 1.19.0; or BoringSSL * libev >= 4.11 * zlib >= 1.2.3 * libc-ares >= 1.7.5 To enable ``-a`` option (getting linked assets from the downloaded resource) in ``nghttp``, the following package is required: * libxml2 >= 2.6.26 To enable systemd support in nghttpx, the following package is required: * libsystemd-dev >= 209 The HPACK tools require the following package: * jansson >= 2.5 To build sources under the examples directory, libevent is required: * libevent-openssl >= 2.0.8 To mitigate heap fragmentation in long running server programs (``nghttpd`` and ``nghttpx``), jemalloc is recommended: * jemalloc .. note:: Alpine Linux currently does not support malloc replacement due to musl limitations. See details in issue `#762 `_. For BoringSSL or aws-lc build, to enable :rfc:`8879` TLS Certificate Compression in applications, the following library is required: * libbrotli-dev >= 1.0.9 To enable mruby support for nghttpx, `mruby `_ is required. We need to build mruby with C++ ABI explicitly turned on, and probably need other mrgems, mruby is managed by git submodule under third-party/mruby directory. Currently, mruby support for nghttpx is disabled by default. To enable mruby support, use ``--with-mruby`` configure option. Note that at the time of this writing, libmruby-dev and mruby packages in Debian/Ubuntu are not usable for nghttp2, since they do not enable C++ ABI. To build mruby, the following packages are required: * ruby * bison nghttpx supports `neverbleed `_, privilege separation engine for OpenSSL. In short, it minimizes the risk of private key leakage when serious bug like Heartbleed is exploited. The neverbleed is disabled by default. To enable it, use ``--with-neverbleed`` configure option. To enable the experimental HTTP/3 support for h2load and nghttpx, the following libraries are required: * `quictls `_; or wolfSSL; or LibreSSL (does not support 0RTT); or aws-lc; or `BoringSSL `_ (commit 3c6315e00ab02d7bc9b8922aff1f85d8f81ee130); or OpenSSL >= 3.5.0 * `ngtcp2 `_ >= 1.23.0 * `nghttp3 `_ >= 1.17.0 Use ``--enable-http3`` configure option to enable HTTP/3 feature for h2load and nghttpx. In order to build optional eBPF program to direct an incoming QUIC UDP datagram to a correct socket for nghttpx, the following libraries are required: * libbpf-dev >= 0.7.0 Use ``--with-libbpf`` configure option to build eBPF program. libelf-dev is needed to build libbpf. For Ubuntu 20.04, you can build libbpf from `the source code `_. nghttpx requires eBPF program for reloading its configuration and hot swapping its executable. Compiling libnghttp2 C source code requires a C99 compiler. gcc 4.8 is known to be adequate. In order to compile the C++ source code, C++23 compliant compiler is required. At least g++ >= 14 and clang++ >= 19 are known to work. .. note:: To enable mruby support in nghttpx, and use ``--with-mruby`` configure option. .. note:: Mac OS X users may need the ``--disable-threads`` configure option to disable multi-threading in nghttpd, nghttpx and h2load to prevent them from crashing. A patch is welcome to make multi threading work on Mac OS X platform. .. note:: To compile the associated applications (nghttp, nghttpd, nghttpx and h2load), you must use the ``--enable-app`` configure option and ensure that the specified requirements above are met. Normally, configure script checks required dependencies to build these applications, and enable ``--enable-app`` automatically, so you don't have to use it explicitly. But if you found that applications were not built, then using ``--enable-app`` may find that cause, such as the missing dependency. .. note:: In order to detect third party libraries, pkg-config is used (however we don't use pkg-config for some libraries (e.g., libev)). By default, pkg-config searches ``*.pc`` file in the standard locations (e.g., /usr/lib/pkgconfig). If it is necessary to use ``*.pc`` file in the custom location, specify paths to ``PKG_CONFIG_PATH`` environment variable, and pass it to configure script, like so: .. code-block:: text $ ./configure PKG_CONFIG_PATH=/path/to/pkgconfig For pkg-config managed libraries, ``*_CFLAG`` and ``*_LIBS`` environment variables are defined (e.g., ``OPENSSL_CFLAGS``, ``OPENSSL_LIBS``). Specifying non-empty string to these variables completely overrides pkg-config. In other words, if they are specified, pkg-config is not used for detection, and user is responsible to specify the correct values to these variables. For complete list of these variables, run ``./configure -h``. If you are using Ubuntu 22.04 LTS, run the following to install the required packages: .. code-block:: text sudo apt-get install g++ clang make binutils autoconf automake \ autotools-dev libtool pkg-config \ zlib1g-dev libssl-dev libxml2-dev libev-dev \ libevent-dev libjansson-dev \ libc-ares-dev libjemalloc-dev libsystemd-dev \ ruby-dev bison libelf-dev Building nghttp2 from release tar archive ----------------------------------------- The nghttp2 project regularly releases tar archives which includes nghttp2 source code, and generated build files. They can be downloaded from `Releases `_ page. Building nghttp2 from git requires autotools development packages. Building from tar archives does not require them, and thus it is much easier. The usual build step is as follows: .. code-block:: text $ tar xf nghttp2-X.Y.Z.tar.bz2 $ cd nghttp2-X.Y.Z $ ./configure $ make Building from git ----------------- Building from git is easy, but please be sure that at least autoconf 2.68 is used: .. code-block:: text $ git submodule update --init $ autoreconf -i $ automake $ autoconf $ ./configure $ make Notes for building on Windows (MSVC) ------------------------------------ The easiest way to build native Windows nghttp2 dll is use `cmake `_. The free version of `Visual C++ Build Tools `_ works fine. 1. Install cmake for windows 2. Open "Visual C++ ... Native Build Tool Command Prompt", and inside nghttp2 directly, run ``cmake``. 3. Then run ``cmake --build`` to build library. 4. nghttp2.dll, nghttp2.lib, nghttp2.exp are placed under lib directory. Note that the above steps most likely produce nghttp2 library only. No bundled applications are compiled. Notes for building on Windows (Mingw/Cygwin) -------------------------------------------- Under Mingw environment, you can only compile the library, it's ``libnghttp2-X.dll`` and ``libnghttp2.a``. If you want to compile the applications(``h2load``, ``nghttp``, ``nghttpx``, ``nghttpd``), you need to use the Cygwin environment. Under Cygwin environment, to compile the applications you need to compile and install the libev first. Secondly, you need to undefine the macro ``__STRICT_ANSI__``, if you not, the functions ``fdopen``, ``fileno`` and ``strptime`` will not available. the sample command like this: .. code-block:: text $ export CFLAGS="-U__STRICT_ANSI__ -I$libev_PREFIX/include -L$libev_PREFIX/lib" $ export CXXFLAGS=$CFLAGS $ ./configure $ make If you want to compile the applications under ``examples/``, you need to remove or rename the ``event.h`` from libev's installation, because it conflicts with libevent's installation. Notes for installation on Linux systems -------------------------------------------- After installing nghttp2 tool suite with ``make install`` one might experience a similar error: .. code-block:: text nghttpx: error while loading shared libraries: libnghttp2.so.14: cannot open shared object file: No such file or directory This means that the tool is unable to locate the ``libnghttp2.so`` shared library. To update the shared library cache run ``sudo ldconfig``. Building the documentation -------------------------- .. note:: Documentation is still incomplete. To build the documentation, run: .. code-block:: text $ make html The documents will be generated under ``doc/manual/html/``. The generated documents will not be installed with ``make install``. The online documentation is available at https://nghttp2.org/documentation/ Build HTTP/3 enabled h2load and nghttpx --------------------------------------- To build h2load and nghttpx with HTTP/3 feature enabled, run the configure script with ``--enable-http3``. For nghttpx to reload configurations and swapping its executable while gracefully terminating old worker processes, eBPF is required. Run the configure script with ``--enable-http3 --with-libbpf`` to build eBPF program. The QUIC keying material must be set with ``--frontend-quic-secret-file`` in order to keep the existing connections alive during reload. The detailed steps to build HTTP/3 enabled h2load and nghttpx follow. Build aws-lc: .. code-block:: text $ git clone --depth 1 -b v5.4.0 https://github.com/aws/aws-lc $ cd aws-lc $ cmake -B build -DDISABLE_GO=ON --install-prefix=$PWD/opt $ make -j$(nproc) -C build $ cmake --install build $ cd .. Build nghttp3: .. code-block:: text $ git clone --depth 1 -b v1.18.0 https://github.com/ngtcp2/nghttp3 $ cd nghttp3 $ git submodule update --init --depth 1 $ autoreconf -i $ ./configure --prefix=$PWD/build --enable-lib-only $ make -j$(nproc) $ make install $ cd .. Build ngtcp2: .. code-block:: text $ git clone --depth 1 -b v1.25.0 https://github.com/ngtcp2/ngtcp2 $ cd ngtcp2 $ git submodule update --init --depth 1 $ autoreconf -i $ ./configure --prefix=$PWD/build --enable-lib-only --with-boringssl \ BORINGSSL_CFLAGS="-I$PWD/../aws-lc/opt/include" \ BORINGSSL_LIBS="-L$PWD/../aws-lc/opt/lib -lssl -lcrypto" $ make -j$(nproc) $ make install $ cd .. If your Linux distribution does not have libbpf-dev >= 0.7.0, build from source: .. code-block:: text $ git clone --depth 1 -b v1.7.0 https://github.com/libbpf/libbpf $ cd libbpf $ PREFIX=$PWD/build make -C src install $ cd .. Build nghttp2: .. code-block:: text $ git clone https://github.com/nghttp2/nghttp2 $ cd nghttp2 $ git submodule update --init $ autoreconf -i $ ./configure --with-mruby --enable-http3 --with-libbpf \ CC=clang-19 CXX=clang++-19 \ PKG_CONFIG_PATH="$PWD/../aws-lc/opt/lib/pkgconfig:$PWD/../nghttp3/build/lib/pkgconfig:$PWD/../ngtcp2/build/lib/pkgconfig:$PWD/../libbpf/build/lib64/pkgconfig" \ LDFLAGS="$LDFLAGS -Wl,-rpath,$PWD/../aws-lc/opt/lib -Wl,-rpath,$PWD/../libbpf/build/lib64" $ make -j$(nproc) The eBPF program ``reuseport_kern.o`` should be found under bpf directory. Pass ``--quic-bpf-program-file=bpf/reuseport_kern.o`` option to nghttpx to load it. See also `HTTP/3 section in nghttpx - HTTP/2 proxy - HOW-TO `_. Unit tests ---------- Unit tests are done by simply running ``make check``. Integration tests ----------------- We have the integration tests for the nghttpx proxy server. The tests are written in the `Go programming language `_ and uses its testing framework. We depend on the following libraries: * golang.org/x/net/http2 * golang.org/x/net/websocket * https://github.com/tatsuhiro-t/go-nghttp2 Go modules will download these dependencies automatically. To run the tests, run the following command under ``integration-tests`` directory: .. code-block:: text $ make it Inside the tests, we use port 3009 to run the test subject server. Migration from v0.7.15 or earlier --------------------------------- nghttp2 v1.0.0 introduced several backward incompatible changes. In this section, we describe these changes and how to migrate to v1.0.0. ALPN protocol ID is now ``h2`` and ``h2c`` ++++++++++++++++++++++++++++++++++++++++++ Previously we announced ``h2-14`` and ``h2c-14``. v1.0.0 implements final protocol version, and we changed ALPN ID to ``h2`` and ``h2c``. The macros ``NGHTTP2_PROTO_VERSION_ID``, ``NGHTTP2_PROTO_VERSION_ID_LEN``, ``NGHTTP2_CLEARTEXT_PROTO_VERSION_ID``, and ``NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN`` have been updated to reflect this change. Basically, existing applications do not have to do anything, just recompiling is enough for this change. Use word "client magic" where we use "client connection preface" ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ We use "client connection preface" to mean first 24 bytes of client connection preface. This is technically not correct, since client connection preface is composed of 24 bytes client magic byte string followed by SETTINGS frame. For clarification, we call "client magic" for this 24 bytes byte string and updated API. * ``NGHTTP2_CLIENT_CONNECTION_PREFACE`` was replaced with ``NGHTTP2_CLIENT_MAGIC``. * ``NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN`` was replaced with ``NGHTTP2_CLIENT_MAGIC_LEN``. * ``NGHTTP2_BAD_PREFACE`` was renamed as ``NGHTTP2_BAD_CLIENT_MAGIC`` The already deprecated ``NGHTTP2_CLIENT_CONNECTION_HEADER`` and ``NGHTTP2_CLIENT_CONNECTION_HEADER_LEN`` were removed. If application uses these macros, just replace old ones with new ones. Since v1.0.0, client magic is sent by library (see next subsection), so client application may just remove these macro use. Client magic is sent by library +++++++++++++++++++++++++++++++ Previously nghttp2 library did not send client magic, which is first 24 bytes byte string of client connection preface, and client applications have to send it by themselves. Since v1.0.0, client magic is sent by library via first call of ``nghttp2_session_send()`` or ``nghttp2_session_mem_send2()``. The client applications which send client magic must remove the relevant code. Remove HTTP Alternative Services (Alt-Svc) related code +++++++++++++++++++++++++++++++++++++++++++++++++++++++ Alt-Svc specification is not finalized yet. To make our API stable, we have decided to remove all Alt-Svc related API from nghttp2. * ``NGHTTP2_EXT_ALTSVC`` was removed. * ``nghttp2_ext_altsvc`` was removed. We have already removed the functionality of Alt-Svc in v0.7 series and they have been essentially noop. The application using these macro and struct, remove those lines. Use nghttp2_error in nghttp2_on_invalid_frame_recv_callback +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Previously ``nghttp2_on_invalid_frame_recv_cb_called`` took the ``error_code``, defined in ``nghttp2_error_code``, as parameter. But they are not detailed enough to debug. Therefore, we decided to use more detailed ``nghttp2_error`` values instead. The application using this callback should update the callback signature. If it treats ``error_code`` as HTTP/2 error code, update the code so that it is treated as ``nghttp2_error``. Receive client magic by default +++++++++++++++++++++++++++++++ Previously nghttp2 did not process client magic (24 bytes byte string). To make it deal with it, we had to use ``nghttp2_option_set_recv_client_preface()``. Since v1.0.0, nghttp2 processes client magic by default and ``nghttp2_option_set_recv_client_preface()`` was removed. Some application may want to disable this behaviour, so we added ``nghttp2_option_set_no_recv_client_magic()`` to achieve this. The application using ``nghttp2_option_set_recv_client_preface()`` with nonzero value, just remove it. The application using ``nghttp2_option_set_recv_client_preface()`` with zero value or not using it must use ``nghttp2_option_set_no_recv_client_magic()`` with nonzero value. Client, Server and Proxy programs --------------------------------- The ``src`` directory contains the HTTP/2 client, server and proxy programs. nghttp - client +++++++++++++++ ``nghttp`` is a HTTP/2 client. It can connect to the HTTP/2 server with prior knowledge, HTTP Upgrade and ALPN TLS extension. It has verbose output mode for framing information. Here is sample output from ``nghttp`` client: .. code-block:: text $ nghttp -nv https://nghttp2.org [ 0.190] Connected The negotiated protocol: h2 [ 0.212] recv SETTINGS frame (niv=2) [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] [ 0.212] send SETTINGS frame (niv=2) [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] [ 0.212] send SETTINGS frame ; ACK (niv=0) [ 0.212] send PRIORITY frame (dep_stream_id=0, weight=201, exclusive=0) [ 0.212] send PRIORITY frame (dep_stream_id=0, weight=101, exclusive=0) [ 0.212] send PRIORITY frame (dep_stream_id=0, weight=1, exclusive=0) [ 0.212] send PRIORITY frame (dep_stream_id=7, weight=1, exclusive=0) [ 0.212] send PRIORITY frame (dep_stream_id=3, weight=1, exclusive=0) [ 0.212] send HEADERS frame ; END_STREAM | END_HEADERS | PRIORITY (padlen=0, dep_stream_id=11, weight=16, exclusive=0) ; Open new stream :method: GET :path: / :scheme: https :authority: nghttp2.org accept: */* accept-encoding: gzip, deflate user-agent: nghttp2/1.0.1-DEV [ 0.221] recv SETTINGS frame ; ACK (niv=0) [ 0.221] recv (stream_id=13) :method: GET [ 0.221] recv (stream_id=13) :scheme: https [ 0.221] recv (stream_id=13) :path: /stylesheets/screen.css [ 0.221] recv (stream_id=13) :authority: nghttp2.org [ 0.221] recv (stream_id=13) accept-encoding: gzip, deflate [ 0.222] recv (stream_id=13) user-agent: nghttp2/1.0.1-DEV [ 0.222] recv PUSH_PROMISE frame ; END_HEADERS (padlen=0, promised_stream_id=2) [ 0.222] recv (stream_id=13) :status: 200 [ 0.222] recv (stream_id=13) date: Thu, 21 May 2015 16:38:14 GMT [ 0.222] recv (stream_id=13) content-type: text/html [ 0.222] recv (stream_id=13) last-modified: Fri, 15 May 2015 15:38:06 GMT [ 0.222] recv (stream_id=13) etag: W/"555612de-19f6" [ 0.222] recv (stream_id=13) link: ; rel=preload; as=stylesheet [ 0.222] recv (stream_id=13) content-encoding: gzip [ 0.222] recv (stream_id=13) server: nghttpx nghttp2/1.0.1-DEV [ 0.222] recv (stream_id=13) via: 1.1 nghttpx [ 0.222] recv (stream_id=13) strict-transport-security: max-age=31536000 [ 0.222] recv HEADERS frame ; END_HEADERS (padlen=0) ; First response header [ 0.222] recv DATA frame ; END_STREAM [ 0.222] recv (stream_id=2) :status: 200 [ 0.222] recv (stream_id=2) date: Thu, 21 May 2015 16:38:14 GMT [ 0.222] recv (stream_id=2) content-type: text/css [ 0.222] recv (stream_id=2) last-modified: Fri, 15 May 2015 15:38:06 GMT [ 0.222] recv (stream_id=2) etag: W/"555612de-9845" [ 0.222] recv (stream_id=2) content-encoding: gzip [ 0.222] recv (stream_id=2) server: nghttpx nghttp2/1.0.1-DEV [ 0.222] recv (stream_id=2) via: 1.1 nghttpx [ 0.222] recv (stream_id=2) strict-transport-security: max-age=31536000 [ 0.222] recv HEADERS frame ; END_HEADERS (padlen=0) ; First push response header [ 0.228] recv DATA frame ; END_STREAM [ 0.228] send GOAWAY frame (last_stream_id=2, error_code=NO_ERROR(0x00), opaque_data(0)=[]) The HTTP Upgrade is performed like so: .. code-block:: text $ nghttp -nvu http://nghttp2.org [ 0.011] Connected [ 0.011] HTTP Upgrade request GET / HTTP/1.1 Host: nghttp2.org Connection: Upgrade, HTTP2-Settings Upgrade: h2c HTTP2-Settings: AAMAAABkAAQAAP__ Accept: */* User-Agent: nghttp2/1.0.1-DEV [ 0.018] HTTP Upgrade response HTTP/1.1 101 Switching Protocols Connection: Upgrade Upgrade: h2c [ 0.018] HTTP Upgrade success [ 0.018] recv SETTINGS frame (niv=2) [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] [ 0.018] send SETTINGS frame (niv=2) [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] [ 0.018] send SETTINGS frame ; ACK (niv=0) [ 0.018] send PRIORITY frame (dep_stream_id=0, weight=201, exclusive=0) [ 0.018] send PRIORITY frame (dep_stream_id=0, weight=101, exclusive=0) [ 0.018] send PRIORITY frame (dep_stream_id=0, weight=1, exclusive=0) [ 0.018] send PRIORITY frame (dep_stream_id=7, weight=1, exclusive=0) [ 0.018] send PRIORITY frame (dep_stream_id=3, weight=1, exclusive=0) [ 0.018] send PRIORITY frame (dep_stream_id=11, weight=16, exclusive=0) [ 0.019] recv (stream_id=1) :method: GET [ 0.019] recv (stream_id=1) :scheme: http [ 0.019] recv (stream_id=1) :path: /stylesheets/screen.css [ 0.019] recv (stream_id=1) host: nghttp2.org [ 0.019] recv (stream_id=1) user-agent: nghttp2/1.0.1-DEV [ 0.019] recv PUSH_PROMISE frame ; END_HEADERS (padlen=0, promised_stream_id=2) [ 0.019] recv (stream_id=1) :status: 200 [ 0.019] recv (stream_id=1) date: Thu, 21 May 2015 16:39:16 GMT [ 0.019] recv (stream_id=1) content-type: text/html [ 0.019] recv (stream_id=1) content-length: 6646 [ 0.019] recv (stream_id=1) last-modified: Fri, 15 May 2015 15:38:06 GMT [ 0.019] recv (stream_id=1) etag: "555612de-19f6" [ 0.019] recv (stream_id=1) link: ; rel=preload; as=stylesheet [ 0.019] recv (stream_id=1) accept-ranges: bytes [ 0.019] recv (stream_id=1) server: nghttpx nghttp2/1.0.1-DEV [ 0.019] recv (stream_id=1) via: 1.1 nghttpx [ 0.019] recv HEADERS frame ; END_HEADERS (padlen=0) ; First response header [ 0.019] recv DATA frame ; END_STREAM [ 0.019] recv (stream_id=2) :status: 200 [ 0.019] recv (stream_id=2) date: Thu, 21 May 2015 16:39:16 GMT [ 0.019] recv (stream_id=2) content-type: text/css [ 0.019] recv (stream_id=2) content-length: 38981 [ 0.019] recv (stream_id=2) last-modified: Fri, 15 May 2015 15:38:06 GMT [ 0.019] recv (stream_id=2) etag: "555612de-9845" [ 0.019] recv (stream_id=2) accept-ranges: bytes [ 0.019] recv (stream_id=2) server: nghttpx nghttp2/1.0.1-DEV [ 0.019] recv (stream_id=2) via: 1.1 nghttpx [ 0.019] recv HEADERS frame ; END_HEADERS (padlen=0) ; First push response header [ 0.026] recv DATA frame [ 0.027] recv DATA frame [ 0.027] send WINDOW_UPDATE frame (window_size_increment=33343) [ 0.032] send WINDOW_UPDATE frame (window_size_increment=33707) [ 0.032] recv DATA frame ; END_STREAM [ 0.032] recv SETTINGS frame ; ACK (niv=0) [ 0.032] send GOAWAY frame (last_stream_id=2, error_code=NO_ERROR(0x00), opaque_data(0)=[]) Using the ``-s`` option, ``nghttp`` prints out some timing information for requests, sorted by completion time: .. code-block:: text $ nghttp -nas https://nghttp2.org/ ***** Statistics ***** Request timing: responseEnd: the time when last byte of response was received relative to connectEnd requestStart: the time just before first byte of request was sent relative to connectEnd. If '*' is shown, this was pushed by server. process: responseEnd - requestStart code: HTTP status code size: number of bytes received as response body without inflation. URI: request URI see http://www.w3.org/TR/resource-timing/#processing-model sorted by 'complete' id responseEnd requestStart process code size request path 13 +37.19ms +280us 36.91ms 200 2K / 2 +72.65ms * +36.38ms 36.26ms 200 8K /stylesheets/screen.css 17 +77.43ms +38.67ms 38.75ms 200 3K /javascripts/octopress.js 15 +78.12ms +38.66ms 39.46ms 200 3K /javascripts/modernizr-2.0.js Using the ``-r`` option, ``nghttp`` writes more detailed timing data to the given file in HAR format. nghttpd - server ++++++++++++++++ ``nghttpd`` is a multi-threaded static web server. By default, it uses SSL/TLS connection. Use ``--no-tls`` option to disable it. ``nghttpd`` only accepts HTTP/2 connections via ALPN or direct HTTP/2 connections. No HTTP Upgrade is supported. The ``-p`` option allows users to configure server push. Just like ``nghttp``, it has a verbose output mode for framing information. Here is sample output from ``nghttpd``: .. code-block:: text $ nghttpd --no-tls -v 8080 IPv4: listen 0.0.0.0:8080 IPv6: listen :::8080 [id=1] [ 1.521] send SETTINGS frame (niv=1) [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] [id=1] [ 1.521] recv SETTINGS frame (niv=2) [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] [id=1] [ 1.521] recv SETTINGS frame ; ACK (niv=0) [id=1] [ 1.521] recv PRIORITY frame (dep_stream_id=0, weight=201, exclusive=0) [id=1] [ 1.521] recv PRIORITY frame (dep_stream_id=0, weight=101, exclusive=0) [id=1] [ 1.521] recv PRIORITY frame (dep_stream_id=0, weight=1, exclusive=0) [id=1] [ 1.521] recv PRIORITY frame (dep_stream_id=7, weight=1, exclusive=0) [id=1] [ 1.521] recv PRIORITY frame (dep_stream_id=3, weight=1, exclusive=0) [id=1] [ 1.521] recv (stream_id=13) :method: GET [id=1] [ 1.521] recv (stream_id=13) :path: / [id=1] [ 1.521] recv (stream_id=13) :scheme: http [id=1] [ 1.521] recv (stream_id=13) :authority: localhost:8080 [id=1] [ 1.521] recv (stream_id=13) accept: */* [id=1] [ 1.521] recv (stream_id=13) accept-encoding: gzip, deflate [id=1] [ 1.521] recv (stream_id=13) user-agent: nghttp2/1.0.0-DEV [id=1] [ 1.521] recv HEADERS frame ; END_STREAM | END_HEADERS | PRIORITY (padlen=0, dep_stream_id=11, weight=16, exclusive=0) ; Open new stream [id=1] [ 1.521] send SETTINGS frame ; ACK (niv=0) [id=1] [ 1.521] send HEADERS frame ; END_HEADERS (padlen=0) ; First response header :status: 200 server: nghttpd nghttp2/1.0.0-DEV content-length: 10 cache-control: max-age=3600 date: Fri, 15 May 2015 14:49:04 GMT last-modified: Tue, 30 Sep 2014 12:40:52 GMT [id=1] [ 1.522] send DATA frame ; END_STREAM [id=1] [ 1.522] stream_id=13 closed [id=1] [ 1.522] recv GOAWAY frame (last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[]) [id=1] [ 1.522] closed nghttpx - proxy +++++++++++++++ ``nghttpx`` is a multi-threaded reverse proxy for HTTP/3, HTTP/2, and HTTP/1.1, and powers http://nghttp2.org and supports HTTP/2 server push. We reworked ``nghttpx`` command-line interface, and as a result, there are several incompatibles from 1.8.0 or earlier. This is necessary to extend its capability, and secure the further feature enhancements in the future release. Please read `Migration from nghttpx v1.8.0 or earlier `_ to know how to migrate from earlier releases. ``nghttpx`` implements `important performance-oriented features `_ in TLS, such as session IDs, session tickets (with automatic key rotation), dynamic record sizing, ALPN, forward secrecy and HTTP/2. ``nghttpx`` also offers the functionality to share ticket keys among multiple ``nghttpx`` instances via memcached. ``nghttpx`` has 2 operation modes: ================== ======================== ================ ============= Mode option Frontend Backend Note ================== ======================== ================ ============= default mode HTTP/3, HTTP/2, HTTP/1.1 HTTP/1.1, HTTP/2 Reverse proxy ``--http2-proxy`` HTTP/3, HTTP/2, HTTP/1.1 HTTP/1.1, HTTP/2 Forward proxy ================== ======================== ================ ============= The interesting mode at the moment is the default mode. It works like a reverse proxy and listens for HTTP/3, HTTP/2, and HTTP/1.1 and can be deployed as a SSL/TLS terminator for existing web server. In all modes, the frontend connections are encrypted by SSL/TLS by default. To disable encryption, use the ``no-tls`` keyword in ``--frontend`` option. If encryption is disabled, incoming HTTP/1.1 connections can be upgraded to HTTP/2 through HTTP Upgrade. On the other hard, backend connections are not encrypted by default. To encrypt backend connections, use ``tls`` keyword in ``--backend`` option. ``nghttpx`` supports a configuration file. See the ``--conf`` option and sample configuration file ``nghttpx.conf.sample``. In the default mode, ``nghttpx`` works as reverse proxy to the backend server: .. code-block:: text Client <-- (HTTP/3, HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/1.1, HTTP/2) --> Web Server [reverse proxy] With the ``--http2-proxy`` option, it works as forward proxy, and it is so called secure HTTP/2 proxy: .. code-block:: text Client <-- (HTTP/3, HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy [secure proxy] (e.g., Squid, ATS) The ``Client`` in the above example needs to be configured to use ``nghttpx`` as secure proxy. At the time of this writing, both Chrome and Firefox support secure HTTP/2 proxy. One way to configure Chrome to use a secure proxy is to create a proxy.pac script like this: .. code-block:: javascript function FindProxyForURL(url, host) { return "HTTPS SERVERADDR:PORT"; } ``SERVERADDR`` and ``PORT`` is the hostname/address and port of the machine nghttpx is running on. Please note that Chrome requires a valid certificate for secure proxy. Then run Chrome with the following arguments: .. code-block:: text $ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn The backend HTTP/2 connections can be tunneled through an HTTP proxy. The proxy is specified using ``--backend-http-proxy-uri``. The following figure illustrates how nghttpx talks to the outside HTTP/2 proxy through an HTTP proxy: .. code-block:: text Client <-- (HTTP/3, HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) -- --===================---> HTTP/2 Proxy (HTTP proxy tunnel) (e.g., nghttpx -s) Benchmarking tool ----------------- The ``h2load`` program is a benchmarking tool for HTTP/3, HTTP/2, and HTTP/1.1. The UI of ``h2load`` is heavily inspired by ``weighttp`` (https://github.com/lighttpd/weighttp). The typical usage is as follows: .. code-block:: text $ h2load -n100000 -c100 -m100 https://localhost:8443/ starting benchmark... spawning thread #0: 100 concurrent clients, 100000 total requests Protocol: TLSv1.2 Cipher: ECDHE-RSA-AES128-GCM-SHA256 Server Temp Key: ECDH P-256 256 bits progress: 10% done progress: 20% done progress: 30% done progress: 40% done progress: 50% done progress: 60% done progress: 70% done progress: 80% done progress: 90% done progress: 100% done finished in 771.26ms, 129658 req/s, 4.71MB/s requests: 100000 total, 100000 started, 100000 done, 100000 succeeded, 0 failed, 0 errored status codes: 100000 2xx, 0 3xx, 0 4xx, 0 5xx traffic: 3812300 bytes total, 1009900 bytes headers, 1000000 bytes data min max mean sd +/- sd time for request: 25.12ms 124.55ms 51.07ms 15.36ms 84.87% time for connect: 208.94ms 254.67ms 241.38ms 7.95ms 63.00% time to 1st byte: 209.11ms 254.80ms 241.51ms 7.94ms 63.00% The above example issued total 100,000 requests, using 100 concurrent clients (in other words, 100 HTTP/2 sessions), and a maximum of 100 streams per client. With the ``-t`` option, ``h2load`` will use multiple native threads to avoid saturating a single core on client side. .. warning:: **Don't use this tool against publicly available servers.** That is considered a DOS attack. Please only use it against your private servers. If the experimental HTTP/3 is enabled, h2load can send requests to HTTP/3 server. To do this, use ``--h3`` option (or ``--alpn-list=h3`` option) like so: .. code-block:: text $ h2load --h3 https://127.0.0.1:4433 For nghttp2 v1.58 or earlier, use ``--npn-list`` instead of ``--alpn-list``. HPACK tools ----------- The ``src`` directory contains the HPACK tools. The ``deflatehd`` program is a command-line header compression tool. The ``inflatehd`` program is a command-line header decompression tool. Both tools read input from stdin and write output to stdout. Errors are written to stderr. They take JSON as input and output. We (mostly) use the same JSON data format described at https://github.com/http2jp/hpack-test-case. deflatehd - header compressor +++++++++++++++++++++++++++++ The ``deflatehd`` program reads JSON data or HTTP/1-style header fields from stdin and outputs compressed header block in JSON. For the JSON input, the root JSON object must include a ``cases`` key. Its value has to include the sequence of input header set. They share the same compression context and are processed in the order they appear. Each item in the sequence is a JSON object and it must include a ``headers`` key. Its value is an array of JSON objects, which includes exactly one name/value pair. Example: .. code-block:: json { "cases": [ { "headers": [ { ":method": "GET" }, { ":path": "/" } ] }, { "headers": [ { ":method": "POST" }, { ":path": "/" } ] } ] } With the ``-t`` option, the program can accept more familiar HTTP/1 style header field blocks. Each header set is delimited by an empty line: Example: .. code-block:: text :method: GET :scheme: https :path: / :method: POST user-agent: nghttp2 The output is in JSON object. It should include a ``cases`` key and its value is an array of JSON objects, which has at least the following keys: seq The index of header set in the input. input_length The sum of the length of the name/value pairs in the input. output_length The length of the compressed header block. percentage_of_original_size ``output_length`` / ``input_length`` * 100 wire The compressed header block as a hex string. headers The input header set. header_table_size The header table size adjusted before deflating the header set. Examples: .. code-block:: json { "cases": [ { "seq": 0, "input_length": 66, "output_length": 20, "percentage_of_original_size": 30.303030303030305, "wire": "01881f3468e5891afcbf83868a3d856659c62e3f", "headers": [ { ":authority": "example.org" }, { ":method": "GET" }, { ":path": "/" }, { ":scheme": "https" }, { "user-agent": "nghttp2" } ], "header_table_size": 4096 } , { "seq": 1, "input_length": 74, "output_length": 10, "percentage_of_original_size": 13.513513513513514, "wire": "88448504252dd5918485", "headers": [ { ":authority": "example.org" }, { ":method": "POST" }, { ":path": "/account" }, { ":scheme": "https" }, { "user-agent": "nghttp2" } ], "header_table_size": 4096 } ] } The output can be used as the input for ``inflatehd`` and ``deflatehd``. With the ``-d`` option, the extra ``header_table`` key is added and its associated value includes the state of dynamic header table after the corresponding header set was processed. The value includes at least the following keys: entries The entry in the header table. If ``referenced`` is ``true``, it is in the reference set. The ``size`` includes the overhead (32 bytes). The ``index`` corresponds to the index of header table. The ``name`` is the header field name and the ``value`` is the header field value. size The sum of the spaces entries occupied, this includes the entry overhead. max_size The maximum header table size. deflate_size The sum of the spaces entries occupied within ``max_deflate_size``. max_deflate_size The maximum header table size the encoder uses. This can be smaller than ``max_size``. In this case, the encoder only uses up to first ``max_deflate_size`` buffer. Since the header table size is still ``max_size``, the encoder has to keep track of entries outside the ``max_deflate_size`` but inside the ``max_size`` and make sure that they are no longer referenced. Example: .. code-block:: json { "cases": [ { "seq": 0, "input_length": 66, "output_length": 20, "percentage_of_original_size": 30.303030303030305, "wire": "01881f3468e5891afcbf83868a3d856659c62e3f", "headers": [ { ":authority": "example.org" }, { ":method": "GET" }, { ":path": "/" }, { ":scheme": "https" }, { "user-agent": "nghttp2" } ], "header_table_size": 4096, "header_table": { "entries": [ { "index": 1, "name": "user-agent", "value": "nghttp2", "referenced": true, "size": 49 }, { "index": 2, "name": ":scheme", "value": "https", "referenced": true, "size": 44 }, { "index": 3, "name": ":path", "value": "/", "referenced": true, "size": 38 }, { "index": 4, "name": ":method", "value": "GET", "referenced": true, "size": 42 }, { "index": 5, "name": ":authority", "value": "example.org", "referenced": true, "size": 53 } ], "size": 226, "max_size": 4096, "deflate_size": 226, "max_deflate_size": 4096 } } , { "seq": 1, "input_length": 74, "output_length": 10, "percentage_of_original_size": 13.513513513513514, "wire": "88448504252dd5918485", "headers": [ { ":authority": "example.org" }, { ":method": "POST" }, { ":path": "/account" }, { ":scheme": "https" }, { "user-agent": "nghttp2" } ], "header_table_size": 4096, "header_table": { "entries": [ { "index": 1, "name": ":method", "value": "POST", "referenced": true, "size": 43 }, { "index": 2, "name": "user-agent", "value": "nghttp2", "referenced": true, "size": 49 }, { "index": 3, "name": ":scheme", "value": "https", "referenced": true, "size": 44 }, { "index": 4, "name": ":path", "value": "/", "referenced": false, "size": 38 }, { "index": 5, "name": ":method", "value": "GET", "referenced": false, "size": 42 }, { "index": 6, "name": ":authority", "value": "example.org", "referenced": true, "size": 53 } ], "size": 269, "max_size": 4096, "deflate_size": 269, "max_deflate_size": 4096 } } ] } inflatehd - header decompressor +++++++++++++++++++++++++++++++ The ``inflatehd`` program reads JSON data from stdin and outputs decompressed name/value pairs in JSON. The root JSON object must include the ``cases`` key. Its value has to include the sequence of compressed header blocks. They share the same compression context and are processed in the order they appear. Each item in the sequence is a JSON object and it must have at least a ``wire`` key. Its value is a compressed header block as a hex string. Example: .. code-block:: json { "cases": [ { "wire": "8285" }, { "wire": "8583" } ] } The output is a JSON object. It should include a ``cases`` key and its value is an array of JSON objects, which has at least following keys: seq The index of the header set in the input. headers A JSON array that includes decompressed name/value pairs. wire The compressed header block as a hex string. header_table_size The header table size adjusted before inflating compressed header block. Example: .. code-block:: json { "cases": [ { "seq": 0, "wire": "01881f3468e5891afcbf83868a3d856659c62e3f", "headers": [ { ":authority": "example.org" }, { ":method": "GET" }, { ":path": "/" }, { ":scheme": "https" }, { "user-agent": "nghttp2" } ], "header_table_size": 4096 } , { "seq": 1, "wire": "88448504252dd5918485", "headers": [ { ":method": "POST" }, { ":path": "/account" }, { "user-agent": "nghttp2" }, { ":scheme": "https" }, { ":authority": "example.org" } ], "header_table_size": 4096 } ] } The output can be used as the input for ``deflatehd`` and ``inflatehd``. With the ``-d`` option, the extra ``header_table`` key is added and its associated value includes the state of the dynamic header table after the corresponding header set was processed. The format is the same as ``deflatehd``. Contribution ------------ [This text was composed based on 1.2. License section of curl/libcurl project.] When contributing with code, you agree to put your changes and new code under the same license nghttp2 is already using unless stated and agreed otherwise. When changing existing source code, do not alter the copyright of the original file(s). The copyright will still be owned by the original creator(s) or those who have been assigned copyright by the original author(s). By submitting a patch to the nghttp2 project, you (or your employer, as the case may be) agree to assign the copyright of your submission to us. .. the above really needs to be reworded to pass legal muster. We will credit you for your changes as far as possible, to give credit but also to keep a trace back to who made what changes. Please always provide us with your full real name when contributing! See `Contribution Guidelines `_ for more details. Versioning ---------- In general, we follow `Semantic Versioning `_. We may release PATCH releases between the regular releases, mainly for severe security bug fixes. We have no plan to break API compatibility changes involving soname bump, so MAJOR version will stay 1 for the foreseeable future. License ------- The MIT License PK0]σ4i4iman/man1/nghttpx.1nu[.\" Man page generated from reStructuredText .\" by the Docutils 0.22.4 manpage writer. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "NGHTTPX" "1" "Jul 29, 2026" "1.70.0" "nghttp2" .SH NAME nghttpx \- HTTP/2 proxy .SH SYNOPSIS .sp \fBnghttpx\fP [OPTIONS]... [ ] .SH DESCRIPTION .sp A reverse proxy for HTTP/3, HTTP/2, and HTTP/1. .INDENT 0.0 .TP .B Set path to server\(aqs private key. Required unless \(dqno\-tls\(dq parameter is used in \fB\-\-frontend\fP option. .UNINDENT .INDENT 0.0 .TP .B Set path to server\(aqs certificate. Required unless \(dqno\-tls\(dq parameter is used in \fB\-\-frontend\fP option. .UNINDENT .SH OPTIONS .sp The options are categorized into several groups. .SS Connections .INDENT 0.0 .TP .B \-b, \-\-backend=(,|unix:)[;[[:...]][[;]...] Set backend host and port. The multiple backend addresses are accepted by repeating this option. UNIX domain socket can be specified by prefixing path name with \(dqunix:\(dq (e.g., unix:/var/run/backend.sock). .sp Optionally, if s are given, the backend address is only used if request matches the pattern. The pattern matching is closely designed to ServeMux in net/http package of Go programming language. consists of path, host + path or just host. The path must start with \(dq\fI/\fP\(dq. If it ends with \(dq\fI/\fP\(dq, it matches all request path in its subtree. To deal with the request to the directory without trailing slash, the path which ends with \(dq\fI/\fP\(dq also matches the request path which only lacks trailing \(aq\fI/\fP\(aq (e.g., path \(dq\fI/foo/\fP\(dq matches request path \(dq\fI/foo\fP\(dq). If it does not end with \(dq\fI/\fP\(dq, it performs exact match against the request path. If host is given, it performs a match against the request host. For a request received on the frontend listener with \(dqsni\-fwd\(dq parameter enabled, SNI host is used instead of a request host. If host alone is given, \(dq\fI/\fP\(dq is appended to it, so that it matches all request paths under the host (e.g., specifying \(dqnghttp2.org\(dq equals to \(dqnghttp2.org/\(dq). CONNECT method is treated specially. It does not have path, and we don\(aqt allow empty path. To workaround this, we assume that CONNECT method has \(dq\fI/\fP\(dq as path. .sp Patterns with host take precedence over patterns with just path. Then, longer patterns take precedence over shorter ones. .sp Host can include \(dq*\(dq in the left most position to indicate wildcard match (only suffix match is done). The \(dq*\(dq must match at least one character. For example, host pattern \(dq*.nghttp2.org\(dq matches against \(dqwww.nghttp2.org\(dq and \(dqgit.ngttp2.org\(dq, but does not match against \(dqnghttp2.org\(dq. The exact hosts match takes precedence over the wildcard hosts match. .sp If path part ends with \(dq*\(dq, it is treated as wildcard path. The wildcard path behaves differently from the normal path. For normal path, match is made around the boundary of path component separator,\(dq\fI/\fP\(dq. On the other hand, the wildcard path does not take into account the path component separator. All paths which include the wildcard path without last \(dq*\(dq as prefix, and are strictly longer than wildcard path without last \(dq*\(dq are matched. \(dq*\(dq must match at least one character. For example, the pattern \(dq\fI/foo*\fP\(dq matches \(dq\fI/foo/\fP\(dq and \(dq\fI/foobar\fP\(dq. But it does not match \(dq\fI/foo\fP\(dq, or \(dq\fI/fo\fP\(dq. .sp If is omitted or empty string, \(dq\fI/\fP\(dq is used as pattern, which matches all request paths (catch\-all pattern). The catch\-all backend must be given. .sp When doing a match, nghttpx made some normalization to pattern, request host and path. For host part, they are converted to lower case. For path part, percent\-encoded unreserved characters defined in RFC 3986 are decoded, and any dot\-segments (\(dq..\(dq and \(dq.\(dq) are resolved and removed. .sp For example, \fB\-b\fP\(aq127.0.0.1,8080;nghttp2.org/httpbin/\(aq matches the request host \(dqnghttp2.org\(dq and the request path \(dq\fI/httpbin/get\fP\(dq, but does not match the request host \(dqnghttp2.org\(dq and the request path \(dq\fI/index.html\fP\(dq. .sp The multiple s can be specified, delimiting them by \(dq:\(dq. Specifying \fB\-b\fP\(aq127.0.0.1,8080;nghttp2.org:www.nghttp2.org\(aq has the same effect to specify \fB\-b\fP\(aq127.0.0.1,8080;nghttp2.org\(aq and \fB\-b\fP\(aq127.0.0.1,8080;www.nghttp2.org\(aq. .sp The backend addresses sharing same are grouped together forming load balancing group. .sp Several parameters are accepted after . The parameters are delimited by \(dq;\(dq. The available parameters are: \(dqproto=\(dq, \(dqtls\(dq, \(dqsni=\(dq, \(dqfall=\(dq, \(dqrise=\(dq, \(dqaffinity=\(dq, \(dqdns\(dq, \(dqredirect\-if\-not\-tls\(dq, \(dqupgrade\-scheme\(dq, \(dqmruby=\(dq, \(dqread\-timeout=\(dq, \(dqwrite\-timeout=\(dq, \(dqgroup=\(dq, \(dqgroup\-weight=\(dq, \(dqweight=\(dq, and \(dqdnf\(dq. The parameter consists of keyword, and optionally followed by \(dq=\(dq and value. For example, the parameter \(dqproto=h2\(dq consists of the keyword \(dqproto\(dq and value \(dqh2\(dq. The parameter \(dqtls\(dq consists of the keyword \(dqtls\(dq without value. Each parameter is described as follows. .sp The backend application protocol can be specified using optional \(dqproto\(dq parameter, and in the form of \(dqproto=\(dq. should be one of the following list without quotes: \(dqh2\(dq, \(dqhttp/1.1\(dq. The default value of is \(dqhttp/1.1\(dq. Note that usually \(dqh2\(dq refers to HTTP/2 over TLS. But in this option, it may mean HTTP/2 over cleartext TCP unless \(dqtls\(dq keyword is used (see below). .sp TLS can be enabled by specifying optional \(dqtls\(dq parameter. TLS is not enabled by default. .sp With \(dqsni=\(dq parameter, it can override the TLS SNI field value with given . This will default to the backend name .sp The feature to detect whether backend is online or offline can be enabled using optional \(dqfall\(dq and \(dqrise\(dq parameters. Using \(dqfall=\(dq parameter, if nghttpx cannot connect to a this backend times in a row, this backend is assumed to be offline, and it is excluded from load balancing. If is 0, this backend never be excluded from load balancing whatever times nghttpx cannot connect to it, and this is the default. There is also \(dqrise=\(dq parameter. After backend was excluded from load balancing group, nghttpx periodically attempts to make a connection to the failed backend, and if the connection is made successfully times in a row, the backend is assumed to be online, and it is now eligible for load balancing target. If is 0, a backend is permanently offline, once it goes in that state, and this is the default behaviour. .sp The session affinity is enabled using \(dqaffinity=\(dq parameter. If \(dqip\(dq is given in , client IP based session affinity is enabled. If \(dqcookie\(dq is given in , cookie based session affinity is enabled. If \(dqnone\(dq is given in , session affinity is disabled, and this is the default. The session affinity is enabled per . If at least one backend has \(dqaffinity\(dq parameter, and its is not \(dqnone\(dq, session affinity is enabled for all backend servers sharing the same . It is advised to set \(dqaffinity\(dq parameter to all backend explicitly if session affinity is desired. The session affinity may break if one of the backend gets unreachable, or backend settings are reloaded or replaced by API. .sp If \(dqaffinity=cookie\(dq is used, the additional configuration is required. \(dqaffinity\-cookie\-name=\(dq must be used to specify a name of cookie to use. Optionally, \(dqaffinity\-cookie\-path=\(dq can be used to specify a path which cookie is applied. The optional \(dqaffinity\-cookie\-secure=\(dq controls the Secure attribute of a cookie. The default value is \(dqauto\(dq, and the Secure attribute is determined by a request scheme. If a request scheme is \(dqhttps\(dq, then Secure attribute is set. Otherwise, it is not set. If is \(dqyes\(dq, the Secure attribute is always set. If is \(dqno\(dq, the Secure attribute is always omitted. \(dqaffinity\-cookie\-stickiness=\(dq controls stickiness of this affinity. If is \(dqloose\(dq, removing or adding a backend server might break the affinity and the request might be forwarded to a different backend server. If is \(dqstrict\(dq, removing the designated backend server breaks affinity, but adding new backend server does not cause breakage. If the designated backend server becomes unavailable, new backend server is chosen as if the request does not have an affinity cookie. defaults to \(dqloose\(dq. .sp By default, name resolution of backend host name is done at start up, or reloading configuration. If \(dqdns\(dq parameter is given, name resolution takes place dynamically. This is useful if backend address changes frequently. If \(dqdns\(dq is given, name resolution of backend host name at start up, or reloading configuration is skipped. .sp If \(dqredirect\-if\-not\-tls\(dq parameter is used, the matched backend requires that frontend connection is TLS encrypted. If it isn\(aqt, nghttpx responds to the request with 308 status code, and https URI the client should use instead is included in Location header field. The port number in redirect URI is 443 by default, and can be changed using \fB\-\-redirect\-https\-port\fP option. If at least one backend has \(dqredirect\-if\-not\-tls\(dq parameter, this feature is enabled for all backend servers sharing the same . It is advised to set \(dqredirect\-if\-no\-tls\(dq parameter to all backends explicitly if this feature is desired. .sp If \(dqupgrade\-scheme\(dq parameter is used along with \(dqtls\(dq parameter, HTTP/2 :scheme pseudo header field is changed to \(dqhttps\(dq from \(dqhttp\(dq when forwarding a request to this particular backend. This is a workaround for a backend server which requires \(dqhttps\(dq :scheme pseudo header field on TLS encrypted connection. .sp \(dqmruby=\(dq parameter specifies a path to mruby script file which is invoked when this pattern is matched. All backends which share the same pattern must have the same mruby path. .sp \(dqread\-timeout=\(dq and \(dqwrite\-timeout=\(dq parameters specify the read and write timeout of the backend connection when this pattern is matched. All backends which share the same pattern must have the same timeouts. If these timeouts are entirely omitted for a pattern, \fB\-\-backend\-read\-timeout\fP and \fB\-\-backend\-write\-timeout\fP are used. .sp \(dqgroup=\(dq parameter specifies the name of group this backend address belongs to. By default, it belongs to the unnamed default group. The name of group is unique per pattern. \(dqgroup\-weight=\(dq parameter specifies the weight of the group. The higher weight gets more frequently selected by the load balancing algorithm. must be [1, 256] inclusive. The weight 8 has 4 times more weight than 2. must be the same for all addresses which share the same . If \(dqgroup\-weight\(dq is omitted in an address, but the other address which belongs to the same group specifies \(dqgroup\-weight\(dq, its weight is used. If no \(dqgroup\-weight\(dq is specified for all addresses, the weight of a group becomes 1. \(dqgroup\(dq and \(dqgroup\-weight\(dq are ignored if session affinity is enabled. .sp \(dqweight=\(dq parameter specifies the weight of the backend address inside a group which this address belongs to. The higher weight gets more frequently selected by the load balancing algorithm. must be [1, 256] inclusive. The weight 8 has 4 times more weight than weight 2. If this parameter is omitted, weight becomes 1. \(dqweight\(dq is ignored if session affinity is enabled. .sp If \(dqdnf\(dq parameter is specified, an incoming request is not forwarded to a backend and just consumed along with the request body (actually a backend server never be contacted). It is expected that the HTTP response is generated by mruby script (see \(dqmruby=\(dq parameter above). \(dqdnf\(dq is an abbreviation of \(dqdo not forward\(dq. .sp Since \(dq;\(dq and \(dq:\(dq are used as delimiter, must not contain these characters. In order to include \(dq:\(dq in , one has to specify \(dq%3A\(dq (which is percent\-encoded from of \(dq:\(dq) instead. Since \(dq;\(dq has special meaning in shell, the option value must be quoted. .sp Default: \fB127.0.0.1,80\fP .UNINDENT .INDENT 0.0 .TP .B \-f, \-\-frontend=(,|unix:)[[;]...] Set frontend host and port. If is \(aq*\(aq, it assumes all addresses including both IPv4 and IPv6. UNIX domain socket can be specified by prefixing path name with \(dqunix:\(dq (e.g., unix:/var/run/nghttpx.sock). This option can be used multiple times to listen to multiple addresses. .sp This option can take 0 or more parameters, which are described below. Note that \(dqapi\(dq and \(dqhealthmon\(dq parameters are mutually exclusive. .sp Optionally, TLS can be disabled by specifying \(dqno\-tls\(dq parameter. TLS is enabled by default. .sp If \(dqsni\-fwd\(dq parameter is used, when performing a match to select a backend server, SNI host name received from the client is used instead of the request host. See \fB\-\-backend\fP option about the pattern match. .sp To make this frontend as API endpoint, specify \(dqapi\(dq parameter. This is disabled by default. It is important to limit the access to the API frontend. Otherwise, someone may change the backend server, and break your services, or expose confidential information to the outside the world. .sp To make this frontend as health monitor endpoint, specify \(dqhealthmon\(dq parameter. This is disabled by default. Any requests which come through this address are replied with 200 HTTP status, without no body. .sp To accept PROXY protocol version 1 and 2 on frontend connection, specify \(dqproxyproto\(dq parameter. This is disabled by default. .sp To receive HTTP/3 (QUIC) traffic, specify \(dqquic\(dq parameter. It makes nghttpx listen on UDP port rather than TCP port. UNIX domain socket, \(dqapi\(dq, and \(dqhealthmon\(dq parameters cannot be used with \(dqquic\(dq parameter. .sp Default: \fB*,3000\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backlog= Set listen backlog size. .sp Default: \fB65536\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-address\-family=(auto|IPv4|IPv6) Specify address family of backend connections. If \(dqauto\(dq is given, both IPv4 and IPv6 are considered. If \(dqIPv4\(dq is given, only IPv4 address is considered. If \(dqIPv6\(dq is given, only IPv6 address is considered. .sp Default: \fBauto\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-http\-proxy\-uri= Specify proxy URI in the form \%/[:@]:. If a proxy requires authentication, specify and . Note that they must be properly percent\-encoded. This proxy is used when the backend connection is HTTP/2. First, make a CONNECT request to the proxy and it connects to the backend on behalf of nghttpx. This forms tunnel. After that, nghttpx performs SSL/TLS handshake with the downstream through the tunnel. The timeouts when connecting and making CONNECT request can be specified by \fB\-\-backend\-read\-timeout\fP and \fB\-\-backend\-write\-timeout\fP options. .UNINDENT .SS Performance .INDENT 0.0 .TP .B \-n, \-\-workers= Set the number of worker threads. .sp Default: \fB1\fP .UNINDENT .INDENT 0.0 .TP .B \-\-single\-thread Run everything in one thread inside the worker process. This feature is provided for better debugging experience, or for the platforms which lack thread support. If threading is disabled, this option is always enabled. .UNINDENT .INDENT 0.0 .TP .B \-\-read\-rate= Set maximum average read rate on frontend connection. Setting 0 to this option means read rate is unlimited. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-read\-burst= Set maximum read burst size on frontend connection. Setting 0 to this option means read burst size is unlimited. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-write\-rate= Set maximum average write rate on frontend connection. Setting 0 to this option means write rate is unlimited. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-write\-burst= Set maximum write burst size on frontend connection. Setting 0 to this option means write burst size is unlimited. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-worker\-read\-rate= Set maximum average read rate on frontend connection per worker. Setting 0 to this option means read rate is unlimited. Not implemented yet. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-worker\-read\-burst= Set maximum read burst size on frontend connection per worker. Setting 0 to this option means read burst size is unlimited. Not implemented yet. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-worker\-write\-rate= Set maximum average write rate on frontend connection per worker. Setting 0 to this option means write rate is unlimited. Not implemented yet. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-worker\-write\-burst= Set maximum write burst size on frontend connection per worker. Setting 0 to this option means write burst size is unlimited. Not implemented yet. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-worker\-frontend\-connections= Set maximum number of simultaneous connections frontend accepts. Setting 0 means unlimited. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-connections\-per\-host= Set maximum number of backend concurrent connections (and/or streams in case of HTTP/2) per origin host. This option is meaningful when \fB\-\-http2\-proxy\fP option is used. The origin host is determined by authority portion of request URI (or :authority header field for HTTP/2). To limit the number of connections per frontend for default mode, use \fB\-\-backend\-connections\-per\-frontend\fP\&. .sp Default: \fB8\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-connections\-per\-frontend= Set maximum number of backend concurrent connections (and/or streams in case of HTTP/2) per frontend. This option is only used for default mode. 0 means unlimited. To limit the number of connections per host with \fB\-\-http2\-proxy\fP option, use \fB\-\-backend\-connections\-per\-host\fP\&. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-rlimit\-nofile= Set maximum number of open files (RLIMIT_NOFILE) to . If 0 is given, nghttpx does not set the limit. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-rlimit\-memlock= Set maximum number of bytes of memory that may be locked into RAM. If 0 is given, nghttpx does not set the limit. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-request\-buffer= Set buffer size used to store backend request. .sp Default: \fB16K\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-response\-buffer= Set buffer size used to store backend response. .sp Default: \fB128K\fP .UNINDENT .INDENT 0.0 .TP .B \-\-fastopen= Enables \(dqTCP Fast Open\(dq for the listening socket and limits the maximum length for the queue of connections that have not yet completed the three\-way handshake. If value is 0 then fast open is disabled. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-no\-kqueue Don\(aqt use kqueue. This option is only applicable for the platforms which have kqueue. For other platforms, this option will be simply ignored. .UNINDENT .SS Timeout .INDENT 0.0 .TP .B \-\-frontend\-http2\-idle\-timeout= Specify idle timeout for HTTP/2 frontend connection. If no active streams exist for this duration, connection is closed. .sp Default: \fB3m\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http3\-idle\-timeout= Specify idle timeout for HTTP/3 frontend connection. If no active streams exist for this duration, connection is closed. .sp Default: \fB3m\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-write\-timeout= Specify write timeout for all frontend connections. .sp Default: \fB30s\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-keep\-alive\-timeout= Specify keep\-alive timeout for frontend HTTP/1 connection. .sp Default: \fB1m\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-header\-timeout= Specify duration that the server waits for an HTTP request header fields to be received completely. On timeout, HTTP/1 and HTTP/2 connections are closed. For HTTP/3, the stream is shutdown, and the connection itself is left intact. .sp Default: \fB1m\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-stream\-read\-timeout= Specify read timeout for HTTP/2 and HTTP/3 streams in the frontend connection. 0 means no timeout. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-stream\-write\-timeout= Specify write timeout for HTTP/2 and HTTP/3 streams in the frontend connection. 0 means no timeout. .sp Default: \fB1m\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-stream\-read\-timeout= Specify read timeout for HTTP/2 streams in the backend connection. 0 means no timeout. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-stream\-write\-timeout= Specify write timeout for HTTP/2 streams in the backend connection. 0 means no timeout. .sp Default: \fB1m\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-read\-timeout= Specify read timeout for backend connection. .sp Default: \fB1m\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-write\-timeout= Specify write timeout for backend connection. .sp Default: \fB30s\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-connect\-timeout= Specify timeout before establishing TCP connection to backend. .sp Default: \fB30s\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-keep\-alive\-timeout= Specify keep\-alive timeout for backend HTTP/1 connection. .sp Default: \fB2s\fP .UNINDENT .INDENT 0.0 .TP .B \-\-listener\-disable\-timeout= After accepting connection failed, connection listener is disabled for a given amount of time. Specifying 0 disables this feature. .sp Default: \fB30s\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http2\-setting\-timeout= Specify timeout before SETTINGS ACK is received from client. .sp Default: \fB10s\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-http2\-settings\-timeout= Specify timeout before SETTINGS ACK is received from backend server. .sp Default: \fB10s\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-max\-backoff= Specify maximum backoff interval. This is used when doing health check against offline backend (see \(dqfail\(dq parameter in \fB\-\-backend\fP option). It is also used to limit the maximum interval to temporarily disable backend when nghttpx failed to connect to it. These intervals are calculated using exponential backoff, and consecutive failed attempts increase the interval. This option caps its maximum value. .sp Default: \fB2m\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-min\-write\-rate= Set the minimum rate of stream data that client must read from frontend connection. The specifies the number of bytes per second. If client is unable to read at least the given rate, the connection is closed. The implementation extends the timeout by the stream data to send. Use \fB\-\-frontend\-initial\-write\-rate\-timeout\fP to specify the initial timeout. Use \fB\-\-frontend\-max\-write\-rate\-timeout\fP to set the maximum timeout. Setting 0 disables this feature. .sp Default: \fB0\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-initial\-write\-rate\-timeout= Set the initial timeout set by \fB\-\-frontend\-min\-write\-rate\fP feature. .sp Default: \fB10s\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-max\-write\-rate\-timeout= Set the maximum timeout set by \fB\-\-frontend\-min\-write\-rate\fP feature. .sp Default: \fB1m\fP .UNINDENT .SS SSL/TLS .INDENT 0.0 .TP .B \-\-ciphers= Set allowed cipher list for frontend connection. The format of the string is described in OpenSSL ciphers(1). This option sets cipher suites for TLSv1.2. Use \fB\-\-tls13\-ciphers\fP for TLSv1.3. .sp Default: \fBECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:DHE\-RSA\-AES128\-GCM\-SHA256:DHE\-RSA\-AES256\-GCM\-SHA384\fP .UNINDENT .INDENT 0.0 .TP .B \-\-tls13\-ciphers= Set allowed cipher list for frontend connection. The format of the string is described in OpenSSL ciphers(1). This option sets cipher suites for TLSv1.3. Use \fB\-\-ciphers\fP for TLSv1.2. .sp Default: \fBTLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256\fP .UNINDENT .INDENT 0.0 .TP .B \-\-client\-ciphers= Set allowed cipher list for backend connection. The format of the string is described in OpenSSL ciphers(1). This option sets cipher suites for TLSv1.2. Use \fB\-\-tls13\-client\-ciphers\fP for TLSv1.3. .sp Default: \fBECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:DHE\-RSA\-AES128\-GCM\-SHA256:DHE\-RSA\-AES256\-GCM\-SHA384\fP .UNINDENT .INDENT 0.0 .TP .B \-\-tls13\-client\-ciphers= Set allowed cipher list for backend connection. The format of the string is described in OpenSSL ciphers(1). This option sets cipher suites for TLSv1.3. Use \fB\-\-client\-ciphers\fP for TLSv1.2. .sp Default: \fBTLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256\fP .UNINDENT .INDENT 0.0 .TP .B \-\-groups= Set the supported group list for frontend connections. is a colon separated list of group NID or names in the preference order. The supported curves depend on the linked OpenSSL library. This function requires OpenSSL >= 1.0.2. .sp Default: \fBX25519:P\-256:P\-384:P\-521\fP .UNINDENT .INDENT 0.0 .TP .B \-k, \-\-insecure Don\(aqt verify backend server\(aqs certificate if TLS is enabled for backend connections. .UNINDENT .INDENT 0.0 .TP .B \-\-cacert= Set path to trusted CA certificate file. It is used in backend TLS connections to verify peer\(aqs certificate. The file must be in PEM format. It can contain multiple certificates. If the linked OpenSSL is configured to load system wide certificates, they are loaded at startup regardless of this option. .UNINDENT .INDENT 0.0 .TP .B \-\-private\-key\-passwd\-file= Path to file that contains password for the server\(aqs private key. If none is given and the private key is password protected it\(aqll be requested interactively. .UNINDENT .INDENT 0.0 .TP .B \-\-subcert=:[[;]...] Specify additional certificate and private key file. nghttpx will choose certificates based on the hostname indicated by client using TLS SNI extension. If nghttpx is built with OpenSSL >= 1.0.2, the signature algorithms (e.g., ECDSA+SHA256) presented by client are also taken into consideration. This allows nghttpx to send ML\-DSA or ECDSA certificate to modern clients, while sending RSA based certificate to older clients. This option can be used multiple times. .sp Additional parameter can be specified in . The available is \(dqsct\-dir=

\(dq. .sp \(dqsct\-dir=\(dq specifies the path to directory which contains *.sct files for TLS signed_certificate_timestamp extension (RFC 6962). This feature requires OpenSSL >= 1.0.2. See also \fB\-\-tls\-sct\-dir\fP option. .UNINDENT .INDENT 0.0 .TP .B \-\-dh\-param\-file= Path to file that contains DH parameters in PEM format. Without this option, DHE cipher suites are not available. .UNINDENT .INDENT 0.0 .TP .B \-\-alpn\-list= Comma delimited list of ALPN protocol identifier sorted in the order of preference. That means most desirable protocol comes first. The parameter must be delimited by a single comma only and any white spaces are treated as a part of protocol string. .sp Default: \fBh2,http/1.1\fP .UNINDENT .INDENT 0.0 .TP .B \-\-verify\-client Require and verify client certificate. .UNINDENT .INDENT 0.0 .TP .B \-\-verify\-client\-cacert= Path to file that contains CA certificates to verify client certificate. The file must be in PEM format. It can contain multiple certificates. .UNINDENT .INDENT 0.0 .TP .B \-\-verify\-client\-tolerate\-expired Accept expired client certificate. Operator should handle the expired client certificate by some means (e.g., mruby script). Otherwise, this option might cause a security risk. .UNINDENT .INDENT 0.0 .TP .B \-\-client\-private\-key\-file= Path to file that contains client private key used in backend client authentication. .UNINDENT .INDENT 0.0 .TP .B \-\-client\-cert\-file= Path to file that contains client certificate used in backend client authentication. .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-min\-proto\-version= Specify minimum SSL/TLS protocol. The name matching is done in case\-insensitive manner. The versions between \fB\-\-tls\-min\-proto\-version\fP and \fB\-\-tls\-max\-proto\-version\fP are enabled. If the protocol list advertised by client does not overlap this range, you will receive the error message \(dqunknown protocol\(dq. The available versions are: TLSv1.3 and TLSv1.2 .sp Default: \fBTLSv1.2\fP .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-max\-proto\-version= Specify maximum SSL/TLS protocol. The name matching is done in case\-insensitive manner. The versions between \fB\-\-tls\-min\-proto\-version\fP and \fB\-\-tls\-max\-proto\-version\fP are enabled. If the protocol list advertised by client does not overlap this range, you will receive the error message \(dqunknown protocol\(dq. The available versions are: TLSv1.3 and TLSv1.2 .sp Default: \fBTLSv1.3\fP .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-ticket\-key\-file= Path to file that contains random data to construct TLS session ticket parameters. If aes\-128\-cbc is given in \fB\-\-tls\-ticket\-key\-cipher\fP, the file must contain exactly 48 bytes. If aes\-256\-cbc is given in \fB\-\-tls\-ticket\-key\-cipher\fP, the file must contain exactly 80 bytes. This options can be used repeatedly to specify multiple ticket parameters. If several files are given, only the first key is used to encrypt TLS session tickets. Other keys are accepted but server will issue new session ticket with first key. This allows session key rotation. Please note that key rotation does not occur automatically. User should rearrange files or change options values and restart nghttpx gracefully. If opening or reading given file fails, all loaded keys are discarded and it is treated as if none of this option is given. If this option is not given or an error occurred while opening or reading a file, key is generated every 1 hour internally and they are valid for 12 hours. This is recommended if ticket key sharing between nghttpx instances is not required. .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-ticket\-key\-memcached=,[;tls] Specify address of memcached server to get TLS ticket keys for session resumption. This enables shared TLS ticket key between multiple nghttpx instances. nghttpx does not set TLS ticket key to memcached. The external ticket key generator is required. nghttpx just gets TLS ticket keys from memcached, and use them, possibly replacing current set of keys. It is up to extern TLS ticket key generator to rotate keys frequently. See \(dqTLS SESSION TICKET RESUMPTION\(dq section in manual page to know the data format in memcached entry. Optionally, memcached connection can be encrypted with TLS by specifying \(dqtls\(dq parameter. .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-ticket\-key\-memcached\-address\-family=(auto|IPv4|IPv6) Specify address family of memcached connections to get TLS ticket keys. If \(dqauto\(dq is given, both IPv4 and IPv6 are considered. If \(dqIPv4\(dq is given, only IPv4 address is considered. If \(dqIPv6\(dq is given, only IPv6 address is considered. .sp Default: \fBauto\fP .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-ticket\-key\-memcached\-interval= Set interval to get TLS ticket keys from memcached. .sp Default: \fB10m\fP .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-ticket\-key\-memcached\-max\-retry= Set maximum number of consecutive retries before abandoning TLS ticket key retrieval. If this number is reached, the attempt is considered as failure, and \(dqfailure\(dq count is incremented by 1, which contributed to the value controlled \fB\-\-tls\-ticket\-key\-memcached\-max\-fail\fP option. .sp Default: \fB3\fP .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-ticket\-key\-memcached\-max\-fail= Set maximum number of consecutive failure before disabling TLS ticket until next scheduled key retrieval. .sp Default: \fB2\fP .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-ticket\-key\-cipher= Specify cipher to encrypt TLS session ticket. Specify either aes\-128\-cbc or aes\-256\-cbc. By default, aes\-128\-cbc is used. .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-ticket\-key\-memcached\-cert\-file= Path to client certificate for memcached connections to get TLS ticket keys. .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-ticket\-key\-memcached\-private\-key\-file= Path to client private key for memcached connections to get TLS ticket keys. .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-dyn\-rec\-warmup\-threshold= Specify the threshold size for TLS dynamic record size behaviour. During a TLS session, after the threshold number of bytes have been written, the TLS record size will be increased to the maximum allowed (16K). The max record size will continue to be used on the active TLS session. After \fB\-\-tls\-dyn\-rec\-idle\-timeout\fP has elapsed, the record size is reduced to 1300 bytes. Specify 0 to always use the maximum record size, regardless of idle period. This behaviour applies to all TLS based frontends, and TLS HTTP/2 backends. .sp Default: \fB1M\fP .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-dyn\-rec\-idle\-timeout= Specify TLS dynamic record size behaviour timeout. See \fB\-\-tls\-dyn\-rec\-warmup\-threshold\fP for more information. This behaviour applies to all TLS based frontends, and TLS HTTP/2 backends. .sp Default: \fB1s\fP .UNINDENT .INDENT 0.0 .TP .B \-\-no\-http2\-cipher\-block\-list Allow block listed cipher suite on frontend HTTP/2 connection. See \% for the complete HTTP/2 cipher suites block list. .UNINDENT .INDENT 0.0 .TP .B \-\-client\-no\-http2\-cipher\-block\-list Allow block listed cipher suite on backend HTTP/2 connection. See \% for the complete HTTP/2 cipher suites block list. .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-sct\-dir= Specifies the directory where *.sct files exist. All *.sct files in are read, and sent as extension_data of TLS signed_certificate_timestamp (RFC 6962) to client. These *.sct files are for the certificate specified in positional command\-line argument , or certificate option in configuration file. For additional certificates, use \fB\-\-subcert\fP option. This option requires OpenSSL >= 1.0.2. .UNINDENT .INDENT 0.0 .TP .B \-\-psk\-secrets= Read list of PSK identity and secrets from . This is used for frontend connection. The each line of input file is formatted as :, where is PSK identity, and is secret in hex. An empty line, and line which starts with \(aq#\(aq are skipped. The default enabled cipher list might not contain any PSK cipher suite. In that case, desired PSK cipher suites must be enabled using \fB\-\-ciphers\fP option. The desired PSK cipher suite may be block listed by HTTP/2. To use those cipher suites with HTTP/2, consider to use \fB\-\-no\-http2\-cipher\-block\-list\fP option. But be aware its implications. .UNINDENT .INDENT 0.0 .TP .B \-\-client\-psk\-secrets= Read PSK identity and secrets from . This is used for backend connection. The each line of input file is formatted as :, where is PSK identity, and is secret in hex. An empty line, and line which starts with \(aq#\(aq are skipped. The first identity and secret pair encountered is used. The default enabled cipher list might not contain any PSK cipher suite. In that case, desired PSK cipher suites must be enabled using \fB\-\-client\-ciphers\fP option. The desired PSK cipher suite may be block listed by HTTP/2. To use those cipher suites with HTTP/2, consider to use \fB\-\-client\-no\-http2\-cipher\-block\-list\fP option. But be aware its implications. .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-no\-postpone\-early\-data By default, except for QUIC connections, nghttpx postpones forwarding HTTP requests sent in early data, including those sent in partially in it, until TLS handshake finishes. If all backend server recognizes \(dqEarly\-Data\(dq header field, using this option makes nghttpx not postpone forwarding request and get full potential of 0\-RTT data. .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-max\-early\-data= Sets the maximum amount of 0\-RTT data that server accepts. .sp Default: \fB16K\fP .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-ktls Enable ktls. .UNINDENT .INDENT 0.0 .TP .B \-\-ech\-config\-file= Read Encrypted Client Hello (ECH) server configuration from . See \fB\-\-ech\-retry\-config\-file\fP for details. .UNINDENT .INDENT 0.0 .TP .B \-\-ech\-retry\-config\-file= This option and \fB\-\-ech\-config\-file\fP option read Encrypted Client Hello (ECH) server configuration from . If \fB\-\-ech\-retry\-config\-file\fP is used, the configurations are included in the retry configurations. The file format must be PEM ECH file described in RFC 9934. These options can be used repeatedly to read multiple files. \fB\-\-ech\-retry\-config\-file\fP must be used at least once when enabling ECH. .UNINDENT .SS HTTP/2 .INDENT 0.0 .TP .B \-c, \-\-frontend\-http2\-max\-concurrent\-streams= Set the maximum number of the concurrent streams in one frontend HTTP/2 session. .sp Default: \fB100\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-http2\-max\-concurrent\-streams= Set the maximum number of the concurrent streams in one backend HTTP/2 session. This sets maximum number of concurrent opened pushed streams. The maximum number of concurrent requests are set by a remote server. .sp Default: \fB100\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http2\-window\-size= Sets the per\-stream initial window size of HTTP/2 frontend connection. .sp Default: \fB65535\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http2\-connection\-window\-size= Sets the per\-connection window size of HTTP/2 frontend connection. .sp Default: \fB65535\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-http2\-window\-size= Sets the initial window size of HTTP/2 backend connection. .sp Default: \fB65535\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-http2\-connection\-window\-size= Sets the per\-connection window size of HTTP/2 backend connection. .sp Default: \fB2147483647\fP .UNINDENT .INDENT 0.0 .TP .B \-\-http2\-no\-cookie\-crumbling Don\(aqt crumble cookie header field. .UNINDENT .INDENT 0.0 .TP .B \-\-padding= Add at most bytes to a HTTP/2 frame payload as padding. Specify 0 to disable padding. This option is meant for debugging purpose and not intended to enhance protocol security. .UNINDENT .INDENT 0.0 .TP .B \-\-no\-server\-push Disable HTTP/2 server push. Server push is supported by default mode and HTTP/2 frontend via Link header field. It is also supported if both frontend and backend are HTTP/2 in default mode. In this case, server push from backend session is relayed to frontend, and server push via Link header field is also supported. .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http2\-optimize\-write\-buffer\-size (Experimental) Enable write buffer size optimization in frontend HTTP/2 TLS connection. This optimization aims to reduce write buffer size so that it only contains bytes which can send immediately. This makes server more responsive to prioritized HTTP/2 stream because the buffering of lower priority stream is reduced. This option is only effective on recent Linux platform. .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http2\-optimize\-window\-size (Experimental) Automatically tune connection level window size of frontend HTTP/2 TLS connection. If this feature is enabled, connection window size starts with the default window size, 65535 bytes. nghttpx automatically adjusts connection window size based on TCP receiving window size. The maximum window size is capped by the value specified by \fB\-\-frontend\-http2\-connection\-window\-size\fP\&. Since the stream is subject to stream level window size, it should be adjusted using \fB\-\-frontend\-http2\-window\-size\fP option as well. This option is only effective on recent Linux platform. .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http2\-encoder\-dynamic\-table\-size= Specify the maximum dynamic table size of HPACK encoder in the frontend HTTP/2 connection. The decoder (client) specifies the maximum dynamic table size it accepts. Then the negotiated dynamic table size is the minimum of this option value and the value which client specified. .sp Default: \fB4K\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http2\-decoder\-dynamic\-table\-size= Specify the maximum dynamic table size of HPACK decoder in the frontend HTTP/2 connection. .sp Default: \fB4K\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-http2\-encoder\-dynamic\-table\-size= Specify the maximum dynamic table size of HPACK encoder in the backend HTTP/2 connection. The decoder (backend) specifies the maximum dynamic table size it accepts. Then the negotiated dynamic table size is the minimum of this option value and the value which backend specified. .sp Default: \fB4K\fP .UNINDENT .INDENT 0.0 .TP .B \-\-backend\-http2\-decoder\-dynamic\-table\-size= Specify the maximum dynamic table size of HPACK decoder in the backend HTTP/2 connection. .sp Default: \fB4K\fP .UNINDENT .SS Mode .INDENT 0.0 .TP .B (default mode) Accept HTTP/2, and HTTP/1.1 over SSL/TLS. \(dqno\-tls\(dq parameter is used in \fB\-\-frontend\fP option, accept HTTP/2 and HTTP/1.1 over cleartext TCP. The incoming HTTP/1.1 connection can be upgraded to HTTP/2 through HTTP Upgrade. .UNINDENT .INDENT 0.0 .TP .B \-s, \-\-http2\-proxy Like default mode, but enable forward proxy. This is so called HTTP/2 proxy mode. .UNINDENT .SS Logging .INDENT 0.0 .TP .B \-L, \-\-log\-level= Set the severity level of log output. must be one of INFO, NOTICE, WARN, ERROR and FATAL. .sp Default: \fBNOTICE\fP .UNINDENT .INDENT 0.0 .TP .B \-\-accesslog\-file= Set path to write access log. To reopen file, send USR1 signal to nghttpx. .UNINDENT .INDENT 0.0 .TP .B \-\-accesslog\-syslog Send access log to syslog. If this option is used, \fB\-\-accesslog\-file\fP option is ignored. .UNINDENT .INDENT 0.0 .TP .B \-\-accesslog\-format= Specify format string for access log. The default format is combined format. The following variables are available: .INDENT 7.0 .IP \(bu 2 $remote_addr: client IP address. .IP \(bu 2 $time_local: local time in Common Log format. .IP \(bu 2 $time_iso8601: local time in ISO 8601 format. .IP \(bu 2 $request: HTTP request line. .IP \(bu 2 $status: HTTP response status code. .IP \(bu 2 $body_bytes_sent: the number of bytes sent to client as response body. .IP \(bu 2 $http_: value of HTTP request header where \(aq_\(aq in is replaced with \(aq\-\(aq. .IP \(bu 2 $remote_port: client port. .IP \(bu 2 $server_port: server port. .IP \(bu 2 $request_time: request processing time in seconds with milliseconds resolution. .IP \(bu 2 $pid: PID of the running process. .IP \(bu 2 $alpn: ALPN identifier of the protocol which generates the response. For HTTP/1, ALPN is always http/1.1, regardless of minor version. .IP \(bu 2 $tls_cipher: cipher used for SSL/TLS connection. .IP \(bu 2 $tls_client_fingerprint_sha256: SHA\-256 fingerprint of client certificate. .IP \(bu 2 $tls_client_fingerprint_sha1: SHA\-1 fingerprint of client certificate. .IP \(bu 2 $tls_client_subject_name: subject name in client certificate. .IP \(bu 2 $tls_client_issuer_name: issuer name in client certificate. .IP \(bu 2 $tls_client_serial: serial number in client certificate. .IP \(bu 2 $tls_protocol: protocol for SSL/TLS connection. .IP \(bu 2 $tls_session_id: session ID for SSL/TLS connection. .IP \(bu 2 $tls_session_reused: \(dqr\(dq if SSL/TLS session was reused. Otherwise, \(dq.\(dq .IP \(bu 2 $tls_sni: SNI server name for SSL/TLS connection. .IP \(bu 2 $tls_ech_accepted: \(dqe\(dq if ECH was accepted in SSL/TLS session. Otherwise, \(dq.\(dq .IP \(bu 2 $backend_host: backend host used to fulfill the request. \(dq\-\(dq if backend host is not available. .IP \(bu 2 $backend_port: backend port used to fulfill the request. \(dq\-\(dq if backend host is not available. .IP \(bu 2 $method: HTTP method .IP \(bu 2 $path: Request path including query. For CONNECT request, authority is recorded. .IP \(bu 2 $path_without_query: $path up to the first \(aq?\(aq character. For CONNECT request, authority is recorded. .IP \(bu 2 $protocol_version: HTTP version (e.g., HTTP/1.1, HTTP/2) .UNINDENT .sp The variable can be enclosed by \(dq{\(dq and \(dq}\(dq for disambiguation (e.g., ${remote_addr}). .sp Default: \fB$remote_addr \- \- [$time_local] \(dq$request\(dq $status $body_bytes_sent \(dq$http_referer\(dq \(dq$http_user_agent\(dq\fP .UNINDENT .INDENT 0.0 .TP .B \-\-accesslog\-write\-early Write access log when response header fields are received from backend rather than when request transaction finishes. .UNINDENT .INDENT 0.0 .TP .B \-\-errorlog\-file= Set path to write error log. To reopen file, send USR1 signal to nghttpx. stderr will be redirected to the error log file unless \fB\-\-errorlog\-syslog\fP is used. .sp Default: \fB/dev/stderr\fP .UNINDENT .INDENT 0.0 .TP .B \-\-errorlog\-syslog Send error log to syslog. If this option is used, \fB\-\-errorlog\-file\fP option is ignored. .UNINDENT .INDENT 0.0 .TP .B \-\-syslog\-facility= Set syslog facility to . .sp Default: \fBdaemon\fP .UNINDENT .SS HTTP .INDENT 0.0 .TP .B \-\-add\-x\-forwarded\-for Append X\-Forwarded\-For header field to the downstream request. .UNINDENT .INDENT 0.0 .TP .B \-\-strip\-incoming\-x\-forwarded\-for Strip X\-Forwarded\-For header field from inbound client requests. .UNINDENT .INDENT 0.0 .TP .B \-\-no\-add\-x\-forwarded\-proto Don\(aqt append additional X\-Forwarded\-Proto header field to the backend request. If inbound client sets X\-Forwarded\-Proto, and \fB\-\-no\-strip\-incoming\-x\-forwarded\-proto\fP option is used, they are passed to the backend. .UNINDENT .INDENT 0.0 .TP .B \-\-no\-strip\-incoming\-x\-forwarded\-proto Don\(aqt strip X\-Forwarded\-Proto header field from inbound client requests. .UNINDENT .INDENT 0.0 .TP .B \-\-add\-forwarded= Append RFC 7239 Forwarded header field with parameters specified in comma delimited list . The supported parameters are \(dqby\(dq, \(dqfor\(dq, \(dqhost\(dq, and \(dqproto\(dq. By default, the value of \(dqby\(dq and \(dqfor\(dq parameters are obfuscated string. See \fB\-\-forwarded\-by\fP and \fB\-\-forwarded\-for\fP options respectively. Note that nghttpx does not translate non\-standard X\-Forwarded\-* header fields into Forwarded header field, and vice versa. .UNINDENT .INDENT 0.0 .TP .B \-\-strip\-incoming\-forwarded Strip Forwarded header field from inbound client requests. .UNINDENT .INDENT 0.0 .TP .B \-\-forwarded\-by=(obfuscated|ip|) Specify the parameter value sent out with \(dqby\(dq parameter of Forwarded header field. If \(dqobfuscated\(dq is given, the string is randomly generated at startup. If \(dqip\(dq is given, the interface address of the connection, including port number, is sent with \(dqby\(dq parameter. In case of UNIX domain socket, \(dqlocalhost\(dq is used instead of address and port. User can also specify the static obfuscated string. The limitation is that it must start with \(dq_\(dq, and only consists of character set [A\-Za\-z0\-9._\-], as described in RFC 7239. .sp Default: \fBobfuscated\fP .UNINDENT .INDENT 0.0 .TP .B \-\-forwarded\-for=(obfuscated|ip) Specify the parameter value sent out with \(dqfor\(dq parameter of Forwarded header field. If \(dqobfuscated\(dq is given, the string is randomly generated for each client connection. If \(dqip\(dq is given, the remote client address of the connection, without port number, is sent with \(dqfor\(dq parameter. In case of UNIX domain socket, \(dqlocalhost\(dq is used instead of address. .sp Default: \fBobfuscated\fP .UNINDENT .INDENT 0.0 .TP .B \-\-no\-via Don\(aqt append to Via header field. If Via header field is received, it is left unaltered. .UNINDENT .INDENT 0.0 .TP .B \-\-no\-strip\-incoming\-early\-data Don\(aqt strip Early\-Data header field from inbound client requests. .UNINDENT .INDENT 0.0 .TP .B \-\-no\-location\-rewrite Don\(aqt rewrite location header field in default mode. When \fB\-\-http2\-proxy\fP is used, location header field will not be altered regardless of this option. .UNINDENT .INDENT 0.0 .TP .B \-\-host\-rewrite Rewrite host and :authority header fields in default mode. When \fB\-\-http2\-proxy\fP is used, these headers will not be altered regardless of this option. .UNINDENT .INDENT 0.0 .TP .B \-\-altsvc= Specify protocol ID, port, host and origin of alternative service. , and are optional. Empty and are allowed and they are treated as nothing is specified. They are advertised in alt\-svc header field only in HTTP/1.1 frontend. This option can be used multiple times to specify multiple alternative services. Example: \fB\-\-altsvc\fP=\(dqh2,443,,,ma=3600; persist=1\(dq .UNINDENT .INDENT 0.0 .TP .B \-\-http2\-altsvc= Just like \fB\-\-altsvc\fP option, but this altsvc is only sent in HTTP/2 frontend. .UNINDENT .INDENT 0.0 .TP .B \-\-add\-request\-header=
Specify additional header field to add to request header set. The field name must be lowercase. This option just appends header field and won\(aqt replace anything already set. This option can be used several times to specify multiple header fields. Example: \fB\-\-add\-request\-header\fP=\(dqfoo: bar\(dq .UNINDENT .INDENT 0.0 .TP .B \-\-add\-response\-header=
Specify additional header field to add to response header set. The field name must be lowercase. This option just appends header field and won\(aqt replace anything already set. This option can be used several times to specify multiple header fields. Example: \fB\-\-add\-response\-header\fP=\(dqfoo: bar\(dq .UNINDENT .INDENT 0.0 .TP .B \-\-request\-header\-field\-buffer= Set maximum buffer size for incoming HTTP request header field list. This is the sum of header name and value in bytes. If trailer fields exist, they are counted towards this number. .sp Default: \fB64K\fP .UNINDENT .INDENT 0.0 .TP .B \-\-max\-request\-header\-fields= Set maximum number of incoming HTTP request header fields. If trailer fields exist, they are counted towards this number. .sp Default: \fB100\fP .UNINDENT .INDENT 0.0 .TP .B \-\-response\-header\-field\-buffer= Set maximum buffer size for incoming HTTP response header field list. This is the sum of header name and value in bytes. If trailer fields exist, they are counted towards this number. .sp Default: \fB64K\fP .UNINDENT .INDENT 0.0 .TP .B \-\-max\-response\-header\-fields= Set maximum number of incoming HTTP response header fields. If trailer fields exist, they are counted towards this number. .sp Default: \fB500\fP .UNINDENT .INDENT 0.0 .TP .B \-\-error\-page=(|*)= Set file path to custom error page served when nghttpx originally generates HTTP error status code . must be greater than or equal to 400, and at most 599. If \(dq*\(dq is used instead of , it matches all HTTP status code. If error status code comes from backend server, the custom error pages are not used. .UNINDENT .INDENT 0.0 .TP .B \-\-server\-name= Change server response header field value to . .sp Default: \fBnghttpx\fP .UNINDENT .INDENT 0.0 .TP .B \-\-no\-server\-rewrite Don\(aqt rewrite server header field in default mode. When \fB\-\-http2\-proxy\fP is used, these headers will not be altered regardless of this option. .UNINDENT .INDENT 0.0 .TP .B \-\-redirect\-https\-port= Specify the port number which appears in Location header field when redirect to HTTPS URI is made due to \(dqredirect\-if\-not\-tls\(dq parameter in \fB\-\-backend\fP option. .sp Default: \fB443\fP .UNINDENT .INDENT 0.0 .TP .B \-\-require\-http\-scheme Always require http or https scheme in HTTP request. It also requires that https scheme must be used for an encrypted connection. Otherwise, http scheme must be used. This option is recommended for a server deployment which directly faces clients and the services it provides only require http or https scheme. .UNINDENT .SS API .INDENT 0.0 .TP .B \-\-api\-max\-request\-body= Set the maximum size of request body for API request. .sp Default: \fB32M\fP .UNINDENT .SS DNS .INDENT 0.0 .TP .B \-\-dns\-cache\-timeout= Set duration that cached DNS results remain valid. Note that nghttpx caches the unsuccessful results as well. .sp Default: \fB10s\fP .UNINDENT .INDENT 0.0 .TP .B \-\-dns\-lookup\-timeout= Set timeout that DNS server is given to respond to the initial DNS query. For the 2nd and later queries, server is given time based on this timeout, and it is scaled linearly. .sp Default: \fB250ms\fP .UNINDENT .INDENT 0.0 .TP .B \-\-dns\-max\-try= Set the number of DNS query before nghttpx gives up name lookup. .sp Default: \fB3\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-max\-requests= The number of requests that single frontend connection can process. For HTTP/2, this is the number of streams in one HTTP/2 connection. For HTTP/1, this is the number of keep alive requests. This is hint to nghttpx, and it may allow additional few requests. The default value is unlimited. .UNINDENT .SS Debug .INDENT 0.0 .TP .B \-\-frontend\-http2\-dump\-request\-header= Dumps request headers received by HTTP/2 frontend to the file denoted in . The output is done in HTTP/1 header field format and each header block is followed by an empty line. This option is not thread safe and MUST NOT be used with option \fB\-n\fP, where >= 2. .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http2\-dump\-response\-header= Dumps response headers sent from HTTP/2 frontend to the file denoted in . The output is done in HTTP/1 header field format and each header block is followed by an empty line. This option is not thread safe and MUST NOT be used with option \fB\-n\fP, where >= 2. .UNINDENT .INDENT 0.0 .TP .B \-o, \-\-frontend\-frame\-debug Print HTTP/2 frames in frontend to stderr. This option is not thread safe and MUST NOT be used with option \fB\-n\fP=N, where N >= 2. .UNINDENT .SS Process .INDENT 0.0 .TP .B \-D, \-\-daemon Run in a background. If \fB\-D\fP is used, the current working directory is changed to \(aq\fI/\fP\(aq. .UNINDENT .INDENT 0.0 .TP .B \-\-pid\-file= Set path to save PID of this program. .UNINDENT .INDENT 0.0 .TP .B \-\-user= Run this program as . This option is intended to be used to drop root privileges. .UNINDENT .INDENT 0.0 .TP .B \-\-single\-process Run this program in a single process mode for debugging purpose. Without this option, nghttpx creates at least 2 processes: main and worker processes. If this option is used, main and worker are unified into a single process. nghttpx still spawns additional process if neverbleed is used. In the single process mode, the signal handling feature is disabled. .UNINDENT .INDENT 0.0 .TP .B \-\-max\-worker\-processes= The maximum number of worker processes. nghttpx spawns new worker process when it reloads its configuration. The previous worker process enters graceful termination period and will terminate when it finishes handling the existing connections. However, if reloading configurations happen very frequently, the worker processes might be piled up if they take a bit long time to finish the existing connections. With this option, if the number of worker processes exceeds the given value, the oldest worker process is terminated immediately. Specifying 0 means no limit and it is the default behaviour. .UNINDENT .INDENT 0.0 .TP .B \-\-worker\-process\-grace\-shutdown\-period= Maximum period for a worker process to terminate gracefully. When a worker process enters in graceful shutdown period (e.g., when nghttpx reloads its configuration) and it does not finish handling the existing connections in the given period of time, it is immediately terminated. Specifying 0 means no limit and it is the default behaviour. .UNINDENT .SS Scripting .INDENT 0.0 .TP .B \-\-mruby\-file= Set mruby script file .UNINDENT .INDENT 0.0 .TP .B \-\-ignore\-per\-pattern\-mruby\-error Ignore mruby compile error for per\-pattern mruby script file. If error occurred, it is treated as if no mruby file were specified for the pattern. .UNINDENT .SS HTTP/3 and QUIC .INDENT 0.0 .TP .B \-\-frontend\-quic\-idle\-timeout= Specify an idle timeout for QUIC connection. .sp Default: \fB30s\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-quic\-debug\-log Output QUIC debug log to \fI/dev/stderr.\fP .UNINDENT .INDENT 0.0 .TP .B \-\-quic\-bpf\-program\-file= Specify a path to eBPF program file reuseport_kern.o to direct an incoming QUIC UDP datagram to a correct socket. .sp Default: \fB/usr/local/lib/nghttp2/reuseport_kern.o\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-quic\-early\-data Enable early data on frontend QUIC connections. nghttpx sends \(dqEarly\-Data\(dq header field to a backend server if a request is received in early data and handshake has not finished. All backend servers should deal with possibly replayed requests. .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-quic\-qlog\-dir= Specify a directory where a qlog file is written for frontend QUIC connections. A qlog file is created per each QUIC connection. The file name is ISO8601 basic format, followed by \(dq\-\(dq, server Source Connection ID and \(dq.sqlog\(dq. .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-quic\-require\-token Require an address validation token for a frontend QUIC connection. Server sends a token in Retry packet or NEW_TOKEN frame in the previous connection. .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-quic\-congestion\-controller= Specify a congestion controller algorithm for a frontend QUIC connection. should be either \(dqcubic\(dq or \(dqbbr\(dq. .sp Default: \fBcubic\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-quic\-secret\-file= Path to file that contains secure random data to be used as QUIC keying materials. It is used to derive keys for encrypting tokens and Connection IDs. It is not used to encrypt QUIC packets. Each line of this file must contain exactly 136 bytes hex\-encoded string (when decoded the byte string is 68 bytes long). The first 3 bits of decoded byte string are used to identify the keying material. An empty line or a line which starts \(aq#\(aq is ignored. The file can contain more than one keying materials. Because the identifier is 3 bits, at most 8 keying materials are read and the remaining data is discarded. The first keying material in the file is primarily used for encryption and decryption for new connection. The other ones are used to decrypt data for the existing connections. Specifying multiple keying materials enables key rotation. Please note that key rotation does not occur automatically. User should update files or change options values and restart nghttpx gracefully. If opening or reading given file fails, all loaded keying materials are discarded and it is treated as if none of this option is given. If this option is not given or an error occurred while opening or reading a file, a keying material is generated internally on startup and reload. .UNINDENT .INDENT 0.0 .TP .B \-\-quic\-server\-id= Specify server ID encoded in Connection ID to identify this particular server instance. Connection ID is encrypted and this part is not visible in public. It must be 4 bytes long and must be encoded in hex string (which is 8 bytes long). If this option is omitted, a random server ID is generated on startup and configuration reload. .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-quic\-initial\-rtt= Specify the initial RTT of the frontend QUIC connection. .sp Default: \fB333ms\fP .UNINDENT .INDENT 0.0 .TP .B \-\-no\-quic\-bpf Disable eBPF. .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http3\-window\-size= Sets the per\-stream initial window size of HTTP/3 frontend connection. .sp Default: \fB256K\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http3\-connection\-window\-size= Sets the per\-connection window size of HTTP/3 frontend connection. .sp Default: \fB1M\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http3\-max\-window\-size= Sets the maximum per\-stream window size of HTTP/3 frontend connection. The window size is adjusted based on the receiving rate of stream data. The initial value is the value specified by \fB\-\-frontend\-http3\-window\-size\fP and the window size grows up to bytes. .sp Default: \fB6M\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http3\-max\-connection\-window\-size= Sets the maximum per\-connection window size of HTTP/3 frontend connection. The window size is adjusted based on the receiving rate of stream data. The initial value is the value specified by \fB\-\-frontend\-http3\-connection\-window\-size\fP and the window size grows up to bytes. .sp Default: \fB8M\fP .UNINDENT .INDENT 0.0 .TP .B \-\-frontend\-http3\-max\-concurrent\-streams= Set the maximum number of the concurrent streams in one frontend HTTP/3 connection. .sp Default: \fB100\fP .UNINDENT .SS Misc .INDENT 0.0 .TP .B \-\-conf= Load configuration from . Please note that nghttpx always tries to read the default configuration file if \fB\-\-conf\fP is not given. .sp Default: \fB/etc/nghttpx/nghttpx.conf\fP .UNINDENT .INDENT 0.0 .TP .B \-\-include= Load additional configurations from . File is read when configuration parser encountered this option. This option can be used multiple times, or even recursively. .UNINDENT .INDENT 0.0 .TP .B \-v, \-\-version Print version and exit. .UNINDENT .INDENT 0.0 .TP .B \-h, \-\-help Print this help and exit. .UNINDENT .sp The argument is an integer and an optional unit (e.g., 10K is 10 * 1024). Units are K, M and G (powers of 1024). .sp The argument is an integer and an optional unit (e.g., 1s is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms (hours, minutes, seconds and milliseconds, respectively). If a unit is omitted, a second is used as unit. .SH FILES .INDENT 0.0 .TP .B \fI/etc/nghttpx/nghttpx.conf\fP The default configuration file path nghttpx searches at startup. The configuration file path can be changed using \fB\-\-conf\fP option. .sp Those lines which are staring \fB#\fP are treated as comment. .sp The option name in the configuration file is the long command\-line option name with leading \fB\-\-\fP stripped (e.g., \fBfrontend\fP). Put \fB=\fP between option name and value. Don\(aqt put extra leading or trailing spaces. .sp When specifying arguments including characters which have special meaning to a shell, we usually use quotes so that shell does not interpret them. When writing this configuration file, quotes for this purpose must not be used. For example, specify additional request header field, do this: .INDENT 7.0 .INDENT 3.5 .sp .EX add\-request\-header=foo: bar .EE .UNINDENT .UNINDENT .sp instead of: .INDENT 7.0 .INDENT 3.5 .sp .EX add\-request\-header=\(dqfoo: bar\(dq .EE .UNINDENT .UNINDENT .sp The options which do not take argument in the command\-line \fItake\fP argument in the configuration file. Specify \fByes\fP as an argument (e.g., \fBhttp2\-proxy=yes\fP). If other string is given, it is ignored. .sp To specify private key and certificate file which are given as positional arguments in command\-line, use \fBprivate\-key\-file\fP and \fBcertificate\-file\fP\&. .sp \fB\-\-conf\fP option cannot be used in the configuration file and will be ignored if specified. .TP .B Error log Error log is written to stderr by default. It can be configured using \fB\-\-errorlog\-file\fP\&. The format of log message is as follows: .sp (:) .INDENT 7.0 .TP .B It is a combination of date and time when the log is written. It is in ISO 8601 format. .TP .B It is a main process ID. .TP .B It is a process ID which writes this log. .TP .B It is a thread ID which writes this log. It would be unique within . .TP .B and They are source file name, and line number which produce this log. .TP .B It is a log message body. .UNINDENT .UNINDENT .SH SIGNALS .INDENT 0.0 .TP .B SIGQUIT Shutdown gracefully. First accept pending connections and stop accepting connection. After all connections are handled, nghttpx exits. .TP .B SIGHUP Reload configuration file given in \fB\-\-conf\fP\&. .TP .B SIGUSR1 Reopen log files. .UNINDENT .sp SIGUSR2 .INDENT 0.0 .INDENT 3.5 Fork and execute nghttpx. It will execute the binary in the same path with same command\-line arguments and environment variables. As of nghttpx version 1.20.0, the new main process sends SIGQUIT to the original main process when it is ready to serve requests. For the earlier versions of nghttpx, user has to send SIGQUIT to the original main process. .sp The difference between SIGUSR2 (+ SIGQUIT) and SIGHUP is that former is usually used to execute new binary, and the main process is newly spawned. On the other hand, the latter just reloads configuration file, and the same main process continues to exist. .UNINDENT .UNINDENT .sp \fBNote:\fP .INDENT 0.0 .INDENT 3.5 nghttpx consists of multiple processes: one process for processing these signals, and another one for processing requests. The former spawns the latter. The former is called main process, and the latter is called worker process. If neverbleed is enabled, the worker process spawns neverbleed daemon process which does RSA key processing. The above signal must be sent to the main process. If the other processes received one of them, it is ignored. This behaviour of these processes may change in the future release. In other words, in the future release, the processes other than main process may terminate upon the reception of these signals. Therefore these signals should not be sent to the processes other than main process. .UNINDENT .UNINDENT .SH SERVER PUSH .sp nghttpx supports HTTP/2 server push in default mode with Link header field. nghttpx looks for Link header field (RFC 5988 \%) in response headers from backend server and extracts URI\-reference with parameter \fBrel=preload\fP (see preload \%) and pushes those URIs to the frontend client. Here is a sample Link header field to initiate server push: .INDENT 0.0 .INDENT 3.5 .sp .EX Link: ; rel=preload Link: ; rel=preload .EE .UNINDENT .UNINDENT .sp Currently, the following restriction is applied for server push: .INDENT 0.0 .IP 1. 3 The associated stream must have method \(dqGET\(dq or \(dqPOST\(dq. The associated stream\(aqs status code must be 200. .UNINDENT .sp This limitation may be loosened in the future release. .sp nghttpx also supports server push if both frontend and backend are HTTP/2 in default mode. In this case, in addition to server push via Link header field, server push from backend is forwarded to frontend HTTP/2 session. .sp HTTP/2 server push will be disabled if \fB\-\-http2\-proxy\fP is used. .SH UNIX DOMAIN SOCKET .sp nghttpx supports UNIX domain socket with a filename for both frontend and backend connections. .sp Please note that current nghttpx implementation does not delete a socket with a filename. And on start up, if nghttpx detects that the specified socket already exists in the file system, nghttpx first deletes it. However, if SIGUSR2 is used to execute new binary and both old and new configurations use same filename, new binary does not delete the socket and continues to use it. .SH TLS SESSION RESUMPTION .sp nghttpx supports TLS session resumption through both session ID and session ticket. .SS SESSION ID RESUMPTION .sp By default, session ID is shared by all worker threads. .SS TLS SESSION TICKET RESUMPTION .sp By default, session ticket is shared by all worker threads. The automatic key rotation is also enabled by default. Every an hour, new encryption key is generated, and previous encryption key becomes decryption only key. We set session timeout to 12 hours, and thus we keep at most 12 keys. .sp If \fB\-\-tls\-ticket\-key\-memcached\fP is given, encryption keys are retrieved from memcached. nghttpx just reads keys from memcached; one has to deploy key generator program to update keys frequently (e.g., every 1 hour). The example key generator tlsticketupdate.go is available under contrib directory in nghttp2 archive. The memcached entry key is \fBnghttpx:tls\-ticket\-key\fP\&. The data format stored in memcached is the binary format described below: .INDENT 0.0 .INDENT 3.5 .sp .EX +\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+ | VERSION (4) |LEN (2)|KEY(48 or 80) ... +\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+ ^ | | | +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+ (LEN, KEY) pair can be repeated .EE .UNINDENT .UNINDENT .sp All numbers in the above figure is bytes. All integer fields are network byte order. .sp First 4 bytes integer VERSION field, which must be 1. The 2 bytes integer LEN field gives the length of following KEY field, which contains key. If \fB\-\-tls\-ticket\-key\-cipher\fP=aes\-128\-cbc is used, LEN must be 48. If \fB\-\-tls\-ticket\-key\-cipher\fP=aes\-256\-cbc is used, LEN must be 80. LEN and KEY pair can be repeated multiple times to store multiple keys. The key appeared first is used as encryption key. All the remaining keys are used as decryption only. .sp By default, connections to memcached server are not encrypted. To enable encryption, use \fBtls\fP keyword in \fB\-\-tls\-ticket\-key\-memcached\fP option. .sp If \fB\-\-tls\-ticket\-key\-file\fP is given, encryption key is read from the given file. In this case, nghttpx does not rotate key automatically. To rotate key, one has to restart nghttpx (see SIGNALS). .SH CERTIFICATE TRANSPARENCY .sp nghttpx supports TLS \fBsigned_certificate_timestamp\fP extension (RFC 6962 \%). The relevant options are \fB\-\-tls\-sct\-dir\fP and \fBsct\-dir\fP parameter in \fB\-\-subcert\fP\&. They takes a directory, and nghttpx reads all files whose extension is \fB\&.sct\fP under the directory. The \fB*.sct\fP files are encoded as \fBSignedCertificateTimestamp\fP struct described in section 3.2 of RFC 69662 \%\&. This format is the same one used by nginx\-ct \% and mod_ssl_ct \%\&. ct\-submit \% can be used to submit certificates to log servers, and obtain the \fBSignedCertificateTimestamp\fP struct which can be used with nghttpx. .SH MRUBY SCRIPTING .sp \fBWarning:\fP .INDENT 0.0 .INDENT 3.5 The current mruby extension API is experimental and not frozen. The API is subject to change in the future release. .UNINDENT .UNINDENT .sp \fBWarning:\fP .INDENT 0.0 .INDENT 3.5 Almost all string value returned from method, or attribute is a fresh new mruby string, which involves memory allocation, and copies. Therefore, it is strongly recommended to store a return value in a local variable, and use it, instead of calling method or accessing attribute repeatedly. .UNINDENT .UNINDENT .sp nghttpx allows users to extend its capability using mruby scripts. nghttpx has 2 hook points to execute mruby script: request phase and response phase. The request phase hook is invoked after all request header fields are received from client. The response phase hook is invoked after all response header fields are received from backend server. These hooks allows users to modify header fields, or common HTTP variables, like authority or request path, and even return custom response without forwarding request to backend servers. .sp There are 2 levels of mruby script invocations: global and per\-pattern. The global mruby script is set by \fB\-\-mruby\-file\fP option and is called for all requests. The per\-pattern mruby script is set by \(dqmruby\(dq parameter in \fB\-b\fP option. It is invoked for a request which matches the particular pattern. The order of hook invocation is: global request phase hook, per\-pattern request phase hook, per\-pattern response phase hook, and finally global response phase hook. If a hook returns a response, any later hooks are not invoked. The global request hook is invoked before the pattern matching is made and changing request path may affect the pattern matching. .sp Please note that request and response hooks of per\-pattern mruby script for a single request might not come from the same script. This might happen after a request hook is executed, backend failed for some reason, and at the same time, backend configuration is replaced by API request, and then the request uses new configuration on retry. The response hook from new configuration, if it is specified, will be invoked. .sp The all mruby script will be evaluated once per thread on startup, and it must instantiate object and evaluate it as the return value (e.g., \fBApp.new\fP). This object is called app object. If app object defines \fBon_req\fP method, it is called with \fBNghttpx::Env\fP object on request hook. Similarly, if app object defines \fBon_resp\fP method, it is called with \fBNghttpx::Env\fP object on response hook. For each method invocation, user can can access \fBNghttpx::Request\fP and \fBNghttpx::Response\fP objects via \fBNghttpx::Env#req\fP and \fBNghttpx::Env#resp\fP respectively. .INDENT 0.0 .TP .B Nghttpx::REQUEST_PHASE Constant to represent request phase. .UNINDENT .INDENT 0.0 .TP .B Nghttpx::RESPONSE_PHASE Constant to represent response phase. .UNINDENT .INDENT 0.0 .TP .B class Nghttpx::Env Object to represent current request specific context. .INDENT 7.0 .TP .B attribute [R] req Return \fBRequest\fP object. .UNINDENT .INDENT 7.0 .TP .B attribute [R] resp Return \fBResponse\fP object. .UNINDENT .INDENT 7.0 .TP .B attribute [R] ctx Return Ruby hash object. It persists until request finishes. So values set in request phase hook can be retrieved in response phase hook. .UNINDENT .INDENT 7.0 .TP .B attribute [R] phase Return the current phase. .UNINDENT .INDENT 7.0 .TP .B attribute [R] remote_addr Return IP address of a remote client. If connection is made via UNIX domain socket, this returns the string \(dqlocalhost\(dq. .UNINDENT .INDENT 7.0 .TP .B attribute [R] server_addr Return address of server that accepted the connection. This is a string which specified in \fB\-\-frontend\fP option, excluding port number, and not a resolved IP address. For UNIX domain socket, this is a path to UNIX domain socket. .UNINDENT .INDENT 7.0 .TP .B attribute [R] server_port Return port number of the server frontend which accepted the connection from client. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_used Return true if TLS is used on the connection. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_sni Return the TLS SNI value which client sent in this connection. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_client_fingerprint_sha256 Return the SHA\-256 fingerprint of a client certificate. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_client_fingerprint_sha1 Return the SHA\-1 fingerprint of a client certificate. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_client_issuer_name Return the issuer name of a client certificate. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_client_subject_name Return the subject name of a client certificate. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_client_serial Return the serial number of a client certificate. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_client_not_before Return the start date of a client certificate in seconds since the epoch. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_client_not_after Return the end date of a client certificate in seconds since the epoch. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_cipher Return a TLS cipher negotiated in this connection. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_protocol Return a TLS protocol version negotiated in this connection. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_session_id Return a session ID for this connection in hex string. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_session_reused Return true if, and only if a SSL/TLS session is reused. .UNINDENT .INDENT 7.0 .TP .B attribute [R] alpn Return ALPN identifier negotiated in this connection. .UNINDENT .INDENT 7.0 .TP .B attribute [R] tls_handshake_finished Return true if SSL/TLS handshake has finished. If it returns false in the request phase hook, the request is received in TLSv1.3 early data (0\-RTT) and might be vulnerable to the replay attack. nghttpx will send Early\-Data header field to backend servers to indicate this. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class Nghttpx::Request Object to represent request from client. The modification to Request object is allowed only in request phase hook. .INDENT 7.0 .TP .B attribute [R] http_version_major Return HTTP major version. .UNINDENT .INDENT 7.0 .TP .B attribute [R] http_version_minor Return HTTP minor version. .UNINDENT .INDENT 7.0 .TP .B attribute [R/W] method HTTP method. On assignment, copy of given value is assigned. We don\(aqt accept arbitrary method name. We will document them later, but well known methods, like GET, PUT and POST, are all supported. .UNINDENT .INDENT 7.0 .TP .B attribute [R/W] authority Authority (i.e., example.org), including optional port component . On assignment, copy of given value is assigned. .UNINDENT .INDENT 7.0 .TP .B attribute [R/W] scheme Scheme (i.e., http, https). On assignment, copy of given value is assigned. .UNINDENT .INDENT 7.0 .TP .B attribute [R/W] path Request path, including query component (i.e., /index.html). On assignment, copy of given value is assigned. The path does not include authority component of URI. This may include query component. nghttpx makes certain normalization for path. It decodes percent\-encoding for unreserved characters (see \%), and resolves \(dq..\(dq and \(dq.\(dq. But it may leave characters which should be percent\-encoded as is. So be careful when comparing path against desired string. .UNINDENT .INDENT 7.0 .TP .B attribute [R] headers Return Ruby hash containing copy of request header fields. Changing values in returned hash does not change request header fields actually used in request processing. Use \fBNghttpx::Request#add_header\fP or \fBNghttpx::Request#set_header\fP to change request header fields. .UNINDENT .INDENT 7.0 .TP .B add_header(key, value) Add header entry associated with key. The value can be single string or array of string. It does not replace any existing values associated with key. .UNINDENT .INDENT 7.0 .TP .B set_header(key, value) Set header entry associated with key. The value can be single string or array of string. It replaces any existing values associated with key. .UNINDENT .INDENT 7.0 .TP .B clear_headers() Clear all existing request header fields. .UNINDENT .INDENT 7.0 .TP .B push(uri) Initiate to push resource identified by \fIuri\fP\&. Only HTTP/2 protocol supports this feature. For the other protocols, this method is noop. \fIuri\fP can be absolute URI, absolute path or relative path to the current request. For absolute or relative path, scheme and authority are inherited from the current request. Currently, method is always GET. nghttpx will issue request to backend servers to fulfill this request. The request and response phase hooks will be called for pushed resource as well. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class Nghttpx::Response Object to represent response from backend server. .INDENT 7.0 .TP .B attribute [R] http_version_major Return HTTP major version. .UNINDENT .INDENT 7.0 .TP .B attribute [R] http_version_minor Return HTTP minor version. .UNINDENT .INDENT 7.0 .TP .B attribute [R/W] status HTTP status code. It must be in the range [200, 999], inclusive. The non\-final status code is not supported in mruby scripting at the moment. .UNINDENT .INDENT 7.0 .TP .B attribute [R] headers Return Ruby hash containing copy of response header fields. Changing values in returned hash does not change response header fields actually used in response processing. Use \fBNghttpx::Response#add_header\fP or \fBNghttpx::Response#set_header\fP to change response header fields. .UNINDENT .INDENT 7.0 .TP .B add_header(key, value) Add header entry associated with key. The value can be single string or array of string. It does not replace any existing values associated with key. .UNINDENT .INDENT 7.0 .TP .B set_header(key, value) Set header entry associated with key. The value can be single string or array of string. It replaces any existing values associated with key. .UNINDENT .INDENT 7.0 .TP .B clear_headers() Clear all existing response header fields. .UNINDENT .INDENT 7.0 .TP .B return(body) Return custom response \fIbody\fP to a client. When this method is called in request phase hook, the request is not forwarded to the backend, and response phase hook for this request will not be invoked. When this method is called in response phase hook, response from backend server is canceled and discarded. The status code and response header fields should be set before using this method. To set status code, use \fBNghttpx::Response#status\fP\&. If status code is not set, 200 is used. To set response header fields, \fBNghttpx::Response#add_header\fP and \fBNghttpx::Response#set_header\fP\&. When this method is invoked in response phase hook, the response headers are filled with the ones received from backend server. To send completely custom header fields, first call \fBNghttpx::Response#clear_headers\fP to erase all existing header fields, and then add required header fields. It is an error to call this method twice for a given request. .UNINDENT .INDENT 7.0 .TP .B send_info(status, headers) Send non\-final (informational) response to a client. \fIstatus\fP must be in the range [100, 199], inclusive. \fIheaders\fP is a hash containing response header fields. Its key must be a string, and the associated value must be either string or array of strings. Since this is not a final response, even if this method is invoked, request is still forwarded to a backend unless \fBNghttpx::Response#return\fP is called. This method can be called multiple times. It cannot be called after \fBNghttpx::Response#return\fP is called. .UNINDENT .UNINDENT .SS MRUBY EXAMPLES .sp Modify request path: .INDENT 0.0 .INDENT 3.5 .sp .EX class App def on_req(env) env.req.path = \(dq/apps#{env.req.path}\(dq end end App.new .EE .UNINDENT .UNINDENT .sp Don\(aqt forget to instantiate and evaluate object at the last line. .sp Restrict permission of viewing a content to a specific client addresses: .INDENT 0.0 .INDENT 3.5 .sp .EX class App def on_req(env) allowed_clients = [\(dq127.0.0.1\(dq, \(dq::1\(dq] if env.req.path.start_with?(\(dq/log/\(dq) && !allowed_clients.include?(env.remote_addr) then env.resp.status = 404 env.resp.return \(dqpermission denied\(dq end end end App.new .EE .UNINDENT .UNINDENT .SH API ENDPOINTS .sp nghttpx exposes API endpoints to manipulate it via HTTP based API. By default, API endpoint is disabled. To enable it, add a dedicated frontend for API using \fB\-\-frontend\fP option with \(dqapi\(dq parameter. All requests which come from this frontend address, will be treated as API request. .sp The response is normally JSON dictionary, and at least includes the following keys: .INDENT 0.0 .TP .B status The status of the request processing. The following values are defined: .INDENT 7.0 .TP .B Success The request was successful. .TP .B Failure The request was failed. No change has been made. .UNINDENT .TP .B code HTTP status code .UNINDENT .sp Additionally, depending on the API endpoint, \fBdata\fP key may be present, and its value contains the API endpoint specific data. .sp We wrote \(dqnormally\(dq, since nghttpx may return ordinal HTML response in some cases where the error has occurred before reaching API endpoint (e.g., header field is too large). .sp The following section describes available API endpoints. .SS POST /api/v1beta1/backendconfig .sp This API replaces the current backend server settings with the requested ones. The request method should be POST, but PUT is also acceptable. The request body must be nghttpx configuration file format. For configuration file format, see FILES section. The line separator inside the request body must be single LF (0x0A). Currently, only \fBbackend\fP option is parsed, the others are simply ignored. The semantics of this API is replace the current backend with the backend options in request body. Describe the desired set of backend severs, and nghttpx makes it happen. If there is no \fBbackend\fP option is found in request body, the current set of backend is replaced with the \fBbackend\fP option\(aqs default value, which is \fB127.0.0.1,80\fP\&. .sp The replacement is done instantly without breaking existing connections or requests. It also avoids any process creation as is the case with hot swapping with signals. .sp The one limitation is that only numeric IP address is allowed in \fBbackend\fP in request body unless \(dqdns\(dq parameter is used while non numeric hostname is allowed in command\-line or configuration file is read using \fB\-\-conf\fP\&. .SS GET /api/v1beta1/configrevision .sp This API returns configuration revision of the current nghttpx. The configuration revision is opaque string, and it changes after each reloading by SIGHUP. With this API, an external application knows that whether nghttpx has finished reloading its configuration by comparing the configuration revisions between before and after reloading. It is recommended to disable persistent (keep\-alive) connection for this purpose in order to avoid to send a request using the reused connection which may bound to an old process. .sp This API returns response including \fBdata\fP key. Its value is JSON object, and it contains at least the following key: .INDENT 0.0 .TP .B configRevision The configuration revision of the current nghttpx .UNINDENT .SH SEE ALSO .sp \fBnghttp(1)\fP, \fBnghttpd(1)\fP, \fBh2load(1)\fP .SH Author Tatsuhiro Tsujikawa .SH Copyright 2012, 2015, 2016, Tatsuhiro Tsujikawa .\" End of generated man page. PK0]ȭ%=%=man/man1/h2load.1nu[.\" Man page generated from reStructuredText .\" by the Docutils 0.22.4 manpage writer. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "H2LOAD" "1" "Jul 29, 2026" "1.70.0" "nghttp2" .SH NAME h2load \- HTTP/2 benchmarking tool .SH SYNOPSIS .sp \fBh2load\fP [OPTIONS]... [URI]... .SH DESCRIPTION .sp benchmarking tool for HTTP/2 server .INDENT 0.0 .TP .B Specify URI to access. Multiple URIs can be specified. URIs are used in this order for each client. All URIs are used, then first URI is used and then 2nd URI, and so on. The scheme, host and port in the subsequent URIs, if present, are ignored. Those in the first URI are used solely. Definition of a base URI overrides all scheme, host or port values. .UNINDENT .SH OPTIONS .INDENT 0.0 .TP .B \-n, \-\-requests= Number of requests across all clients. If it is used with \fB\-\-timing\-script\-file\fP option, this option specifies the number of requests each client performs rather than the number of requests across all clients. This option is ignored if timing\-based benchmarking is enabled (see \fB\-\-duration\fP option). .sp Default: \fB1\fP .UNINDENT .INDENT 0.0 .TP .B \-c, \-\-clients= Number of concurrent clients. With \fB\-r\fP option, this specifies the maximum number of connections to be made. .sp Default: \fB1\fP .UNINDENT .INDENT 0.0 .TP .B \-t, \-\-threads= Number of native threads. .sp Default: \fB1\fP .UNINDENT .INDENT 0.0 .TP .B \-i, \-\-input\-file= Path of a file with multiple URIs are separated by EOLs. This option will disable URIs getting from command\-line. If \(aq\-\(aq is given as , URIs will be read from stdin. URIs are used in this order for each client. All URIs are used, then first URI is used and then 2nd URI, and so on. The scheme, host and port in the subsequent URIs, if present, are ignored. Those in the first URI are used solely. Definition of a base URI overrides all scheme, host or port values. .UNINDENT .INDENT 0.0 .TP .B \-m, \-\-max\-concurrent\-streams= Max concurrent streams to issue per session. When http/1.1 is used, this specifies the number of HTTP pipelining requests in\-flight. .sp Default: \fB1\fP .UNINDENT .INDENT 0.0 .TP .B \-f, \-\-max\-frame\-size= Maximum frame size that the local endpoint is willing to receive. .sp Default: \fB16K\fP .UNINDENT .INDENT 0.0 .TP .B \-w, \-\-window\-bits= Sets the stream level initial window size to (2**)\-1. For QUIC, is capped to 26 (roughly 64MiB). It defaults to 24 (16MiB) for QUIC, and 30 for other protocols. .UNINDENT .INDENT 0.0 .TP .B \-W, \-\-connection\-window\-bits= Sets the connection level initial window size to (2**)\-1. .sp Default: \fB30\fP .UNINDENT .INDENT 0.0 .TP .B \-H, \-\-header=
Add/Override a header to the requests. .UNINDENT .INDENT 0.0 .TP .B \-\-ciphers= Set allowed cipher list for TLSv1.2 or earlier. The format of the string is described in OpenSSL ciphers(1). .sp Default: \fBECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:DHE\-RSA\-AES128\-GCM\-SHA256:DHE\-RSA\-AES256\-GCM\-SHA384\fP .UNINDENT .INDENT 0.0 .TP .B \-\-tls13\-ciphers= Set allowed cipher list for TLSv1.3. The format of the string is described in OpenSSL ciphers(1). .sp Default: \fBTLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_SHA256\fP .UNINDENT .INDENT 0.0 .TP .B \-p, \-\-no\-tls\-proto= Specify ALPN identifier of the protocol to be used when accessing http URI without SSL/TLS. Available protocols: h2c and http/1.1 .sp Default: \fBh2c\fP .UNINDENT .INDENT 0.0 .TP .B \-d, \-\-data= Post FILE to server. The request method is changed to POST. For http/1.1 connection, if \fB\-d\fP is used, the maximum number of in\-flight pipelined requests is set to 1. .UNINDENT .INDENT 0.0 .TP .B \-r, \-\-rate= Specifies the fixed rate at which connections are created. The rate must be a positive integer, representing the number of connections to be made per rate period. The maximum number of connections to be made is given in \fB\-c\fP option. This rate will be distributed among threads as evenly as possible. For example, with \fB\-t\fP2 and \fB\-r\fP4, each thread gets 2 connections per period. When the rate is 0, the program will run as it normally does, creating connections at whatever variable rate it wants. The default value for this option is 0. \fB\-r\fP and \fB\-D\fP are mutually exclusive. .UNINDENT .INDENT 0.0 .TP .B \-\-rate\-period= Specifies the time period between creating connections. The period must be a positive number, representing the length of the period in time. This option is ignored if the rate option is not used. The default value for this option is 1s. .UNINDENT .INDENT 0.0 .TP .B \-D, \-\-duration= Specifies the main duration for the measurements in case of timing\-based benchmarking. \fB\-D\fP and \fB\-r\fP are mutually exclusive. .UNINDENT .INDENT 0.0 .TP .B \-\-warm\-up\-time= Specifies the time period before starting the actual measurements, in case of timing\-based benchmarking. Needs to provided along with \fB\-D\fP option. .UNINDENT .INDENT 0.0 .TP .B \-T, \-\-connection\-active\-timeout= Specifies the maximum time that h2load is willing to keep a connection open, regardless of the activity on said connection. must be a positive integer, specifying the amount of time to wait. When no timeout value is set (either active or inactive), h2load will keep a connection open indefinitely, waiting for a response. .UNINDENT .INDENT 0.0 .TP .B \-N, \-\-connection\-inactivity\-timeout= Specifies the amount of time that h2load is willing to wait to see activity on a given connection. must be a positive integer, specifying the amount of time to wait. When no timeout value is set (either active or inactive), h2load will keep a connection open indefinitely, waiting for a response. .UNINDENT .INDENT 0.0 .TP .B \-\-timing\-script\-file= Path of a file containing one or more lines separated by EOLs. Each script line is composed of two tab\-separated fields. The first field represents the time offset from the start of execution, expressed as a positive value of milliseconds with microsecond resolution. The second field represents the URI. This option will disable URIs getting from command\-line. If \(aq\-\(aq is given as , script lines will be read from stdin. Script lines are used in order for each client. If \fB\-n\fP is given, it must be less than or equal to the number of script lines, larger values are clamped to the number of script lines. If \fB\-n\fP is not given, the number of requests will default to the number of script lines. The scheme, host and port defined in the first URI are used solely. Values contained in other URIs, if present, are ignored. Definition of a base URI overrides all scheme, host or port values. \fB\-\-timing\-script\-file\fP and \fB\-\-rps\fP are mutually exclusive. .UNINDENT .INDENT 0.0 .TP .B \-B, \-\-base\-uri=(|unix:) Specify URI from which the scheme, host and port will be used for all requests. The base URI overrides all values defined either at the command line or inside input files. If argument starts with \(dqunix:\(dq, then the rest of the argument will be treated as UNIX domain socket path. The connection is made through that path instead of TCP. In this case, scheme is inferred from the first URI appeared in the command line or inside input files as usual. .UNINDENT .INDENT 0.0 .TP .B \-\-alpn\-list= Comma delimited list of ALPN protocol identifier sorted in the order of preference. That means most desirable protocol comes first. The parameter must be delimited by a single comma only and any white spaces are treated as a part of protocol string. .sp Default: \fBh2,http/1.1\fP .UNINDENT .INDENT 0.0 .TP .B \-\-h1 Short hand for \fB\-\-alpn\-list\fP=http/1.1 \fB\-\-no\-tls\-proto\fP=http/1.1, which effectively force http/1.1 for both http and https URI. .UNINDENT .INDENT 0.0 .TP .B \-\-h3 Short hand for \fB\-\-alpn\-list\fP=h3, which effectively forces HTTP/3. .UNINDENT .INDENT 0.0 .TP .B \-\-header\-table\-size= Specify decoder header table size. .sp Default: \fB4K\fP .UNINDENT .INDENT 0.0 .TP .B \-\-encoder\-header\-table\-size= Specify encoder header table size. The decoder (server) specifies the maximum dynamic table size it accepts. Then the negotiated dynamic table size is the minimum of this option value and the value which server specified. .sp Default: \fB4K\fP .UNINDENT .INDENT 0.0 .TP .B \-\-log\-file= Write per\-request information to a file as tab\-separated columns: start time as microseconds since epoch; HTTP status code; microseconds until end of response. More columns may be added later. Rows are ordered by end\-of\- response time when using one worker thread, but may appear slightly out of order with multiple threads due to buffering. Status code is \-1 for failed streams. .UNINDENT .INDENT 0.0 .TP .B \-\-qlog\-file\-base= Enable qlog output and specify base file name for qlogs. Qlog is emitted for each connection. For a given base name \(dqbase\(dq, each output file name becomes \(dqbase.M.N.sqlog\(dq where M is worker ID and N is client ID (e.g. \(dqbase.0.3.sqlog\(dq). Only effective in QUIC runs. .UNINDENT .INDENT 0.0 .TP .B \-\-connect\-to=[:] Host and port to connect instead of using the authority in . .UNINDENT .INDENT 0.0 .TP .B \-\-rps= Specify request per second for each client. \fB\-\-rps\fP and \fB\-\-timing\-script\-file\fP are mutually exclusive. .UNINDENT .INDENT 0.0 .TP .B \-\-groups= Specify the supported groups. .sp Default: \fBX25519:P\-256:P\-384:P\-521\fP .UNINDENT .INDENT 0.0 .TP .B \-\-no\-udp\-gso Disable UDP GSO. .UNINDENT .INDENT 0.0 .TP .B \-\-max\-udp\-payload\-size= Specify the maximum outgoing UDP datagram payload size. .UNINDENT .INDENT 0.0 .TP .B \-\-ktls Enable ktls. .UNINDENT .INDENT 0.0 .TP .B \-\-sni= Send in TLS SNI, overriding the host name specified in URI. .UNINDENT .INDENT 0.0 .TP .B \-\-histogram Plot histogram for performance statistics. .UNINDENT .INDENT 0.0 .TP .B \-\-tls\-session\-file= Read TLS session from , and set it to all TLS connections to perform the session resumption. It is also used to store the new TLS session. At most one session is written to the given file. .UNINDENT .INDENT 0.0 .TP .B \-\-output\-file= Write the measurement results to in JSON format. This basically includes all numbers reported to the normal output. In addition, for performance measurements, all raw samples are included. .UNINDENT .INDENT 0.0 .TP .B \-v, \-\-verbose Output debug information. .UNINDENT .INDENT 0.0 .TP .B \-\-version Display version information and exit. .UNINDENT .INDENT 0.0 .TP .B \-h, \-\-help Display this help and exit. .UNINDENT .sp The argument is an integer and an optional unit (e.g., 10K is 10 * 1024). Units are K, M and G (powers of 1024). .sp The argument is an integer and an optional unit (e.g., 1s is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms (hours, minutes, seconds and milliseconds, respectively). If a unit is omitted, a second is used as unit. .SH OUTPUT .SS REQUEST METRICS .INDENT 0.0 .TP .B requests .INDENT 7.0 .TP .B total The total number of requests h2load was instructed to make. .TP .B started The number of requests initiated by the tool. .TP .B done The number of requests that reached completion. .TP .B succeeded Requests resulting in an HTTP 2xx or 3xx status code. .TP .B failed The total number of failed requests. This includes both \fBerrored\fP requests and requests that completed with a non\-2xx/3xx status code. .TP .B errored A subset of \fBfailed\fP where the requests failed due to network\-level issues (e.g., TCP resets, \fBRST_STREAM\fP) rather than HTTP status codes. .TP .B timeout A subset of \fBerrored\fP where the connection timed out before completion. .UNINDENT .TP .B status codes The specific count of received HTTP status codes categorized by class (2xx, 3xx, 4xx, 5xx). .UNINDENT .SS TRAFFIC METRICS .INDENT 0.0 .TP .B traffic .INDENT 7.0 .TP .B total Total application data bytes received \(dqon the wire\(dq (decrypted if using TLS). .TP .B headers Total bytes used for response headers (pre\-decompression). .INDENT 7.0 .TP .B space savings Header compression efficiency, calculated as: .sp (1 \- headers / decompressed_headers) * 100 .sp where \fBheaders\fP is the compressed size and \fBdecompressed_headers\fP is the size after decompression. .UNINDENT .TP .B data Total bytes received in response bodies. .UNINDENT .UNINDENT .SS PERFORMANCE STATISTICS .INDENT 0.0 .TP .B Metric Definitions .INDENT 7.0 .TP .B request The duration from sending the first byte of a request to receiving the last byte of the response. .TP .B connect The time taken to establish a connection, including TLS handshakes. .TP .B TTFB The duration until the first byte of application data is received from the server (decrypted if using TLS). .TP .B req/s The requests per second measured individually across all clients. .TP .B min RTT The minimum RTT (QUIC). .TP .B smoothed RTT The smoothed RTT (QUIC). .TP .B packets sent The number of packets sent (QUIC). .TP .B packets recv The number of packets received (QUIC). .TP .B packets lost The number of packets declared lost (QUIC). .TP .B GRO packets The number of packets received in a single recvmsg call (QUIC). .UNINDENT .TP .B Distribution Fields .INDENT 7.0 .TP .B min / max The absolute minimum and maximum values recorded. .TP .B median The 50th percentile value. .TP .B p95 / p99 The 95th and 99th percentiles, indicating tail performance. .TP .B mean The arithmetic average of all samples. .TP .B sd The standard deviation (measure of data dispersion). .TP .B +/\- sd The percentage of successful samples falling within one standard deviation of the mean (mean +/\- sd). .UNINDENT .UNINDENT .SH FLOW CONTROL .sp h2load sets large flow control window by default, and effectively disables flow control to avoid under utilization of server performance. To set smaller flow control window, use \fB\-w\fP and \fB\-W\fP options. For example, use \fB\-w16 \-W16\fP to set default window size described in HTTP/2 protocol specification. .SH SEE ALSO .sp \fBnghttp(1)\fP, \fBnghttpd(1)\fP, \fBnghttpx(1)\fP .SH Author Tatsuhiro Tsujikawa .SH Copyright 2012, 2015, 2016, Tatsuhiro Tsujikawa .\" End of generated man page. PK0]man/man1/nghttpd.1nu[.\" Man page generated from reStructuredText .\" by the Docutils 0.22.4 manpage writer. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "NGHTTPD" "1" "Jul 29, 2026" "1.70.0" "nghttp2" .SH NAME nghttpd \- HTTP/2 server .SH SYNOPSIS .sp \fBnghttpd\fP [OPTION]... [ ] .SH DESCRIPTION .sp HTTP/2 server .INDENT 0.0 .TP .B Specify listening port number. .UNINDENT .INDENT 0.0 .TP .B Set path to server\(aqs private key. Required unless \fB\-\-no\-tls\fP is specified. .UNINDENT .INDENT 0.0 .TP .B Set path to server\(aqs certificate. Required unless \fB\-\-no\-tls\fP is specified. .UNINDENT .SH OPTIONS .INDENT 0.0 .TP .B \-a, \-\-address= The address to bind to. If not specified the default IP address determined by getaddrinfo is used. .UNINDENT .INDENT 0.0 .TP .B \-D, \-\-daemon Run in a background. If \fB\-D\fP is used, the current working directory is changed to \(aq\fI/\fP\(aq. Therefore if this option is used, \fB\-d\fP option must be specified. .UNINDENT .INDENT 0.0 .TP .B \-V, \-\-verify\-client The server sends a client certificate request. If the client did not return a certificate, the handshake is terminated. Currently, this option just requests a client certificate and does not verify it. .UNINDENT .INDENT 0.0 .TP .B \-d, \-\-htdocs= Specify document root. If this option is not specified, the document root is the current working directory. .UNINDENT .INDENT 0.0 .TP .B \-v, \-\-verbose Print debug information such as reception/ transmission of frames and name/value pairs. .UNINDENT .INDENT 0.0 .TP .B \-\-no\-tls Disable SSL/TLS. .UNINDENT .INDENT 0.0 .TP .B \-c, \-\-header\-table\-size= Specify decoder header table size. .UNINDENT .INDENT 0.0 .TP .B \-\-encoder\-header\-table\-size= Specify encoder header table size. The decoder (client) specifies the maximum dynamic table size it accepts. Then the negotiated dynamic table size is the minimum of this option value and the value which client specified. .UNINDENT .INDENT 0.0 .TP .B \-\-color Force colored log output. .UNINDENT .INDENT 0.0 .TP .B \-p, \-\-push== Push resources s when is requested. This option can be used repeatedly to specify multiple push configurations. and s are relative to document root. See \fB\-\-htdocs\fP option. Example: \fB\-p\fP/=/foo.png \fB\-p\fP/doc=/bar.css .UNINDENT .INDENT 0.0 .TP .B \-b, \-\-padding= Add at most bytes to a frame payload as padding. Specify 0 to disable padding. .UNINDENT .INDENT 0.0 .TP .B \-m, \-\-max\-concurrent\-streams= Set the maximum number of the concurrent streams in one HTTP/2 session. .sp Default: \fB100\fP .UNINDENT .INDENT 0.0 .TP .B \-n, \-\-workers= Set the number of worker threads. .sp Default: \fB1\fP .UNINDENT .INDENT 0.0 .TP .B \-e, \-\-error\-gzip Make error response gzipped. .UNINDENT .INDENT 0.0 .TP .B \-w, \-\-window\-bits= Sets the stream level initial window size to 2**\-1. .UNINDENT .INDENT 0.0 .TP .B \-W, \-\-connection\-window\-bits= Sets the connection level initial window size to 2**\-1. .UNINDENT .INDENT 0.0 .TP .B \-\-dh\-param\-file= Path to file that contains DH parameters in PEM format. Without this option, DHE cipher suites are not available. .UNINDENT .INDENT 0.0 .TP .B \-\-early\-response Start sending response when request HEADERS is received, rather than complete request is received. .UNINDENT .INDENT 0.0 .TP .B \-\-trailer=
Add a trailer header to a response.
must not include pseudo header field (header field name starting with \(aq:\(aq). The trailer is sent only if a response has body part. Example: \fB\-\-trailer\fP \(aqfoo: bar\(aq. .UNINDENT .INDENT 0.0 .TP .B \-\-hexdump Display the incoming traffic in hexadecimal (Canonical hex+ASCII display). If SSL/TLS is used, decrypted data are used. .UNINDENT .INDENT 0.0 .TP .B \-\-echo\-upload Send back uploaded content if method is POST or PUT. .UNINDENT .INDENT 0.0 .TP .B \-\-mime\-types\-file= Path to file that contains MIME media types and the extensions that represent them. .sp Default: \fB/etc/mime.types\fP .UNINDENT .INDENT 0.0 .TP .B \-\-no\-content\-length Don\(aqt send content\-length header field. .UNINDENT .INDENT 0.0 .TP .B \-\-groups= Specify the supported groups. .sp Default: \fBX25519:P\-256:P\-384:P\-521\fP .UNINDENT .INDENT 0.0 .TP .B \-\-ktls Enable ktls. .UNINDENT .INDENT 0.0 .TP .B \-\-version Display version information and exit. .UNINDENT .INDENT 0.0 .TP .B \-h, \-\-help Display this help and exit. .UNINDENT .sp The argument is an integer and an optional unit (e.g., 10K is 10 * 1024). Units are K, M and G (powers of 1024). .SH SEE ALSO .sp \fBnghttp(1)\fP, \fBnghttpx(1)\fP, \fBh2load(1)\fP .SH Author Tatsuhiro Tsujikawa .SH Copyright 2012, 2015, 2016, Tatsuhiro Tsujikawa .\" End of generated man page. PK0]aman/man1/nghttp.1nu[.\" Man page generated from reStructuredText .\" by the Docutils 0.22.4 manpage writer. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "NGHTTP" "1" "Jul 29, 2026" "1.70.0" "nghttp2" .SH NAME nghttp \- HTTP/2 client .SH SYNOPSIS .sp \fBnghttp\fP [OPTIONS]... ... .SH DESCRIPTION .sp HTTP/2 client .INDENT 0.0 .TP .B Specify URI to access. .UNINDENT .SH OPTIONS .INDENT 0.0 .TP .B \-v, \-\-verbose Print debug information such as reception and transmission of frames and name/value pairs. Specifying this option multiple times increases verbosity. .UNINDENT .INDENT 0.0 .TP .B \-n, \-\-null\-out Discard downloaded data. .UNINDENT .INDENT 0.0 .TP .B \-O, \-\-remote\-name Save download data in the current directory. The filename is derived from URI. If URI ends with \(aq\fI/\fP\(aq, \(aqindex.html\(aq is used as a filename. Not implemented yet. .UNINDENT .INDENT 0.0 .TP .B \-t, \-\-timeout= Timeout each request after . Set 0 to disable timeout. .UNINDENT .INDENT 0.0 .TP .B \-w, \-\-window\-bits= Sets the stream level initial window size to 2**\-1. .UNINDENT .INDENT 0.0 .TP .B \-W, \-\-connection\-window\-bits= Sets the connection level initial window size to 2**\-1. .UNINDENT .INDENT 0.0 .TP .B \-a, \-\-get\-assets Download assets such as stylesheets, images and script files linked from the downloaded resource. Only links whose origins are the same with the linking resource will be downloaded. nghttp prioritizes resources using HTTP/2 dependency based priority. The priority order, from highest to lowest, is html itself, css, javascript and images. .UNINDENT .INDENT 0.0 .TP .B \-s, \-\-stat Print statistics. .UNINDENT .INDENT 0.0 .TP .B \-H, \-\-header=
Add a header to the requests. Example: \fB\-H\fP\(aq:method: PUT\(aq .UNINDENT .INDENT 0.0 .TP .B \-\-trailer=
Add a trailer header to the requests.
must not include pseudo header field (header field name starting with \(aq:\(aq). To send trailer, one must use \fB\-d\fP option to send request body. Example: \fB\-\-trailer\fP \(aqfoo: bar\(aq. .UNINDENT .INDENT 0.0 .TP .B \-\-cert= Use the specified client certificate file. The file must be in PEM format. .UNINDENT .INDENT 0.0 .TP .B \-\-key= Use the client private key file. The file must be in PEM format. .UNINDENT .INDENT 0.0 .TP .B \-d, \-\-data= Post FILE to server. If \(aq\-\(aq is given, data will be read from stdin. .UNINDENT .INDENT 0.0 .TP .B \-m, \-\-multiply= Request each URI times. By default, same URI is not requested twice. This option disables it too. .UNINDENT .INDENT 0.0 .TP .B \-u, \-\-upgrade Perform HTTP Upgrade for HTTP/2. This option is ignored if the request URI has https scheme. If \fB\-d\fP is used, the HTTP upgrade request is performed with OPTIONS method. .UNINDENT .INDENT 0.0 .TP .B \-\-extpri= Sets RFC 9218 priority of given URI. must be the wire format of priority header field (e.g., \(dqu=3,i\(dq). This option can be used multiple times, and N\-th \fB\-\-extpri\fP option sets priority of N\-th URI in the command line. If the number of this option is less than the number of URI, the last option value is repeated. If there is no \fB\-\-extpri\fP option, urgency is 3, and incremental is false. .UNINDENT .INDENT 0.0 .TP .B \-M, \-\-peer\-max\-concurrent\-streams= Use as SETTINGS_MAX_CONCURRENT_STREAMS value of remote endpoint as if it is received in SETTINGS frame. .sp Default: \fB100\fP .UNINDENT .INDENT 0.0 .TP .B \-c, \-\-header\-table\-size= Specify decoder header table size. If this option is used multiple times, and the minimum value among the given values except for last one is strictly less than the last value, that minimum value is set in SETTINGS frame payload before the last value, to simulate multiple header table size change. .UNINDENT .INDENT 0.0 .TP .B \-\-encoder\-header\-table\-size= Specify encoder header table size. The decoder (server) specifies the maximum dynamic table size it accepts. Then the negotiated dynamic table size is the minimum of this option value and the value which server specified. .UNINDENT .INDENT 0.0 .TP .B \-b, \-\-padding= Add at most bytes to a frame payload as padding. Specify 0 to disable padding. .UNINDENT .INDENT 0.0 .TP .B \-r, \-\-har= Output HTTP transactions in HAR format. If \(aq\-\(aq is given, data is written to stdout. .UNINDENT .INDENT 0.0 .TP .B \-\-color Force colored log output. .UNINDENT .INDENT 0.0 .TP .B \-\-continuation Send large header to test CONTINUATION. .UNINDENT .INDENT 0.0 .TP .B \-\-no\-content\-length Don\(aqt send content\-length header field. .UNINDENT .INDENT 0.0 .TP .B \-\-hexdump Display the incoming traffic in hexadecimal (Canonical hex+ASCII display). If SSL/TLS is used, decrypted data are used. .UNINDENT .INDENT 0.0 .TP .B \-\-no\-push Disable server push. .UNINDENT .INDENT 0.0 .TP .B \-\-max\-concurrent\-streams= The number of concurrent pushed streams this client accepts. .UNINDENT .INDENT 0.0 .TP .B \-\-expect\-continue Perform an Expect/Continue handshake: wait to send DATA (up to a short timeout) until the server sends a 100 Continue interim response. This option is ignored unless combined with the \fB\-d\fP option. .UNINDENT .INDENT 0.0 .TP .B \-y, \-\-no\-verify\-peer Suppress warning on server certificate verification failure. .UNINDENT .INDENT 0.0 .TP .B \-\-ktls Enable ktls. .UNINDENT .INDENT 0.0 .TP .B \-\-version Display version information and exit. .UNINDENT .INDENT 0.0 .TP .B \-h, \-\-help Display this help and exit. .UNINDENT .sp The argument is an integer and an optional unit (e.g., 10K is 10 * 1024). Units are K, M and G (powers of 1024). .sp The argument is an integer and an optional unit (e.g., 1s is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms (hours, minutes, seconds and milliseconds, respectively). If a unit is omitted, a second is used as unit. .SH SEE ALSO .sp \fBnghttpd(1)\fP, \fBnghttpx(1)\fP, \fBh2load(1)\fP .SH Author Tatsuhiro Tsujikawa .SH Copyright 2012, 2015, 2016, Tatsuhiro Tsujikawa .\" End of generated man page. PK0][ {doc/alt-openldap11/ANNOUNCEMENTnu[A N N O U N C E M E N T -- OpenLDAP 2.4 The OpenLDAP Project is pleased to announce the availability of OpenLDAP Software 2.4, a suite of the Lightweight Directory Access Protocol (v3) servers, clients, utilities, and development tools. This release contains the following major enhancements: * Slapd(8) enhancements - Syncrepl enhancements, including push-mode and Multi-Provider support - Dynamic configuration enhancements, including online schema editing and full access control - Dynamic monitoring enhancements, including cache usage information * New overlays - Attribute value constraints - Dynamic Directory Services (RFC2589) - Reverse Group Membership maintenance (memberof) * Clients and tools - Full support of request/response controls - New ldapexop tool for arbitrary extend operations - Support of DNS SRV records for default server * Significant performance enhancements throughout the client and server code base * Multiple new features in libldap and liblber * Expanded documentation - Function-complete manual pages - Numerous new examples in the Admin Guide This release includes the following major components: * slapd - a stand-alone LDAP directory server * -lldap - a LDAP client library * -llber - a lightweight BER/DER encoding/decoding library * LDIF tools - data conversion tools for use with slapd * LDAP tools - A collection of command line LDAP utilities * Admin Guide, Manual Pages - associated documentation In addition, there are some contributed components: * LDAPC++ - a LDAP C++ SDK * Various slapd modules and slapi plugins ACKNOWLEDGEMENTS OpenLDAP Software is developed by the OpenLDAP Project. The Project consists of a team of volunteers who use the Internet to coordinate their activities. The Project is an organized activity of the OpenLDAP Foundation. OpenLDAP Software is derived from University of Michigan LDAP, release 3.3. AVAILABILITY This software is available under the OpenLDAP Public License, an non-restrictive, "free", open-source license. Download information is available at: http://www.OpenLDAP.org/software/download/ SUPPORT OpenLDAP Software is user supported: http://www.openldap.org/support/ The OpenLDAP Administrator's Guide, which includes quick start instructions, is available at: http://www.openldap.org/doc/admin/ The project maintains a FAQ which you may find useful: http://www.openldap.org/faq/ In addition, there are also a number of discussion lists related to OpenLDAP Software. A list of mailing lists is available at: http://www.OpenLDAP.org/lists/ To report bugs, please use project's Issue Tracking System: http://www.openldap.org/its/ The OpenLDAP home page containing lots of interesting information and online documentation is available at this URL: http://www.OpenLDAP.org/ SUPPORTED PLATFORMS This release has been ported to many UNIX (and UNIX-like) platforms including Darwin, FreeBSD, Linux, NetBSD, OpenBSD and most commercial UNIX systems. The release has also been ported (in part or in whole) to other platforms including Apple MacOS X, IBM zOS, and Microsoft Windows NT/2000/etc. --- OpenLDAP is a registered trademark of the OpenLDAP Foundation. Copyright 1999-2021 The OpenLDAP Foundation, Redwood City, California, USA. All Rights Reserved. Permission to copy and distribute verbatim copies of this document is granted. PK0]a#e doc/alt-openldap11/READMEnu[OpenLDAP 2.4 README For a description of what this distribution contains, see the ANNOUNCEMENT file in this directory. For a description of changes from previous releases, see the CHANGES file in this directory. This is 2.4 release, it includes significant changes from prior releases. REQUIRED SOFTWARE Building OpenLDAP Software requires a number of software packages to be preinstalled. Additional information regarding prerequisite software can be found in the OpenLDAP Administrator's Guide. Base system (libraries and tools): Standard C compiler (required) Cyrus SASL 2.1.21+ (recommended) OpenSSL 0.9.7+ (recommended) Reentrant POSIX REGEX software (required) SLAPD: BDB and HDB backends require Oracle Berkeley DB 4.4 - 4.8, or 5.0 - 5.1. It is highly recommended to apply the patches from Oracle for a given release. CLIENTS/CONTRIB ware: Depends on package. See per package README. MAKING AND INSTALLING THE DISTRIBUTION Please see the INSTALL file for basic instructions. More detailed instructions can be found in the OpenLDAP Administrator's Guide (see DOCUMENTATION section). DOCUMENTATION The OpenLDAP Administrator's Guide is available in the guide.html file in the doc/guide/admin directory. The guide and a number of other documents are available at . The distribution also includes manual pages for most programs and library APIs. See ldap(3) for details. The OpenLDAP website is available and contains the latest LDAP news, releases announcements, pointers to other LDAP resources, etc.. It is located at . The OpenLDAP Software FAQ is available at . SUPPORT / FEEDBACK / PROBLEM REPORTS / DISCUSSIONS OpenLDAP Software is user supported. If you have problems, please review the OpenLDAP FAQ and archives of the OpenLDAP-software and OpenLDAP-bugs mailing lists . If you cannot find the answer, please enquire on the OpenLDAP-software list. Issues, such as bug reports, should be reported using our Issue Tracking System . Do not use this system for software enquiries. Please direct these to an appropriate mailing list. CONTRIBUTING See for information regarding how to contribute code or documentation to the OpenLDAP Project for inclusion in OpenLDAP Software. While you are encouraged to coordinate and discuss the development activities on the mailing list prior to submission, it is noted that contributions must be submitted using the Issue Tracking System to be considered. --- $OpenLDAP$ This work is part of OpenLDAP Software . Copyright 1998-2021 The OpenLDAP Foundation. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted only as authorized by the OpenLDAP Public License. A copy of this license is available in the file LICENSE in the top-level directory of the distribution or, alternatively, at . OpenLDAP is a registered trademark of the OpenLDAP Foundation. PK0]kdoc/alt-openldap11/CHANGESnu[OpenLDAP 2.4 Change Log OpenLDAP 2.4.59 Release (2021/06/03) Fixed libldap TLSv1.3 cipher suites with OpenSSL 1.1.1 (ITS#9521) Fixed libldap double free of LDAP_OPT_DEFBASE (ITS#9530) Fixed slapd syncrepl handling of add+delete on single value attr (ITS#9295) Fixed slapd-mdb cursor init check (ITS#9526) Fixed slapd-mdb deletion of context entry (ITS#9531) Fixed slapd-mdb off-by-one affecting search scope (ITS#9557) Fixed slapo-pcache locking during expiration (ITS#9529) Contrib Fixed slapo-autogroup to not thrash thread context (ITS#9494) Documentation ldap_modify(3) - Delete non-existent mod_next parameter (ITS#9559) OpenLDAP 2.4.58 Release (2021/03/16) Fixed slapd validity checks for issuerAndThisUpdateCheck (ITS#9454) Fixed slapd to alloc new conn struct after freeing old one (ITS#9458) Fixed slapd syncrepl to check all contextCSNs (ITS#9282) Fixed slapd-bdb lockdetect config (ITS#9449) OpenLDAP 2.4.57 Release (2021/01/18) Fixed ldapexop to use correct return code (ITS#9417) Fixed slapd to remove asserts in UUIDNormalize (ITS#9391) Fixed slapd to remove assert in csnValidate (ITS#9410) Fixed slapd validity checks for issuerAndThisUpdateCheck (ITS#9411, ITS#9427) Fixed slapd validity checks for serialNumberAndIssuerCheck (ITS#9404, ITS#9424) Fixed slapd AVA sort with invalid RDN (ITS#9412) Fixed slapd ldap_X509dn2bv to check for invalid BER after RDN count (ITS#9423, ITS#9425) Fixed slapd saslauthz to remove asserts in validation (ITS#9406, ITS#9407) Fixed slapd saslauthz to use slap_sl_free on normalized DN (ITS#9409) Fixed slapd saslauthz SEGV in slap_parse_user (ITS#9413) Fixed slapd modrdn memory leak (ITS#9420) Fixed slapd double-free in vrfilter (ITS#9408) Fixed slapd cancel operation to correctly terminate (ITS#9428) Fixed slapd-ldap fix binds on retry with closed connection (ITS#9400) Fixed slapo-syncprov to ignore duplicate sessionlog entries (ITS#9394) OpenLDAP 2.4.56 Release (2020/11/10) Fixed slapd to remove assert in certificateListValidate (ITS#9383) Fixed slapd to remove assert in csnNormalize23 (ITS#9384) Fixed slapd to better parse ldapi listener URIs (ITS#9379) OpenLDAP 2.4.55 Release (2020/10/26) Fixed slapd normalization handling with modrdn (ITS#9370) Fixed slapd-meta to check ldap_install_tls return code (ITS#9366) Contrib Fixed nssov misplaced semicolon (ITS#8731, ITS#9368) OpenLDAP 2.4.54 Release (2020/10/12) Fixed slapd delta-syncrepl to ignore delete ops on deleted entry (ITS#9342) Fixed slapd delta-syncrepl to be fully serialized (ITS#9330) Fixed slapd delta-syncrepl MOD on zero-length context entry (ITS#9352) Fixed slapd syncrepl to be fully serialized (ITS#8102) Fixed slapd syncrepl to call check_syncprov on fresh consumer (ITS#9345) Fixed slapd syncrepl to propagate errors from overlay_entry_get_ov (ITS#9355) Fixed slapd syncrepl to not create empty ADD ops (ITS#9359) Fixed slapd syncrepl replace usage on single valued attrs (ITS#9295) Fixed slapd-monitor fix monitor_back_register_database for empty suffix DB (ITS#9353) Fixed slapo-accesslog normalizer for reqStart (ITS#9358) Fixed slapo-accesslog to not generate new contextCSN on purge (ITS#9361) Fixed slapo-syncprov contextCSN generation with empty suffix (ITS#9015) Fixed slapo-syncprov sessionlog to use a TAVL tree (ITS#8486) OpenLDAP 2.4.53 Release (2020/09/07) Added slapd syncrepl additional SYNC logging (ITS#9043) Fixed slapd syncrepl segfault on NULL cookie on REFRESH (ITS#9282) Fixed slapd syncrepl to use fresh connection on REFRESH fallback (ITS#9338) Fixed slapo-ppolicy race condition for pwdFailureTime (ITS#9302,ITS#9334) Build Require OpenSSL 1.0.2 or later (ITS#9323) Fixed libldap compilation issue with broken C compilers (ITS#9332) OpenLDAP 2.4.52 Release (2020/08/28) Added libldap LDAP_OPT_X_TLS_REQUIRE_SAN option (ITS#9318) Added libldap OpenSSL support for multiple EECDH curves (ITS#9054) Added slapd OpenSSL support for multiple EECDH curves (ITS#9054) Fixed librewrite malloc/free corruption (ITS#9249) Fixed libldap hang when using UDP and server down (ITS#9328) Fixed slapd syncrepl rare deadlock due to network issues (ITS#9324) Fixed slapd syncrepl regression that could trigger an assert (ITS#9329) Fixed slapd-mdb index error with collapsed range (ITS#9135) OpenLDAP 2.4.51 Release (2020/08/11) Added slapo-ppolicy implement Netscape password policy controls (ITS#9279) Fixed libldap retry loop in ldap_int_tls_connect (ITS#8650) Fixed libldap to use getaddrinfo in ldap_pvt_get_fqdn (ITS#9287) Fixed slapd to enforce singular existence of some overlays (ITS#9309) Fixed slapd syncrepl to not delete non-replicated attrs (ITS#9227) Fixed slapd syncrepl to correctly delete entries on resync (ITS#9282) Fixed slapd syncrepl to use replace on single valued attrs (ITS#9294, ITS#9295) Fixed slapd-perl dynamic config with threaded slapd (ITS#7573) Fixed slapo-ppolicy to expose the ppolicy control (ITS#9285) Fixed slapo-ppolicy race condition for pwdFailureTime (ITS#9302) Fixed slapo-ppolicy so it can only exist once per DB (ITS#9309) Fixed slapo-chain to check referral (ITS#9262) Build Environment Fix test064 so it no longer uses bashisms (ITS#9263) Contrib Fix default prefix value for pw-argon2, pw-pbkdf2 modules (ITS#9248) slapo-allowed - Fix usage of uninitialized variable (ITS#9308) Documentation ldap_parse_result(3) - Document ldap_parse_intermediate (ITS#9271) OpenLDAP 2.4.50 Release (2020/04/28) Fixed client benign typos (ITS#8890) Fixed libldap type cast (ITS#9175) Fixed libldap retry loop in ldap_int_tls_connect (ITS#8650) Fixed libldap_r race on Windows mutex initialization (ITS#9181) Fixed liblunicode memory leak (ITS#9198) Fixed slapd benign typos (ITS#8890) Fixed slapd to limit depth of nested filters (ITS#9202) Fixed slapd-mdb memory leak in dnSuperiorMatch (ITS#9214) Fixed slapo-pcache database initialization (ITS#9182) Fixed slapo-ppolicy callback (ITS#9171) Build Fix olcDatabaseDummy initialization for windows (ITS#7074) Fix detection for ws2tcpip.h for windows (ITS#8383) Fix back-mdb types for windows (ITS#7878) Contrib Update ldapc++ config.guess and config.sub to support newer architectures (ITS#7855) Added pw-argon2 module (ITS#9233, ITS#8575, ITS#9203, ITS#9206) Documentation slapd-ldap(5) - Clarify idassert-authzfrom behavior (ITS#9003) slapd-meta(5) - Remove client-pr option (ITS#8683) slapindex(8) - Fix truncate option information for back-mdb (ITS#9230) OpenLDAP 2.4.49 Release (2020/01/30) Added slapd-monitor database entry count for slapd-mdb (ITS#9154) Fixed client tools to not add controls on cancel/abandon (ITS#9145) Fixed client tools SyncInfo message to be LDIF compliant (ITS#8116) Fixed libldap to correctly free sb (ITS#9081, ITS#8755) Fixed libldap descriptor leak if ldaps fails (ITS#9147) Fixed libldap remove unnecessary global mutex for GnuTLS (ITS#9069) Fixed slapd syntax evaluation of preferredDeliveryMethod (ITS#9067) Fixed slapd to relax domainScope control check (ITS#9100) Fixed slapd to have cleaner error handling during connection setup (ITS#9112) Fixed slapd data check when processing cancel exop (ITS#9124) Fixed slapd attribute description processing (ITS#9128) Fixed slapd-ldap to set oldctrls correctly (ITS#9076) Fixed slapd-mdb to honor unchecked limit with alias deref (ITS#7657) Fixed slapd-mdb missing final commit with slapindex (ITS#9095) Fixed slapd-mdb drop attr mappings added in an aborted txn (ITS#9091) Fixed slapd-mdb nosync FLAG configuration handling (ITS#9150) Fixed slapd-monitor global operation counter reporting (ITS#9119) Fixed slapo-ppolicy when used with slapauth (ITS#8629) Fixed slapo-ppolicy to add a missed normalised copy of pwdChangedTime (ITS#9126) Fixed slapo-syncprov fix sessionlog init (ITS#9146) Fixed slapo-unique loop termination (ITS#9077) Build Environment Fix mkdep to honor TMPDIR if set (ITS#9062) Remove ICU library detection (ITS#9144) Update config.guess and config.sub to support newer architectures (ITS#7855) Disable ITS8521 regression test as it is no longer valid (ITS#9015) Documentation admin24 - Fix inconsistent whitespace in replication section (ITS#9153) admin24 - Fix dmoz URL to point to Curlie (ITS#9289) slapd-config(5)/slapd.conf(5) - Fix missing bold tag for keyword (ITS#9063) slapd-ldap(5) - Document "tls none" option (ITS#9071) slapo-ppolicy(5) - Correctly document pwdGraceAuthnLimit (ITS#9065) OpenLDAP 2.4.48 Release (2019/07/24) Added libldap OpenSSL Elliptic Curve support (ITS#7595) Added libldap Expose OpenLDAP specific interfaces via openldap.h (ITS#8671) Added slapd-monitor support for slapd-mdb (ITS#7770) Fixed liblber leaks (ITS#8727) Fixed liblber with partial flush (ITS#8864) Fixed libldap ASYNC TLS so it works (ITS#8957,ITS#8980) Fixed libldap ASYNC connections with Solaris 10 (ITS#8968) Fixed libldap with SASL_NOCANON=on and ldapi connections (ITS#7585) Fixed libldap to be able to unset syncrepl TLS options (ITS#7042) Fixed libldap race condition in ldap_int_initialize (ITS#7996, ITS#8450) Fixed libldap return code in ldap_create_assertion_control_value (ITS#8674) Fixed libldap to correctly disable IPv6 when configured to do so (ITS#8754) Fixed libldap to correctly close TLS connection (ITS#8755) Fixed libldap with non-blocking TLS and referals (ITS#8167) Fixed libldap_r handling of deprecated OpenSSL function (ITS#8353) Fixed liblunicode case correspondence (ITS#8508) Fixed slapd with an idletimeout of less than four seconds (ITS#8952) Fixed slapd config parser variable for Windows64 (ITS#9012) Fixed slapd syncrepl fallback handling with delta-syncrepl (ITS#9015) Fixed slapd telephoneNumberNormalize, cert DN validation (ITS#8999) Fixed slapd syncrepl for relax with delta-syncrepl (ITS#8037) Fixed slapd to restrict rootDN proxyauthz to its own databases (ITS#9038) Fixed slapd to initialize SASL SSF per connection (ITS#9052) Fixed slapd-ldap starttls connections timeout behavior (ITS#8963) Fixed slapd-ldap segfault when entry result doesn't match filter (ITS#8997) Fixed slapd-meta conversion from slapd.conf to cn=config (ITS#8743) Fixed slapd-meta assertion when network interface goes down (ITS#8841) Fixed slapd-mdb fix bitshift integer overflow (ITS#8989) Fixed slapd-mdb index cleanup with cn=config (ITS#8472) Fixed slapd-mdb to improve performance with alias deref (ITS#7657) Fixed slapo-accesslog with SLAP_MOD_SOFT modifications (ITS#8990) Fixed slapo-accesslog possible assert with exops (ITS#8971) Fixed slapo-chain to correctly reject multiple chaining URIs (ITS#8637) Fixed slapo-chain conversion from slapd.conf to cn=config (ITS#8799) Fixed slapo-memberof conversion from slapd.conf to cn=config (ITS#8663) Fixed slapo-memberof for group name change to itself (ITS#9000) Fixed slapo-ppolicy behavior when pwdInHistory is changed (ITS#8349) Fixed slapo-rwm to not free original filter (ITS#8964) Fixed slapo-syncprov contextCSN generation (ITS#9015) Build Environment Fixed slapd to only link to BDB libraries with static build (ITS#8948) Fixed libldap implicit declaration with LDAP_CONNECTIONLESS (ITS#8794) Fixed libldap double inclusion of limits.h in cyrus.c (ITS#9041) Documentation General - Fixed minor typos (ITS#8764, ITS#8761) admin24 - Miscellaneous updates promoting mdb and fixing examples (ITS#9031) slapd.access(5) - Note MDB is the primary backend (ITS#8881) slapd.backends(5) - Note MDB is the recommended backend (ITS#8771) slapd-ldap(5) - Document starttls parameter (ITS#8693) Contrib Added slapo-lastbind capability to forward authTimestamp updates (ITS#7721) OpenLDAP 2.4.47 Release (2018/12/19) Added slapd-sock DN qualifier for subtrees to be processed (ITS#8051) Added slapd-sock ability to send extended operations to external listeners (ITS#8714) Fixed liblber to avoid incremental access to user-supplied bv in dupbv (ITS#8752) Fixed libldap dn to domain parsing with bad input (ITS#8842) Fixed slapd slapcat to correctly honor -g option (ITS#8667) Fixed slapd to correctly handle NO_SUCH_OBJECT with dynamic groups (ITS#8923) Fixed slapd to check status of rdnNormalize (ITS#8932) Fixed slapd cn=config when modifying slapo-syncprov config (ITS#8616) Fixed slapd sasl authz-policy "all" behavior (ITS#8909) Fixed slapd sasl minor typo (ITS#8918) Fixed slapd to correctly hide hidden DBs in the rootDSE (ITS#8912) Fixed slapd domainScope control to match Microsoft specification (ITS#8840) Fixed slapd-bdb/hdb/mdb to not convert certain IDLs to ranges (ITS#8868) Fixed slapo-accesslog deadlock during cleanup (ITS#8752) Fixed slapo-memberof cn=config modifications (ITS#8663) Fixed slapo-ppolicy with multi-provider replication (ITS#8927) Fixed slapo-syncprov with NULL modlist (ITS#8843) Build Environment Added slapd reproducible build support (ITS#8928) Fixed missing includes with OpenSSL 1.0.2 (ITS#8809) Contrib Fixed slapo-pbkdf2 hash generation (ITS#8878) Documentation admin24 fixed minor typo (ITS#8887) OpenLDAP 2.4.46 Release (2018/03/22) Fixed libldap connection delete callbacks when TLS fails to start (ITS#8717) Fixed libldap to not reuse tls_session if TLS hostname check fails (ITS#7373) Fixed libldap cross-compiling with OpenSSL 1.1 (ITS#8687) Fixed libldap OpenSSL 1.1.1 compatibility with BIO_method (ITS#8791) Fixed libldap MozNSS CA certificate hash matching (ITS#7374) Fixed libldap MozNSS with PEM certs when also using an NSS cert db (ITS#7389) Fixed libldap MozNSS initialization (ITS#8484) Fixed libldap GnuTLS with GNUTLS_E_AGAIN (ITS#8650) Fixed libldap memory leak with cancel operations (ITS#8782) Fixed slapd Eventlog registry key creation on 64-bit Windows (ITS#8705) Fixed slapd to maintain SSF across SASL binds (ITS#8796) Fixed slapd syncrepl deadlock when updating cookie (ITS#8752) Fixed slapd syncrepl callback to always be last in the stack (ITS#8752) Fixed slapd telephoneNumberNormalize when the value is spaces and hyphens (ITS#8778) Fixed slapd CSN queue processing (ITS#8801) Fixed slapd-ldap TLS connection timeout with high latency connections (ITS#8720) Fixed slapd-ldap to ignore unknown schema when omit-unknown-schema is set (ITS#7520) Fixed slapd-mdb with an optimization for long lived read transactions (ITS#8226) Fixed slapd-meta assert when olcDbRewrite is modified (ITS#8404) Fixed slapd-sock with LDAP_MOD_INCREMENT operations (ITS#8692) Fixed slapo-accesslog cleanup to only occur on failed operations (ITS#8752) Fixed slapo-dds entryTTL to actually decrease as per RFC 2589 (ITS#7100) Fixed slapo-syncprov memory leak with delete operations (ITS#8690) Fixed slapo-syncprov to not clear pending operation when checkpointing (ITS#8444) Fixed slapo-syncprov to correctly record contextCSN values in the accesslog (ITS#8100) Fixed slapo-syncprov not to log checkpoints to accesslog db (ITS#8607) Fixed slapo-syncprov to process changes from this SID on REFRESH (ITS#8800) Fixed slapo-syncprov session log parsing to not block other operations (ITS#8486) Build Environment Fixed Windows build with newer MINGW version (ITS#8697) Fixed compiler warnings and removed unused variables (ITS#8578) Contrib Fixed ldapc++ Control structure (ITS#8583) Documentation Delete stub manpage for back-ldbm (ITS#8713) Fixed ldap_bind(3) to mention the LDAP_SASL_SIMPLE mechanism (ITS#8121) Fixed ldap.conf(5) to note SASL_MECH/SASL_REALM are no longer user-only (ITS#8818) Fixed slapd-config(5) typo for olcTLSCipherSuite (ITS#8715) Fixed slapo-syncprov(5) indexing requirements (ITS#5048) OpenLDAP 2.4.45 Release (2017/06/01) Added slapd support for OpenSSL 1.1.0 series (ITS#8353, ITS#8533, ITS#8634) Fixed libldap to fail ldap_result if the handle is already bad (ITS#8585) Fixed libldap to expose error if user specified CA doesn't exist (ITS#8529) Fixed libldap handling of Diffie-Hellman parameters (ITS#7506) Fixed libldap GnuTLS use after free (ITS#8385) Fixed libldap SASL initialization (ITS#8648) Fixed slapd bconfig rDN escape handling (ITS#8574) Fixed slapd segfault with invalid hostname (ITS#8631) Fixed slapd sasl SEGV rebind in same session (ITS#8568) Fixed slapd syncrepl filter handling (ITS#8413) Fixed slapd syncrepl infinite looping mods with delta-sync MPR (ITS#8432) Fixed slapd callback struct so older modules without writewait should function. Custom modules may need to be updated for sc_writewait callback (ITS#8435) Fixed slapd-ldap/meta broken LDAP_TAILQ macro (ITS#8576) Fixed slapd-mdb so it passes ITS6794 regression test (ITS#6794) Fixed slapd-mdb double free with size zero paged result (ITS#8655) Fixed slapd-meta uninitialized diagnostic message (ITS#8442) Fixed slapo-accesslog to honor pauses during purge for cn=config update (ITS#8423) Fixed slapo-accesslog with multiple modifications to the same attribute (ITS#6545) Fixed slapo-relay to correctly initialize sc_writewait (ITS#8428) Fixed slapo-sssvlv double free (ITS#8592) Fixed slapo-unique with empty modifications (ITS#8266) Build Environment Added test065 for proxyauthz (ITS#8571) Fix test008 to be portable (ITS#8414) Fix test064 to wait for slapd to start (ITS#8644) Fix its4336 regression test (ITS#8534) Fix its4337 regression test (ITS#8535) Fix regression tests to execute on all backends (ITS#8539) Contrib Added slapo-autogroup(5) man page (ITS#8569) Added passwd missing conversion scripts for apr1 (ITS#6826) Fixed contrib modules where the writewait callback was not correctly initialized (ITS#8435) Fixed smbk5pwd to build with newer OpenSSL releases (ITS#8525) Documentation admin24 fixed tls_cipher_suite bindconf option (ITS#8099) admin24 fixed typo cn=config to be slapd.d (ITS#8449) admin24 fixed slapo-syncprov information to be current (ITS#8253) admin24 fixed typo in access control docs (ITS#7341, ITS#8391) admin24 fixed minor typo in tuning guide (ITS#8499) admin24 fixed information about the limits option (ITS#7700) admin24 fixed missing options for syncrepl configuration (ITS#7700) admin24 fixed accesslog documentation to note it should not be replicated (ITS#8344) admin24 fix typos (ITS#8562) admin24 fix replication sections to include back-mdb (ITS#8563) Fixed ldap.conf(5) missing information on SASL_NOCANON option (ITS#7177) Fixed ldapsearch(1) information on the V[V] flag behavior (ITS#7177, ITS#6339) Fixed slapd-config(5), slapd.conf(5) clarification on interval keyword for refreshAndPersist (ITS#8538) Fixed slapd-config(5), slapd.conf(5) clarify serverID requirements (ITS#8635) Fixed slapd-config(5), slapd.conf(5) clarification on loglevel settings (ITS#8123) Fixed slapo-ppolicy(5) to clearly note rootdn requirement (ITS#8565) Fixed slapo-memberof(5) to note it is not safe to use with replication (ITS#8613) Fixed slapo-syncprov(5) documentation to be current (ITS#8253) Fixed slapadd(8) manpage to note slapd-mdb (ITS#8215) Fixed various minor grammar issues in the man pages (ITS#8544) Fixed various typos (ITS#8587) OpenLDAP 2.4.44 Release (2016/02/05) Fixed slapd-bdb/hdb missing olcDbChecksum config attr (ITS#8337) Fixed slapd-mdb behavior with long lived read transactions (ITS#8226) Fixed slapd-mdb cleanup after failed transaction (ITS#8360) Fixed slapd-sql missing id_query/olcSqlIdQuery (ITS#8329) Fixed slapo-accesslog callback initialization (ITS#8351) Fixed slapo-ppolicy pwdMaxRecordedFailure must never be zero (ITS#8327) Fixed slapo-syncprov abandon processing (ITS#8354) Fixed slapo-syncprov ctxcsn snapshot on refresh (ITS#8281, ITS#8365) Documentation admin24 Stop linking to Berkeley DB downloads (ITS#8362) admin24 Update documentation for LMDB preference OpenLDAP 2.4.43 Release (2015/11/30) Fixed liblber remove obsolete assert (ITS#8240, ITS#8301) Fixed libldap file URLs on windows (ITS#8273) Fixed libldap microsecond timer for windows (ITS#8295) Fixed slap tools minor one time memory leak (ITS#8082) Fixed slapd to avoid redundant processing of abandon ops (ITS#8232) Fixed slapd syncrepl SEGV when present list is NULL (ITS#8231, ITS#8042) Fixed slapd segfault with invalid SASL URI (ITS#8218) Fixed slapd configuration parser with unbalanced quotes (ITS#8233) Fixed slapd syncrepl check with config db on windows (ITS#8277) Fixed slapd with mod Increment and inherited attribute type (ITS#8289) Fixed slapd-ldap SEGV after failed retry (ITS#8173) Fixed slapd-ldap to skip client controls in ldap_back_entry_get (ITS#8244) Fixed slapd-null to have an option to return a search entry (ITS#8249) Fixed slapd-relay to correctly handle quoted options (ITS#8284) Fixed slapo-accesslog delta-sync MPR with interrupted refresh phase (ITS#8281) Fixed slapo-dds segfault when using slapo-memberof (ITS#8133) Fixed slapo-ppolicy to allow purging of stale pwdFailureTime attributes (ITS#8185) Fixed slapo-ppolicy to release entry on failure (ITS#7537) Fixed slapo-ppolicy to fall back to default policy if there is a parsing error (ITS#8234) Fixed slapo-syncprov with interrupted refresh phase (ITS#8281) Fixed slapo-refint with subtree renames (ITS#8220) Fixed slapo-rwm missing olcDropUnrequested attribute (ITS#7889) Fixed slapo-rwm parsing to avoid double-escaping rewrite rules (ITS#7964) Build Environment Fixed ldif-filter option parsing (ITS#8292) Fixed slapd-tester EOL handling in test output for windows (ITS#8280) Fixed slapd-tester executable suffix for windows (ITS#8216) Fixed test061 timing issues (ITS#8297) Contrib Added libnettle support to pw-pbkdf2 (ITS#8198) Fixed smbk5pwd compiler warnings with libnettle (ITS#8235) Fixed passwd symbol collisions with other crypto libraries (ITS#8294) Documentation Updated guide to reflect changes to how TLS is handled with syncrepl (ITS#7897) OpenLDAP 2.4.42 Release (2015/08/14) Fixed liblber address length for CLDAP (ITS#8158) Fixed libldap dnssrv potential overflow with port number (ITS#7027,ITS#8195) Fixed slapd cn=config when updating olcAttributeTypes (ITS#8199) Fixed slapd-mdb to correctly update search candidates for scoped searches (ITS#8203) Fixed slapo-ppolicy with redundant mod ops on glued trees (ITS#8184) Fixed slapo-rwm crash when deleting rewrite rules (ITS#8213) Build Environment Fixed libdb detection with gcc 5.x (ITS#8056) OpenLDAP 2.4.41 Release (2015/06/21) Fixed ldapsearch to explicitly flush its buffer (ITS#8118) Fixed libldap async connections (ITS#8090) Fixed libldap double free of request during abandon (ITS#7967) Fixed libldap error string for LDAP_X_CONNECTING (ITS#8093) Fixed libldap segfault in ldap_sync_initialize (ITS#8001) Fixed libldap ldif-wrap off by one error (ITS#8003) Fixed libldap handling of TLS in async mode (ITS#8022) Fixed libldap null pointer dereference (ITS#8028) Fixed libldap mutex handling with LDAP_OPT_SESSION_REFCNT (ITS#8050) Fixed slapd slapadd config db import of minimal frontend entry (ITS#8150) Fixed slapd slapadd onetime leak with -w (ITS#8014) Fixed slapd sasl auxprop crash with invalid config (ITS#8092) Fixed slapd syncrepl delta-mpr issue with overlays and slapd.conf (ITS#7976) Fixed slapd syncrepl mutex for cookie state (ITS#7968) Fixed slapd syncrepl memory leaks (ITS#8035) Fixed slapd syncrepl to free presentlist at end of refresh mode (ITS#8038) Fixed slapd syncrepl to streamline presentlist (ITS#8042) Fixed slapd syncrepl concurrency when CHECK_CSN is enabled (ITS#8120) Fixed slapd rootdn checks for hidden backends (ITS#8108) Fixed slapd segfault when using matched values control (ITS#8046) Fixed slapd-ldap reconnection behavior on remote failure (ITS#8142) Fixed slapd-mdb minor case typo (ITS#8049) Fixed slapd-mdb one-level search (ITS#7975) Fixed slapd-mdb heap corruption (ITS#7965) Fixed slapd-mdb crash after deleting in-use schema (ITS#7995) Fixed slapd-mdb minor code cleanup (ITS#8011) Fixed slapd-mdb to return errors when using incorrect env flags (ITS#8016) Fixed slapd-mdb to correctly update search candidates (ITS#8036, ITS#7904) Fixed slapd-mdb when there were more than 65535 aliases in scope (ITS#8103) Fixed slapd-mdb alias deref when objectClass is not indexed (ITS#8146) Fixed slapd-meta TLS initialization with ldaps URIs (ITS#8022) Fixed slapd-meta to have better error logging (ITS#8131) Fixed slapd-perl conversion to cn=config (ITS#8105) Fixed slapd-sql autocommit config variable (ITS#8129,ITS#6613) Fixed slapo-collect segfault (ITS#7797) Fixed slapo-constraint with 0 count constraint (ITS#7780,ITS#7781) Fixed slapo-deref with empty attribute list (ITS#8027) Fixed slapo-memberof to correctly reject invalid members (ITS#8107) Fixed slapo-sock result parser for CONTINUE (ITS#8048) Fixed slapo-syncprov synprov_matchops usage of test_filter (ITS#8013) Fixed slapo-syncprov segfault on disconnect/abandon (ITS#5452,ITS#8012) Fixed slapo-syncprov memory leak (ITS#8039) Fixed slapo-syncprov segfault on disconnect/abandon (ITS#8043) Fixed slapo-syncprov deadlock when autogroup is in use (ITS#8063) Fixed slapo-syncprov potential loss of changes when under load (ITS#8081) Fixed slapo-unique enforcement of uniqueness with manageDSAit control (ITS#8057) Build Environment Fixed ftello reference for Win32 (ITS#8127) Enhanced contrib modules build paths (ITS#7782) Fixed contrib/autogroup internal operation identity (ITS#8006) Fixed contrib/autogroup to skip internal ops with accesslog (ITS#8065) Fixed contrib/passwd/sha2 compiler warning (ITS#8000) Fixed contrib/noopsrch compiler warning (ITS#7998) Fixed contrib/dupent compiler warnings (ITS#7997) Test suite: Added vrFilter test (ITS#8046) Contrib Added pbkdf2 sha256 and sha512 schemes (ITS#7977) Fixed autogroup modification callback responses (ITS#6970) Fixed nssov compare with usergroup (ITS#8079) Fixed nssov password change behavior (ITS#8080) Fixed nssov updated to 0.9.4 (ITS#8097) Documentation Added ldap_get_option(3) LDAP_FEATURE_INFO_VERSION information (ITS#8032) Added ldap_get_option(3) LDAP_OPT_API_INFO_VERSION information (ITS#8032) Fixed slapd-config(5), slapd.conf(5) tls_cipher_suite option (ITS#8099) Fixed slapd-meta(5), slapd-ldap(5) tls_cipher_suite option (ITS#8099) Fixed slapd-meta(5) fix minor typo (ITS#7769) OpenLDAP 2.4.40 Release (2014/09/20) Fixed libldap DNS SRV priority handling (ITS#7027) Fixed libldap don't leak libldap err codes (ITS#7676) Fixed libldap CR/LF handling (ITS#4635) Fixed libldap ldif-wrap length (ITS#7871) Fixed libldap GnuTLS ciphersuite parsing (ITS#7500) Fixed libldap GnuTLS with newer versions (ITS#7430,ITS#6359) Fixed libldif to correctly handle 4096 character lines (ITS#7859) Fixed librewrite reference counting (ITS#7723) Fixed slapacl with back-mdb reader transactions (ITS#7920) Fixed slapd syncrepl to send cookie on fallback (ITS#7849) Fixed slapd syncrepl SEGV when abandoning a connection (ITS#7928) Fixed slapd slapcat with external schema (ITS#7895) Fixed slapd schema RDN normalization (ITS#7935) Fixed slapd with repeated language tags (ITS#7941) Fixed slapd modrdn crash on naming attr with no matching rule (ITS#7850) Fixed slapd memory leak in control handling (ITS#7942) Fixed slapd-ldap removed dead code (ITS#7922) Fixed slapd-mdb to work concurrently with slapadd (ITS#7798) Fixed slapd-mdb with paged results (ITS#7705, ITS#7800) Fixed slapd-mdb slapcat with nonexistent indices (ITS#7870) Fixed slapd-mdb long lived reader transactions (ITS#7904) Fixed slapd-mdb memory leak on matchedDN (ITS#7872) Fixed slapd-mdb sorting of attribute values (ITS#7902) Fixed slapd-mdb to flag attribute values as sorted (ITS#7903) Fixed slapd-mdb index config handling (ITS#7912) Fixed slapd-mdb entry release handling (ITS#7915) Fixed slapd-mdb with aliases and referrals (ITS#7927) Fixed slapd-mdb alias dereferencing (ITS#7702) Fixed slapd-sock socket flushing (ITS#7937) Fixed slapo-accesslog attribute normalization (ITS#7934) Fixed slapo-accesslog internal search logging (ITS#7929) Fixed slapo-auditlog connection destroy logic (ITS#7906,ITS#7923) Fixed slapo-chain interaction with slapo-rwm (ITS#7930) Fixed slapo-constraint connection destroy logic (ITS#7906,ITS#7923) Fixed slapo-dds connection destroy logic (ITS#7906,ITS#7923) Fixed slapo-dyngroup connection destroy logic (ITS#7906,ITS#7923) Fixed slapo-memberof attr count (ITS#7893) Fixed slapo-memberof frontendDB handling (ITS#7249) Fixed slapo-memberof internal search logging (ITS#7929) Fixed slapo-pcache config processing (ITS#7919) Fixed slapo-pcache connection destroy logic (ITS#7906,ITS#7923) Added slapo-ppolicy ORDERING rules (ITS#7838) Fixed slapo-ppolicy timestamp resolution to use microseconds (ITS#7161) Fixed slapo-ppolicy connection destroy logic (ITS#7906,ITS#7923) Fixed slapo-refint to check for pauses in cn=config (ITS#7873) Fixed slapo-refint internal search logging (ITS#7929) Fixed slapo-refint connection destroy logic (ITS#7906,ITS#7923) Fixed slapo-seqmod connection destroy logic (ITS#7906,ITS#7923) Fixed slapo-slapover connection destroy logic (ITS#7906,ITS#7923) Fixed slapo-sock db_init (ITS#7868) Fixed slapo-sssvlv fix olcSssVlvMaxPerConn (ITS#7908) Fixed slapo-translucent double free (ITS#7587) Fixed slapo-translucent to work with manageDSAit (ITS#7864) Fixed slapo-translucent to use local backend with local entries (ITS#7915) Fixed slapo-unique connection destroy logic (ITS#7906,ITS#7923) Fixed slapcacl with invalid suffix (ITS#7827) Build Environment Remove support for gcrypt (ITS#7877) BDB 6.0.20 and later is not supported (ITS#7890) Fixed ODBC link check (ITS#7891) Fixed slapd.ldif frontend config (ITS#7933) Contrib Added pbkdf2 module (ITS#7742) Fixed autogroup double free (ITS#7831) Fixed autogroup modification callback responses (ITS#6970) Fixed ldapc++ memory leak in Async connection (ITS#7806) Fixed nssov install path (ITS#7858) Fixed passwd rpath (ITS#7885) Fixed apr1 do_phk_hash argument order (ITS#7869) Fixed slapd-sha2 buffer overrun (ITS#7851) Documentation Fixed slapd.ldif man page reference (ITS#7803) Fixed slapd.conf(5) man page to reference exattrs (ITS#7847) Fixed guide to work with mkrelease (ITS#7887) Fixed ldap_get_dn(3) ldap_ava definition (ITS#7860) OpenLDAP 2.4.39 Release (2014/01/26) Fixed libldap MozNSS crash (ITS#7783) Fixed libldap memory leak with SASL (ITS#7757) Fixed libldap assert in parse_passwdpolicy_control (ITS#7759) Fixed libldap shortcut NULL RDNs (ITS#7762) Fixed libldap deref to use correct control Fixed liblmdb keysizes with mdb_update_key (ITS#7756) Fixed slapd cn=config olcDbConfig modification (ITS#7750) Fixed slapd-bdb/hdb to bail out of search if config is paused (ITS#7761) Fixed slapd-bdb/hdb indexing issue with derived attributes (ITS#7778) Fixed slapd-mdb to bail out of search if config is paused (ITS#7761) Fixed slapd-mdb indexing issue with derived attributes (ITS#7778) Fixed slapd-perl to bail out of search if config is paused (ITS#7761) Fixed slapd-sql to bail out of search if config is paused (ITS#7761) Fixed slapo-constraint handling of softadd/softdel (ITS#7773) Fixed slapo-syncprov assert with findbase (ITS#7749) Build Environment Test suite: Use $(MAKE) for tests (ITS#7753) Documentation admin24 fix TLSDHParamFile to be correct (ITS#7684) OpenLDAP 2.4.38 Release (2013/11/16) Fixed liblmdb nordahead flag (ITS#7734) Fixed liblmdb to check cursor index before cursor_del (ITS#7733) Fixed liblmdb wasted space on split (ITS#7589) Fixed slapd for certs with a NULL issuerDN (ITS#7746) Fixed slapd cn=config with empty nested includes (ITS#7739) Fixed slapd syncrepl memory leak with delta-sync MPR (ITS#7735) Fixed slapd-bdb/hdb to stop processing on dn not found (ITS#7741) Fixed slapd-bdb/hdb with indexed ANDed filters (ITS#7743) Fixed slapd-mdb to stop processing on dn not found (ITS#7741) Fixed slapd-mdb dangling reader (ITS#7662) Fixed slapd-mdb matching rule for OlcDbEnvFlags (ITS#7737) Fixed slapd-mdb with indexed ANDed filters (ITS#7743) Fixed slapd-meta from blocking other threads (ITS#7740) Fixed slapo-syncprov assert with findbase (ITS#7749) OpenLDAP 2.4.37 Release (2013/10/27) Added liblmdb nordahead environment flag (ITS#7725) Fixed client tools CLDAP with IPv6 (ITS#7695) Fixed libldap CLDAP with IPv6 (ITS#7695) Fixed libldap lock ordering with abandon op (ITS#7712) Fixed liblmdb segfault with mdb_cursor_del (ITS#7718) Fixed liblmdb when converting to writemap (ITS#7715) Fixed liblmdb assert on MDB_NEXT with delete (ITS#7722) Fixed liblmdb wasted space on split (ITS#7589) Fixed slapd cn=config with olcTLSProtocolMin (ITS#7685) Fixed slapd-bdb/hdb optimize index updates (ITS#7329) Fixed slapd-ldap chaining with cn=config (ITS#7381, ITS#7434) Fixed slapd-ldap chaining with controls (ITS#7687) Fixed slapd-mdb optimize index updates (ITS#7329) Fixed slapd-meta chaining with cn=config (ITS#7381, ITS#7434) Fixed slapo-constraint to no-op on nonexistent entries (ITS#7692) Fixed slapo-dds assert on startup (ITS#7699) Fixed slapo-memberof to not replicate internal ops (ITS#7710) Fixed slapo-refint to not replicate internal ops (ITS#7710) Build Environment Fixed slapd-mdb ptr arithmetic on void *s (ITS#7720) Documentation ldapsearch(1) minor typo fix (ITS#7680) slapd-passwd(5) minor typo fix (ITS#7680) OpenLDAP 2.4.36 Release (2013/08/17) Added back-meta target filter patterns (ITS#7609) Added liblmdb mdb_txn_env to API (ITS#7660) Fixed libldap CLDAP with uninit'd memory (ITS#7582) Fixed libldap with UDP (ITS#7583) Fixed libldap OpenSSL TLS versions (ITS#7645) Fixed liblmdb MDB_PREV behavior (ITS#7556) Fixed liblmdb transaction issues (ITS#7515) Fixed liblmdb mdb_drop overflow page return (ITS#7561) Fixed liblmdb nested split (ITS#7592) Fixed liblmdb overflow page behavior (ITS#7620) Fixed liblmdb race condition with read and write txns (ITS#7635) Fixed liblmdb mdb_del behavior with MDB_DUPSORT and mdb_del (ITS#7658) Fixed slapd cn=config with unknown schema elements (ITS#7608) Fixed slapd cn=config with loglevel 0 (ITS#7611) Fixed slapd slapi filterlist free behavior (ITS#7636) Fixed slapd slapi control free behavior (ITS#7641) Fixed slapd schema countryString as directoryString (ITS#7659) Fixed slapd schema telephoneNumber as directoryString (ITS#7659) Fixed slapd-bdb/hdb to wait for read locks in tool mode (ITS#6365) Fixed slapd-mdb behavior with alias dereferencing (ITS#7577) Fixed slapd-mdb modrdn and base-scoped searches (ITS#7604) Fixed slapd-mdb refcount behavior (ITS#7628) Fixed slapd-meta binding flag is set (ITS#7524) Fixed slapd-meta with minimal config (ITS#7581) Fixed slapd-meta missing results messages (ITS#7591) Added slapd-meta TCP keepalive support (ITS#7513) Fixed slapo-sssvlv double free (ITS#7588) Fixed slaptest to list -Q option (ITS#7568) Build Environment Fixed slapd-meta declaration warnings (ITS#7654) Contrib Fixed nssov group enumeration bug (ITS#7569) Fixed autogroup when URI has no attrs (ITS#7580) Documentation admin24 Update database backend notes (ITS#7590) ldap.conf(5) fixed typos (ITS#7568) ldapmodify(1) remove replog reference (ITS#7562) ldif(5) remove replog reference (ITS#7562) slapd-config(5) remove replog reference (ITS#7562) slapd.conf(5) remove replog reference (ITS#7562) slapd-config(5) document TLSProtocolMin (ITS#5655,ITS#7645) slapd.conf(5) document TLSProtocolMin (ITS#5655,ITS#7645) OpenLDAP 2.4.35 Release (2013/03/31) Fixed liblmdb mdb_cursor_put with MDB_MULTIPLE (ITS#7551) Fixed liblmdb page rebalance (ITS#7536) Fixed liblmdb missing parens (ITS#7377) Fixed liblmdb mdb_cursor_del crash (ITS#7553) Fixed slapd syncrepl updateCookie status (ITS#7531) Fixed slapd connection logging (ITS#7543) Fixed slapd segfault on modify (ITS#7542, ITS#7432) Fixed slapd-mdb to reject undefined attrs (ITS#7540) Fixed slapo-pcache with +/- attrsets (ITS#7552) Build Environment don't install DB_CONFIG if no BDB backends (ITS#7533) Documentation slapschema(8) fix tool name (ITS#7534) admin24 fixed pcache example (ITS#7546) admin24 fixed config examples (ITS#7522) OpenLDAP 2.4.34 Release (2013/03/01) Fixed libldap connections with EINTR (ITS#7476) Fixed libldap lineno overflow in ldif_read_record (ITS#7497) Fixed liblmdb mdb_env_open flag handling (ITS#7453) Fixed liblmdb mdb_midl_sort array optimization (ITS#7432) Fixed liblmdb freelist with large entries (ITS#7455) Fixed liblmdb to check for filled dirty page list (ITS#7491) Fixed liblmdb to validate data limits (ITS#7485) Fixed liblmdb mdb_update_key for large keys (ITS#7505) Fixed ldapmodify to not core dump with invalid LDIF (ITS#7477) Fixed slapd syncrepl for old entries in MPR setup (ITS#7427) Fixed slapd signedness for index_substr_any_* (ITS#7449) Fixed slapd enforce SLAPD_MAX_DAEMON_THREADS (ITS#7450) Fixed slapd mutex in send_ldap_ber (ITS#6164) Added slapd-ldap onerr option (ITS#7492) Added slapd-ldap keepalive support (ITS#7501) Fixed slapd-ldif with empty dir (ITS#7451) Fixed slapd-mdb to reopen attr DBs after env reopen (ITS#7416) Fixed slapd-mdb handling of missing entries (ITS#7483,7496) Fixed slapd-mdb environment flag setting (ITS#7452) Fixed slapd-mdb with sub db slapcat (ITS#7469) Fixed slapd-mdb to correctly work with toolthreads > 2 (ITS#7488,ITS#7527) Fixed slapd-mdb subtree search speed (ITS#7473) Fixed slapd-meta conversion to cn=config (ITS#7525) Fixed slapd-meta segfault when modifying olcDbUri (ITS#7526) Fixed slapd-sql back-config support (ITS#7499) Fixed slapo-constraint handle uri and restrict correctly (ITS#7418) Fixed slapo-constraint with multi-provider replication (ITS#7426) Fixed slapo-constraint segfault (ITS#7431) Fixed slapo-deref control initialization (ITS#7436) Fixed slapo-deref control exposure (ITS#7445) Fixed slapo-memberof with internal ops (ITS#7487) Fixed slapo-pcache matching rules for config db (ITS#7459) Fixed slapo-rwm modrdn cleanup (ITS#7414) Fixed slapo-sssvlv maxperconn parameter (ITS#7484) Build Environment Fixed slapo-constraint test suite (ITS#7423) Contrib Added nssov nssov_config support (ITS#7518) Added nssov password_prohibit_message (ITS#7518) Fixed ldapc++ with gcc-4.7 (ITS#7281,ITS#7304) Fixed nssov olcNssPamSession handling (ITS#7481) Fixed nssov connection DN (ITS#7518) Add missing Makefile for various modules (ITS#7308) Unify Makefile structure for modules (ITS#7309) Fixed slapo-allowed attribute replication (ITS#7493) Fixed slapo-passwd SHA2 to correctly zero buffer (ITS#7490) Documentation ldapurl(1) fix example usage (ITS#7454) ldap_get_option(3) fixed trailing whitespace (ITS#7411) slapd-config(5) olcExtraAttrs is per db (ITS#7421) slapd-overlays(5) update manpage index (ITS#7489) slapo-dynlist(5) Search behavior notes (ITS#7486) slapo-valsort(5) Document valsort control syntax (ITS#7523) OpenLDAP 2.4.33 Release (2012/10/10) Added slapd-meta cn=config support Fixed libldap MozNSS slot picking (ITS#7359) Fixed libldap MozNSS with tokenname:certnickname format (ITS#7360) Fixed liblmdb POSIX semaphore cleanup on environment close (ITS#7364) Fixed liblmdb mdb_page_split (ITS#7385, ITS#7229) Fixed slapd alock handling on Windows (ITS#7361) Fixed slapd acl handling with zero-length values (ITS#7350) Fixed slapd syncprov to not reference ops inside a lock (ITS#7172) Fixed slapd delta-syncrepl MPR with large attribute values (ITS#7354) Fixed slapd slapd_rw_destroy function (ITS#7390) Fixed slapd-ldap idassert bind handling (ITS#7403) Fixed slapd-mdb slapadd -q -w double free (ITS#7356) Fixed slapd-mdb to close read txn in reindex commit (ITS#7386) Fixed slapo-constraint with multiple modifications (ITS#7168) Build Environment Fixed build with Visual Studio (ITS#7358) Fixed liblmdb posix semaphore use on BSD system (ITS#7363) Add slapo-constraint test suite (ITS#7344, ITS#7366) Contrib Updated radius passwd module for NAS-Identifier (ITS#7357) Documentation slapo-refint(5) Note that refint is not replicated (ITS#7405) OpenLDAP 2.4.32 Release (2012/07/31) Added slappasswd loadable module support (ITS#7284) Fixed tools to not clobber SASL_NOCANON (ITS#7271) Fixed libldap function declarations (ITS#7293) Fixed libldap double free (ITS#7270) Fixed libldap debug level setting (ITS#7290) Fixed libldap MozNSS PEM/certdb handling (ITS#7276) Fixed libldap MozNSS cipher suite selection (ITS#7285) Fixed libldap MozNSS error handling (ITS#7287) Fixed libldap MozNSS cipher suite being ignored (ITS#7289) Fixed libldap MozNSS infinite loop (ITS#7291) Fixed libldap MozNSS context token for certdb (ITS#7312) Fixed libldap MozNSS store certificate object (ITS#7313) Fixed libldap MozNSS fix init and cleanup (ITS#7314) Fixed libldap MozNSS slot and pin usage (ITS#7315) Fixed libldap MozNSS to avoid infinite loop (ITS#7316) Fixed libldap MozNSS untrusted issuer error (ITS#7331) Fixed libldap gettime() regression (ITS#6262) Fixed libldap sasl handling (ITS#7118, ITS#7133) Fixed libldap to correctly free socket with TLS (ITS#7241) Fixed liblmdb leaf node handling (ITS#7266) Fixed liblmdb mutexes on Apple/Windows (ITS#7251) Fixed slapd config index renumbering (ITS#6987) Fixed slapd duplicate error response (ITS#7076) Fixed slapd parsing of PermissiveModify control (ITS#7298) Fixed slapd-bdb/hdb cache hang under high load (ITS#7222) Fixed slapd-bdb/hdb alias checking (ITS#7303) Fixed slapd-bdb/hdb olcDbConfig changes work immediately (ITS#7338) Fixed slapd-ldap to encode user DN during password change (ITS#7319) Fixed slapd-ldap assertion when proxying to MS AD (ITS#6851) Fixed slapd-ldap monitoring (ITS#7182, ITS#7225) Fixed slapd-mdb with tool mode (ITS#7255) Fixed slapd-mdb with approx indexing (ITS#7279) Fixed slapd-mdb dn2id delete (ITS#7302) Fixed slapd-mdb memory leak in online indexer (ITS#7323) Fixed slapd-mdb db corruption when hitting maxsize (ITS#7337) Fixed slapd-mdb aborts with online indexing (ITS#7339) Fixed slapd-perl panic (ITS#7325) Fixed slapo-accesslog memory leaks with sync replication (ITS#7292) Fixed slapo-syncprov memory leaks with sync replication (ITS#7292) Fixed contrib/smbk5pwd to not compile with MozNSS (ITS#7327) Fixed contrib/sha2 portability (ITS#7267) Fixed contrib/sha2 thread safety (ITS#7269) Added contrib/sha2 {SSHA256}, {SSHA384}, {SSHA512} support (ITS#7278) Build Environment Fixed test057 timing issues (ITS#7317) Fixed compilation with MS Visual Studio (ITS#7332) Contrib Added slapi_[get|free]_client_ip() (ITS#7305) Documentation slapo-sssvlv Added note about criticality (ITS#7253) admin24 Fix peername.regex typo (ITS#7282) Fixed slapd-config file include example (ITS#7318) slapd-ldap(5) Reference RFC4526 (ITS#7294) slapd-meta(5) Reference RFC4526 (ITS#7294) OpenLDAP 2.4.31 Release (2012/04/21) Added slapo-accesslog support for reqEntryUUID (ITS#6656) Fixed libldap IPv6 URL detection (ITS#7194) Fixed libldap rebinding on failed connection (ITS#7207) Fixed liblmdb alignment of MDB_db members (ITS#7191) Fixed liblmdb branch page merging on deletes (ITS#7190) Fixed liblmdb page split with MDB_APPEND (ITS#7213) Fixed liblmdb free page usage with entry deletion (ITS#7210) Fixed liblmdb to use IOV_MAX if it is defined and small (ITS#7196) Fixed liblmdb key alignment (ITS#7219) Fixed liblmdb mdb_page_split (ITS#7229) Fixed liblmdb with zero length IDLs (ITS#7230) Fixed slapd listener initialization (ITS#7233) Fixed slapd cn=config with olcTLSVerifyClient (ITS#7197) Fixed slapd delta-syncrepl fallback on non-leaf error (ITS#7195) Fixed slapd to reject MPR setups with bad serverID setting (ITS#7200) Fixed slapd approxIndexer key generation (ITS#7203) Fixed slapd modification of olcSuffix (ITS#7205) Fixed slapd schema validation with missing definitions (ITS#7224) Fixed slapd syncrepl -c with supplied CSN values (ITS#7245) Fixed slapd-bdb/hdb idlcache with only one element (ITS#7231) Fixed slapd-perl modify with binary values (ITS#7149) Fixed slapd-shell cn=config support (ITS#7201) Fixed slapd-shell modify with binary values (ITS#7149) Fixed slapo-accesslog deadlock with non-logged write ops (ITS#7088) Fixed slapo-syncprov sessionlog check (ITS#7218) Fixed slapo-syncprov entry leak (ITS#7234) Fixed slapo-syncprov startup initialization (ITS#7235) Build Environment Fixed test022 to check ldapsearch results (ITS#7228) Fixed test044 when back-monitor is disabled (ITS#7204) Documentation Fixed slapschema(8) formatting (ITS#7188) Fixed limdb functionality documentation (ITS#7238) Fixed ldap_get_option(3) note inheritance behavior (ITS#7240) OpenLDAP 2.4.30 Release (2012/02/29) Fixed libldap socket polling for writes (ITS#7167) Fixed liblutil string modifications (ITS#7174) Fixed slapd crash when attrsOnly is true (ITS#7143) Fixed slapd syncrepl delete handling (ITS#7052,ITS#7162) Fixed slapd-mdb slapadd with -q (ITS#7170) Fixed slapd-mdb slapadd with -w (ITS#7180) Fixed slapd-mdb slapindex with -q and -t (ITS#7176) Fixed slapo-pcache time-to-refesh handling (ITS#7178) Fixed slapo-syncprov loop detection (ITS#6024) Build Environment Fixed POSIX make support (ITS#7160) Fixed slapd-mdb build on POSIX (ITS#7160) Documentation Added option "-o" to ldap*(1) pages (ITS#7152) Fixed ldap*(1) page cleanup (ITS#7177) Fixed ldap_modify(3) prototypes (ITS#7173) OpenLDAP 2.4.29 Release (2012/02/12) Fixed libldap MozNSS deferred initialization handling (ITS#7136) Fixed libldap MozNSS with TLSCertificateKeyFile not set (ITS#7135) Fixed slapd cn=config modification of first schema element (ITS#7098) Fixed slapd operation reuse (ITS#7107) Fixed slapd blocked writers to not interfere with pool pause (ITS#7115) Fixed slapd connection loop connindex usage (ITS#7131) Fixed slapd double mutex unlock via connection_done (ITS#7125) Fixed slapd check order in connection_write (ITS#7113) Fixed slapd slapadd to exit on failure (ITS#7142) Fixed slapd syncrepl reference to freed memory (ITS#7127,ITS#7132) Fixed slapd syncrepl to ignore some errors on delete (ITS#7052) Fixed slapd syncrepl to handle missing oldRDN (ITS#7144) Fixed slapd-mdb to handle overlays in tool mode (ITS#7099) Fixed slapd-mdb segfaults with page splits (ITS#7121) Fixed slapd-mdb cleanup on transaction abort (ITS#7140) Fixed slapd-mdb with attribute descriptions (ITS#7146) Fixed slapd-meta to correctly handle multiple targets (ITS#7050) Fixed slapd-monitor compare op to update cached entry (ITS#7123) Fixed slapd-perl initialization (ITS#7075) Fixed slapd-sql to properly initialize be_cf_ocs (ITS#7158) Fixed slapo-dds to properly exit when in tool mode (ITS#7099) Fixed slapo-rwm not leave empty lots with normalized attrs (ITS#7143) Fixed slapo-syncprov with already abandoned operation (ITS#7150) Fixed contrib/smbk5pwd uninitialized keys in shadowLastChange (ITS#7138) Build Environment Fixed ldapsearch build on windows (ITS#7156) Fixed test001 to skip back-ldif (ITS#7101) Documentation admin24 Fix typo (ITS#7117) OpenLDAP 2.4.28 Release (2011/11/26) Fixed back-mdb out of order slapadd (ITS#7090) OpenLDAP 2.4.27 Release (2011/11/24) Added libldap support for draft-wahl-ldap-session (ITS#6984) Added slapd support for draft-wahl-ldap-session (ITS#6984) Added slapadd pipelining capability (ITS#7078) Added slapd Add-if-not-present (ITS#6561) Added slapd delta-syncrepl MPR (ITS#6734,ITS#7029,ITS#7031) Added slapd-mdb experimental backend (ITS#7079) Added slapd-passwd dynamic config support Added slapd-perl dynamic config support Added slapd-shell dynamic config support Added slapd-sock support as an overlay (ITS#6666) Added slapd-sql dynamic config support Added contrib/passwd APR1 support (ITS#6826) Fixed slapi linking on AIX (ITS#3272) Fixed ldapmodify crash with LDIF controls (ITS#7039) Fixed ldapsearch to honor timeout and timelimit (ITS#7009) Fixed libldap endless looping (ITS#7035) Fixed libldap TLS to not check hostname when using 'allow' (ITS#7014) Fixed libldap GnuTLS cert dn parse (ITS#7051) Fixed libldap MozNSS correctly destroy SSL_PeerCertificate (ITS#6980) Fixed libldap MozNSS with issuer expiration and verify never (ITS#6998) Fixed libldap MozNSS memory leak (ITS#7001) Fixed libldap MozNSS allow/try behavior (ITS#7002) Fixed libldap MozNSS to be thread safe (ITS#7022) Fixed libldap MozNSS SSL_ForceHandshake to use a mutex (ITS#7034) Fixed libldap MozNSS with wildcard certs (ITS#7006) Fixed liblutil MD5 initialization (ITS#6982) Fixed slapadd common code into slapcommon (ITS#6737) Fixed slapd backend connection initialization (ITS#6993) Fixed slapd frontend DB parsing in cn=config (ITS#7016) Fixed slapd hang with {numbered} overlay insertion (ITS#7030) Fixed slapd inet_ntop usage (ITS#6925) Fixed slapd cn=config deletion of bitmasks (ITS#7083) Fixed slapd cn=config modify replace/delete crash (ITS#7065) Fixed slapd schema UTF8StringNormalize with 0 length values (ITS#7059) Fixed slapd with dynamic acls for cn=config (ITS#7066) Fixed slapd response callbacks (ITS#6059,ITS#7062) Fixed slapd no_connection warnings with ldapi (ITS#6548,ITS#7092) Fixed slapd return code processing (ITS#7060) Fixed slapd sl_malloc various issues (ITS#6437) Fixed slapd startup behavior (ITS#6848) Fixed slapd syncrepl crash with non-replicated ops (ITS#6892) Fixed slapd syncrepl with modrdn (ITS#7000,ITS#6472) Fixed slapd syncrepl timeout when using refreshAndPersist (ITS#6999) Fixed slapd syncrepl deletes need a non-empty CSN (ITS#7052) Fixed slapd syncrepl glue for empty suffix (ITS#7037) Fixed slapd results cleanup (ITS#6763,ITS#7053) Fixed slapd validation of args for TLSCertificateFile (ITS#7012) Fixed slapd-bdb/hdb to build entry DN based on parent DN (ITS#5326) Fixed slapd-hdb with zero-length entries (ITS#7073) Fixed slapd-hdb duplicate entries in subtree IDL cache (ITS#6983) Fixed slapo-constraint conversion to back-config (ITS#6986) Fixed slapo-dds tag in refresh response (ITS#6886) Fixed slapo-dds TTL tolerance (ITS#7017) Fixed slapo-lastbind so authTimestamp is manageable (ITS#6873) Fixed slapo-pcache response cleanup (ITS#6981) Fixed slapo-ppolicy pwdAllowUserChange behavior (ITS#7021) Fixed slapo-sssvlv issue with greaterThanorEqual (ITS#6985) Fixed slapo-sssvlv to only return requested attrs (ITS#7061) Fixed slapo-syncprov DSA attribute filtering for Persist mode (ITS#7019) Fixed slapo-syncprov when consumer has newer state of our SID (ITS#7040) Fixed slapo-syncprov crash (ITS#7025) Fixed slapo-unique URI checking of "host" portion (ITS#7018) Fixed contrib/autogroup double-free (ITS#6972) Fixed contrib/smbk5pwd cn=config deletion of bitmasks (ITS#7083) Fixed contrib/smbk5pwd on 64-bit systems (ITS#7082) Build Environment Added missing LDIF form of schema files (ITS#7063) Fixed build for Solaris native compilers (ITS#6992) Fixed creation and installation of slapd.ldif (ITS#7015) Fixed libnet linking (ITS#7071) Documentation admin24 Fix table numbering (ITS#7003) slapd.conf(5) Fixed TLSCACertificateFile information (ITS#7023) ldapmodify(1) Fixed minor typo in -S option description (ITS#7086) ldap_sync(3) Document ldap_sync_destroy (ITS#7028) slapo-unique(5) Fix keyword quoting (ITS#7028) OpenLDAP 2.4.26 Release (2011/06/30) Added libldap LDAP_OPT_X_TLS_PACKAGE (ITS#6969) Fixed libldap MozNSS with CACertDir (ITS#6975) Fixed libldap MozNSS with PR_SetEnv (ITS#6862) Fixed libldap descriptor leak (ITS#6929) Fixed libldap socket leak (ITS#6930) Fixed libldap get option crash (ITS#6931) Fixed libldap lockup (ITS#6898) Fixed libldap ASYNC TLS setup (ITS#6828) Fixed libldap with missing \n terminations (ITS#6947) Fixed tools double free (ITS#6946) Fixed tools verbose output (ITS#6977) Fixed ldapmodify SEGV on invalid LDIF (ITS#6978) Added slapd extra_attrs database option (ITS#6513) Fixed slapd asserts (ITS#6932) Fixed slapd configfile param on windows (ITS#6933) Fixed slapd config with global chaining (ITS#6843) Fixed slapd uninitialized variables (ITS#6935) Fixed slapd config objectclass is readonly (ITS#6963) Fixed slapd entry response with control (ITS#6899) Fixed slapd with unknown attrs (ITS#6819) Fixed slapd normalization of schema RDN (ITS#6967) Fixed slapd operations cache to 10 op limit (ITS#6944) Fixed slapd syncrepl crash with non-replicated ops (ITS#6892) Fixed slapd-bdb/hdb with sparse index ranges (ITS#6961) Fixed slapd-monitor stray code cleanup (ITS#6974) Fixed back-ldap ppolicy updates (ITS#6711) Fixed back-ldap with id-assert (ITS#6817) Fixed slapd-meta reentry issues (ITS#6909) Fixed slapd-sql length of data type (ITS#6657,ITS#6691) Added slapo-accesslog filter matching (ITS#6815) Fixed slapo-accesslog with invalid attrs (ITS#6819) Added slapo-auditlog connID and peername logging (ITS#6936) Fixed slapo-memberof with accesslog (ITS#6329,ITS#6766,ITS#6915) Fixed slapo-pcache with unknown attrs (ITS#6823) Fixed slapo-pcache with '1.1', '+', and '*' attrs (ITS#6950) Fixed slapo-pcache buffersize issues (ITS#6951) Fixed slapo-pcache refresh (ITS#6953) Fixed slapo-pcache with pCacheBind (ITS#6954) Fixed slapo-pcache database corruption (ITS#6831) Fixed slapo-rwm with attributes with no equality rule (ITS#6943) Fixed slapo-sssvlv limits check when global (ITS#6973) Fixed slapo-syncprov with replicated subtrees (ITS#6872) Fixed slapo-unique with managedsait (ITS#6641) Fixed slapo-unique filter with zero-length values (ITS#6901) Added contrib/acl GSS naming extensions ACL module Fixed contrib/smbk5pwd with shadowLastChange (ITS#6955) Build Environment Fixed builds that do not have GETTIMEOFDAY (ITS#6885) Fixed libldap libfetch dependancy (ITS#6889) Documentation ldap_get_dn(3) add man page (ITS#6959) slapd-backends(5) update recommended database backend (ITS#6904) slapd-bdb(5) update recommended database backend (ITS#6904) slapd-hdb(5) update recommended database backend (ITS#6904) slapo-nssov(5) Fixed typo (ITS#6934) admin24 update that cn=config is preferred (ITS#6905) admin24 update information about indexes (ITS#6906) admin24 fix --enable-wrappers option (ITS#6971) OpenLDAP 2.4.25 Release (2011/03/26) Fixed ldapsearch pagedresults loop (ITS#6755) Fixed tools for incompatible args (ITS#6849) Fixed libldap MozNSS crash (ITS#6863) Fixed slapd add objectclasses in order (ITS#6837) Added slapd ordering for uidNumber and gidNumber (ITS#6852) Fixed slapd segfault when adding values out of order (ITS#6858) Fixed slapd sortval handling (ITS#6845) Fixed slapd-bdb with slapadd/index quick option (ITS#6853) Fixed slapd-ldap chain cn=config support (ITS#6837) Fixed slapd-ldap chain with slapd.conf (ITS#6857) Fixed slapd-meta deadlock (ITS#6846) Fixed slapo-sssvlv with multiple requests (ITS#6850) Fixed contrib/lastbind install rules (ITS#6238) Fixed contrib/cloak install rules (ITS#6877) Build Environment Fixed windows NT threads build (ITS#6859) Fixed libldap/lberl/util if/else usage (ITS#6832) Fixed Windows odbc32 detection (ITS#6125) Fixed Windows msys build (ITS#6870) Fixed test020 exit codes (ITS#6404) Documentation admin24 guide ldapi usage (ITS#6839) admin24 guide conversion notes (ITS#6834) admin24 guide fix drawback math for syncrepl (ITS#6866) admin24 guide note manpages are definitive (ITS#6855) OpenLDAP 2.4.24 Release (2011/02/10) Added LDIF line wrapping setting (ITS#6645) Added MozNSS support (ITS#6714,ITS#6742,ITS#6790,ITS#6791) Added MozNSS support (ITS#6802,ITS#6811,ITS#6816,ITS#5696) Added libldap cert x500UniqueIdentifier handling (ITS#6741) Added libldap_r,libldap formal concurrency API (ITS#6625,ITS#5421) Added slapadd attribute value checking (ITS#6592) Added slapcat continue mode for problematic DBs (ITS#6482) Added slapd syncrepl suffixmassage support (ITS#6781) Added slapd multiple listener threads (ITS#6780) Added slapd extensible match for ordering rules (ITS#6532) Added slapd-meta paged results control forwarding (ITS#6664) Added slapd-meta subtree-include support (ITS#6801) Added slapd-null back-config support (ITS#6624) Added slapd-sql autocommit support (ITS#6612) Added slapd-sql support for long long keys (ITS#6617) Added slapo-sssvlv multiple sorts per connection (ITS#6686) Added contrib/autogroup LDAP URI with attribute filter (ITS#6536) Added contrib/dupent module (ITS#6630) Added contrib/lastbind (ITS#6238) Added contrib/kinit for kerberos tickets Added contrib/noopsrch for entry counting (ITS#6598) Fixed client tools control logging (ITS#6775) Fixed client tools one time leak (ITS#6778) Fixed liblber to not close invalid sockets (ITS#6585) Fixed liblber unmatched brace handling (ITS#6764) Fixed liblber error setting (ITS#6732) Fixed liblber memory debugging (ITS#6733) Fixed libldap connectionless warnings (ITS#6747) Fixed libldap dnssrv port format specifier (ITS#6644) Fixed libldap EOF handling (ITS#6723) Fixed libldap GnuTLS hang on socket close (ITS#6673) Fixed libldap sasl partial write handling (ITS#6639) Fixed libldap search leak (ITS#6453) Fixed libldap referral chasing (ITS#6602) Fixed libldap leak when chasing referrals (ITS#6744) Fixed libldap url parsing with NULL host (ITS#6653) Fixed libldap ldap_open_internal_connection (ITS#6788) Fixed libldap sync checking for BER errors (ITS#6738) Fixed libldap variable usage (ITS#6813) Fixed liblutil getpass prompts (ITS#6702) Fixed ldapsearch segfault with deref (ITS#6638) Fixed ldapsearch multiple controls parsing (ITS#6651) Fixed slapd SlapReply usage (ITS#6758) Fixed slapd acl parsing overflow (ITS#6611) Fixed slapd acl when resuming parsing (ITS#6804) Fixed slapd Compare operation (ITS#6753) Fixed slapd default config acls with overlays (ITS#6822) Fixed slapd assert control (ITS#5862) Fixed slapd assertions and debugging (ITS#6759) Fixed slapd config leak with olcDbDirectory (ITS#6634) Fixed slapd connectionless warnings (ITS#6747) Fixed slapd listeners destruction (ITS#6736) Fixed slapd to free controls if needed (ITS#6629) Fixed slapd to stop if given unknown options (ITS#6754) Fixed slapd filter leak (ITS#6635) Fixed slapd matching rules for strict ordering (ITS#6722) Fixed slapd when first acl is value dependent (ITS#6693) Fixed slapd modify to return actual error (ITS#6581) Fixed slapd modrdn with empty DN (ITS#6768) Fixed slapd c_authz_backend setting (ITS#6824) Fixed slapd sortvals of attributes with 1 value (ITS#6715) Fixed slapd syncrepl reuse of presence list (ITS#6707) Fixed slapd syncrepl uninitialized return code (ITS#6719) Fixed slapd syncrepl variable initialization (ITS#6739) Fixed slapd syncrepl refresh to use complete cookie (ITS#6807) Fixed slapd-bdb hasSubordinates generation (ITS#6712) Fixed slapd-bdb entry cache delete failure (ITS#6577) Fixed slapd-bdb entry cache leak on multi-core systems (ITS#6660) Fixed slapd-bdb error propagation to overlays (ITS#6633) Fixed slapd-bdb slapadd -q with glued dbs (ITS#6794) Fixed slapd-ldap debug output of timeout (ITS#6721) Fixed slapd-ldap DNSSRV referral chaining (ITS#6565) Fixed slapd-ldap chaining with bind failures (ITS#6607) Fixed slapd-ldap chaining with onelevel scope (ITS#6699) Fixed slapd-ldap chaining with ppolicy (ITS#6540) Fixed slapd-ldap with SASL/EXTERNAL (ITS#6642) Fixed slapd-ldap crash on matchedDN (ITS#6793) Fixed slapd-ldap with unknown objectClasses (ITS#6814) Fixed slapd-ldif error strings (ITS#6731) Fixed slapd-ndb to honor rootpw setting (ITS#6661) Fixed slapd-ndb hasSubordinates generation (ITS#6712) Fixed slapd-ndb variable initialization (ITS#6806) Fixed slapd-ndb with out of order attributes (ITS#6821) Fixed slapd-meta anon retry with failed auth method (ITS#6643) Fixed slapd-meta rebind proc (ITS#6665) Fixed slapd-meta to correctly rebind as user (ITS#6574) Fixed slapd-meta with SASL/EXTERNAL (ITS#6642) Fixed slapd-meta matchedDN return code (ITS#6774) Fixed slapd-meta candidate selection (ITS#6799) Fixed slapd-meta attribute normalization (ITS#6818) Fixed slapd-monitor hasSubordinates generation (ITS#6712) Fixed slapd-monitor abandon processing (ITS#6783) Fixed slapd-monitor entry locks (ITS#6787) Fixed slapd-sock missing newline in Compare operation (ITS#6809) Fixed slapd-sql with null objectClass (ITS#6616) Fixed slapd-sql hasSubordinates generation (ITS#6712) Fixed slapo-accesslog with controls (ITS#6652) Fixed slapo-dynlist Compare operation (ITS#6752) Fixed slapo-dynlist entry handling (ITS#6752) Fixed slapo-memberof CSN generation (ITS#6766) Fixed slapo-memberof log messages (ITS#6748) Fixed slapo-memberof with an empty groupOfNames (ITS#6670) Fixed slapo-memberof with modrdn operations (ITS#6700) Fixed slapo-pcache callback freeing (ITS#6640) Fixed slapo-pcache to ignore undefined attrs (ITS#6600) Fixed slapo-pcache pointer freeing (ITS#6797) Fixed slapo-pcache with negative caching (ITS#6796) Fixed slapo-pcache monitoring cleanup (ITS#6808) Fixed slapo-ppolicy don't update opattrs on consumers (ITS#6608) Fixed slapo-ppolicy to allow userPassword deletion (ITS#6620) Fixed slapo-refint when last group member is deleted (ITS#6663) Fixed slapo-refint with subtree rename (ITS#6730) Fixed slapo-rwm double free (ITS#6720) Fixed slapo-rwm crash (ITS#6632,ITS#6727) Fixed slapo-rwm entry handling (ITS#6760) Fixed slapo-rwm response hang (ITS#6792) Fixed slapo-sssvlv initialization (ITS#6649) Fixed slapo-sssvlv to not advertise when unused (ITS#6647) Fixed slapo-sssvlv result code (ITS#6685) Fixed slapo-syncprov to send error if consumer is newer (ITS#6606) Fixed slapo-syncprov filter race condition (ITS#6708) Fixed slapo-syncprov active mod race (ITS#6709) Fixed slapo-syncprov to refresh if context is dirty (ITS#6710) Fixed slapo-syncprov CSN updates to all consumers (ITS#6718) Fixed slapo-syncprov sessionlog ordering (ITS#6716) Fixed slapo-syncprov sessionlog with adds (ITS#6503) Fixed slapo-syncprov mutex (ITS#6438) Fixed slapo-syncprov mincsn check with MPR (ITS#6717) Fixed slapo-syncprov control leak (ITS#6795) Fixed slapo-syncprov error codes (ITS#6812) Fixed slapo-translucent entry leak (ITS#6746) Fixed contrib/autogroup install location (ITS#6684) Fixed contrib/autogroup crash with ppolicy (ITS#6684) Fixed contrib/autogroup with non-DN URIs (ITS#6684) Fixed contrib/autogroup with memberOf overlay (ITS#6684) Fixed contrib/cloak when returning multiple entries (ITS#6762) Fixed contrib/nssov to only close socket on shutdown (ITS#6676) Fixed contrib/nssov multi platform support (ITS#6604) Build Environment Added support for [unsigned] long long (ITS#6622) Added slapd support for BDB 5.0+ (ITS#6698) Fixed config.guess/sub to pick up newer OSes (ITS#6547) Fixed libldap mutex code - cleanup (ITS#6672) Fixed libldap unnecessary ifdef's (ITS#6603) Fixed slapd-tester EOF handling (ITS#6723) Fixed slapd-tester filter initialization (ITS#6735) Fixed test scripts with alternate testdir (ITS#6782) Removed antiquated SunOS LWP support (ITS#6669) Documentation admin24 guide fix examples (ITS#6681) admin24 guide typo fixes (ITS#6609) admin24 guide refint rootdn requirement (ITS#6364) admin24 add pcache overlay section (ITS#6521) ldap_open(3) document ldap_set_urllist_proc (ITS#6601) ldap.conf(5) GnuTLS cipher spec info (ITS#6525) slapd.conf(5) GnlTLS cipher spec info (ITS#6525) slapd.conf(5) multi-listener support (ITS#6780) slapd-config(5) GnuTLS cipher spec info (ITS#6525) slapd-config(5) multi-listener support (ITS#6780) slapd-meta(5) note deprecated items (ITS#6800) slapd-meta(5) document subtree-include (ITS#6801) slapo-pcache(5) note rootdn requirement (ITS#6522) slapo-refint(5) rootdn requirement (ITS#6364) OpenLDAP 2.4.23 Release (2010/06/30) Fixed libldap to return server's error code (ITS#6569) Fixed libldap memleaks (ITS#6568) Fixed liblutil off-by-one with delta (ITS#6541) Fixed slapd acls with glued databases (ITS#6468) Fixed slapd syncrepl rid logging (ITS#6533) Fixed slapd modrdn handling of invalid values (ITS#6570) Fixed slapd-bdb hasSubordinates computation (ITS#6549) Fixed slapd-bdb to use memcpy instead for strcpy (ITS#6474) Fixed slapd-bdb entry cache delete failure (ITS#6577) Fixed slapd-ldap to return control responses (ITS#6530) Fixed slapo-ppolicy to use Debug (ITS#6566) Fixed slapo-refint to zero out freed DN vals (ITS#6572) Fixed slapo-rwm to use Debug (ITS#6566) Fixed slapo-sssvlv to use Debug (ITS#6566) Fixed slapo-syncprov lost deletes in refresh phase (ITS#6555) Fixed slapo-valsort to use Debug (ITS#6566) Fixed contrib/nssov network.c missing patch (ITS#6562) Build Environment Fixed test043 attribute sorting (ITS#6553) Documentation slapd-config(5) note default rootdn (ITS#6546) OpenLDAP 2.4.22 Release (2010/04/24) Added slapd SLAP_SCHEMA_EXPOSE flag for hidden schema elements (ITS#6435) Added slapd tools selective iterations (ITS#6442) Added slapd syncrepl TCP keepalive (ITS#6389) Added slapo-ldap idassert-passthru (ITS#6456) Added slapo-pbind Fixed libldap gmtime re-entrancy (ITS#6262) Fixed libldap gssapi off by one error (ITS#6223) Fixed libldap GnuTLS serial length (ITS#6460) Fixed libldap MozNSS context and PEM support (ITS#6432) Fixed libldap referral on bind behavior(ITS#6510) Fixed slapd acl non-entry internal searches (ITS#6481) Fixed slapd acl attrval style initialization (ITS#6520) Fixed slapd certificateListValidate (ITS#6466) Fixed slapd empty URI parsing (ITS#6465) Fixed slapd glued misplaced entries (ITS#6506) Fixed slapd glued paged cookies (ITS#6507) Fixed slapd glued paged results (ITS#6504) Fixed slapd gmtime re-entrancy (ITS#6262) Fixed slapd to ignore controls with unrecognized flags (ITS#6480) Fixed slapd entry ownership (ITS#5340) Fixed slapd sasl auxprop_lookup (ITS#6441) Fixed slapd sasl auxprop ssf (ITS#5195) Fixed slapd syncrepl for attributes with no matching rule (ITS#6458) Fixed slapd syncrepl for unknown attrs and delta-sync (ITS#6473) Fixed slapd syncrepl loop with moddn (ITS#6472) Fixed slapo-accesslog to not replicate internal purges (ITS#6519) Fixed slapd-bdb contextCSN updates from updatedn (ITS#6469) Fixed slapd-bdb lockobj zeroing (ITS#6501) Fixed slapd-ldap/meta control criticality (ITS#6523) Fixed slapd-ldap/meta with ordered values (ITS#6516) Fixed slapo-collect entry ownership (ITS#5340,ITS#6423) Fixed slapo-dds with NULL backend (ITS#6490) Fixed slapo-dynlist entry ownership (ITS#5340,ITS#6423) Fixed slapo-memberof attr count (ITS#6508) Fixed slapo-pcache to release its own entries (ITS#6484) Fixed slapo-pcache with NULL backend (ITS#6490) Fixed slapo-rwm entry release handling (ITS#6484) Fixed slapo-rwm memory handling with rewrites (ITS#6526) Fixed slapo-rwm olcRwmMap handling (ITS#6436) Fixed slapo-rwm entry ownership (ITS#5340,ITS#6423) Fixed slapo-syncprov memory leak (ITS#6459) Fixed slapo-translucent counter increment (ITS#6497) Fixed slapo-valsort entry ownership (ITS#5340,ITS#6423) Fixed contrib/sha2 adds mechs for more hashes (ITS#6433) Fixed contrib/nssov to use nss-pam-ldapd (ITS#6488) Build Environment Added back-ldif, back-null test support (ITS#5810) Documentation admin24 avoid explicit moduleload statements (ITS#6486) admin24 broken link fixes (ITS#6493,ITS#6515) slapd.access(5) val.regex explanation (ITS#5804) OpenLDAP 2.4.21 Release (2009/12/20) Fixed liblutil for negative microsecond offsets (ITS#6405) Fixed slapd global settings to work without restart (ITS#6428) Fixed slapd looping with SSL/TLS connections (ITS#6412) Fixed slapd syncrepl freeing tasks from queue (ITS#6413) Fixed slapd syncrepl parsing of tls defaults (ITS#6419) Fixed slapd syncrepl uninitialized variables (ITS#6425) Fixed slapd-config Adds with Abstract classes (ITS#6408) Fixed slapo-dynlist behavior with simple filters (ITS#6421) Fixed slapd-ldif access outside database directory (ITS#6414) Fixed slapd-null extraneous assert (ITS#6403) Fixed slapo-translucent with back-null (ITS#6403) Fixed slapo-unique criteria checking (ITS#6270) Build Environment Deleted broken LBER_INVALID macro (ITS#6402) Fixed test058 kill usage (ITS#6420) Fixed meta regression test (ITS#6418) Documentation slapd-meta(5) Note deprecated functions (ITS#6424) admin24 fix set example for group of groups (ITS#6382) admin24 fix dynamic group documentation (ITS#6290) OpenLDAP 2.4.20 Release (2009/11/27) Fixed client tools with LDAP options (ITS#6283) Fixed liblber embedded NUL values in BerValues (ITS#6353) Fixed liblber inverted LBER_USE_DER test (ITS#6348) Fixed liblber to return failure on certain failures (ITS#6344) Fixed libldap connection initialization (ITS#6386) Fixed libldap sasl buffer sizing (ITS#6327,ITS#6334) Fixed libldap uninitialized return value (ITS#6355) Fixed libldap unlimited timeout (ITS#6388) Added slapd handling of hex server IDs (ITS#6297) Added slapd syncrepl contextCSN storing in subentry (ITS#6373) Fixed slapd asserts in minimal environment (ITS#6361) Fixed slapd authid-rewrite parsing (ITS#6392) Fixed slapd checks of str2filter (ITS#6391) Fixed slapd configArgs initialization (ITS#6363) Fixed slapd debug handling of LDAP_DEBUG_ANY (ITS#6324) Fixed slapd db_open with connection_fake_init (ITS#6381) Fixed slapd with embedded \0 in bervals (ITS#6378,ITS#6379) Fixed slapd inclusion of ac/unistd.h (ITS#6342) Fixed slapd invalid dn log message (ITS#6309) Fixed slapd lockup on shutdown (ITS#6372) Fixed slapd onetime leak (ITS#6398) Fixed slapd RID range to be decimal only (ITS#6394) Fixed slapd sl_free to better reclaim memory (ITS#6380) Fixed slapd syncrepl deletes in MirrorMode (ITS#6368) Fixed slapd syncrepl to use correct SID (ITS#6367) Fixed slapd termination for one level DNs (ITS#6338) Fixed slapd tls_accept to retry in certain cases (ITS#6304) Fixed slapd-bdb/hdb cache corruption (ITS#6341) Fixed slapd-bdb/hdb entry cache (ITS#6360) Fixed slapd-ldap leak (ITS#6326) Fixed slapd-relay bind segfault (ITS#6337) Fixed slapo-accesslog ensure CSNs are normalized (ITS#6400) Fixed slapo-memberof operational attr updates (ITS#6329) Fixed slapo-pcache entry dupe (ITS#6310) Fixed slapo-syncprov checkpoint conversion (ITS#6370) Fixed slapo-syncprov deadlock (ITS#6335) Fixed slapo-syncprov memory leak (ITS#6376) Fixed slapo-syncprov out of order changes (ITS#6346) Fixed slapo-syncprov psearch with stale cookie (ITS#6397) Build Environment Added additional operations for ITS#6332 Fixed memrchr define (ITS#6351) Fixed slapd MAXPATHLEN handling (ITS#6342) Added test050 rapid add/mod/del sequence (ITS#6368) Fixed test057 handling of memberof/refint (ITS#6343) Fixed slapd test error ignoring (ITS#6345) Fixed liblutil constant (ITS#5909) Documentation admin24 fix RFC4511 and other references (ITS#6399) ldap_get_dn(3) typos (ITS#5366) ldap.conf(5) clarify comment usage (ITS#6384) slapd.conf(5) note hex server IDs (ITS#6297) slapd-config(5) note hex server IDs (ITS#6297) OpenLDAP 2.4.19 Release (2009/10/06) Fixed client tools with null timeouts (ITS#6282) Fixed slapadd to warn about missing attrs for consumers (ITS#6281) Fixed slapd acl cache (ITS#6287) Fixed slapd tools to allow -n for conversion (ITS#6258) Fixed slapd-ldap with null timeouts (ITS#6282) Fixed slapd-ldap with strong binds with relay/translucent (ITS#6296) Fixed slapd-ldif buffer overflow (ITS#6303) Fixed slapo-auditlog comments when modifying (ITS#6286) Fixed slapo-dynlist lock leak (ITS#6308) Fixed slapo-pcache cache corruption (ITS#6242) Fixed slapo-sssvlv sort control dereferencing (ITS#6288) Fixed contrib/autogroup segfaults (ITS#6279) Fixed contrib/nssov getgroupbymembers (ITS#6291) Fixed contrib/smbk5pwd rpath linking (ITS#6323) Build Environment Fixed --enable-deref support (ITS#6311) Fixed contrib/autogroup default libtool path (ITS#6284) Deleted nadf.schema (ITS#6140) OpenLDAP 2.4.18 Release (2009/09/06) Fixed client tools common options (ITS#6049) Fixed liblber speed and other problems (ITS#6215) Added libldap MozNSS PEM support (ITS#6278) Added libldap option for SASL_USERNAME (ITS#6257) Fixed libldap error parsing (ITS#6197) Fixed libldap native getpass usage (ITS#4643) Fixed libldap tls_check_hostname for OpenSSL and MozNSS (ITS#6239) Added slapd tcp buffers support (ITS#6234) Fixed slapd allow mirrormode to be set to FALSE (ITS#5946) Fixed slapd certificate list parsing (ITS#6241) Fixed slapd writers blocking (ITS#6276) Fixed slapd dncachesize behavior to unlimited by default (ITS#6222) Fixed slapd incorrectly applying writetimeout when not set (ITS#6220) Fixed slapd with duplicate empty lines for olcDbConfig (ITS#6240) Fixed slapd server URL matching (ITS#5942) Fixed slapd subordinate needs a suffix (ITS#6216) Fixed slapd syncrepl decrement on possible NULL value (ITS#6256) Fixed slapd tools to properly close database (ITS#6214) Fixed slapd uninitialized SlapReply components (ITS#6101) Fixed slapd-meta starttls with targets (ITS#6190) Fixed slapd-monitor stats with glued subordinates (ITS#6243) Fixed slapd-ndb startup (ITS#6203) Fixed slapd-relay various issues (ITS#6133) Fixed slapd-relay response/cleanup callback mismatch (ITS#6154) Fixed slapd-sql with baseObject query (ITS#6172) Fixed slapd-sql with empty attribute (ITS#6163) Fixed slapo-dynlist uninitialized var (ITS#6266) Fixed slapo-pcache multiple enhancements (ITS#6152,ITS#5178) Fixed slapo-ppolicy updating operational attributes (ITS#6265) Fixed slapo-translucent attribute return (ITS#6254) Fixed slapo-translucent filter matching (ITS#6255) Fixed slapo-translucent to honor sizelimit (ITS#6253) Fixed slapo-unique filter matching (ITS#6077) Fixed tools off by one error (ITS#6233) Fixed tools resource leaks (ITS#6145) Added contrib/allowed (ITS#4730) Fixed contrib/autogroup with RE24 (ITS#6227) Fixed contrib/nss symbols (ITS#6273) Build Environment Tests note which backend is being tested (ITS#5810) Fixed test056-monitor with custom ports (ITS#6213) Documentation admin24 fix broken link (ITS#6264) ldap_open(3) document URI (ITS#6261) ldap_set/get_option(3) SASL/TLS options added (ITS#6260) man page format updates (ITS#6023) OpenLDAP 2.4.17 Release (2009/07/13) Fixed liblber to use ber_strnlen (ITS#6080) Fixed libldap GnuTLS private key init (ITS#6053) Fixed libldap openssl digest initialization (ITS#6192) Fixed libldap tls NULL error messages (ITS#6079) Fixed libldap_r missing stub (ITS#6188) Fixed liblutil opendir/closedir on windows (ITS#6041) Fixed liblutil for _GNU_SOURCE (ITS#5464,ITS#5666) Added slapd sasl auxprop support (ITS#6147) Added slapd schema checking tool (ITS#6150) Added slapd writetimeout keyword (ITS#5836) Fixed slapd abandon/cancel handling for some ops (ITS#6157) Fixed slapd access setstyle to expand (ITS#6179) Fixed slapd assert with closing connections (ITS#6111) Fixed slapd bind race condition (ITS#6189) Fixed slapd cancel behavior (ITS#6137) Fixed slapd cert validation (ITS#6098) Fixed slapd connection_destroy assert (ITS#6089) Fixed slapd csn normalization (ITS#6195) Fixed slapd errno handling (ITS#6037) Fixed slapd global alloc handling (ITS#6054) Fixed slapd hung writers (ITS#5836) Fixed slapd ldapi issues (ITS#6056) Fixed slapd moduleload with static backends and modules (ITS#6016) Fixed slapd normalization of updated schema attributes (ITS#5540) Fixed slapd olcLimits handling (ITS#6159) Fixed slapd olcLogLevel with hex levels (ITS#6162) Fixed slapd pagedresults stacked control with overlays (ITS#6056) Fixed slapd password-hash incorrect limit on arg length (ITS#6139) Fixed slapd readonly restrictions (ITS#6109) Fixed slapd sending cancelled operations results (ITS#6103) Fixed slapd slapi_entry_has_children (ITS#6132) Fixed slapd sockets usage on windows (ITS#6039) Fixed slapd some abandon and cancel race conditions (ITS#6104) Fixed slapd tls context after changes (ITS#6135) Fixed slapd-bdb/hdb adjust dncachesize if too low (ITS#6176) Fixed slapd-bdb/hdb crashes during delete (ITS#6177) Fixed slapd-bdb/hdb multiple olcIndex for same attr (ITS#6196) Fixed slapd-hdb freeing of already freed entries (ITS#6074) Fixed slapd-hdb entryinfo cleanup (ITS#6088) Fixed slapd-hdb dncache lockups (ITS#6095) Fixed slapd-ldap deadlock with non-responsive TLS URIs (ITS#6167) Fixed slapd-relay to return failure on failure (ITS#5328) Fixed slapd-sql with BACKSQL_ARBITRARY_KEY defined (ITS#6100) Fixed slapo-collect collectinfo ordering (ITS#6076) Fixed slapo-collect missing equality match rule (ITS#6075) Fixed slapo-dds entry expiration (ITS#6169) Fixed slapo-perl symbols (ITS#5658) Fixed slapo-ppolicy to honor pwdLockout (ITS#6168) Fixed slapo-ppolicy to return check modules error message (ITS#6082) Fixed slapo-refint refint_repair handling (ITS#6056) Added slapo-rwm rwm-drop-unrequested-attrs config option (ITS#6057) Fixed slapo-rwm dn passing (ITS#6070) Fixed slapo-rwm entry free (ITS#6058) Fixed slapo-rwm entry release (ITS#6081) Fixed slapo-translucent entry gathering (ITS#6156) Fixed tools returning ldif errors (ITS#5892) Fixed contrib/smbk5pwd use of private functions (ITS#5535) Build Environment Added test056-monitor (ITS#5540) Added test057-memberof-refint (ITS#5395) Fixed winsock detection for windows (ITS#6102, ITS#6078) Removed GSSAPI configure option (ITS#6091,ITS#6092,ITS#6093,ITS#5369) Documentation admin24 relocate configuration examples (ITS#6183) admin24 fixed example regex (ITS#6052) admin24 removed temporary back-monitor note (ITS#6130) admin24 slapd.conf to cn=config conversion process (ITS#6060) man page consistency fixes (ITS#6023) ldapcompare(1) note -e option (ITS#6107) ldapdelete(1) note -e option (ITS#6107) ldapmodify(1) note -e option (ITS#6107) ldapmodrdn(1) note -e option (ITS#6107) ldapsearch(1) output format description (ITS#6146) ldapurl(1) note -e option (ITS#6107) ldapwhoami(1) note -e option (ITS#6107) ldap_result(3) Add RETURN VALUE heading (ITS#6180) ldap.conf(5) improve sizelimit/timelimit limits (ITS#6127) slapd.access(5) Fix to use expand (ITS#6179) slapd.conf(5) document default modulepath (ITS#5829) slapd.conf(5) pidfile/argsfile description fix (ITS#5975) slapd-config(5) document default modulepath (ITS#5829) slapd-config(5) pidfile/argsfile description fix (ITS#5975) slapo-constraint(5) clarify URI example (ITS#6118) slapo-unique(5) explicitly note rootdn requirement (ITS#6108) slapadd(8) note it does indexing (ITS#6160) OpenLDAP 2.4.16 Release (2009/04/05) Fixed libldap GnuTLS with x509v1 CA certs (ITS#5992) Fixed libldap GnuTLS with CA chains (ITS#5991) Fixed libldap GnuTLS TLSVerifyClient try (ITS#5981) Fixed libldap segfault in checking cert/DN (ITS#5976) Fixed libldap peer cert double free (ITS#5849) Fixed libldap referral chasing (ITS#5980) Fixed slapd backglue with empty DBs (ITS#5986) Fixed slapd ctxcsn race condition (ITS#6001) Fixed slapd debug message (ITS#6027) Fixed slapd redundant module loading (ITS#6030) Fixed slapd schema_init freed value (ITS#6036) Fixed slapd syncrepl newCookie sync messages (ITS#5972) Fixed slapd syncrepl hang during shutdown (ITS#6011) Fixed slapd syncrepl too many MPR messages (ITS#6020) Fixed slapd syncrepl skipped entries with MPR (ITS#5988) Fixed slapd-bdb/hdb cachesize handling (ITS#5860) Fixed slapd-bdb/hdb with slapcat with empty dn (ITS#6006) Fixed slapd-bdb/hdb with NULL transactions (ITS#6012) Fixed slapd-ldap incorrect referral handling (ITS#6003,ITS#5916) Fixed slapd-ldap/meta with broken AD results (ITS#5977) Fixed slapd-ldap/meta with invalid attrs again (ITS#5959) Fixed slapo-accesslog interaction with ppolicy (ITS#5979) Fixed slapo-dynlist conversion to cn=config (ITS#6002) Fixed slapo-syncprov newCookie sync messages (ITS#5972) Fixed slapd-syncprov too many MPR messages (ITS#6020) Fixed slapo-syncprov consumer lockout (ITS#5985) Fixed slapo-syncprov modtarget tracking (ITS#5999) Fixed slapo-syncprov multiple CSN propagation (ITS#5973) Fixed slapo-syncprov race condition (ITS#6045) Fixed slapo-syncprov sending cookies without CSN (ITS#6024) Fixed slapo-syncprov skipped entries with MPR (ITS#5988) Fixed tools passphrase free (ITS#6014) Build Environment Cleaned up alloc/free functions for Windows (ITS#6005) Fixed running of autosave files in testsuite (ITS#6026) Documentation admin24 clarified MPR URI requirements (ITS#5942,ITS#5987) Added ldapexop(1) manual page (ITS#5982) slapd-ldap/meta(5) added missing TLS options (ITS#5989) OpenLDAP 2.4.15 Release (2009/02/24) Fixed libldap alias dereferencing in C API again (ITS#5916) Fixed libldap GnuTLS compilation (ITS#5955) Fixed slapd bconfig conversion again (ITS#5346) Fixed slapd behavior with superior objectClasses again (ITS#5517) Fixed slapd RFC4512 behavior with same attr in RDN (ITS#5968) Fixed slapd corrupt contextCSN (ITS#5947) Fixed slapd syncrepl order to match on add/delete (ITS#5954) Fixed slapd adding rdn with other values (ITS#5965) Fixed slapd-bdb/hdb behavior with unallocatable shm (ITS#5956) Fixed slapd-ldap/meta with entries with invalid attrs (ITS#5959) Fixed slapd-relay control initialization (ITS#5724) Fixed slapo-pcache caching invalid entries (ITS#5927) Fixed slapo-syncprov csn updates (ITS#5969) Fixed slapo-rwm objectClass preservation (ITS#5760) Fixed slapo-rwm rwm_bva_rewrite handling (ITS#5960) Build Environment Fixed tester library linking for windows (ITS#5740) OpenLDAP 2.4.14 Release (2009/02/14) Added libldap option to disable SASL host canonicalization (ITS#5812) Added libldap TLS_PROTOCOL_MIN (ITS#5655) Added libldap GnuTLS support for TLS_CIPHER_SUITE (ITS#5887) Added libldap GnuTLS setting random file (ITS#5462) Added libldap alias dereferencing in C API (ITS#5916) Fixed libldap chasing multiple referrals (ITS#5853) Fixed libldap deref handling (ITS#5768) Fixed libldap NULL pointer deref (ITS#5934) Fixed libldap peer cert memory leak (ITS#5849) Fixed libldap interaction with GnuTLS CN IP-based matches (ITS#5789) Fixed libldap intermediate response behavior (ITS#5896) Fixed libldap IPv6 address handling (ITS#5937) Fixed libldap_r deref building (ITS#5768) Fixed libldap_r slapd lockup when paused during shutdown (ITS#5841) Added slapd syncrepl default retry setting (ITS#5825) Added slapd val.regex expansion (ITS#5804) Added slapd TLS_PROTOCOL_MIN (ITS#5655) Added slapd slapi_pw_find (ITS#2615,ITS#4359) Added slapd compatibility with MSAD ranged values (ITS#5927) Fixed slapd bconfig to return error codes (ITS#5867) Fixed slapd bconfig encoding incorrectly (ITS#5897) Fixed slapd bconfig dangling pointers (ITS#5924) Fixed slapd behavior with superior objectClasses (ITS#5517) Fixed slapd connection assert (ITS#5835) Fixed slapd epoll handling (ITS#5886) Fixed slapd frontend/backend options handling (ITS#5857) Fixed slapd glue with MPR (ITS#5925) Fixed slapd logging on Windows (ITS#5392) Fixed slapd listener comparison (ITS#5613) Fixed slapd manageDSAit with glue entries (ITS#5921) Fixed slapd relax behavior with structuralObjectClass (ITS#5792) Fixed slapd syncrepl rename handling (ITS#5809) Fixed slapd syncrepl MPR when adding new server (ITS#5850) Fixed slapd syncrepl MPR with deleted entries (ITS#5843) Fixed slapd syncrepl replication with glued DB (ITS#5866) Fixed slapd syncrepl replication with moddn (ITS#5901) Fixed slapd syncrepl replication with referrals (ITS#5881) Fixed slapd syncrepl replication with config tree (ITS#5935) Fixed slapd wake_sds close on Windows (ITS#5855) Fixed slapd-bdb/hdb dncachesize handling (ITS#5860) Fixed slapd-bdb/hdb RFC4528 control support (ITS#5861) Fixed slapd-bdb/hdb trickle task usage (ITS#5864) Fixed slapd-hdb idlcache with empty suffix (ITS#5859) Fixed slapd-ldap idassert-bind validity checking (ITS#5863) Fixed slapd-ldap/meta RFC4525 increment support (ITS#5912) Fixed slapd-ldap/meta search dereferencing (ITS#5916) Fixed slapd-ldap/meta with intermediate response (ITS#5931) Fixed slapd-ldif numerous bugs (ITS#5408) Fixed slapd-ldif rename on same DN (ITS#5319) Fixed slapd-ldif deadlock (ITS#5329) Fixed slapd-meta double response sending (ITS#5854) Fixed slapd-meta alias deref for retry (ITS#5889) Fixed slapd-relay recursion detection (ITS#5943) Fixed slapd-sock descriptor leak (ITS#5939) Fixed slapo-accesslog on glued dbs (ITS#5907) Fixed slapo-dynlist handling of flags (ITS#5898) Fixed slapo-memberof multiple instantiation (ITS#5903) Fixed slapo-pcache filter sorting (ITS#5756) Fixed slapo-ppolicy to not be global (ITS#5858) Fixed slapo-rwm double free (ITS#5923) Fixed slapo-rwm with back-config (ITS#5906) Fixed slapo-rwm olcRwmRewrite modification (ITS#5940) Added slapo-rwm newRDN rewriting (ITS#5834) Added slapadd progress meter (ITS#5922) Updated contrib/addpartial module (ITS#5764) Added contrib/cloak module (ITS#5872) Added contrib/smbk5pwd gcrypt support (ITS#5410) Added contrib/passwd sha2 support (ITS#5660) Build Environment Fixed test006 appending to log file (ITS#5910) Fixed test036,test039 behavior on error (ITS#5893) Fixed test048 sed pathname substitution (ITS#5910) Fixed test049,test050 to work on windows (ITS#5842) Updated test017,test018,test019 to cover more cases (ITS#5883) Removed patch for BerkeleyDB 4.7.25 (Official patch available) Fixed MSVC 9.0 build issues (ITS#5888) Fixed gss detection on Solaris (ITS#5846) Fixed uuid_create/uuid_unparse_lower detection (ITS#5905) Fixed liblutil tavl_delete to macroize constants (ITS#5909) Documentation admin24 added limits chapter (ITS#5818) admin24 access-control clarify global ACLS (ITS#5851,ITS#5852) admin24 search on nested naming contexts (ITS#5788) admin24 consistent loglevel documentation (ITS#5904) slapd-bdb/hdb expansion on dncachesize behavior (ITS#5721) slapo-constraint(5) example fix (ITS#5895) slap*(8) man pages should mention slapd-config (ITS#5828) slapacl(8c) fix wording (ITS#5918) slapd(8) document sid (ITS#5873) slapd.access(5) clarify global ACLS (ITS#5851,ITS#5852) slapadd/cat/index(8) note -n 0 for slapd-config (ITS#5891) Added SEE ALSO slapd-config(5) to relevant man pages (ITS#5914) OpenLDAP 2.4.13 Release (2008/11/24) Added libldap dereference control support (ITS#5768) Fixed libldap parameter checking (ITS#5817) Fixed liblutil hex conversion (ITS#5699) Fixed liblutil returning undefined data (ITS#5748) Fixed libldap error code return (ITS#5762) Fixed libldap interaction with GnuTLS CN IP-based matches (ITS#5789) Fixed libldap MAXHOSTNAMELEN typo (ITS#5815) Fixed libldap Ipv6 detection (ITS#5739) Fixed libldap setuid usage with .ldaprc (ITS#4750) Fixed slapacl crash (ITS#5820) Fixed slapd acl checks on ADD (ITS#4556,ITS#5723) Fixed slapd acl application to newly created backends (ITS#5572) Fixed slapd #if/#elif issues in thread includes (ITS#5824) Added slapd keyword add_content_acl for add checks (ITS#4556,ITS#5723) Fixed slapd concurrent access to connections (ITS#5814) Fixed slapd config backend olcLogFile support (ITS#5765) Fixed slapd contextCSN pending list (ITS#5709) Fixed slapd control criticality (ITS#5785) Added slapd dn.this search limits (ITS#5734) Fixed slapd error status on shutdown (ITS#5745) Fixed slapd filter substring handling (ITS#5803) Fixed slapd nameUIDPretty bitstring parsing (ITS#5750) Fixed slapd null termination of password (ITS#5794) Fixed slapd overlay/database open with real structure (ITS#5724) Fixed slapd parsing of read entry control (ITS#5741) Added slapd PMI schema (ITS#5695) Added slapd private databases in global overlays (ITS#5735,ITS#5736) Fixed slapd rdn generation when it isn't specified (ITS#5819) Fixed slapd slapd.conf validation to LDIF (ITS#5755) Fixed slapd startup scan for CSN (ITS#5640) Fixed slapd statslog printing of released entry (ITS#5775) Added slapd support for certificateListExactMatch (ITS#5700) Fixed slapd syncrepl event loss (ITS#5710) Fixed slapd syncrepl MOD of attrs with no EQ rule (ITS#5781) Fixed slapd syncrepl rename handling (ITS#5809) Fixed slapd syncrepl schema checking (ITS#5798) Fixed slapd syncrepl filter leak (ITS#5826) Fixed slapd undef promote (ITS#5783,ITS#5795) Added slapd What failed? control (ITS#5784) Fixed slapd-bdb/hdb invalid db crash (ITS#5698) Added slapd-bdb/hdb dbpagesize keyword Added slapd-bdb/hdb checksum keyword Fixed slapd-bdb/hdb indexing of entryDN (ITS#5790) Fixed slapd-bdb/hdb lookup of entryDN with equality (ITS#5791) Fixed slapd-bdb/hdb uninitialized bli_flag Fixed slapd-ldap snprintf buffer overflow test (ITS#4467) Fixed slapd-ldap search stop on minor failure (ITS#5816) Fixed slapd-ldif file rename on windows (ITS#5774) Fixed slapd-null read controls support (ITS#5757) Fixed slapd-sql value length with right index (ITS#5779) Fixed slapo-chain/translucent back-config support (ITS#5736) Fixed slapo-chain SEGV with search references (ITS#5742) Fixed slapo-collect compile with C89 (ITS#5747) Added slapo-constraint support for LDAP URI constraints (ITS#5704) Added slapo-constraint support for constraining rename (ITS#5703) Added slapo-constraint support for relax control (ITS#5705) Added slapo-constraint "set" type (ITS#5702) Fixed slapo-constraint filter parsing error (ITS#5751) Added slapo-dynlist URI restriction ability (ITS#5761) Fixed slapo-ppolicy unaligned BerElement (ITS#5770) Fixed slapo-rwm objectClass preservation (ITS#5760) Fixed slapo-rwm rewriting undefined filter (ITS#5731) Fixed slapo-rwm rewritten DN-valued attrs (ITS#5772) Fixed slapo-rwm reusing freed filter (ITS#5732) Fixed slapo-rwm entry get (ITS#5773) Fixed slapo-syncprov runqueue removal (ITS#5776) Fixed slapo-syncprov unreplicatable ops (ITS#5709) Fixed slapo-syncprov psearch leak (ITS#5827) Added slapo-translucent try local bind when remote fails (ITS#5656) Added slapo-translucent support for PasswordModify exop (ITS#5656) Fixed tools simple bind without SASL (ITS#5753) Fixed tools unaligned BerElement (ITS#5770) Fixed contrib nssov crash on empty groups (ITS#5800) Fixed contrib nssov crash with nssov-map (ITS#5801) Fixed contrib nssov filter and search limits (ITS#5802) Added contrib smbk5pwd honor principal expiration (ITS#5766) Build Environment Added ldapurl command Added slapd GSSAPI refactoring (ITS#5369) Added slapo-deref overlay (ITS#5768) Documentation admin24 added olcLimits to example (ITS#5746) admin24 consolidated on whitespace (ITS#5759) slapd.conf,config(5) subordinate/olcSubordinate keyword (ITS#5788) slapd.conf(5) fixed disable keyword for limits (ITS#5821) slapo-dds(5) manageDIT to relax (ITS#5780) slapo-dds(5) rootdn requirement added (ITS#5811) slapo-syncprov(5) sessionlog clarification (ITS#5806) OpenLDAP 2.4.12 Release (2008/10/12) Fixed libldap ldap_utf8_strchar arguments (ITS#5720) Fixed libldap TLS_CRLFILE (ITS#5677) Fixed liblutil executables on Windows (ITS#5604) Fixed liblutil microsecond overflows on Windows (ITS#5668) Fixed librewrite memory handling (ITS#5691) Fixed slapd aci performance (ITS#5636) Fixed slapd aci's with sets (ITS#5627) Fixed slapd attribute leak (ITS#5683) Fixed slapd config backend with index greater than sibs (ITS#5684) Fixed slapd custom attribute inheritance (ITS#5642) Fixed slapd dynacl mask handling (ITS#5637) Fixed slapd firstComponentMatch normalization (ITS#5634) Added slapd caseIgnoreListMatch (ITS#5608) Fixed slapd connection events enabled twice (ITS#5725) Fixed slapd memory handling (ITS#5691) Fixed slapd objectClass canonicalization (ITS#5681) Fixed slapd objectClass termination (ITS#5682) Fixed slapd overlay control registration (ITS#5649) Fixed slapd runqueue checking (ITS#5726) Fixed slapd spurious text output (ITS#5688) Fixed slapd socket closing on Windows (ITS#5606) Fixed slapd sortvals comparison (ITS#5578) Added slapd substitute syntax support (ITS#5663) Fixed slapd syncrepl contextCSN detection (ITS#5675) Fixed slapd syncrepl error logging (ITS#5618) Fixed slapd syncrepl runqueue interval (ITS#5719) Fixed slapd-bdb entry return if attr not present (ITS#5650) Fixed slapd-bdb olcDbMode syntax (ITS#5713) Fixed slapd-bdb/hdb release search entries earlier (ITS#5728,ITS#5730) Fixed slapd-bdb/hdb subtree search with empty suffix (ITS#5729) Fixed slapd-dnssrv memory handling (ITS#5691) Fixed slapd-ldap,slapd-meta invalid filter behavior (ITS#5614) Fixed slapd-meta memory handling (ITS#5691) Fixed slapd-meta objectClass filtering (ITS#5647) Fixed slapd-meta quarantine behavior (ITS#5592) Added slapd-ndb experimental backend Fixed slapd-relay initialization (ITS#5643) Fixed slapd-sql freeing of connection (ITS#5607) Fixed slapd-sql fault on NULL fields (ITS#5653) Fixed slapo-accesslog entryCSN generation on purge (ITS#5694) Fixed slapo-constraint string termination (ITS#5609) Fixed slapo-dynlist expansion with mapped attributes (ITS#5717) Fixed slapo-memberof internal operations DN (ITS#5622) Fixed slapo-pcache attrset crash (ITS#5665) Fixed slapo-pcache caching with invalid schema (ITS#5680) Fixed slapo-ppolicy control return on password modify exop (ITS#5711) Fixed slapo-rwm callback cleanup (ITS#5601,ITS#5687) Fixed slapo-rwm attr mapping and merging (ITS#5624) Fixed slapo-rwm objectClass filtering (ITS#5647) Fixed slapo-translucent back-config support (ITS#5689) Fixed slapo-translucent filter usage on merged entries (ITS#5679) Fixed slapo-unique filter validation (ITS#5581) Fixed slapo-unique suffix testing (ITS#5641) Build Environment Fixed ODBC library detection (ITS#5602) Removed pre-BerkeleyDB 4.4 support Added BerkeleyDB 4.7 support (ITS#5523) Included patch for BerkeleyDB 4.7.25 (build/db.4.7.25.patch) Added slapo-collect overlay with enhancements(ITS#5659) Documentation Added slapd-ldap(5), slapd-meta(5) noundeffilter (ITS#5614) Fixed slapd-ldap(5), slapd-meta(5), slapo-pcache(5) schema requirements (ITS#5680) Added slapo-collect(5) man page (ITS#5706) Added slapo-pcache(5) proxycheckcacheability option (ITS#5680) Added slapo-retcode(5) retcode.conf location (ITS#5633) admin24 dontusecopy control update (ITS#5718) admin24 guide updates (ITS#5616) admin24 octetString fix (ITS#5670) OpenLDAP 2.4.11 Release (2008/07/16) Fixed liblber ber_get_next length decoding (ITS#5580) Added libldap assertion control (ITS#5560) Fixed libldap GnuTLS CRL result handling (ITS#5577) Fixed libldap GnuTLS SSF computation (ITS#5585) Fixed liblutil missing return code (ITS#5615) Fixed slapd cert serial number parsing (ITS#5588) Fixed slapd check for structural_class failures (ITS#5540) Fixed slapd config backend renumbering (ITS#5571) Fixed slapd configContext OID (ITS#5383) Fixed slapd crash with no listeners (ITS#5563) Fixed slapd equality rules for olcRootDN/olcSchemaDN (ITS#5540) Fixed slapd sets memory leak (ITS#5557) Fixed slapd sortvals binary search (ITS#5578) Fixed slapd syncrepl updates with multiple providers (ITS#5597) Fixed slapd syncrepl superior objectClass delete/add (ITS#5600) Fixed slapd syncrepl/slapo-syncprov contextCSN updates as internal ops (ITS#5596) Added slapd-ldap/slapd-meta option to filter out search references (ITS#5593) Fixed slapd-meta link to slapd-ldap (ITS#5355) Fixed slapd-sock, back-shell buffer count (ITS#5558) Fixed slapo-dynlist dg attrs lookup (ITS#5583) Fixed slapo-dynlist entry release (ITS#5135) Fixed slapo-memberof replace handling (ITS#5584) Added slapo-nssov contrib module Fixed slapo-pcache handling of negative search caches (ITS#5546) Fixed slapo-ppolicy DNs with whitespaces (ITS#5552) Fixed slapo-ppolicy modify with internal ops (ITS#5569) Fixed slapo-syncprov ACL evaluation (ITS#5548) Fixed slapo-syncprov crash with delcsn (ITS#5589) Fixed slapo-syncprov full reload (ITS#5564) Fixed slapo-syncprov missing olcSpReloadHint attr(ITS#5591) Fixed slapo-unique filter normalization (ITS#5581) Fixed contrib smbk5pwd terminator (ITS#5575) Build Environment Fixed test048 to skip if threads is not available (ITS#5529) Documentation Added slapo-pcache(5) sizelimit caching (ITS#5559) Added slapd-access(5) add and delete privs (ITS#5566) admin24 GnuTLS documentation (ITS#5554) OpenLDAP 2.4.10 Release (2008/06/08) Fixed libldap file descriptor leak with SELinux (ITS#5507) Fixed libldap ld_defconn cleanup if it was freed (ITS#5518, ITS#5525) Fixed libldap msgid handling (ITS#5318) Fixed libldap t61 infinite loop (ITS#5542) Fixed libldap_r missing stubs (ITS#5519) Fixed slapd initialization of sr_msgid, rs->sr_tag (ITS#5461) Fixed slapd missing termination of integerFilter keys (ITS#5503) Fixed slapd multiple attrs in URI (ITS#5516) Fixed slapd sasl_ssf retrieval (ITS#5403) Fixed slapd socket assert (ITS#5489) Fixed slapd syncrepl cookie (ITS#5536) Fixed slapd-bdb/hdb MAXPATHLEN (ITS#5531) Fixed slapd-bdb indexing in single ADD/MOD (ITS#5521) Fixed slapd-ldap entry_get() op-dependent behavior (ITS#5513) Fixed slapd-meta quarantine crash (ITS#5522) Fixed slapo-refint to allow setting modifiers name (ITS#5505) Fixed slapo-syncprov contextCSN passing on syncprov consumers (ITS#5488) Fixed slapo-syncprov csn update with delta-syncrepl (ITS#5493) Fixed slapo-syncprov op2.o_extra reset (ITS#5501, #5506) Fixed slapo-syncprov searching wrong backend (ITS#5487) Fixed slapo-syncprov sending ops without queued CSNs (ITS#5465) Fixed slapo-syncprov max csn search on startup (ITS#5537) Fixed slapo-unique config structs (ITS#5526) Fixed slapo-unique filter terminator (ITS#5511) Documentation Add search privileges documentation (ITS#5512) admin24 security document updates (ITS#5524) OpenLDAP 2.4.9 Release (2008/05/07) Fixed libldap to use unsigned port (ITS#5436) Fixed libldap error message for missing close paren (ITS#5458) Fixed libldap_r tpool pause checks (ITS#5364, #5407) Fixed slapcat error checking (ITS#5387) Fixed slapd abstract objectClass inheritance check (ITS#5474) Fixed slapd add operations requiring naming attrs (ITS#5412) Fixed slapd connection handling (ITS#5469) Fixed slapd delta-syncrepl resync (ITS#5378) Fixed slapd frontendDB backend selection (ITS#5419) Fixed slapd pagedresults stale state (ITS#5409) Fixed slapd pointer dereference (ITS#5388) Fixed slapd null argument dereference (ITS#5435) Fixed slapd REP_ENTRY flags (ITS#5340) Fixed slapd sets attribute description parsing (ITS#5402) Fixed slapd syncrepl hang on back-config (ITS#5407) Fixed slapd syncrepl compare_csns crash (ITS#5413) Fixed slapd syncrepl contextCSN update clash (ITS#5426) Fixed slapd syncrepl/glue failure (ITS#5430) Fixed slapd syncrepl crash on empty CSN (ITS#5432) Fixed slapd syncrepl refreshAndPersist (ITS#5454) Fixed slapd syncrepl modrdn processing (ITS#5397) Fixed slapd syncrepl MPR partial refresh (ITS#5470) Fixed slapd value list termination (ITS#5450) Fixed slapd/slapo-accesslog rq mutex usage (ITS#5442) Fixed slapd-bdb ID_NOCACHE handling (ITS#5439) Fixed slapd-bdb entryinfo state if db_lock fails (ITS#5455) Fixed slapd-bdb referral rewrite (ITS#5339) Fixed slapd-config overlay stacking (ITS#5346) Fixed slapd-config attribute publishing (ITS#5383) Fixed slapd-ldap connection handler (ITS#5404) Fixed slapd-ldif file name handling & multi-suffix/dir catch (ITS#5408) Fixed slapd-meta connections on error (ITS#5440) Fixed slapd-meta crash on search (ITS#5481) Fixed slapo-accesslog null callback stack crash (ITS#5490) Fixed slapo-auditlog unnecessary syscall (ITS#5441) Added slapo-dynlist mapping to dynamic attrs generation (ITS#5466) Fixed slapo-refint dnSubtreeMatch (ITS#5427) Fixed slapo-refint global referential integrity (ITS#5428) Fixed slapo-syncprov psearch on closed connection (ITS#5401) Fixed slapo-syncprov psearch task delay (ITS#5405) Fixed slapo-syncprov psearch filter identity (ITS#5418, #5486) Fixed slapo-syncprov/glue contextCSN update (ITS#5433) Fixed slapo-syncprov/glue search ops (ITS#5434) Fixed slapo-syncprov null cookie (ITS#5437,#5444) Fixed slapo-syncprov double-free (ITS#5445) Fixed slapo-syncprov free syncop correctly (ITS#5484) Fixed slapo-syncprov glue deadlock (ITS#5451) Build Environment Fixed leave function naming for OSF1 (ITS#5411) Documentation Fixed slapd.access(5) authz-regexp documented behavior (ITS#5400) Fixed slapd.meta(5) idassert-* documentation (ITS#5406) admin24 delta-syncrepl documentation (ITS#5476) admin24 set documentation (ITS#5278,ITS#5279,ITS#5281) admin24 slapo-ppolicy documentation (ITS#5479) admin24 syncrepl directives update (ITS#5425) OpenLDAP 2.4.8 Release (2008/02/19) Fixed ldapmodify verbose logging (ITS#5247) Fixed ldapdelete with sizelimit (ITS#5294) Fixed ldapdelete with subentries control (ITS#5293) Fixed ldapsearch exit code init (ITS#5317) Fixed libldap extended decoding (ITS#5304) Fixed libldap filter abort (ITS#5300) Fixed libldap ldap_parse_sasl_bind_result (ITS#5263) Fixed libldap result codes for open (ITS#5338) Fixed libldap search timeout crash (ITS#5291) Fixed libldap paged results crash (ITS#5315) Fixed libldap cipher suite with GnuTLS (ITS#5341) Fixed slapd support for 2.1 CSN (ITS#5348) Fixed slapd include handling (ITS#5276) Fixed slapd modrdn check for valid new DN (ITS#5344) Fixed slapd multi-step SASL binds (ITS#5298) Fixed slapd non-atomic signal variables (ITS#5248) Fixed slapd overlay ordering when moving to slapd.d (ITS#5284) Fixed slapd NULL printf (ITS#5264) Fixed slapd NULL set values (ITS#5286) Fixed slapd SEGV with SASL/OTP (ITS#5259) Fixed slapd timestamp race condition (ITS#5370) Fixed slapd cn=config crash on delete (ITS#5343) Fixed slapd cn=config global acls (ITS#5352) Fixed slapd truncated cookie (ITS#5362) Fixed slapd sasl with CLEARTEXT (ITS#5368) Fixed slapd str2entry with no attrs (ITS#5308) Fixed slapd TLSVerifyClient default (ITS#5360) Fixed slapd HAVE_TLS dependency (ITS#5379) Fixed slapd delta-syncrepl refresh mode (ITS#5376) Fixed slapd ACL sets URI attrs (ITS#5384) Fixed slapd invalid entryUUID filter (ITS#5386) Fixed slapd-bdb idlcache on adds (ITS#5086) Fixed slapd-bdb crash with modrdn (ITS#5358) Fixed slapd-bdb SEGV with bdb4.6 (ITS#5322) Fixed slapd-bdb modrdn to same dn (ITS#5319) Fixed slapd-bdb MPR (ITS#5332) Added slapd-bdb/slapd-hdb DB encryption (ITS#5359) Fixed slapd-ldif delete (ITS#5265) Fixed slapd-meta link to slapd-ldap (ITS#5355) Fixed slapd-meta setting of sm_nvalues (ITS#5375) Fixed slapd-monitor crash (ITS#5311) Fixed slapd-relay compare (ITS#4937) Added slapd-sock (ITS#4094) Fixed slapo-accesslog cleanup on successful response (ITS#5374) Added slapo-autogroup contrib module (ITS#5145) Added slapo-constraint cross-attribute constraints (ITS#4987) Fixed slapo-memberof objectClass inheritance (ITS#5299) Added slapo-memberof global overlay support (ITS#5301) Fixed slapo-memberof leak (ITS#5302) Fixed slapo-ppolicy only password check with policy (ITS#5285) Fixed slapo-ppolicy del/replace password without new one (ITS#5373) Fixed slapo-syncprov hang on checkpoint (ITS#5261) Added slapo-translucent local searching (ITS#5283) Removed lint Build Environment Fixed libldap_r threaded library linking (ITS#4982) Fixed libldap use of %n (ITS#5324) Fixed test047 to skip if rwm is not available (ITS#5292) Documentation DB_CONFIG.example URL wrong in comments (ITS#5288) Add cn=config example for auditlog (ITS#5245) ldapmodify(1) clarification for RFC2849 (ITS#5312) OpenLDAP 2.4.7 Release (2007/12/14) Added slapd ordered indexing of integer attributes (ITS#5239) Fixed slapd paged results control handling (ITS#5191) Fixed slapd sasl-host parsing (ITS#5209) Fixed slapd filter normalization (ITS#5212) Fixed slapd multiple suffix checking (ITS#5186) Fixed slapd paged results handling when using rootdn (ITS#5230) Fixed slapd syncrepl presentlist handling (ITS#5231) Fixed slapd core schema 'c' definition for RFC4519 (ITS#5236) Fixed slapd 3-way multi-provider replication (ITS#5238) Fixed slapd hash collisions in index slots (ITS#5183) Fixed slapd replication of dSAOperation attributes (ITS#5268) Fixed slapadd contextCSN updating (ITS#5225) Fixed slapd-bdb/hdb to report and fail on internal errors (ITS#5232) Fixed slapd-bdb/hdb dn2entry lock bug (ITS#5257) Fixed slapd-bdb/hdb dn2id lock bug (ITS#5262) Fixed slapd-hdb caching on rename ops (ITS#5221) Fixed slapo-accesslog abandoned op cleanup (ITS#5161) Fixed slapo-dds deleting from nonexistent db (ITS#5267) Fixed slapo-memberOf deleted values saving (ITS#5258) Fixed slapo-pcache op->o_abandon handling (ITS#5187) Fixed slapo-ppolicy single password check on modify (ITS#5146) Fixed slapo-ppolicy internal search (ITS#5235) Fixed slapo-syncprov refresh and persist cookie sending (ITS#5210) Fixed slapo-syncprov ignore invalid cookies (ITS#5211) Fixed slapo-translucent interaction with slapo-rwm (ITS#4889) Updated contrib addpartial module (ITS#3593) Build Environment Fixed liblber socket library linking (ITS#5224) Fixed Windows slapd.def rules (ITS#5215) Documentation Fixed grammar errors (ITS#5223) Refint overlay doc contribution (ITS#5217) Dynamic Lists doc contribution to the admin guide (ITS#5216) Fixed ldappasswd(1) and ldapmodify(1) typos (ITS#5269) Fixed domain factor typos (ITS#5237) Fixed slapd.conf(5) maxderefdepth default value typo (ITS#5200) Clarified slapd.conf(5) limits issues in syncrepl (ITS#5243) Fixed slapd-config(5) maxderefdepth default value typo (ITS#5200) Patches for minor typos in man pages (ITS#5228) admin24/replication.sdf spelling (ITS#5270) OpenLDAP 2.4.6 Release (2007/10/31) Initial release for "general use". PK0]T?Цlicenses/alt-openldap11/LICENSEnu[The OpenLDAP Public License Version 2.8, 17 August 2003 Redistribution and use of this software and associated documentation ("Software"), with or without modification, are permitted provided that the following conditions are met: 1. Redistributions in source form must retain copyright statements and notices, 2. Redistributions in binary form must reproduce applicable copyright statements and notices, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution, and 3. Redistributions must contain a verbatim copy of this document. The OpenLDAP Foundation may revise this license from time to time. Each revision is distinguished by a version number. You may use this Software under terms of this license revision or under the terms of any subsequent revision of the license. THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S) OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The names of the authors and copyright holders must not be used in advertising or otherwise to promote the sale, use or other dealing in this Software without specific, written prior permission. Title to copyright in this Software shall at all times remain with copyright holders. OpenLDAP is a registered trademark of the OpenLDAP Foundation. Copyright 1999-2003 The OpenLDAP Foundation, Redwood City, California, USA. All Rights Reserved. Permission to copy and distribute verbatim copies of this document is granted. PK0])) ) !licenses/alt-openldap11/COPYRIGHTnu[Copyright 1998-2021 The OpenLDAP Foundation All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted only as authorized by the OpenLDAP Public License. A copy of this license is available in the file LICENSE in the top-level directory of the distribution or, alternatively, at . OpenLDAP is a registered trademark of the OpenLDAP Foundation. Individual files and/or contributed packages may be copyright by other parties and/or subject to additional restrictions. This work is derived from the University of Michigan LDAP v3.3 distribution. Information concerning this software is available at . This work also contains materials derived from public sources. Additional information about OpenLDAP can be obtained at . --- Portions Copyright 1998-2012 Kurt D. Zeilenga. Portions Copyright 1998-2006 Net Boolean Incorporated. Portions Copyright 2001-2006 IBM Corporation. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted only as authorized by the OpenLDAP Public License. --- Portions Copyright 1999-2008 Howard Y.H. Chu. Portions Copyright 1999-2008 Symas Corporation. Portions Copyright 1998-2003 Hallvard B. Furuseth. Portions Copyright 2007-2011 Gavin Henry. Portions Copyright 2007-2011 Suretec Systems Ltd. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that this notice is preserved. The names of the copyright holders may not be used to endorse or promote products derived from this software without their specific prior written permission. This software is provided ``as is'' without express or implied warranty. --- Portions Copyright (c) 1992-1996 Regents of the University of Michigan. All rights reserved. Redistribution and use in source and binary forms are permitted provided that this notice is preserved and that due credit is given to the University of Michigan at Ann Arbor. The name of the University may not be used to endorse or promote products derived from this software without specific prior written permission. This software is provided ``as is'' without express or implied warranty. PK0]n@NNman/man5/ldif.5nu[.lf 1 stdin .TH LDIF 5 "2021/06/03" "OpenLDAP 2.4.59" .\" $OpenLDAP$ .\" Copyright 1998-2021 The OpenLDAP Foundation All Rights Reserved. .\" Copying restrictions apply. See COPYRIGHT/LICENSE. .SH NAME ldif \- LDAP Data Interchange Format .SH DESCRIPTION The LDAP Data Interchange Format (LDIF) is used to represent LDAP entries and change records in text form. LDAP tools, such as .BR ldapadd (1) and .BR ldapsearch (1), read and write LDIF entry records. .BR ldapmodify (1) reads LDIF change records. .LP This manual page provides a basic description of LDIF. A formal specification of LDIF is published in RFC 2849. .SH ENTRY RECORDS .LP LDIF entry records are used to represent directory entries. The basic form of an entry record is: .LP .nf .ft tt dn: : : :: :< ... .ft .fi .LP The value may be specified as UTF-8 text or as base64 encoded data, or a URI may be provided to the location of the attribute value. .LP A line may be continued by starting the next line with a single space or tab, e.g., .LP .nf .ft tt dn: cn=Barbara J Jensen,dc=exam ple,dc=com .ft .fi .LP Lines beginning with a sharp sign ('#') are ignored. .LP Multiple attribute values are specified on separate lines, e.g., .LP .nf .ft tt cn: Barbara J Jensen cn: Babs Jensen .ft .fi .LP If an value contains a non-printing character, or begins with a space or a colon ':', the is followed by a double colon and the value is encoded in base 64 notation. e.g., the value " begins with a space" would be encoded like this: .LP .nf .ft tt cn:: IGJlZ2lucyB3aXRoIGEgc3BhY2U= .ft .fi .LP If the attribute value is located in a file, the is followed by a ':<' and a file: URI. e.g., the value contained in the file /tmp/value would be listed like this: .LP .nf .ft tt cn:< file:///tmp/value .ft .fi Other URI schemes (ftp,http) may be supported as well. .LP Multiple entries within the same LDIF file are separated by blank lines. .SH ENTRY RECORD EXAMPLE Here is an example of an LDIF file containing three entries. .LP .nf .ft tt dn: cn=Barbara J Jensen,dc=example,dc=com cn: Barbara J Jensen cn: Babs Jensen objectclass: person description:< file:///tmp/babs sn: Jensen dn: cn=Bjorn J Jensen,dc=example,dc=com cn: Bjorn J Jensen cn: Bjorn Jensen objectclass: person sn: Jensen dn: cn=Jennifer J Jensen,dc=example,dc=com cn: Jennifer J Jensen cn: Jennifer Jensen objectclass: person sn: Jensen jpegPhoto:: /9j/4AAQSkZJRgABAAAAAQABAAD/2wBDABALD A4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQ ERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVG ... .ft .fi .LP Note that the description in Barbara Jensen's entry is read from file:///tmp/babs and the jpegPhoto in Jennifer Jensen's entry is encoded using base 64. .SH CHANGE RECORDS LDIF change records are used to represent directory change requests. Each change record starts with line indicating the distinguished name of the entry being changed: .LP .nf dn: .fi .LP .nf changetype: <[modify|add|delete|modrdn]> .fi .LP Finally, the change information itself is given, the format of which depends on what kind of change was specified above. For a \fIchangetype\fP of \fImodify\fP, the format is one or more of the following: .LP .nf add: : : ... \- .fi .LP Or, for a replace modification: .LP .nf replace: : : ... \- .fi .LP If no \fIattributetype\fP lines are given to replace, the entire attribute is to be deleted (if present). .LP Or, for a delete modification: .LP .nf delete: : : ... \- .fi .LP If no \fIattributetype\fP lines are given to delete, the entire attribute is to be deleted. .LP For a \fIchangetype\fP of \fIadd\fP, the format is: .LP .nf : : ... : : .fi .LP For a \fIchangetype\fP of \fImodrdn\fP or \fImoddn\fP, the format is: .LP .nf newrdn: deleteoldrdn: 0 | 1 newsuperior: .fi .LP where a value of 1 for deleteoldrdn means to delete the values forming the old rdn from the entry, and a value of 0 means to leave the values as non-distinguished attributes in the entry. The newsuperior line is optional and, if present, specifies the new superior to move the entry to. .LP For a \fIchangetype\fP of \fIdelete\fP, no additional information is needed in the record. .LP Note that attribute values may be presented using base64 or in files as described for entry records. Lines in change records may be continued in the manner described for entry records as well. .SH CHANGE RECORD EXAMPLE The following sample LDIF file contains a change record of each type of change. .LP .nf dn: cn=Babs Jensen,dc=example,dc=com changetype: add objectclass: person objectclass: extensibleObject cn: babs cn: babs jensen sn: jensen dn: cn=Babs Jensen,dc=example,dc=com changetype: modify add: givenName givenName: Barbara givenName: babs \- replace: description description: the fabulous babs \- delete: sn sn: jensen \- dn: cn=Babs Jensen,dc=example,dc=com changetype: modrdn newrdn: cn=Barbara J Jensen deleteoldrdn: 0 newsuperior: ou=People,dc=example,dc=com dn: cn=Barbara J Jensen,ou=People,dc=example,dc=com changetype: delete .fi .SH INCLUDE STATEMENT The LDIF parser has been extended to support an .B include statement for referencing other LDIF files. The .B include statement must be separated from other records by a blank line. The referenced file is specified using a file: URI and all of its contents are incorporated as if they were part of the original LDIF file. As above, other URI schemes may be supported. For example: .LP .nf dn: dc=example,dc=com objectclass: domain dc: example include: file:///tmp/example.com.ldif dn: dc=example,dc=org objectclass: domain dc: example .fi This feature is not part of the LDIF specification in RFC 2849 but is expected to appear in a future revision of this spec. It is supported by the .BR ldapadd (1), .BR ldapmodify (1), and .BR slapadd (8) commands. .SH SEE ALSO .BR ldap (3), .BR ldapsearch (1), .BR ldapadd (1), .BR ldapmodify (1), .BR slapadd (8), .BR slapcat (8), .BR slapd\-ldif (5). .LP "LDAP Data Interchange Format," Good, G., RFC 2849. .SH ACKNOWLEDGEMENTS .lf 1 ./../Project .\" Shared Project Acknowledgement Text .B "OpenLDAP Software" is developed and maintained by The OpenLDAP Project . .B "OpenLDAP Software" is derived from the University of Michigan LDAP 3.3 Release. .lf 278 stdin PK0]}HHman/man5/ldap.conf.5nu[.lf 1 stdin .TH LDAP.CONF 5 "2021/06/03" "OpenLDAP 2.4.59" .\" $OpenLDAP$ .\" Copyright 1998-2021 The OpenLDAP Foundation All Rights Reserved. .\" Copying restrictions apply. See COPYRIGHT/LICENSE. .SH NAME ldap.conf, .ldaprc \- LDAP configuration file/environment variables .SH SYNOPSIS /opt/alt/openldap11/etc/openldap/ldap.conf, ldaprc, .ldaprc, $LDAP .SH DESCRIPTION If the environment variable \fBLDAPNOINIT\fP is defined, all defaulting is disabled. .LP The .I ldap.conf configuration file is used to set system-wide defaults to be applied when running .I ldap clients. .LP Users may create an optional configuration file, .I ldaprc or .IR .ldaprc , in their home directory which will be used to override the system-wide defaults file. The file .I ldaprc in the current working directory is also used. .LP .LP Additional configuration files can be specified using the \fBLDAPCONF\fP and \fBLDAPRC\fP environment variables. \fBLDAPCONF\fP may be set to the path of a configuration file. This path can be absolute or relative to the current working directory. The \fBLDAPRC\fP, if defined, should be the basename of a file in the current working directory or in the user's home directory. .LP Environmental variables may also be used to augment the file based defaults. The name of the variable is the option name with an added prefix of \fBLDAP\fP. For example, to define \fBBASE\fP via the environment, set the variable \fBLDAPBASE\fP to the desired value. .LP Some options are user-only. Such options are ignored if present in the .I ldap.conf (or file specified by .BR LDAPCONF ). .LP Thus the following files and variables are read, in order: .nf variable $LDAPNOINIT, and if that is not set: system file /opt/alt/openldap11/etc/openldap/ldap.conf, user files $HOME/ldaprc, $HOME/.ldaprc, ./ldaprc, system file $LDAPCONF, user files $HOME/$LDAPRC, $HOME/.$LDAPRC, ./$LDAPRC, variables $LDAP. .fi Settings late in the list override earlier ones. .SH SYNTAX The configuration options are case-insensitive; their value, on a case by case basis, may be case-sensitive. .LP Blank lines are ignored. .br Lines beginning with a hash mark (`#') are comments, and ignored. .LP Valid lines are made of an option's name (a sequence of non-blanks, conventionally written in uppercase, although not required), followed by a value. The value starts with the first non-blank character after the option's name, and terminates at the end of the line, or at the last sequence of blanks before the end of the line. The tokenization of the value, if any, is delegated to the handler(s) for that option, if any. Quoting values that contain blanks may be incorrect, as the quotes would become part of the value. For example, .nf # Wrong - erroneous quotes: URI "ldap:// ldaps://" # Right - space-separated list of URIs, without quotes: URI ldap:// ldaps:// # Right - DN syntax needs quoting for Example, Inc: BASE ou=IT staff,o="Example, Inc",c=US # or: BASE ou=IT staff,o=Example\\2C Inc,c=US # Wrong - comment on same line as option: DEREF never # Never follow aliases .fi .LP A line cannot be longer than LINE_MAX, which should be more than 2000 bytes on all platforms. There is no mechanism to split a long line on multiple lines, either for beautification or to overcome the above limit. .SH OPTIONS The different configuration options are: .TP .B URI Specifies the URI(s) of an LDAP server(s) to which the .I LDAP library should connect. The URI scheme may be any of .BR ldap , .B ldaps or .BR ldapi , which refer to LDAP over TCP, LDAP over SSL (TLS) and LDAP over IPC (UNIX domain sockets), respectively. Each server's name can be specified as a domain-style name or an IP address literal. Optionally, the server's name can followed by a ':' and the port number the LDAP server is listening on. If no port number is provided, the default port for the scheme is used (389 for ldap://, 636 for ldaps://). For LDAP over IPC, .B name is the name of the socket, and no .B port is required, nor allowed; note that directory separators must be URL-encoded, like any other characters that are special to URLs; so the socket /usr/local/var/ldapi must be specified as ldapi://%2Fusr%2Flocal%2Fvar%2Fldapi A space separated list of URIs may be provided. .TP .B BASE Specifies the default base DN to use when performing ldap operations. The base must be specified as a Distinguished Name in LDAP format. .TP .B BINDDN Specifies the default bind DN to use when performing ldap operations. The bind DN must be specified as a Distinguished Name in LDAP format. .B This is a user-only option. .TP .B DEREF Specifies how alias dereferencing is done when performing a search. The .B can be specified as one of the following keywords: .RS .TP .B never Aliases are never dereferenced. This is the default. .TP .B searching Aliases are dereferenced in subordinates of the base object, but not in locating the base object of the search. .TP .B finding Aliases are only dereferenced when locating the base object of the search. .TP .B always Aliases are dereferenced both in searching and in locating the base object of the search. .RE .TP .TP .B HOST Specifies the name(s) of an LDAP server(s) to which the .I LDAP library should connect. Each server's name can be specified as a domain-style name or an IP address and optionally followed by a ':' and the port number the ldap server is listening on. A space separated list of hosts may be provided. .B HOST is deprecated in favor of .BR URI . .TP .B NETWORK_TIMEOUT Specifies the timeout (in seconds) after which the poll(2)/select(2) following a connect(2) returns in case of no activity. .TP .B PORT Specifies the default port used when connecting to LDAP servers(s). The port may be specified as a number. .B PORT is deprecated in favor of .BR URI. .TP .B REFERRALS Specifies if the client should automatically follow referrals returned by LDAP servers. The default is on. Note that the command line tools .BR ldapsearch (1) &co always override this option. .\" This should only be allowed via ldap_set_option(3) .\".TP .\".B RESTART .\"Determines whether the library should implicitly restart connections (FIXME). .TP .B SIZELIMIT Specifies a size limit (number of entries) to use when performing searches. The number should be a non-negative integer. \fISIZELIMIT\fP of zero (0) specifies a request for unlimited search size. Please note that the server may still apply any server-side limit on the amount of entries that can be returned by a search operation. .TP .B TIMELIMIT Specifies a time limit (in seconds) to use when performing searches. The number should be a non-negative integer. \fITIMELIMIT\fP of zero (0) specifies unlimited search time to be used. Please note that the server may still apply any server-side limit on the duration of a search operation. .TP .B VERSION {2|3} Specifies what version of the LDAP protocol should be used. .TP .B TIMEOUT Specifies a timeout (in seconds) after which calls to synchronous LDAP APIs will abort if no response is received. Also used for any .BR ldap_result (3) calls where a NULL timeout parameter is supplied. .SH SASL OPTIONS If OpenLDAP is built with Simple Authentication and Security Layer support, there are more options you can specify. .TP .B SASL_MECH Specifies the SASL mechanism to use. .TP .B SASL_REALM Specifies the SASL realm. .TP .B SASL_AUTHCID Specifies the authentication identity. .B This is a user-only option. .TP .B SASL_AUTHZID Specifies the proxy authorization identity. .B This is a user-only option. .TP .B SASL_SECPROPS Specifies Cyrus SASL security properties. The .B can be specified as a comma-separated list of the following: .RS .TP .B none (without any other properties) causes the properties defaults ("noanonymous,noplain") to be cleared. .TP .B noplain disables mechanisms susceptible to simple passive attacks. .TP .B noactive disables mechanisms susceptible to active attacks. .TP .B nodict disables mechanisms susceptible to passive dictionary attacks. .TP .B noanonymous disables mechanisms which support anonymous login. .TP .B forwardsec requires forward secrecy between sessions. .TP .B passcred requires mechanisms which pass client credentials (and allows mechanisms which can pass credentials to do so). .TP .B minssf= specifies the minimum acceptable .I security strength factor as an integer approximating the effective key length used for encryption. 0 (zero) implies no protection, 1 implies integrity protection only, 56 allows DES or other weak ciphers, 112 allows triple DES and other strong ciphers, 128 allows RC4, Blowfish and other modern strong ciphers. The default is 0. .TP .B maxssf= specifies the maximum acceptable .I security strength factor as an integer (see .B minssf description). The default is .BR INT_MAX . .TP .B maxbufsize= specifies the maximum security layer receive buffer size allowed. 0 disables security layers. The default is 65536. .RE .TP .B SASL_NOCANON Do not perform reverse DNS lookups to canonicalize SASL host names. The default is off. .SH GSSAPI OPTIONS If OpenLDAP is built with Generic Security Services Application Programming Interface support, there are more options you can specify. .TP .B GSSAPI_SIGN Specifies if GSSAPI signing (GSS_C_INTEG_FLAG) should be used. The default is off. .TP .B GSSAPI_ENCRYPT Specifies if GSSAPI encryption (GSS_C_INTEG_FLAG and GSS_C_CONF_FLAG) should be used. The default is off. .TP .B GSSAPI_ALLOW_REMOTE_PRINCIPAL Specifies if GSSAPI based authentication should try to form the target principal name out of the ldapServiceName or dnsHostName attribute of the targets RootDSE entry. The default is off. .SH TLS OPTIONS If OpenLDAP is built with Transport Layer Security support, there are more options you can specify. These options are used when an .B ldaps:// URI is selected (by default or otherwise) or when the application negotiates TLS by issuing the LDAP StartTLS operation. .LP When using OpenSSL, if neither \fBTLS_CACERT\fP nor \fBTLS_CACERTDIR\fP is set, the system-wide default set of CA certificates is used. .TP .B TLS_CACERT Specifies the file that contains certificates for all of the Certificate Authorities the client will recognize. .TP .B TLS_CACERTDIR Specifies the path of a directory that contains Certificate Authority certificates in separate individual files. The .B TLS_CACERT is always used before .B TLS_CACERTDIR. The specified directory must be managed with the OpenSSL c_rehash utility. This parameter is ignored with GnuTLS. When using Mozilla NSS, may contain a Mozilla NSS cert/key database. If contains a Mozilla NSS cert/key database and CA cert files, OpenLDAP will use the cert/key database and will ignore the CA cert files. .TP .B TLS_CERT Specifies the file that contains the client certificate. .B This is a user-only option. When using Mozilla NSS, if using a cert/key database (specified with TLS_CACERTDIR), TLS_CERT specifies the name of the certificate to use: .nf TLS_CERT Certificate for Sam Carter .fi If using a token other than the internal built in token, specify the token name first, followed by a colon: .nf TLS_CERT my hardware device:Certificate for Sam Carter .fi Use certutil \-L to list the certificates by name: .nf certutil \-d /path/to/certdbdir \-L .fi .TP .B TLS_ECNAME Specify the name of the curve(s) to use for Elliptic curve Diffie-Hellman ephemeral key exchange. This option is only used for OpenSSL. This option is not used with GnuTLS; the curves may be chosen in the GnuTLS ciphersuite specification. .TP .B TLS_KEY Specifies the file that contains the private key that matches the certificate stored in the .B TLS_CERT file. Currently, the private key must not be protected with a password, so it is of critical importance that the key file is protected carefully. .B This is a user-only option. When using Mozilla NSS, TLS_KEY specifies the name of a file that contains the password for the key for the certificate specified with TLS_CERT. The modutil command can be used to turn off password protection for the cert/key database. For example, if TLS_CACERTDIR specifies /home/scarter/.moznss as the location of the cert/key database, use modutil to change the password to the empty string: .nf modutil \-dbdir ~/.moznss \-changepw 'NSS Certificate DB' .fi You must have the old password, if any. Ignore the WARNING about the running browser. Press 'Enter' for the new password. .TP .B TLS_CIPHER_SUITE Specifies acceptable cipher suite and preference order. should be a cipher specification for the TLS library in use (OpenSSL, GnuTLS, or Mozilla NSS). Example: .RS .RS .TP .I OpenSSL: TLS_CIPHER_SUITE HIGH:MEDIUM:+SSLv2 .TP .I GnuTLS: TLS_CIPHER_SUITE SECURE256:!AES-128-CBC .RE To check what ciphers a given spec selects in OpenSSL, use: .nf openssl ciphers \-v .fi With GnuTLS the available specs can be found in the manual page of .BR gnutls\-cli (1) (see the description of the option .BR \-\-priority ). In older versions of GnuTLS, where gnutls\-cli does not support the option \-\-priority, you can obtain the \(em more limited \(em list of ciphers by calling: .nf gnutls\-cli \-l .fi When using Mozilla NSS, the OpenSSL cipher suite specifications are used and translated into the format used internally by Mozilla NSS. There isn't an easy way to list the cipher suites from the command line. The authoritative list is in the source code for Mozilla NSS in the file sslinfo.c in the structure .nf static const SSLCipherSuiteInfo suiteInfo[] .fi .RE .TP .B TLS_PROTOCOL_MIN [.] Specifies minimum SSL/TLS protocol version that will be negotiated. If the server doesn't support at least that version, the SSL handshake will fail. To require TLS 1.x or higher, set this option to 3.(x+1), e.g., .nf TLS_PROTOCOL_MIN 3.2 .fi would require TLS 1.1. Specifying a minimum that is higher than that supported by the OpenLDAP implementation will result in it requiring the highest level that it does support. This parameter is ignored with GnuTLS. .TP .B TLS_RANDFILE Specifies the file to obtain random bits from when /dev/[u]random is not available. Generally set to the name of the EGD/PRNGD socket. The environment variable RANDFILE can also be used to specify the filename. This parameter is ignored with GnuTLS and Mozilla NSS. .TP .B TLS_REQCERT Specifies what checks to perform on server certificates in a TLS session, if any. The .B can be specified as one of the following keywords: .RS .TP .B never The client will not request or check any server certificate. .TP .B allow The server certificate is requested. If no certificate is provided, the session proceeds normally. If a bad certificate is provided, it will be ignored and the session proceeds normally. .TP .B try The server certificate is requested. If no certificate is provided, the session proceeds normally. If a bad certificate is provided, the session is immediately terminated. .TP .B demand | hard These keywords are equivalent. The server certificate is requested. If no certificate is provided, or a bad certificate is provided, the session is immediately terminated. This is the default setting. .RE .TP .B TLS_REQSAN Specifies what checks to perform on the subjectAlternativeName (SAN) extensions in a server certificate when validating the certificate name against the specified hostname of the server. The .B can be specified as one of the following keywords: .RS .TP .B never The client will not check any SAN in the certificate. .TP .B allow The SAN is checked against the specified hostname. If a SAN is present but none match the specified hostname, the SANs are ignored and the usual check against the certificate DN is used. .TP .B try The SAN is checked against the specified hostname. If no SAN is present in the server certificate, the usual check against the certificate DN is used. If a SAN is present but doesn't match the specified hostname, the session is immediately terminated. This setting may be preferred when a mix of certs with and without SANs are in use. This is the default setting. .TP .B demand | hard These keywords are equivalent. The SAN is checked against the specified hostname. If no SAN is present in the server certificate, or no SANs match, the session is immediately terminated. This setting should be used when only certificates with SANs are in use. .RE .TP .B TLS_CRLCHECK Specifies if the Certificate Revocation List (CRL) of the CA should be used to verify if the server certificates have not been revoked. This requires .B TLS_CACERTDIR parameter to be set. This parameter is ignored with GnuTLS and Mozilla NSS. .B can be specified as one of the following keywords: .RS .TP .B none No CRL checks are performed .TP .B peer Check the CRL of the peer certificate .TP .B all Check the CRL for a whole certificate chain .RE .TP .B TLS_CRLFILE Specifies the file containing a Certificate Revocation List to be used to verify if the server certificates have not been revoked. This parameter is only supported with GnuTLS and Mozilla NSS. .SH "ENVIRONMENT VARIABLES" .TP LDAPNOINIT disable all defaulting .TP LDAPCONF path of a configuration file .TP LDAPRC basename of ldaprc file in $HOME or $CWD .TP LDAP Set as from ldap.conf .SH FILES .TP .I /opt/alt/openldap11/etc/openldap/ldap.conf system-wide ldap configuration file .TP .I $HOME/ldaprc, $HOME/.ldaprc user ldap configuration file .TP .I $CWD/ldaprc local ldap configuration file .SH "SEE ALSO" .BR ldap (3), .BR ldap_set_option (3), .BR ldap_result (3), .BR openssl (1), .BR sasl (3) .SH AUTHOR Kurt Zeilenga, The OpenLDAP Project .SH ACKNOWLEDGEMENTS .lf 1 ./../Project .\" Shared Project Acknowledgement Text .B "OpenLDAP Software" is developed and maintained by The OpenLDAP Project . .B "OpenLDAP Software" is derived from the University of Michigan LDAP 3.3 Release. .lf 559 stdin PKA0]Wyperl5/Types/Serialiser/Error.pmnu6$=head1 NAME Types::Serialiser::Error - dummy module for Types::Serialiser =head1 SYNOPSIS # do not "use" yourself =head1 DESCRIPTION This module exists only to provide overload resolution for Storable and similar modules that assume that class name equals module name. See L for more info about this class. =cut use Types::Serialiser (); =head1 AUTHOR Marc Lehmann http://home.schmorp.de/ =cut 1 PKA0]?##perl5/Types/Serialiser.pmnu6$=head1 NAME Types::Serialiser - simple data types for common serialisation formats =encoding utf-8 =head1 SYNOPSIS =head1 DESCRIPTION This module provides some extra datatypes that are used by common serialisation formats such as JSON or CBOR. The idea is to have a repository of simple/small constants and containers that can be shared by different implementations so they become interoperable between each other. =cut package Types::Serialiser; use common::sense; # required to suppress annoying warnings our $VERSION = '1.01'; =head1 SIMPLE SCALAR CONSTANTS Simple scalar constants are values that are overloaded to act like simple Perl values, but have (class) type to differentiate them from normal Perl scalars. This is necessary because these have different representations in the serialisation formats. In the following, functions with zero or one arguments have a prototype of C<()> and C<($)>, respectively, so act as constants and unary operators. =head2 BOOLEANS (Types::Serialiser::Boolean class) This type has only two instances, true and false. A natural representation for these in Perl is C<1> and C<0>, but serialisation formats need to be able to differentiate between them and mere numbers. =over 4 =item $Types::Serialiser::true, Types::Serialiser::true This value represents the "true" value. In most contexts is acts like the number C<1>. It is up to you whether you use the variable form (C<$Types::Serialiser::true>) or the constant form (C). The constant is represented as a reference to a scalar containing C<1> - implementations are allowed to directly test for this. =item $Types::Serialiser::false, Types::Serialiser::false This value represents the "false" value. In most contexts is acts like the number C<0>. It is up to you whether you use the variable form (C<$Types::Serialiser::false>) or the constant form (C). The constant is represented as a reference to a scalar containing C<0> - implementations are allowed to directly test for this. =item Types::Serialiser::as_bool $value Converts a Perl scalar into a boolean, which is useful syntactic sugar. Strictly equivalent to: $value ? $Types::Serialiser::true : $Types::Serialiser::false =item $is_bool = Types::Serialiser::is_bool $value Returns true iff the C<$value> is either C<$Types::Serialiser::true> or C<$Types::Serialiser::false>. For example, you could differentiate between a perl true value and a C by using this: $value && Types::Serialiser::is_bool $value =item $is_true = Types::Serialiser::is_true $value Returns true iff C<$value> is C<$Types::Serialiser::true>. =item $is_false = Types::Serialiser::is_false $value Returns false iff C<$value> is C<$Types::Serialiser::false>. =back =head2 ERROR (Types::Serialiser::Error class) This class has only a single instance, C. It is used to signal an encoding or decoding error. In CBOR for example, and object that couldn't be encoded will be represented by a CBOR undefined value, which is represented by the error value in Perl. =over 4 =item $Types::Serialiser::error, Types::Serialiser::error This value represents the "error" value. Accessing values of this type will throw an exception. The constant is represented as a reference to a scalar containing C - implementations are allowed to directly test for this. =item $is_error = Types::Serialiser::is_error $value Returns false iff C<$value> is C<$Types::Serialiser::error>. =back =cut BEGIN { # for historical reasons, and to avoid extra dependencies in JSON::PP, # we alias *Types::Serialiser::Boolean with JSON::PP::Boolean. package JSON::PP::Boolean; *Types::Serialiser::Boolean:: = *JSON::PP::Boolean::; } { # this must done before blessing to work around bugs # in perl < 5.18 (it seems to be fixed in 5.18). package Types::Serialiser::BooleanBase; use overload "0+" => sub { ${$_[0]} }, "++" => sub { $_[0] = ${$_[0]} + 1 }, "--" => sub { $_[0] = ${$_[0]} - 1 }, fallback => 1; @Types::Serialiser::Boolean::ISA = Types::Serialiser::BooleanBase::; } our $true = do { bless \(my $dummy = 1), Types::Serialiser::Boolean:: }; our $false = do { bless \(my $dummy = 0), Types::Serialiser::Boolean:: }; our $error = do { bless \(my $dummy ), Types::Serialiser::Error:: }; sub true () { $true } sub false () { $false } sub error () { $error } sub as_bool($) { $_[0] ? $true : $false } sub is_bool ($) { UNIVERSAL::isa $_[0], Types::Serialiser::Boolean:: } sub is_true ($) { $_[0] && UNIVERSAL::isa $_[0], Types::Serialiser::Boolean:: } sub is_false ($) { !$_[0] && UNIVERSAL::isa $_[0], Types::Serialiser::Boolean:: } sub is_error ($) { UNIVERSAL::isa $_[0], Types::Serialiser::Error:: } package Types::Serialiser::Error; sub error { require Carp; Carp::croak ("caught attempt to use the Types::Serialiser::error value"); }; use overload "0+" => \&error, "++" => \&error, "--" => \&error, fallback => 1; =head1 NOTES FOR XS USERS The recommended way to detect whether a scalar is one of these objects is to check whether the stash is the C or C stash, and then follow the scalar reference to see if it's C<1> (true), C<0> (false) or C (error). While it is possible to use an isa test, directly comparing stash pointers is faster and guaranteed to work. For historical reasons, the C stash is just an alias for C. When printed, the classname with usually be C, but isa tests and stash pointer comparison will normally work correctly (i.e. Types::Serialiser::true ISA JSON::PP::Boolean, but also ISA Types::Serialiser::Boolean). =head1 A GENERIC OBJECT SERIALIATION PROTOCOL This section explains the object serialisation protocol used by L. It is meant to be generic enough to support any kind of generic object serialiser. This protocol is called "the Types::Serialiser object serialisation protocol". =head2 ENCODING When the encoder encounters an object that it cannot otherwise encode (for example, L can encode a few special types itself, and will first attempt to use the special C serialisation protocol), it will look up the C method on the object. Note that the C method will normally be called I encoding, and I change the data structure that is being encoded in any way, or it might cause memory corruption or worse. If it exists, it will call it with two arguments: the object to serialise, and a constant string that indicates the name of the data model. For example L uses C, and the L and L modules (or any other JSON serialiser), would use C as second argument. The C method can then return zero or more values to identify the object instance. The serialiser is then supposed to encode the class name and all of these return values (which must be encodable in the format) using the relevant form for Perl objects. In CBOR for example, there is a registered tag number for encoded perl objects. The values that C returns must be serialisable with the serialiser that calls it. Therefore, it is recommended to use simple types such as strings and numbers, and maybe array references and hashes (basically, the JSON data model). You can always use a more complex format for a specific data model by checking the second argument, the data model. The "data model" is not the same as the "data format" - the data model indicates what types and kinds of return values can be returned from C. For example, in C it is permissible to return tagged CBOR values, while JSON does not support these at all, so C would be a valid (but too limited) data model name for C. similarly, a serialising format that supports more or less the same data model as JSON could use C as data model without losing anything. =head2 DECODING When the decoder then encounters such an encoded perl object, it should look up the C method on the stored classname, and invoke it with the classname, the constant string to identify the data model/data format, and all the return values returned by C. =head2 EXAMPLES See the C section in the L manpage for more details, an example implementation, and code examples. Here is an example C/C method pair: sub My::Object::FREEZE { my ($self, $model) = @_; ($self->{type}, $self->{id}, $self->{variant}) } sub My::Object::THAW { my ($class, $model, $type, $id, $variant) = @_; $class->new (type => $type, id => $id, variant => $variant) } =head1 BUGS The use of L makes this module much heavier than it should be (on my system, this module: 4kB RSS, overload: 260kB RSS). =head1 SEE ALSO Currently, L and L use these types. =head1 AUTHOR Marc Lehmann http://home.schmorp.de/ =cut 1 PKA0]L==perl5/HTTP/Tiny.pmnu6$# vim: ts=4 sts=4 sw=4 et: package HTTP::Tiny; use strict; use warnings; # ABSTRACT: A small, simple, correct HTTP/1.1 client our $VERSION = '0.088'; sub _croak { require Carp; Carp::croak(@_) } #pod =method new #pod #pod $http = HTTP::Tiny->new( %attributes ); #pod #pod This constructor returns a new HTTP::Tiny object. Valid attributes include: #pod #pod =for :list #pod * C — A user-agent string (defaults to 'HTTP-Tiny/$VERSION'). If #pod C — ends in a space character, the default user-agent string is #pod appended. #pod * C — An instance of L — or equivalent class #pod that supports the C and C methods #pod * C — A hashref of default headers to apply to requests #pod * C — The local IP address to bind to #pod * C — Whether to reuse the last connection (if for the same #pod scheme, host and port) (defaults to 1) #pod * C — Maximum number of redirects allowed (defaults to 5) #pod * C — Maximum response size in bytes (only when not using a data #pod callback). If defined, requests with responses larger than this will return #pod a 599 status code. #pod * C — URL of a proxy server to use for HTTP connections #pod (default is C<$ENV{http_proxy}> — if set) #pod * C — URL of a proxy server to use for HTTPS connections #pod (default is C<$ENV{https_proxy}> — if set) #pod * C — URL of a generic proxy server for both HTTP and HTTPS #pod connections (default is C<$ENV{all_proxy}> — if set) #pod * C — List of domain suffixes that should not be proxied. Must #pod be a comma-separated string or an array reference. (default is #pod C<$ENV{no_proxy}> —) #pod * C — Request timeout in seconds (default is 60) If a socket open, #pod read or write takes longer than the timeout, the request response status code #pod will be 599. #pod * C — A boolean that indicates whether to validate the TLS/SSL #pod certificate of an C — connection (default is true). Changed from false #pod to true in version 0.083. #pod * C — A hashref of C — options to pass through to #pod L #pod * C<$ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT}> - Changes the default #pod certificate verification behavior to not check server identity if set to 1. #pod Only effective if C is not set. Added in version 0.083. #pod #pod #pod An accessor/mutator method exists for each attribute. #pod #pod Passing an explicit C for C, C or C will #pod prevent getting the corresponding proxies from the environment. #pod #pod Errors during request execution will result in a pseudo-HTTP status code of 599 #pod and a reason of "Internal Exception". The content field in the response will #pod contain the text of the error. #pod #pod The C parameter enables a persistent connection, but only to a #pod single destination scheme, host and port. If any connection-relevant #pod attributes are modified via accessor, or if the process ID or thread ID change, #pod the persistent connection will be dropped. If you want persistent connections #pod across multiple destinations, use multiple HTTP::Tiny objects. #pod #pod See L for more on the C and C attributes. #pod #pod =cut my @attributes; BEGIN { @attributes = qw( cookie_jar default_headers http_proxy https_proxy keep_alive local_address max_redirect max_size proxy no_proxy SSL_options verify_SSL ); my %persist_ok = map {; $_ => 1 } qw( cookie_jar default_headers max_redirect max_size ); no strict 'refs'; no warnings 'uninitialized'; for my $accessor ( @attributes ) { *{$accessor} = sub { @_ > 1 ? do { delete $_[0]->{handle} if !$persist_ok{$accessor} && $_[1] ne $_[0]->{$accessor}; $_[0]->{$accessor} = $_[1] } : $_[0]->{$accessor}; }; } } sub agent { my($self, $agent) = @_; if( @_ > 1 ){ $self->{agent} = (defined $agent && $agent =~ / $/) ? $agent . $self->_agent : $agent; } return $self->{agent}; } sub timeout { my ($self, $timeout) = @_; if ( @_ > 1 ) { $self->{timeout} = $timeout; if ($self->{handle}) { $self->{handle}->timeout($timeout); } } return $self->{timeout}; } sub new { my($class, %args) = @_; # Support lower case verify_ssl argument, but only if verify_SSL is not # true. if ( exists $args{verify_ssl} ) { $args{verify_SSL} ||= $args{verify_ssl}; } my $self = { max_redirect => 5, timeout => defined $args{timeout} ? $args{timeout} : 60, keep_alive => 1, verify_SSL => defined $args{verify_SSL} ? $args{verify_SSL} : _verify_SSL_default(), no_proxy => $ENV{no_proxy}, }; bless $self, $class; $class->_validate_cookie_jar( $args{cookie_jar} ) if $args{cookie_jar}; for my $key ( @attributes ) { $self->{$key} = $args{$key} if exists $args{$key} } $self->agent( exists $args{agent} ? $args{agent} : $class->_agent ); $self->_set_proxies; return $self; } sub _verify_SSL_default { my ($self) = @_; # Check if insecure default certificate verification behaviour has been # changed by the user by setting PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT=1 return (($ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT} || '') eq '1') ? 0 : 1; } sub _set_proxies { my ($self) = @_; # get proxies from %ENV only if not provided; explicit undef will disable # getting proxies from the environment # generic proxy if (! exists $self->{proxy} ) { $self->{proxy} = $ENV{all_proxy} || $ENV{ALL_PROXY}; } if ( defined $self->{proxy} ) { $self->_split_proxy( 'generic proxy' => $self->{proxy} ); # validate } else { delete $self->{proxy}; } # http proxy if (! exists $self->{http_proxy} ) { # under CGI, bypass HTTP_PROXY as request sets it from Proxy header local $ENV{HTTP_PROXY} = ($ENV{CGI_HTTP_PROXY} || "") if $ENV{REQUEST_METHOD}; $self->{http_proxy} = $ENV{http_proxy} || $ENV{HTTP_PROXY} || $self->{proxy}; } if ( defined $self->{http_proxy} ) { $self->_split_proxy( http_proxy => $self->{http_proxy} ); # validate $self->{_has_proxy}{http} = 1; } else { delete $self->{http_proxy}; } # https proxy if (! exists $self->{https_proxy} ) { $self->{https_proxy} = $ENV{https_proxy} || $ENV{HTTPS_PROXY} || $self->{proxy}; } if ( $self->{https_proxy} ) { $self->_split_proxy( https_proxy => $self->{https_proxy} ); # validate $self->{_has_proxy}{https} = 1; } else { delete $self->{https_proxy}; } # Split no_proxy to array reference if not provided as such unless ( ref $self->{no_proxy} eq 'ARRAY' ) { $self->{no_proxy} = (defined $self->{no_proxy}) ? [ split /\s*,\s*/, $self->{no_proxy} ] : []; } return; } #pod =method get|head|put|post|patch|delete #pod #pod $response = $http->get($url); #pod $response = $http->get($url, \%options); #pod $response = $http->head($url); #pod #pod These methods are shorthand for calling C for the given method. The #pod URL must have unsafe characters escaped and international domain names encoded. #pod See C for valid options and a description of the response. #pod #pod The C field of the response will be true if the status code is 2XX. #pod #pod =cut for my $sub_name ( qw/get head put post patch delete/ ) { my $req_method = uc $sub_name; no strict 'refs'; eval <<"HERE"; ## no critic sub $sub_name { my (\$self, \$url, \$args) = \@_; \@_ == 2 || (\@_ == 3 && ref \$args eq 'HASH') or _croak(q/Usage: \$http->$sub_name(URL, [HASHREF])/ . "\n"); return \$self->request('$req_method', \$url, \$args || {}); } HERE } #pod =method post_form #pod #pod $response = $http->post_form($url, $form_data); #pod $response = $http->post_form($url, $form_data, \%options); #pod #pod This method executes a C request and sends the key/value pairs from a #pod form data hash or array reference to the given URL with a C of #pod C. If data is provided as an array #pod reference, the order is preserved; if provided as a hash reference, the terms #pod are sorted on key and value for consistency. See documentation for the #pod C method for details on the encoding. #pod #pod The URL must have unsafe characters escaped and international domain names #pod encoded. See C for valid options and a description of the response. #pod Any C header or content in the options hashref will be ignored. #pod #pod The C field of the response will be true if the status code is 2XX. #pod #pod =cut sub post_form { my ($self, $url, $data, $args) = @_; (@_ == 3 || @_ == 4 && ref $args eq 'HASH') or _croak(q/Usage: $http->post_form(URL, DATAREF, [HASHREF])/ . "\n"); my $headers = {}; while ( my ($key, $value) = each %{$args->{headers} || {}} ) { $headers->{lc $key} = $value; } return $self->request('POST', $url, { # Any existing 'headers' key in $args will be overridden with a # normalized version below. %$args, content => $self->www_form_urlencode($data), headers => { %$headers, 'content-type' => 'application/x-www-form-urlencoded' }, } ); } #pod =method mirror #pod #pod $response = $http->mirror($url, $file, \%options) #pod if ( $response->{success} ) { #pod print "$file is up to date\n"; #pod } #pod #pod Executes a C request for the URL and saves the response body to the file #pod name provided. The URL must have unsafe characters escaped and international #pod domain names encoded. If the file already exists, the request will include an #pod C header with the modification timestamp of the file. You #pod may specify a different C header yourself in the C<< #pod $options->{headers} >> hash. #pod #pod The C field of the response will be true if the status code is 2XX #pod or if the status code is 304 (unmodified). #pod #pod If the file was modified and the server response includes a properly #pod formatted C header, the file modification time will #pod be updated accordingly. #pod #pod =cut sub mirror { my ($self, $url, $file, $args) = @_; @_ == 3 || (@_ == 4 && ref $args eq 'HASH') or _croak(q/Usage: $http->mirror(URL, FILE, [HASHREF])/ . "\n"); if ( exists $args->{headers} ) { my $headers = {}; while ( my ($key, $value) = each %{$args->{headers} || {}} ) { $headers->{lc $key} = $value; } $args->{headers} = $headers; } if ( -e $file and my $mtime = (stat($file))[9] ) { $args->{headers}{'if-modified-since'} ||= $self->_http_date($mtime); } my $tempfile = $file . int(rand(2**31)); require Fcntl; sysopen my $fh, $tempfile, Fcntl::O_CREAT()|Fcntl::O_EXCL()|Fcntl::O_WRONLY() or _croak(qq/Error: Could not create temporary file $tempfile for downloading: $!\n/); binmode $fh; $args->{data_callback} = sub { print {$fh} $_[0] }; my $response = $self->request('GET', $url, $args); close $fh or _croak(qq/Error: Caught error closing temporary file $tempfile: $!\n/); if ( $response->{success} ) { rename $tempfile, $file or _croak(qq/Error replacing $file with $tempfile: $!\n/); my $lm = $response->{headers}{'last-modified'}; if ( $lm and my $mtime = $self->_parse_http_date($lm) ) { utime $mtime, $mtime, $file; } } $response->{success} ||= $response->{status} eq '304'; unlink $tempfile; return $response; } #pod =method request #pod #pod $response = $http->request($method, $url); #pod $response = $http->request($method, $url, \%options); #pod #pod Executes an HTTP request of the given method type ('GET', 'HEAD', 'POST', #pod 'PUT', etc.) on the given URL. The URL must have unsafe characters escaped and #pod international domain names encoded. #pod #pod B: Method names are B per the HTTP/1.1 specification. #pod Don't use C when you really want C. See L for #pod how this applies to redirection. #pod #pod If the URL includes a "user:password" stanza, they will be used for Basic-style #pod authorization headers. (Authorization headers will not be included in a #pod redirected request.) For example: #pod #pod $http->request('GET', 'http://Aladdin:open sesame@example.com/'); #pod #pod If the "user:password" stanza contains reserved characters, they must #pod be percent-escaped: #pod #pod $http->request('GET', 'http://john%40example.com:password@example.com/'); #pod #pod A hashref of options may be appended to modify the request. #pod #pod Valid options are: #pod #pod =for :list #pod * C — #pod A hashref containing headers to include with the request. If the value for #pod a header is an array reference, the header will be output multiple times with #pod each value in the array. These headers over-write any default headers. #pod * C — #pod A scalar to include as the body of the request OR a code reference #pod that will be called iteratively to produce the body of the request #pod * C — #pod A code reference that will be called if it exists to provide a hashref #pod of trailing headers (only used with chunked transfer-encoding) #pod * C — #pod A code reference that will be called for each chunks of the response #pod body received. #pod * C — #pod Override host resolution and force all connections to go only to a #pod specific peer address, regardless of the URL of the request. This will #pod include any redirections! This options should be used with extreme #pod caution (e.g. debugging or very special circumstances). It can be given as #pod either a scalar or a code reference that will receive the hostname and #pod whose response will be taken as the address. #pod #pod The C header is generated from the URL in accordance with RFC 2616. It #pod is a fatal error to specify C in the C option. Other headers #pod may be ignored or overwritten if necessary for transport compliance. #pod #pod If the C option is a code reference, it will be called iteratively #pod to provide the content body of the request. It should return the empty #pod string or undef when the iterator is exhausted. #pod #pod If the C option is the empty string, no C or #pod C headers will be generated. #pod #pod If the C option is provided, it will be called iteratively until #pod the entire response body is received. The first argument will be a string #pod containing a chunk of the response body, the second argument will be the #pod in-progress response hash reference, as described below. (This allows #pod customizing the action of the callback based on the C or C #pod received prior to the content body.) #pod #pod Content data in the request/response is handled as "raw bytes". Any #pod encoding/decoding (with associated headers) are the responsibility of the #pod caller. #pod #pod The C method returns a hashref containing the response. The hashref #pod will have the following keys: #pod #pod =for :list #pod * C — #pod Boolean indicating whether the operation returned a 2XX status code #pod * C — #pod URL that provided the response. This is the URL of the request unless #pod there were redirections, in which case it is the last URL queried #pod in a redirection chain #pod * C — #pod The HTTP status code of the response #pod * C — #pod The response phrase returned by the server #pod * C — #pod The body of the response. If the response does not have any content #pod or if a data callback is provided to consume the response body, #pod this will be the empty string #pod * C — #pod A hashref of header fields. All header field names will be normalized #pod to be lower case. If a header is repeated, the value will be an arrayref; #pod it will otherwise be a scalar string containing the value #pod * C - #pod If this field exists, it is the protocol of the response #pod such as HTTP/1.0 or HTTP/1.1 #pod * C #pod If this field exists, it is an arrayref of response hash references from #pod redirects in the same order that redirections occurred. If it does #pod not exist, then no redirections occurred. #pod #pod On an error during the execution of the request, the C field will #pod contain 599, and the C field will contain the text of the error. #pod #pod =cut my %idempotent = map { $_ => 1 } qw/GET HEAD PUT DELETE OPTIONS TRACE/; sub request { my ($self, $method, $url, $args) = @_; @_ == 3 || (@_ == 4 && ref $args eq 'HASH') or _croak(q/Usage: $http->request(METHOD, URL, [HASHREF])/ . "\n"); $args ||= {}; # we keep some state in this during _request # RFC 2616 Section 8.1.4 mandates a single retry on broken socket my $response; for ( 0 .. 1 ) { $response = eval { $self->_request($method, $url, $args) }; last unless $@ && $idempotent{$method} && $@ =~ m{^(?:Socket closed|Unexpected end|SSL read error)}; } if (my $e = $@) { # maybe we got a response hash thrown from somewhere deep if ( ref $e eq 'HASH' && exists $e->{status} ) { $e->{redirects} = delete $args->{_redirects} if @{ $args->{_redirects} || []}; return $e; } # otherwise, stringify it $e = "$e"; $response = { url => $url, success => q{}, status => 599, reason => 'Internal Exception', content => $e, headers => { 'content-type' => 'text/plain', 'content-length' => length $e, }, ( @{$args->{_redirects} || []} ? (redirects => delete $args->{_redirects}) : () ), }; } return $response; } #pod =method www_form_urlencode #pod #pod $params = $http->www_form_urlencode( $data ); #pod $response = $http->get("http://example.com/query?$params"); #pod #pod This method converts the key/value pairs from a data hash or array reference #pod into a C string. The keys and values from the data #pod reference will be UTF-8 encoded and escaped per RFC 3986. If a value is an #pod array reference, the key will be repeated with each of the values of the array #pod reference. If data is provided as a hash reference, the key/value pairs in the #pod resulting string will be sorted by key and value for consistent ordering. #pod #pod =cut sub www_form_urlencode { my ($self, $data) = @_; (@_ == 2 && ref $data) or _croak(q/Usage: $http->www_form_urlencode(DATAREF)/ . "\n"); (ref $data eq 'HASH' || ref $data eq 'ARRAY') or _croak("form data must be a hash or array reference\n"); my @params = ref $data eq 'HASH' ? %$data : @$data; @params % 2 == 0 or _croak("form data reference must have an even number of terms\n"); my @terms; while( @params ) { my ($key, $value) = splice(@params, 0, 2); _croak("form data keys must not be undef") if !defined($key); if ( ref $value eq 'ARRAY' ) { unshift @params, map { $key => $_ } @$value; } else { push @terms, join("=", map { $self->_uri_escape($_) } $key, $value); } } return join("&", (ref $data eq 'ARRAY') ? (@terms) : (sort @terms) ); } #pod =method can_ssl #pod #pod $ok = HTTP::Tiny->can_ssl; #pod ($ok, $why) = HTTP::Tiny->can_ssl; #pod ($ok, $why) = $http->can_ssl; #pod #pod Indicates if SSL support is available. When called as a class object, it #pod checks for the correct version of L and L. #pod When called as an object methods, if C is true or if C #pod is set in C, it checks that a CA file is available. #pod #pod In scalar context, returns a boolean indicating if SSL is available. #pod In list context, returns the boolean and a (possibly multi-line) string of #pod errors indicating why SSL isn't available. #pod #pod =cut sub can_ssl { my ($self) = @_; my($ok, $reason) = (1, ''); # Need IO::Socket::SSL 1.42 for SSL_create_ctx_callback local @INC = @INC; pop @INC if $INC[-1] eq '.'; unless (eval {require IO::Socket::SSL; IO::Socket::SSL->VERSION(1.42)}) { $ok = 0; $reason .= qq/IO::Socket::SSL 1.42 must be installed for https support\n/; } # Need Net::SSLeay 1.49 for MODE_AUTO_RETRY unless (eval {require Net::SSLeay; Net::SSLeay->VERSION(1.49)}) { $ok = 0; $reason .= qq/Net::SSLeay 1.49 must be installed for https support\n/; } # If an object, check that SSL config lets us get a CA if necessary if ( ref($self) && ( $self->{verify_SSL} || $self->{SSL_options}{SSL_verify_mode} ) ) { my $handle = HTTP::Tiny::Handle->new( SSL_options => $self->{SSL_options}, verify_SSL => $self->{verify_SSL}, ); unless ( eval { $handle->_find_CA_file; 1 } ) { $ok = 0; $reason .= "$@"; } } wantarray ? ($ok, $reason) : $ok; } #pod =method connected #pod #pod $host = $http->connected; #pod ($host, $port) = $http->connected; #pod #pod Indicates if a connection to a peer is being kept alive, per the C #pod option. #pod #pod In scalar context, returns the peer host and port, joined with a colon, or #pod C (if no peer is connected). #pod In list context, returns the peer host and port or an empty list (if no peer #pod is connected). #pod #pod B: This method cannot reliably be used to discover whether the remote #pod host has closed its end of the socket. #pod #pod =cut sub connected { my ($self) = @_; if ( $self->{handle} ) { return $self->{handle}->connected; } return; } #--------------------------------------------------------------------------# # private methods #--------------------------------------------------------------------------# my %DefaultPort = ( http => 80, https => 443, ); sub _agent { my $class = ref($_[0]) || $_[0]; (my $default_agent = $class) =~ s{::}{-}g; my $version = $class->VERSION; $default_agent .= "/$version" if defined $version; return $default_agent; } sub _request { my ($self, $method, $url, $args) = @_; my ($scheme, $host, $port, $path_query, $auth) = $self->_split_url($url); if ($scheme ne 'http' && $scheme ne 'https') { die(qq/Unsupported URL scheme '$scheme'\n/); } my $request = { method => $method, scheme => $scheme, host => $host, port => $port, host_port => ($port == $DefaultPort{$scheme} ? $host : "$host:$port"), uri => $path_query, headers => {}, }; my $peer = $args->{peer} || $host; # Allow 'peer' to be a coderef. if ('CODE' eq ref $peer) { $peer = $peer->($host); } # We remove the cached handle so it is not reused in the case of redirect. # If all is well, it will be recached at the end of _request. We only # reuse for the same scheme, host and port my $handle = delete $self->{handle}; if ( $handle ) { unless ( $handle->can_reuse( $scheme, $host, $port, $peer ) ) { $handle->close; undef $handle; } } $handle ||= $self->_open_handle( $request, $scheme, $host, $port, $peer ); $self->_prepare_headers_and_cb($request, $args, $url, $auth); $handle->write_request($request); my $response; do { $response = $handle->read_response_header } until (substr($response->{status},0,1) ne '1'); $self->_update_cookie_jar( $url, $response ) if $self->{cookie_jar}; my @redir_args = $self->_maybe_redirect($request, $response, $args); my $known_message_length; if ($method eq 'HEAD' || $response->{status} =~ /^[23]04/) { # response has no message body $known_message_length = 1; } else { # Ignore any data callbacks during redirection. my $cb_args = @redir_args ? +{} : $args; my $data_cb = $self->_prepare_data_cb($response, $cb_args); $known_message_length = $handle->read_body($data_cb, $response); } if ( $self->{keep_alive} && $handle->connected && $known_message_length && $response->{protocol} eq 'HTTP/1.1' && ($response->{headers}{connection} || '') ne 'close' ) { $self->{handle} = $handle; } else { $handle->close; } $response->{success} = substr( $response->{status}, 0, 1 ) eq '2'; $response->{url} = $url; # Push the current response onto the stack of redirects if redirecting. if (@redir_args) { push @{$args->{_redirects}}, $response; return $self->_request(@redir_args, $args); } # Copy the stack of redirects into the response before returning. $response->{redirects} = delete $args->{_redirects} if @{$args->{_redirects}}; return $response; } sub _open_handle { my ($self, $request, $scheme, $host, $port, $peer) = @_; my $handle = HTTP::Tiny::Handle->new( timeout => $self->{timeout}, SSL_options => $self->{SSL_options}, verify_SSL => $self->{verify_SSL}, local_address => $self->{local_address}, keep_alive => $self->{keep_alive} ); if ($self->{_has_proxy}{$scheme} && ! grep { $host =~ /\Q$_\E$/ } @{$self->{no_proxy}}) { return $self->_proxy_connect( $request, $handle ); } else { return $handle->connect($scheme, $host, $port, $peer); } } sub _proxy_connect { my ($self, $request, $handle) = @_; my @proxy_vars; if ( $request->{scheme} eq 'https' ) { _croak(qq{No https_proxy defined}) unless $self->{https_proxy}; @proxy_vars = $self->_split_proxy( https_proxy => $self->{https_proxy} ); if ( $proxy_vars[0] eq 'https' ) { _croak(qq{Can't proxy https over https: $request->{uri} via $self->{https_proxy}}); } } else { _croak(qq{No http_proxy defined}) unless $self->{http_proxy}; @proxy_vars = $self->_split_proxy( http_proxy => $self->{http_proxy} ); } my ($p_scheme, $p_host, $p_port, $p_auth) = @proxy_vars; if ( length $p_auth && ! defined $request->{headers}{'proxy-authorization'} ) { $self->_add_basic_auth_header( $request, 'proxy-authorization' => $p_auth ); } $handle->connect($p_scheme, $p_host, $p_port, $p_host); if ($request->{scheme} eq 'https') { $self->_create_proxy_tunnel( $request, $handle ); } else { # non-tunneled proxy requires absolute URI $request->{uri} = "$request->{scheme}://$request->{host_port}$request->{uri}"; } return $handle; } sub _split_proxy { my ($self, $type, $proxy) = @_; my ($scheme, $host, $port, $path_query, $auth) = eval { $self->_split_url($proxy) }; unless( defined($scheme) && length($scheme) && length($host) && length($port) && $path_query eq '/' ) { _croak(qq{$type URL must be in format http[s]://[auth@]:/\n}); } return ($scheme, $host, $port, $auth); } sub _create_proxy_tunnel { my ($self, $request, $handle) = @_; $handle->_assert_ssl; my $agent = exists($request->{headers}{'user-agent'}) ? $request->{headers}{'user-agent'} : $self->{agent}; my $connect_request = { method => 'CONNECT', uri => "$request->{host}:$request->{port}", headers => { host => "$request->{host}:$request->{port}", 'user-agent' => $agent, } }; if ( $request->{headers}{'proxy-authorization'} ) { $connect_request->{headers}{'proxy-authorization'} = delete $request->{headers}{'proxy-authorization'}; } $handle->write_request($connect_request); my $response; do { $response = $handle->read_response_header } until (substr($response->{status},0,1) ne '1'); # if CONNECT failed, throw the response so it will be # returned from the original request() method; unless (substr($response->{status},0,1) eq '2') { die $response; } # tunnel established, so start SSL handshake $handle->start_ssl( $request->{host} ); return; } sub _prepare_headers_and_cb { my ($self, $request, $args, $url, $auth) = @_; for ($self->{default_headers}, $args->{headers}) { next unless defined; while (my ($k, $v) = each %$_) { $request->{headers}{lc $k} = $v; $request->{header_case}{lc $k} = $k; } } if (exists $request->{headers}{'host'}) { die(qq/The 'Host' header must not be provided as header option\n/); } $request->{headers}{'host'} = $request->{host_port}; $request->{headers}{'user-agent'} ||= $self->{agent}; $request->{headers}{'connection'} = "close" unless $self->{keep_alive}; # Some servers error on an empty-body PUT/POST without a content-length if ( $request->{method} eq 'PUT' || $request->{method} eq 'POST' ) { if (!defined($args->{content}) || !length($args->{content}) ) { $request->{headers}{'content-length'} = 0; } } if ( defined $args->{content} ) { if ( ref $args->{content} eq 'CODE' ) { if ( exists $request->{'content-length'} && $request->{'content-length'} == 0 ) { $request->{cb} = sub { "" }; } else { $request->{headers}{'content-type'} ||= "application/octet-stream"; $request->{headers}{'transfer-encoding'} = 'chunked' unless exists $request->{headers}{'content-length'} || $request->{headers}{'transfer-encoding'}; $request->{cb} = $args->{content}; } } elsif ( length $args->{content} ) { my $content = $args->{content}; if ( $] ge '5.008' ) { utf8::downgrade($content, 1) or die(qq/Wide character in request message body\n/); } $request->{headers}{'content-type'} ||= "application/octet-stream"; $request->{headers}{'content-length'} = length $content unless $request->{headers}{'content-length'} || $request->{headers}{'transfer-encoding'}; $request->{cb} = sub { substr $content, 0, length $content, '' }; } $request->{trailer_cb} = $args->{trailer_callback} if ref $args->{trailer_callback} eq 'CODE'; } ### If we have a cookie jar, then maybe add relevant cookies if ( $self->{cookie_jar} ) { my $cookies = $self->cookie_jar->cookie_header( $url ); $request->{headers}{cookie} = $cookies if length $cookies; } # if we have Basic auth parameters, add them if ( length $auth && ! defined $request->{headers}{authorization} ) { $self->_add_basic_auth_header( $request, 'authorization' => $auth ); } return; } sub _add_basic_auth_header { my ($self, $request, $header, $auth) = @_; require MIME::Base64; $request->{headers}{$header} = "Basic " . MIME::Base64::encode_base64($auth, ""); return; } sub _prepare_data_cb { my ($self, $response, $args) = @_; my $data_cb = $args->{data_callback}; $response->{content} = ''; if (!$data_cb || $response->{status} !~ /^2/) { if (defined $self->{max_size}) { $data_cb = sub { $_[1]->{content} .= $_[0]; die(qq/Size of response body exceeds the maximum allowed of $self->{max_size}\n/) if length $_[1]->{content} > $self->{max_size}; }; } else { $data_cb = sub { $_[1]->{content} .= $_[0] }; } } return $data_cb; } sub _update_cookie_jar { my ($self, $url, $response) = @_; my $cookies = $response->{headers}->{'set-cookie'}; return unless defined $cookies; my @cookies = ref $cookies ? @$cookies : $cookies; $self->cookie_jar->add( $url, $_ ) for @cookies; return; } sub _validate_cookie_jar { my ($class, $jar) = @_; # duck typing for my $method ( qw/add cookie_header/ ) { _croak(qq/Cookie jar must provide the '$method' method\n/) unless ref($jar) && ref($jar)->can($method); } return; } sub _maybe_redirect { my ($self, $request, $response, $args) = @_; my $headers = $response->{headers}; my ($status, $method) = ($response->{status}, $request->{method}); $args->{_redirects} ||= []; if (($status eq '303' or ($status =~ /^30[1278]/ && $method =~ /^GET|HEAD$/)) and $headers->{location} and @{$args->{_redirects}} < $self->{max_redirect} ) { my $location = ($headers->{location} =~ /^\//) ? "$request->{scheme}://$request->{host_port}$headers->{location}" : $headers->{location} ; return (($status eq '303' ? 'GET' : $method), $location); } return; } sub _split_url { my $url = pop; # URI regex adapted from the URI module my ($scheme, $host, $path_query) = $url =~ m<\A([^:/?#]+)://([^/?#]*)([^#]*)> or die(qq/Cannot parse URL: '$url'\n/); $scheme = lc $scheme; $path_query = "/$path_query" unless $path_query =~ m<\A/>; my $auth = ''; if ( (my $i = index $host, '@') != -1 ) { # user:pass@host $auth = substr $host, 0, $i, ''; # take up to the @ for auth substr $host, 0, 1, ''; # knock the @ off the host # userinfo might be percent escaped, so recover real auth info $auth =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg; } my $port = $host =~ s/:(\d*)\z// && length $1 ? $1 : $scheme eq 'http' ? 80 : $scheme eq 'https' ? 443 : undef; return ($scheme, (length $host ? lc $host : "localhost") , $port, $path_query, $auth); } # Date conversions adapted from HTTP::Date my $DoW = "Sun|Mon|Tue|Wed|Thu|Fri|Sat"; my $MoY = "Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec"; sub _http_date { my ($sec, $min, $hour, $mday, $mon, $year, $wday) = gmtime($_[1]); return sprintf("%s, %02d %s %04d %02d:%02d:%02d GMT", substr($DoW,$wday*4,3), $mday, substr($MoY,$mon*4,3), $year+1900, $hour, $min, $sec ); } sub _parse_http_date { my ($self, $str) = @_; require Time::Local; my @tl_parts; if ($str =~ /^[SMTWF][a-z]+, +(\d{1,2}) ($MoY) +(\d\d\d\d) +(\d\d):(\d\d):(\d\d) +GMT$/) { @tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3); } elsif ($str =~ /^[SMTWF][a-z]+, +(\d\d)-($MoY)-(\d{2,4}) +(\d\d):(\d\d):(\d\d) +GMT$/ ) { @tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3); } elsif ($str =~ /^[SMTWF][a-z]+ +($MoY) +(\d{1,2}) +(\d\d):(\d\d):(\d\d) +(?:[^0-9]+ +)?(\d\d\d\d)$/ ) { @tl_parts = ($5, $4, $3, $2, (index($MoY,$1)/4), $6); } return eval { my $t = @tl_parts ? Time::Local::timegm(@tl_parts) : -1; $t < 0 ? undef : $t; }; } # URI escaping adapted from URI::Escape # c.f. http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1 # perl 5.6 ready UTF-8 encoding adapted from JSON::PP my %escapes = map { chr($_) => sprintf("%%%02X", $_) } 0..255; $escapes{' '}="+"; my $unsafe_char = qr/[^A-Za-z0-9\-\._~]/; sub _uri_escape { my ($self, $str) = @_; return "" if !defined $str; if ( $] ge '5.008' ) { utf8::encode($str); } else { $str = pack("U*", unpack("C*", $str)) # UTF-8 encode a byte string if ( length $str == do { use bytes; length $str } ); $str = pack("C*", unpack("C*", $str)); # clear UTF-8 flag } $str =~ s/($unsafe_char)/$escapes{$1}/g; return $str; } package HTTP::Tiny::Handle; # hide from PAUSE/indexers use strict; use warnings; use Errno qw[EINTR EPIPE]; use IO::Socket qw[SOCK_STREAM]; use Socket qw[SOL_SOCKET SO_KEEPALIVE]; # PERL_HTTP_TINY_IPV4_ONLY is a private environment variable to force old # behavior if someone is unable to boostrap CPAN from a new perl install; it is # not intended for general, per-client use and may be removed in the future my $SOCKET_CLASS = $ENV{PERL_HTTP_TINY_IPV4_ONLY} ? 'IO::Socket::INET' : eval { require IO::Socket::IP; IO::Socket::IP->VERSION(0.32) } ? 'IO::Socket::IP' : 'IO::Socket::INET'; sub BUFSIZE () { 32768 } ## no critic my $Printable = sub { local $_ = shift; s/\r/\\r/g; s/\n/\\n/g; s/\t/\\t/g; s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge; $_; }; my $Token = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]/; my $Field_Content = qr/[[:print:]]+ (?: [\x20\x09]+ [[:print:]]+ )*/x; sub new { my ($class, %args) = @_; return bless { rbuf => '', timeout => 60, max_line_size => 16384, max_header_lines => 64, verify_SSL => HTTP::Tiny::_verify_SSL_default(), SSL_options => {}, %args }, $class; } sub timeout { my ($self, $timeout) = @_; if ( @_ > 1 ) { $self->{timeout} = $timeout; if ( $self->{fh} && $self->{fh}->can('timeout') ) { $self->{fh}->timeout($timeout); } } return $self->{timeout}; } sub connect { @_ == 5 || die(q/Usage: $handle->connect(scheme, host, port, peer)/ . "\n"); my ($self, $scheme, $host, $port, $peer) = @_; if ( $scheme eq 'https' ) { $self->_assert_ssl; } $self->{fh} = $SOCKET_CLASS->new( PeerHost => $peer, PeerPort => $port, $self->{local_address} ? ( LocalAddr => $self->{local_address} ) : (), Proto => 'tcp', Type => SOCK_STREAM, Timeout => $self->{timeout}, ) or die(qq/Could not connect to '$host:$port': $@\n/); binmode($self->{fh}) or die(qq/Could not binmode() socket: '$!'\n/); if ( $self->{keep_alive} ) { unless ( defined( $self->{fh}->setsockopt( SOL_SOCKET, SO_KEEPALIVE, 1 ) ) ) { CORE::close($self->{fh}); die(qq/Could not set SO_KEEPALIVE on socket: '$!'\n/); } } $self->start_ssl($host) if $scheme eq 'https'; $self->{scheme} = $scheme; $self->{host} = $host; $self->{peer} = $peer; $self->{port} = $port; $self->{pid} = $$; $self->{tid} = _get_tid(); return $self; } sub connected { my ($self) = @_; if ( $self->{fh} && $self->{fh}->connected ) { return wantarray ? ( $self->{fh}->peerhost, $self->{fh}->peerport ) : join( ':', $self->{fh}->peerhost, $self->{fh}->peerport ); } return; } sub start_ssl { my ($self, $host) = @_; # As this might be used via CONNECT after an SSL session # to a proxy, we shut down any existing SSL before attempting # the handshake if ( ref($self->{fh}) eq 'IO::Socket::SSL' ) { unless ( $self->{fh}->stop_SSL ) { my $ssl_err = IO::Socket::SSL->errstr; die(qq/Error halting prior SSL connection: $ssl_err/); } } my $ssl_args = $self->_ssl_args($host); IO::Socket::SSL->start_SSL( $self->{fh}, %$ssl_args, SSL_create_ctx_callback => sub { my $ctx = shift; Net::SSLeay::CTX_set_mode($ctx, Net::SSLeay::MODE_AUTO_RETRY()); }, ); unless ( ref($self->{fh}) eq 'IO::Socket::SSL' ) { my $ssl_err = IO::Socket::SSL->errstr; die(qq/SSL connection failed for $host: $ssl_err\n/); } } sub close { @_ == 1 || die(q/Usage: $handle->close()/ . "\n"); my ($self) = @_; CORE::close($self->{fh}) or die(qq/Could not close socket: '$!'\n/); } sub write { @_ == 2 || die(q/Usage: $handle->write(buf)/ . "\n"); my ($self, $buf) = @_; if ( $] ge '5.008' ) { utf8::downgrade($buf, 1) or die(qq/Wide character in write()\n/); } my $len = length $buf; my $off = 0; local $SIG{PIPE} = 'IGNORE'; while () { $self->can_write or die(qq/Timed out while waiting for socket to become ready for writing\n/); my $r = syswrite($self->{fh}, $buf, $len, $off); if (defined $r) { $len -= $r; $off += $r; last unless $len > 0; } elsif ($! == EPIPE) { die(qq/Socket closed by remote server: $!\n/); } elsif ($! != EINTR) { if ($self->{fh}->can('errstr')){ my $err = $self->{fh}->errstr(); die (qq/Could not write to SSL socket: '$err'\n /); } else { die(qq/Could not write to socket: '$!'\n/); } } } return $off; } sub read { @_ == 2 || @_ == 3 || die(q/Usage: $handle->read(len [, allow_partial])/ . "\n"); my ($self, $len, $allow_partial) = @_; my $buf = ''; my $got = length $self->{rbuf}; if ($got) { my $take = ($got < $len) ? $got : $len; $buf = substr($self->{rbuf}, 0, $take, ''); $len -= $take; } # Ignore SIGPIPE because SSL reads can result in writes that might error. # See "Expecting exactly the same behavior as plain sockets" in # https://metacpan.org/dist/IO-Socket-SSL/view/lib/IO/Socket/SSL.pod#Common-Usage-Errors local $SIG{PIPE} = 'IGNORE'; while ($len > 0) { $self->can_read or die(q/Timed out while waiting for socket to become ready for reading/ . "\n"); my $r = sysread($self->{fh}, $buf, $len, length $buf); if (defined $r) { last unless $r; $len -= $r; } elsif ($! != EINTR) { if ($self->{fh}->can('errstr')){ my $err = $self->{fh}->errstr(); die (qq/Could not read from SSL socket: '$err'\n /); } else { die(qq/Could not read from socket: '$!'\n/); } } } if ($len && !$allow_partial) { die(qq/Unexpected end of stream\n/); } return $buf; } sub readline { @_ == 1 || die(q/Usage: $handle->readline()/ . "\n"); my ($self) = @_; while () { if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) { return $1; } if (length $self->{rbuf} >= $self->{max_line_size}) { die(qq/Line size exceeds the maximum allowed size of $self->{max_line_size}\n/); } $self->can_read or die(qq/Timed out while waiting for socket to become ready for reading\n/); my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf}); if (defined $r) { last unless $r; } elsif ($! != EINTR) { if ($self->{fh}->can('errstr')){ my $err = $self->{fh}->errstr(); die (qq/Could not read from SSL socket: '$err'\n /); } else { die(qq/Could not read from socket: '$!'\n/); } } } die(qq/Unexpected end of stream while looking for line\n/); } sub read_header_lines { @_ == 1 || @_ == 2 || die(q/Usage: $handle->read_header_lines([headers])/ . "\n"); my ($self, $headers) = @_; $headers ||= {}; my $lines = 0; my $val; while () { my $line = $self->readline; if (++$lines >= $self->{max_header_lines}) { die(qq/Header lines exceeds maximum number allowed of $self->{max_header_lines}\n/); } elsif ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) { my ($field_name) = lc $1; if (exists $headers->{$field_name}) { for ($headers->{$field_name}) { $_ = [$_] unless ref $_ eq "ARRAY"; push @$_, $2; $val = \$_->[-1]; } } else { $val = \($headers->{$field_name} = $2); } } elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) { $val or die(qq/Unexpected header continuation line\n/); next unless length $1; $$val .= ' ' if length $$val; $$val .= $1; } elsif ($line =~ /\A \x0D?\x0A \z/x) { last; } else { die(q/Malformed header line: / . $Printable->($line) . "\n"); } } return $headers; } sub write_request { @_ == 2 || die(q/Usage: $handle->write_request(request)/ . "\n"); my($self, $request) = @_; $self->write_request_header(@{$request}{qw/method uri headers header_case/}); $self->write_body($request) if $request->{cb}; return; } # Standard request header names/case from HTTP/1.1 RFCs my @rfc_request_headers = qw( Accept Accept-Charset Accept-Encoding Accept-Language Authorization Cache-Control Connection Content-Length Expect From Host If-Match If-Modified-Since If-None-Match If-Range If-Unmodified-Since Max-Forwards Pragma Proxy-Authorization Range Referer TE Trailer Transfer-Encoding Upgrade User-Agent Via ); my @other_request_headers = qw( Content-Encoding Content-MD5 Content-Type Cookie DNT Date Origin X-XSS-Protection ); my %HeaderCase = map { lc($_) => $_ } @rfc_request_headers, @other_request_headers; # to avoid multiple small writes and hence nagle, you can pass the method line or anything else to # combine writes. sub write_header_lines { (@_ >= 2 && @_ <= 4 && ref $_[1] eq 'HASH') || die(q/Usage: $handle->write_header_lines(headers, [header_case, prefix])/ . "\n"); my($self, $headers, $header_case, $prefix_data) = @_; $header_case ||= {}; my $buf = (defined $prefix_data ? $prefix_data : ''); # Per RFC, control fields should be listed first my %seen; for my $k ( qw/host cache-control expect max-forwards pragma range te/ ) { next unless exists $headers->{$k}; $seen{$k}++; my $field_name = $HeaderCase{$k}; my $v = $headers->{$k}; for (ref $v eq 'ARRAY' ? @$v : $v) { $_ = '' unless defined $_; $buf .= "$field_name: $_\x0D\x0A"; } } # Other headers sent in arbitrary order while (my ($k, $v) = each %$headers) { my $field_name = lc $k; next if $seen{$field_name}; if (exists $HeaderCase{$field_name}) { $field_name = $HeaderCase{$field_name}; } else { if (exists $header_case->{$field_name}) { $field_name = $header_case->{$field_name}; } else { $field_name =~ s/\b(\w)/\u$1/g; } $field_name =~ /\A $Token+ \z/xo or die(q/Invalid HTTP header field name: / . $Printable->($field_name) . "\n"); $HeaderCase{lc $field_name} = $field_name; } for (ref $v eq 'ARRAY' ? @$v : $v) { # unwrap a field value if pre-wrapped by user s/\x0D?\x0A\s+/ /g; die(qq/Invalid HTTP header field value ($field_name): / . $Printable->($_). "\n") unless $_ eq '' || /\A $Field_Content \z/xo; $_ = '' unless defined $_; $buf .= "$field_name: $_\x0D\x0A"; } } $buf .= "\x0D\x0A"; return $self->write($buf); } # return value indicates whether message length was defined; this is generally # true unless there was no content-length header and we just read until EOF. # Other message length errors are thrown as exceptions sub read_body { @_ == 3 || die(q/Usage: $handle->read_body(callback, response)/ . "\n"); my ($self, $cb, $response) = @_; my $te = $response->{headers}{'transfer-encoding'} || ''; my $chunked = grep { /chunked/i } ( ref $te eq 'ARRAY' ? @$te : $te ) ; return $chunked ? $self->read_chunked_body($cb, $response) : $self->read_content_body($cb, $response); } sub write_body { @_ == 2 || die(q/Usage: $handle->write_body(request)/ . "\n"); my ($self, $request) = @_; if (exists $request->{headers}{'content-length'}) { return unless $request->{headers}{'content-length'}; return $self->write_content_body($request); } else { return $self->write_chunked_body($request); } } sub read_content_body { @_ == 3 || @_ == 4 || die(q/Usage: $handle->read_content_body(callback, response, [read_length])/ . "\n"); my ($self, $cb, $response, $content_length) = @_; $content_length ||= $response->{headers}{'content-length'}; if ( defined $content_length ) { my $len = $content_length; while ($len > 0) { my $read = ($len > BUFSIZE) ? BUFSIZE : $len; $cb->($self->read($read, 0), $response); $len -= $read; } return length($self->{rbuf}) == 0; } my $chunk; $cb->($chunk, $response) while length( $chunk = $self->read(BUFSIZE, 1) ); return; } sub write_content_body { @_ == 2 || die(q/Usage: $handle->write_content_body(request)/ . "\n"); my ($self, $request) = @_; my ($len, $content_length) = (0, $request->{headers}{'content-length'}); while () { my $data = $request->{cb}->(); defined $data && length $data or last; if ( $] ge '5.008' ) { utf8::downgrade($data, 1) or die(qq/Wide character in write_content()\n/); } $len += $self->write($data); } $len == $content_length or die(qq/Content-Length mismatch (got: $len expected: $content_length)\n/); return $len; } sub read_chunked_body { @_ == 3 || die(q/Usage: $handle->read_chunked_body(callback, $response)/ . "\n"); my ($self, $cb, $response) = @_; while () { my $head = $self->readline; $head =~ /\A ([A-Fa-f0-9]+)/x or die(q/Malformed chunk head: / . $Printable->($head) . "\n"); my $len = hex($1) or last; $self->read_content_body($cb, $response, $len); $self->read(2) eq "\x0D\x0A" or die(qq/Malformed chunk: missing CRLF after chunk data\n/); } $self->read_header_lines($response->{headers}); return 1; } sub write_chunked_body { @_ == 2 || die(q/Usage: $handle->write_chunked_body(request)/ . "\n"); my ($self, $request) = @_; my $len = 0; while () { my $data = $request->{cb}->(); defined $data && length $data or last; if ( $] ge '5.008' ) { utf8::downgrade($data, 1) or die(qq/Wide character in write_chunked_body()\n/); } $len += length $data; my $chunk = sprintf '%X', length $data; $chunk .= "\x0D\x0A"; $chunk .= $data; $chunk .= "\x0D\x0A"; $self->write($chunk); } $self->write("0\x0D\x0A"); if ( ref $request->{trailer_cb} eq 'CODE' ) { $self->write_header_lines($request->{trailer_cb}->()) } else { $self->write("\x0D\x0A"); } return $len; } sub read_response_header { @_ == 1 || die(q/Usage: $handle->read_response_header()/ . "\n"); my ($self) = @_; my $line = $self->readline; $line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) (?: [\x09\x20]+ ([^\x0D\x0A]*) )? \x0D?\x0A/x or die(q/Malformed Status-Line: / . $Printable->($line). "\n"); my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4); $reason = "" unless defined $reason; die (qq/Unsupported HTTP protocol: $protocol\n/) unless $version =~ /0*1\.0*[01]/; return { status => $status, reason => $reason, headers => $self->read_header_lines, protocol => $protocol, }; } sub write_request_header { @_ == 5 || die(q/Usage: $handle->write_request_header(method, request_uri, headers, header_case)/ . "\n"); my ($self, $method, $request_uri, $headers, $header_case) = @_; return $self->write_header_lines($headers, $header_case, "$method $request_uri HTTP/1.1\x0D\x0A"); } sub _do_timeout { my ($self, $type, $timeout) = @_; $timeout = $self->{timeout} unless defined $timeout && $timeout >= 0; my $fd = fileno $self->{fh}; defined $fd && $fd >= 0 or die(qq/select(2): 'Bad file descriptor'\n/); my $initial = time; my $pending = $timeout; my $nfound; vec(my $fdset = '', $fd, 1) = 1; while () { $nfound = ($type eq 'read') ? select($fdset, undef, undef, $pending) : select(undef, $fdset, undef, $pending) ; if ($nfound == -1) { $! == EINTR or die(qq/select(2): '$!'\n/); redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0; $nfound = 0; } last; } $! = 0; return $nfound; } sub can_read { @_ == 1 || @_ == 2 || die(q/Usage: $handle->can_read([timeout])/ . "\n"); my $self = shift; if ( ref($self->{fh}) eq 'IO::Socket::SSL' ) { return 1 if $self->{fh}->pending; } return $self->_do_timeout('read', @_) } sub can_write { @_ == 1 || @_ == 2 || die(q/Usage: $handle->can_write([timeout])/ . "\n"); my $self = shift; return $self->_do_timeout('write', @_) } sub _assert_ssl { my($ok, $reason) = HTTP::Tiny->can_ssl(); die $reason unless $ok; } sub can_reuse { my ($self,$scheme,$host,$port,$peer) = @_; return 0 if $self->{pid} != $$ || $self->{tid} != _get_tid() || length($self->{rbuf}) || $scheme ne $self->{scheme} || $host ne $self->{host} || $port ne $self->{port} || $peer ne $self->{peer} || eval { $self->can_read(0) } || $@ ; return 1; } # Try to find a CA bundle to validate the SSL cert, # prefer Mozilla::CA or fallback to a system file sub _find_CA_file { my $self = shift(); my $ca_file = defined( $self->{SSL_options}->{SSL_ca_file} ) ? $self->{SSL_options}->{SSL_ca_file} : $ENV{SSL_CERT_FILE}; if ( defined $ca_file ) { unless ( -r $ca_file ) { die qq/SSL_ca_file '$ca_file' not found or not readable\n/; } return $ca_file; } local @INC = @INC; pop @INC if $INC[-1] eq '.'; return Mozilla::CA::SSL_ca_file() if eval { require Mozilla::CA; 1 }; # cert list copied from golang src/crypto/x509/root_unix.go foreach my $ca_bundle ( "/etc/ssl/certs/ca-certificates.crt", # Debian/Ubuntu/Gentoo etc. "/etc/pki/tls/certs/ca-bundle.crt", # Fedora/RHEL "/etc/ssl/ca-bundle.pem", # OpenSUSE "/etc/openssl/certs/ca-certificates.crt", # NetBSD "/etc/ssl/cert.pem", # OpenBSD "/usr/local/share/certs/ca-root-nss.crt", # FreeBSD/DragonFly "/etc/pki/tls/cacert.pem", # OpenELEC "/etc/certs/ca-certificates.crt", # Solaris 11.2+ ) { return $ca_bundle if -e $ca_bundle; } die qq/Couldn't find a CA bundle with which to verify the SSL certificate.\n/ . qq/Try installing Mozilla::CA from CPAN\n/; } # for thread safety, we need to know thread id if threads are loaded sub _get_tid { no warnings 'reserved'; # for 'threads' return threads->can("tid") ? threads->tid : 0; } sub _ssl_args { my ($self, $host) = @_; my %ssl_args; # This test reimplements IO::Socket::SSL::can_client_sni(), which wasn't # added until IO::Socket::SSL 1.84 if ( Net::SSLeay::OPENSSL_VERSION_NUMBER() >= 0x01000000 ) { $ssl_args{SSL_hostname} = $host, # Sane SNI support } if ($self->{verify_SSL}) { $ssl_args{SSL_verifycn_scheme} = 'http'; # enable CN validation $ssl_args{SSL_verifycn_name} = $host; # set validation hostname $ssl_args{SSL_verify_mode} = 0x01; # enable cert validation $ssl_args{SSL_ca_file} = $self->_find_CA_file; } else { $ssl_args{SSL_verifycn_scheme} = 'none'; # disable CN validation $ssl_args{SSL_verify_mode} = 0x00; # disable cert validation } # user options override settings from verify_SSL for my $k ( keys %{$self->{SSL_options}} ) { $ssl_args{$k} = $self->{SSL_options}{$k} if $k =~ m/^SSL_/; } return \%ssl_args; } 1; __END__ =pod =encoding UTF-8 =head1 NAME HTTP::Tiny - A small, simple, correct HTTP/1.1 client =head1 VERSION version 0.088 =head1 SYNOPSIS use HTTP::Tiny; my $response = HTTP::Tiny->new->get('http://example.com/'); die "Failed!\n" unless $response->{success}; print "$response->{status} $response->{reason}\n"; while (my ($k, $v) = each %{$response->{headers}}) { for (ref $v eq 'ARRAY' ? @$v : $v) { print "$k: $_\n"; } } print $response->{content} if length $response->{content}; =head1 DESCRIPTION This is a very simple HTTP/1.1 client, designed for doing simple requests without the overhead of a large framework like L. It is more correct and more complete than L. It supports proxies and redirection. It also correctly resumes after EINTR. If L 0.25 or later is installed, HTTP::Tiny will use it instead of L for transparent support for both IPv4 and IPv6. Cookie support requires L or an equivalent class. =head1 METHODS =head2 new $http = HTTP::Tiny->new( %attributes ); This constructor returns a new HTTP::Tiny object. Valid attributes include: =over 4 =item * C — A user-agent string (defaults to 'HTTP-Tiny/$VERSION'). If C — ends in a space character, the default user-agent string is appended. =item * C — An instance of L — or equivalent class that supports the C and C methods =item * C — A hashref of default headers to apply to requests =item * C — The local IP address to bind to =item * C — Whether to reuse the last connection (if for the same scheme, host and port) (defaults to 1) =item * C — Maximum number of redirects allowed (defaults to 5) =item * C — Maximum response size in bytes (only when not using a data callback). If defined, requests with responses larger than this will return a 599 status code. =item * C — URL of a proxy server to use for HTTP connections (default is C<$ENV{http_proxy}> — if set) =item * C — URL of a proxy server to use for HTTPS connections (default is C<$ENV{https_proxy}> — if set) =item * C — URL of a generic proxy server for both HTTP and HTTPS connections (default is C<$ENV{all_proxy}> — if set) =item * C — List of domain suffixes that should not be proxied. Must be a comma-separated string or an array reference. (default is C<$ENV{no_proxy}> —) =item * C — Request timeout in seconds (default is 60) If a socket open, read or write takes longer than the timeout, the request response status code will be 599. =item * C — A boolean that indicates whether to validate the TLS/SSL certificate of an C — connection (default is true). Changed from false to true in version 0.083. =item * C — A hashref of C — options to pass through to L =item * C<$ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT}> - Changes the default certificate verification behavior to not check server identity if set to 1. Only effective if C is not set. Added in version 0.083. =back An accessor/mutator method exists for each attribute. Passing an explicit C for C, C or C will prevent getting the corresponding proxies from the environment. Errors during request execution will result in a pseudo-HTTP status code of 599 and a reason of "Internal Exception". The content field in the response will contain the text of the error. The C parameter enables a persistent connection, but only to a single destination scheme, host and port. If any connection-relevant attributes are modified via accessor, or if the process ID or thread ID change, the persistent connection will be dropped. If you want persistent connections across multiple destinations, use multiple HTTP::Tiny objects. See L for more on the C and C attributes. =head2 get|head|put|post|patch|delete $response = $http->get($url); $response = $http->get($url, \%options); $response = $http->head($url); These methods are shorthand for calling C for the given method. The URL must have unsafe characters escaped and international domain names encoded. See C for valid options and a description of the response. The C field of the response will be true if the status code is 2XX. =head2 post_form $response = $http->post_form($url, $form_data); $response = $http->post_form($url, $form_data, \%options); This method executes a C request and sends the key/value pairs from a form data hash or array reference to the given URL with a C of C. If data is provided as an array reference, the order is preserved; if provided as a hash reference, the terms are sorted on key and value for consistency. See documentation for the C method for details on the encoding. The URL must have unsafe characters escaped and international domain names encoded. See C for valid options and a description of the response. Any C header or content in the options hashref will be ignored. The C field of the response will be true if the status code is 2XX. =head2 mirror $response = $http->mirror($url, $file, \%options) if ( $response->{success} ) { print "$file is up to date\n"; } Executes a C request for the URL and saves the response body to the file name provided. The URL must have unsafe characters escaped and international domain names encoded. If the file already exists, the request will include an C header with the modification timestamp of the file. You may specify a different C header yourself in the C<< $options->{headers} >> hash. The C field of the response will be true if the status code is 2XX or if the status code is 304 (unmodified). If the file was modified and the server response includes a properly formatted C header, the file modification time will be updated accordingly. =head2 request $response = $http->request($method, $url); $response = $http->request($method, $url, \%options); Executes an HTTP request of the given method type ('GET', 'HEAD', 'POST', 'PUT', etc.) on the given URL. The URL must have unsafe characters escaped and international domain names encoded. B: Method names are B per the HTTP/1.1 specification. Don't use C when you really want C. See L for how this applies to redirection. If the URL includes a "user:password" stanza, they will be used for Basic-style authorization headers. (Authorization headers will not be included in a redirected request.) For example: $http->request('GET', 'http://Aladdin:open sesame@example.com/'); If the "user:password" stanza contains reserved characters, they must be percent-escaped: $http->request('GET', 'http://john%40example.com:password@example.com/'); A hashref of options may be appended to modify the request. Valid options are: =over 4 =item * C — A hashref containing headers to include with the request. If the value for a header is an array reference, the header will be output multiple times with each value in the array. These headers over-write any default headers. =item * C — A scalar to include as the body of the request OR a code reference that will be called iteratively to produce the body of the request =item * C — A code reference that will be called if it exists to provide a hashref of trailing headers (only used with chunked transfer-encoding) =item * C — A code reference that will be called for each chunks of the response body received. =item * C — Override host resolution and force all connections to go only to a specific peer address, regardless of the URL of the request. This will include any redirections! This options should be used with extreme caution (e.g. debugging or very special circumstances). It can be given as either a scalar or a code reference that will receive the hostname and whose response will be taken as the address. =back The C header is generated from the URL in accordance with RFC 2616. It is a fatal error to specify C in the C option. Other headers may be ignored or overwritten if necessary for transport compliance. If the C option is a code reference, it will be called iteratively to provide the content body of the request. It should return the empty string or undef when the iterator is exhausted. If the C option is the empty string, no C or C headers will be generated. If the C option is provided, it will be called iteratively until the entire response body is received. The first argument will be a string containing a chunk of the response body, the second argument will be the in-progress response hash reference, as described below. (This allows customizing the action of the callback based on the C or C received prior to the content body.) Content data in the request/response is handled as "raw bytes". Any encoding/decoding (with associated headers) are the responsibility of the caller. The C method returns a hashref containing the response. The hashref will have the following keys: =over 4 =item * C — Boolean indicating whether the operation returned a 2XX status code =item * C — URL that provided the response. This is the URL of the request unless there were redirections, in which case it is the last URL queried in a redirection chain =item * C — The HTTP status code of the response =item * C — The response phrase returned by the server =item * C — The body of the response. If the response does not have any content or if a data callback is provided to consume the response body, this will be the empty string =item * C — A hashref of header fields. All header field names will be normalized to be lower case. If a header is repeated, the value will be an arrayref; it will otherwise be a scalar string containing the value =item * C - If this field exists, it is the protocol of the response such as HTTP/1.0 or HTTP/1.1 =item * C If this field exists, it is an arrayref of response hash references from redirects in the same order that redirections occurred. If it does not exist, then no redirections occurred. =back On an error during the execution of the request, the C field will contain 599, and the C field will contain the text of the error. =head2 www_form_urlencode $params = $http->www_form_urlencode( $data ); $response = $http->get("http://example.com/query?$params"); This method converts the key/value pairs from a data hash or array reference into a C string. The keys and values from the data reference will be UTF-8 encoded and escaped per RFC 3986. If a value is an array reference, the key will be repeated with each of the values of the array reference. If data is provided as a hash reference, the key/value pairs in the resulting string will be sorted by key and value for consistent ordering. =head2 can_ssl $ok = HTTP::Tiny->can_ssl; ($ok, $why) = HTTP::Tiny->can_ssl; ($ok, $why) = $http->can_ssl; Indicates if SSL support is available. When called as a class object, it checks for the correct version of L and L. When called as an object methods, if C is true or if C is set in C, it checks that a CA file is available. In scalar context, returns a boolean indicating if SSL is available. In list context, returns the boolean and a (possibly multi-line) string of errors indicating why SSL isn't available. =head2 connected $host = $http->connected; ($host, $port) = $http->connected; Indicates if a connection to a peer is being kept alive, per the C option. In scalar context, returns the peer host and port, joined with a colon, or C (if no peer is connected). In list context, returns the peer host and port or an empty list (if no peer is connected). B: This method cannot reliably be used to discover whether the remote host has closed its end of the socket. =for Pod::Coverage SSL_options agent cookie_jar default_headers http_proxy https_proxy keep_alive local_address max_redirect max_size no_proxy proxy timeout verify_SSL =head1 TLS/SSL SUPPORT Direct C connections are supported only if L 1.56 or greater and L 1.49 or greater are installed. An error will occur if new enough versions of these modules are not installed or if the TLS encryption fails. You can also use C utility function that returns boolean to see if the required modules are installed. An C connection may be made via an C proxy that supports the CONNECT command (i.e. RFC 2817). You may not proxy C via a proxy that itself requires C to communicate. TLS/SSL provides two distinct capabilities: =over 4 =item * Encrypted communication channel =item * Verification of server identity =back B. This was changed in version 0.083 due to security concerns. The previous default behavior can be enabled by setting C<$ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT}> to 1. Verification is done by checking that that the TLS/SSL connection has a valid certificate corresponding to the host name of the connection and that the certificate has been verified by a CA. Assuming you trust the CA, this will protect against L. Certificate verification requires a file containing trusted CA certificates. If the environment variable C is present, HTTP::Tiny will try to find a CA certificate file in that location. If the L module is installed, HTTP::Tiny will use the CA file included with it as a source of trusted CA's. If that module is not available, then HTTP::Tiny will search several system-specific default locations for a CA certificate file: =over 4 =item * /etc/ssl/certs/ca-certificates.crt =item * /etc/pki/tls/certs/ca-bundle.crt =item * /etc/ssl/ca-bundle.pem =item * /etc/openssl/certs/ca-certificates.crt =item * /etc/ssl/cert.pem =item * /usr/local/share/certs/ca-root-nss.crt =item * /etc/pki/tls/cacert.pem =item * /etc/certs/ca-certificates.crt =back An error will be occur if C is true and no CA certificate file is available. If you desire complete control over TLS/SSL connections, the C attribute lets you provide a hash reference that will be passed through to C, overriding any options set by HTTP::Tiny. For example, to provide your own trusted CA file: SSL_options => { SSL_ca_file => $file_path, } The C attribute could also be used for such things as providing a client certificate for authentication to a server or controlling the choice of cipher used for the TLS/SSL connection. See L documentation for details. =head1 PROXY SUPPORT HTTP::Tiny can proxy both C and C requests. Only Basic proxy authorization is supported and it must be provided as part of the proxy URL: C. HTTP::Tiny supports the following proxy environment variables: =over 4 =item * http_proxy or HTTP_PROXY =item * https_proxy or HTTPS_PROXY =item * all_proxy or ALL_PROXY =back If the C environment variable is set, then this might be a CGI process and C would be set from the C header, which is a security risk. If C is set, C (the upper case variant only) is ignored, but C is considered instead. Tunnelling C over an C proxy using the CONNECT method is supported. If your proxy uses C itself, you can not tunnel C over it. Be warned that proxying an C connection opens you to the risk of a man-in-the-middle attack by the proxy server. The C environment variable is supported in the format of a comma-separated list of domain extensions proxy should not be used for. Proxy arguments passed to C will override their corresponding environment variables. =head1 LIMITATIONS HTTP::Tiny is I with the L: =over 4 =item * "Message Syntax and Routing" [RFC7230] =item * "Semantics and Content" [RFC7231] =item * "Conditional Requests" [RFC7232] =item * "Range Requests" [RFC7233] =item * "Caching" [RFC7234] =item * "Authentication" [RFC7235] =back It attempts to meet all "MUST" requirements of the specification, but does not implement all "SHOULD" requirements. (Note: it was developed against the earlier RFC 2616 specification and may not yet meet the revised RFC 7230-7235 spec.) Additionally, HTTP::Tiny supports the C method of RFC 5789. Some particular limitations of note include: =over =item * HTTP::Tiny focuses on correct transport. Users are responsible for ensuring that user-defined headers and content are compliant with the HTTP/1.1 specification. =item * Users must ensure that URLs are properly escaped for unsafe characters and that international domain names are properly encoded to ASCII. See L, L and L. =item * Redirection is very strict against the specification. Redirection is only automatic for response codes 301, 302, 307 and 308 if the request method is 'GET' or 'HEAD'. Response code 303 is always converted into a 'GET' redirection, as mandated by the specification. There is no automatic support for status 305 ("Use proxy") redirections. =item * There is no provision for delaying a request body using an C header. Unexpected C<1XX> responses are silently ignored as per the specification. =item * Only 'chunked' C is supported. =item * There is no support for a Request-URI of '*' for the 'OPTIONS' request. =item * Headers mentioned in the RFCs and some other, well-known headers are generated with their canonical case. Other headers are sent in the case provided by the user. Except for control headers (which are sent first), headers are sent in arbitrary order. =back Despite the limitations listed above, HTTP::Tiny is considered feature-complete. New feature requests should be directed to L. =head1 SEE ALSO =over 4 =item * L - Higher level UA features for HTTP::Tiny =item * L - HTTP::Tiny wrapper with L/L compatibility =item * L - Wrap L instance in HTTP::Tiny compatible interface =item * L - Required for IPv6 support =item * L - Required for SSL support =item * L - If HTTP::Tiny isn't enough for you, this is the "standard" way to do things =item * L - Required if you want to validate SSL certificates =item * L - Required for SSL support =back =for :stopwords cpan testmatrix url bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan =head1 SUPPORT =head2 Bugs / Feature Requests Please report any bugs or feature requests through the issue tracker at L. You will be notified automatically of any progress on your issue. =head2 Source Code This is open source software. The code repository is available for public review and contribution under the terms of the license. L git clone https://github.com/Perl-Toolchain-Gang/HTTP-Tiny.git =head1 AUTHORS =over 4 =item * Christian Hansen =item * David Golden =back =head1 CONTRIBUTORS =for stopwords Alan Gardner Alessandro Ghedini A. Sinan Unur Brad Gilbert brian m. carlson Chris Nehren Weyl Claes Jakobsson Clinton Gormley Craig Berry David Golden Mitchell Dean Pearce Edward Zborowski Felipe Gasper Graham Knop Greg Kennedy James E Keenan Raspass Jeremy Mates Jess Robinson Karen Etheridge Lukas Eklund Martin J. Evans Martin-Louis Bright Matthew Horsfall Michael R. Davis Mike Doherty Nicolas Rochelemagne Olaf Alders Olivier Mengué Petr Písař sanjay-cpu Serguei Trouchelle Shoichi Kaji SkyMarshal Sören Kornetzki Steve Grazzini Stig Palmquist Syohei YOSHIDA Tatsuhiko Miyagawa Tom Hukins Tony Cook Xavier Guimard =over 4 =item * Alan Gardner =item * Alessandro Ghedini =item * A. Sinan Unur =item * Brad Gilbert =item * brian m. carlson =item * Chris Nehren =item * Chris Weyl =item * Claes Jakobsson =item * Clinton Gormley =item * Craig A. Berry =item * Craig Berry =item * David Golden =item * David Mitchell =item * Dean Pearce =item * Edward Zborowski =item * Felipe Gasper =item * Graham Knop =item * Greg Kennedy =item * James E Keenan =item * James Raspass =item * Jeremy Mates =item * Jess Robinson =item * Karen Etheridge =item * Lukas Eklund =item * Martin J. Evans =item * Martin-Louis Bright =item * Matthew Horsfall =item * Michael R. Davis =item * Mike Doherty =item * Nicolas Rochelemagne =item * Olaf Alders =item * Olivier Mengué =item * Petr Písař =item * sanjay-cpu =item * Serguei Trouchelle =item * Shoichi Kaji =item * SkyMarshal =item * Sören Kornetzki =item * Steve Grazzini =item * Stig Palmquist =item * Syohei YOSHIDA =item * Tatsuhiko Miyagawa =item * Tom Hukins =item * Tony Cook =item * Xavier Guimard =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2023 by Christian Hansen. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut PKA0]4;{g perl5/lib/core/only.pmnu6$package lib::core::only; use strict; use warnings FATAL => 'all'; use Config; sub import { @INC = @Config{qw(privlibexp archlibexp)}; return } =head1 NAME lib::core::only - Remove all non-core paths from @INC to avoid site/vendor dirs =head1 SYNOPSIS use lib::core::only; # now @INC contains only the two core directories To get only the core directories plus the ones for the local::lib in scope: $ perl -mlocal::lib -Mlib::core::only -Mlocal::lib=~/perl5 myscript.pl To attempt to do a self-contained build (but note this will not reliably propagate into subprocesses, see the CAVEATS below): $ PERL5OPT='-mlocal::lib -Mlib::core::only -Mlocal::lib=~/perl5' cpan Please note that it is necessary to use C twice for this to work. First so that C doesn't prevent C from loading (it's not currently in core) and then again after C so that the local paths are not removed. =head1 DESCRIPTION lib::core::only is simply a shortcut to say "please reduce my @INC to only the core lib and archlib (architecture-specific lib) directories of this perl". You might want to do this to ensure a local::lib contains only the code you need, or to test an L tree, or to avoid known bad vendor packages. You might want to use this to try and install a self-contained tree of perl modules. Be warned that that probably won't work (see L). This module was extracted from L's --self-contained feature, and contains the only part that ever worked. I apologise to anybody who thought anything else did. =head1 CAVEATS This does B propagate properly across perl invocations like local::lib's stuff does. It can't. It's only a module import, so it B. If you want to cascade it across invocations, you can set the PERL5OPT environment variable to '-Mlib::core::only' and it'll sort of work. But be aware that taint mode ignores this, so some modules' build and test code probably will as well. You also need to be aware that perl's command line options are not processed in order - -I options take effect before -M options, so perl -Mlib::core::only -Ilib is unlike to do what you want - it's exactly equivalent to: perl -Mlib::core::only If you want to combine a core-only @INC with additional paths, you need to add the additional paths using -M options and the L module: perl -Mlib::core::only -Mlib=lib # or if you're trying to test compiled code: perl -Mlib::core::only -Mblib For more information on the impossibility of sanely propagating this across module builds without help from the build program, see L - and for ways to achieve the old --self-contained feature's results, look at L's tree function, and at L's --local-lib-contained feature. =head1 AUTHOR Matt S. Trout =head1 LICENSE This library is free software under the same terms as perl itself. =head1 COPYRIGHT (c) 2010 the lib::core::only L as specified above. =cut 1; PKA0] perl5/Expect.pmnu7m# -*-cperl-*- # This module is copyrighted as per the usual perl legalese: # Copyright (c) 1997 Austin Schutz. # expect() interface & functionality enhancements (c) 1999 Roland Giersig. # # All rights reserved. This program is free software; you can # redistribute it and/or modify it under the same terms as Perl # itself. # # Don't blame/flame me if you bust your stuff. # Austin Schutz # # This module now is maintained by # Dave Jacoby # use 5.006; package Expect; use strict; use warnings; use IO::Pty 1.11; # We need make_slave_controlling_terminal() use IO::Tty; use POSIX qw(:sys_wait_h :unistd_h); # For WNOHANG and isatty use Fcntl qw(:DEFAULT); # For checking file handle settings. use Carp qw(cluck croak carp confess); use IO::Handle (); use Exporter qw(import); use Errno; use Scalar::Util qw/ looks_like_number /; # This is necessary to make routines within Expect work. @Expect::ISA = qw(IO::Pty); @Expect::EXPORT = qw(expect exp_continue exp_continue_timeout); BEGIN { $Expect::VERSION = '1.38'; # These are defaults which may be changed per object, or set as # the user wishes. # This will be unset, since the default behavior differs between # spawned processes and initialized filehandles. # $Expect::Log_Stdout = 1; $Expect::Log_Group = 1; $Expect::Debug = 0; $Expect::Exp_Max_Accum = 0; # unlimited $Expect::Exp_Internal = 0; $Expect::IgnoreEintr = 0; $Expect::Manual_Stty = 0; $Expect::Multiline_Matching = 1; $Expect::Do_Soft_Close = 0; @Expect::Before_List = (); @Expect::After_List = (); %Expect::Spawned_PIDs = (); } sub version { my ($version) = @_; warn "Version $version is later than $Expect::VERSION. It may not be supported" if ( defined($version) && ( $version > $Expect::VERSION ) ); die "Versions before 1.03 are not supported in this release" if ( ( defined($version) ) && ( $version < 1.03 ) ); return $Expect::VERSION; } sub new { my ($class, @args) = @_; $class = ref($class) if ref($class); # so we can be called as $exp->new() # Create the pty which we will use to pass process info. my ($self) = IO::Pty->new; die "$class: Could not assign a pty" unless $self; bless $self => $class; $self->autoflush(1); # This is defined here since the default is different for # initialized handles as opposed to spawned processes. ${*$self}{exp_Log_Stdout} = 1; $self->_init_vars(); if (@args) { # we got add'l parms, so pass them to spawn return $self->spawn(@args); } return $self; } sub timeout { my $self = shift; ${*$self}{expect_timeout} = shift if @_; return ${*$self}{expect_timeout}; } sub spawn { my ($class, @cmd) = @_; # spawn is passed command line args. my $self; if ( ref($class) ) { $self = $class; } else { $self = $class->new(); } croak "Cannot reuse an object with an already spawned command" if exists ${*$self}{"exp_Command"}; ${*$self}{"exp_Command"} = \@cmd; # set up pipe to detect childs exec error pipe( FROM_CHILD, TO_PARENT ) or die "Cannot open pipe: $!"; pipe( FROM_PARENT, TO_CHILD ) or die "Cannot open pipe: $!"; TO_PARENT->autoflush(1); TO_CHILD->autoflush(1); eval { fcntl( TO_PARENT, Fcntl::F_SETFD, Fcntl::FD_CLOEXEC ); }; my $pid = fork; unless ( defined($pid) ) { warn "Cannot fork: $!" if $^W; return; } if ($pid) { # parent my $errno; ${*$self}{exp_Pid} = $pid; close TO_PARENT; close FROM_PARENT; $self->close_slave(); $self->set_raw() if $self->raw_pty and isatty($self); close TO_CHILD; # so child gets EOF and can go ahead # now wait for child exec (eof due to close-on-exit) or exec error my $errstatus = sysread( FROM_CHILD, $errno, 256 ); die "Cannot sync with child: $!" if not defined $errstatus; close FROM_CHILD; if ($errstatus) { $! = $errno + 0; warn "Cannot exec(@cmd): $!\n" if $^W; return; } } else { # child close FROM_CHILD; close TO_CHILD; $self->make_slave_controlling_terminal(); my $slv = $self->slave() or die "Cannot get slave: $!"; $slv->set_raw() if $self->raw_pty; close($self); # wait for parent before we detach my $buffer; my $errstatus = sysread( FROM_PARENT, $buffer, 256 ); die "Cannot sync with parent: $!" if not defined $errstatus; close FROM_PARENT; close(STDIN); open( STDIN, "<&" . $slv->fileno() ) or die "Couldn't reopen STDIN for reading, $!\n"; close(STDOUT); open( STDOUT, ">&" . $slv->fileno() ) or die "Couldn't reopen STDOUT for writing, $!\n"; close(STDERR); open( STDERR, ">&" . $slv->fileno() ) or die "Couldn't reopen STDERR for writing, $!\n"; { exec(@cmd) }; print TO_PARENT $! + 0; die "Cannot exec(@cmd): $!\n"; } # This is sort of for code compatibility, and to make debugging a little # easier. By code compatibility I mean that previously the process's # handle was referenced by $process{Pty_Handle} instead of just $process. # This is almost like 'naming' the handle to the process. # I think this also reflects Tcl Expect-like behavior. ${*$self}{exp_Pty_Handle} = "spawn id(" . $self->fileno() . ")"; if ( ( ${*$self}{"exp_Debug"} ) or ( ${*$self}{"exp_Exp_Internal"} ) ) { cluck( "Spawned '@cmd'\r\n", "\t${*$self}{exp_Pty_Handle}\r\n", "\tPid: ${*$self}{exp_Pid}\r\n", "\tTty: " . $self->SUPER::ttyname() . "\r\n", ); } $Expect::Spawned_PIDs{ ${*$self}{exp_Pid} } = undef; return $self; } sub exp_init { my ($class, $self) = @_; # take a filehandle, for use later with expect() or interconnect() . # All the functions are written for reading from a tty, so if the naming # scheme looks odd, that's why. bless $self, $class; croak "exp_init not passed a file object, stopped" unless defined( $self->fileno() ); $self->autoflush(1); # Define standard variables.. debug states, etc. $self->_init_vars(); # Turn of logging. By default we don't want crap from a file to get spewed # on screen as we read it. ${*$self}{exp_Log_Stdout} = 0; ${*$self}{exp_Pty_Handle} = "handle id(" . $self->fileno() . ")"; ${*$self}{exp_Pty_Handle} = "STDIN" if $self->fileno() == fileno(STDIN); print STDERR "Initialized ${*$self}{exp_Pty_Handle}.'\r\n" if ${*$self}{"exp_Debug"}; return $self; } # make an alias *init = \&exp_init; ###################################################################### # We're happy OOP people. No direct access to stuff. # For standard read-writeable parameters, we define some autoload magic... my %Writeable_Vars = ( debug => 'exp_Debug', exp_internal => 'exp_Exp_Internal', do_soft_close => 'exp_Do_Soft_Close', max_accum => 'exp_Max_Accum', match_max => 'exp_Max_Accum', notransfer => 'exp_NoTransfer', log_stdout => 'exp_Log_Stdout', log_user => 'exp_Log_Stdout', log_group => 'exp_Log_Group', manual_stty => 'exp_Manual_Stty', restart_timeout_upon_receive => 'exp_Continue', raw_pty => 'exp_Raw_Pty', ); my %Readable_Vars = ( pid => 'exp_Pid', exp_pid => 'exp_Pid', exp_match_number => 'exp_Match_Number', match_number => 'exp_Match_Number', exp_error => 'exp_Error', error => 'exp_Error', exp_command => 'exp_Command', command => 'exp_Command', exp_match => 'exp_Match', match => 'exp_Match', exp_matchlist => 'exp_Matchlist', matchlist => 'exp_Matchlist', exp_before => 'exp_Before', before => 'exp_Before', exp_after => 'exp_After', after => 'exp_After', exp_exitstatus => 'exp_Exit', exitstatus => 'exp_Exit', exp_pty_handle => 'exp_Pty_Handle', pty_handle => 'exp_Pty_Handle', exp_logfile => 'exp_Log_File', logfile => 'exp_Log_File', %Writeable_Vars, ); sub AUTOLOAD { my ($self, @args) = @_; my $type = ref($self) or croak "$self is not an object"; use vars qw($AUTOLOAD); my $name = $AUTOLOAD; $name =~ s/.*:://; # strip fully-qualified portion unless ( exists $Readable_Vars{$name} ) { croak "ERROR: cannot find method `$name' in class $type"; } my $varname = $Readable_Vars{$name}; my $tmp; $tmp = ${*$self}{$varname} if exists ${*$self}{$varname}; if (@args) { if ( exists $Writeable_Vars{$name} ) { my $ref = ref($tmp); if ( $ref eq 'ARRAY' ) { ${*$self}{$varname} = [@args]; } elsif ( $ref eq 'HASH' ) { ${*$self}{$varname} = {@args}; } else { ${*$self}{$varname} = shift @args; } } else { carp "Trying to set read-only variable `$name'" if $^W; } } my $ref = ref($tmp); return ( wantarray ? @{$tmp} : $tmp ) if ( $ref eq 'ARRAY' ); return ( wantarray ? %{$tmp} : $tmp ) if ( $ref eq 'HASH' ); return $tmp; } ###################################################################### sub set_seq { my ( $self, $escape_sequence, $function, $params, @args ) = @_; # Set an escape sequence/function combo for a read handle for interconnect. # Ex: $read_handle->set_seq('',\&function,\@parameters); ${ ${*$self}{exp_Function} }{$escape_sequence} = $function; if ( ( !defined($function) ) || ( $function eq 'undef' ) ) { ${ ${*$self}{exp_Function} }{$escape_sequence} = \&_undef; } ${ ${*$self}{exp_Parameters} }{$escape_sequence} = $params; # This'll be a joy to execute. :) if ( ${*$self}{"exp_Debug"} ) { print STDERR "Escape seq. '" . $escape_sequence; print STDERR "' function for ${*$self}{exp_Pty_Handle} set to '"; print STDERR ${ ${*$self}{exp_Function} }{$escape_sequence}; print STDERR "(" . join( ',', @args ) . ")'\r\n"; } } sub set_group { my ($self, @args) = @_; # Make sure we can read from the read handle if ( !defined( $args[0] ) ) { if ( defined( ${*$self}{exp_Listen_Group} ) ) { return @{ ${*$self}{exp_Listen_Group} }; } else { # Refrain from referencing an undef return; } } @{ ${*$self}{exp_Listen_Group} } = (); if ( $self->_get_mode() !~ 'r' ) { warn( "Attempting to set a handle group on ${*$self}{exp_Pty_Handle}, ", "a non-readable handle!\r\n" ); } while ( my $write_handle = shift @args ) { if ( $write_handle->_get_mode() !~ 'w' ) { warn( "Attempting to set a non-writeable listen handle ", "${*$write_handle}{exp_Pty_handle} for ", "${*$self}{exp_Pty_Handle}!\r\n" ); } push( @{ ${*$self}{exp_Listen_Group} }, $write_handle ); } } sub log_file { my ($self, $file, $mode) = @_; $mode ||= "a"; return ( ${*$self}{exp_Log_File} ) if @_ < 2; # we got no param, return filehandle # $e->log_file(undef) is an acceptable call hence we need to check the number of parameters here if ( ${*$self}{exp_Log_File} and ref( ${*$self}{exp_Log_File} ) ne 'CODE' ) { close( ${*$self}{exp_Log_File} ); } ${*$self}{exp_Log_File} = undef; return if ( not $file ); my $fh = $file; if ( not ref($file) ) { # it's a filename $fh = IO::File->new( $file, $mode ) or croak "Cannot open logfile $file: $!"; } if ( ref($file) ne 'CODE' ) { croak "Given logfile doesn't have a 'print' method" if not $fh->can("print"); $fh->autoflush(1); # so logfile is up to date } ${*$self}{exp_Log_File} = $fh; return $fh; } # I'm going to leave this here in case I might need to change something. # Previously this was calling `stty`, in a most bastardized manner. sub exp_stty { my ($self) = shift; my ($mode) = "@_"; return unless defined $mode; if ( not defined $INC{"IO/Stty.pm"} ) { carp "IO::Stty not installed, cannot change mode"; return; } if ( ${*$self}{"exp_Debug"} ) { print STDERR "Setting ${*$self}{exp_Pty_Handle} to tty mode '$mode'\r\n"; } unless ( POSIX::isatty($self) ) { if ( ${*$self}{"exp_Debug"} or $^W ) { warn "${*$self}{exp_Pty_Handle} is not a tty. Not changing mode"; } return ''; # No undef to avoid warnings elsewhere. } IO::Stty::stty( $self, split( /\s/, $mode ) ); } *stty = \&exp_stty; # If we want to clear the buffer. Otherwise Accum will grow during send_slow # etc. and contain the remainder after matches. sub clear_accum { my ($self) = @_; return $self->set_accum(''); } sub set_accum { my ($self, $accum) = @_; my $old_accum = ${*$self}{exp_Accum}; ${*$self}{exp_Accum} = $accum; # return the contents of the accumulator. return $old_accum; } sub get_accum { my ($self) = @_; return ${*$self}{exp_Accum}; } ###################################################################### # define constants for pattern subs sub exp_continue {"exp_continue"} sub exp_continue_timeout {"exp_continue_timeout"} ###################################################################### # Expect on multiple objects at once. # # Call as Expect::expect($timeout, -i => \@exp_list, @patternlist, # -i => $exp, @pattern_list, ...); # or $exp->expect($timeout, @patternlist, -i => \@exp_list, @patternlist, # -i => $exp, @pattern_list, ...); # # Patterns are arrays that consist of # [ $pattern_type, $pattern, $sub, @subparms ] # # Optional $pattern_type is '-re' (RegExp, default) or '-ex' (exact); # # $sub is optional CODE ref, which is called as &{$sub}($exp, @subparms) # if pattern matched; may return exp_continue or exp_continue_timeout. # # Old-style syntax (pure pattern strings with optional type) also supported. # sub expect { my $self; print STDERR ("expect(@_) called...\n") if $Expect::Debug; if ( defined( $_[0] ) ) { if ( ref( $_[0] ) and $_[0]->isa('Expect') ) { $self = shift; } elsif ( $_[0] eq 'Expect' ) { shift; # or as Expect->expect } } croak "expect(): not enough arguments, should be expect(timeout, [patterns...])" if @_ < 1; my $timeout; if ( looks_like_number($_[0]) or not defined $_[0] ) { $timeout = shift; } else { $timeout = $self->timeout; } my $timeout_hook = undef; my @object_list; my %patterns; my @pattern_list; my @timeout_list; my $curr_list; if ($self) { $curr_list = [$self]; } else { # called directly, so first parameter must be '-i' to establish # object list. $curr_list = []; croak "expect(): ERROR: if called directly (not as \$obj->expect(...), but as Expect::expect(...), first parameter MUST be '-i' to set an object (list) for the patterns to work on." if ( $_[0] ne '-i' ); } # Let's make a list of patterns wanting to be evaled as regexps. my $parm; my $parm_nr = 1; while ( defined( $parm = shift ) ) { print STDERR ("expect(): handling param '$parm'...\n") if $Expect::Debug; if ( ref($parm) ) { if ( ref($parm) eq 'Regexp' ) { push @pattern_list, [ $parm_nr, '-re', $parm, undef ]; } elsif ( ref($parm) eq 'ARRAY' ) { # if ( ref($parm) eq 'ARRAY' ) { my $err = _add_patterns_to_list( \@pattern_list, \@timeout_list, $parm_nr, $parm ); carp( "expect(): Warning: multiple `timeout' patterns (", scalar(@timeout_list), ").\r\n" ) if @timeout_list > 1; $timeout_hook = $timeout_list[-1] if $timeout_list[-1]; croak $err if $err; $parm_nr++; } else { croak("expect(): Unknown pattern ref $parm"); } } else { # not a ref, is an option or raw pattern if ( substr( $parm, 0, 1 ) eq '-' ) { # it's an option print STDERR ("expect(): handling option '$parm'...\n") if $Expect::Debug; if ( $parm eq '-i' ) { # first add collected patterns to object list if ( scalar(@$curr_list) ) { push @object_list, $curr_list if not exists $patterns{"$curr_list"}; push @{ $patterns{"$curr_list"} }, @pattern_list; @pattern_list = (); } # now put parm(s) into current object list if ( ref( $_[0] ) eq 'ARRAY' ) { $curr_list = shift; } else { $curr_list = [shift]; } } elsif ( $parm eq '-re' or $parm eq '-ex' ) { if ( ref( $_[1] ) eq 'CODE' ) { push @pattern_list, [ $parm_nr, $parm, shift, shift ]; } else { push @pattern_list, [ $parm_nr, $parm, shift, undef ]; } $parm_nr++; } else { croak("Unknown option $parm"); } } else { # a plain pattern, check if it is followed by a CODE ref if ( ref( $_[0] ) eq 'CODE' ) { if ( $parm eq 'timeout' ) { push @timeout_list, shift; carp( "expect(): Warning: multiple `timeout' patterns (", scalar(@timeout_list), ").\r\n" ) if @timeout_list > 1; $timeout_hook = $timeout_list[-1] if $timeout_list[-1]; } elsif ( $parm eq 'eof' ) { push @pattern_list, [ $parm_nr, "-$parm", undef, shift ]; } else { push @pattern_list, [ $parm_nr, '-ex', $parm, shift ]; } } else { print STDERR ("expect(): exact match '$parm'...\n") if $Expect::Debug; push @pattern_list, [ $parm_nr, '-ex', $parm, undef ]; } $parm_nr++; } } } # add rest of collected patterns to object list carp "expect(): Empty object list" unless $curr_list; push @object_list, $curr_list if not exists $patterns{"$curr_list"}; push @{ $patterns{"$curr_list"} }, @pattern_list; my $debug = $self ? ${*$self}{exp_Debug} : $Expect::Debug; my $internal = $self ? ${*$self}{exp_Exp_Internal} : $Expect::Exp_Internal; # now start matching... if (@Expect::Before_List) { print STDERR ("Starting BEFORE pattern matching...\r\n") if ( $debug or $internal ); _multi_expect( 0, undef, @Expect::Before_List ); } cluck("Starting EXPECT pattern matching...\r\n") if ( $debug or $internal ); my @ret; @ret = _multi_expect( $timeout, $timeout_hook, map { [ $_, @{ $patterns{"$_"} } ] } @object_list ); if (@Expect::After_List) { print STDERR ("Starting AFTER pattern matching...\r\n") if ( $debug or $internal ); _multi_expect( 0, undef, @Expect::After_List ); } return wantarray ? @ret : $ret[0]; } ###################################################################### # the real workhorse # sub _multi_expect { my ($timeout, $timeout_hook, @params) = @_; if ($timeout_hook) { croak "Unknown timeout_hook type $timeout_hook" unless ( ref($timeout_hook) eq 'CODE' or ref($timeout_hook) eq 'ARRAY' ); } foreach my $pat (@params) { my @patterns = @{$pat}[ 1 .. $#{$pat} ]; foreach my $exp ( @{ $pat->[0] } ) { ${*$exp}{exp_New_Data} = 1; # first round we always try to match if ( exists ${*$exp}{"exp_Max_Accum"} and ${*$exp}{"exp_Max_Accum"} ) { ${*$exp}{exp_Accum} = $exp->_trim_length( ${*$exp}{exp_Accum}, ${*$exp}{exp_Max_Accum} ); } print STDERR ( "${*$exp}{exp_Pty_Handle}: beginning expect.\r\n", "\tTimeout: ", ( defined($timeout) ? $timeout : "unlimited" ), " seconds.\r\n", "\tCurrent time: " . localtime() . "\r\n", ) if $Expect::Debug; # What are we expecting? What do you expect? :-) if ( ${*$exp}{exp_Exp_Internal} ) { print STDERR "${*$exp}{exp_Pty_Handle}: list of patterns:\r\n"; foreach my $pattern (@patterns) { print STDERR ( ' ', defined( $pattern->[0] ) ? '#' . $pattern->[0] . ': ' : '', $pattern->[1], " `", _make_readable( $pattern->[2] ), "'\r\n" ); } print STDERR "\r\n"; } } } my $successful_pattern; my $exp_matched; my $err; my $before; my $after; my $match; my @matchlist; # Set the last loop time to now for time comparisons at end of loop. my $start_loop_time = time(); my $exp_cont = 1; READLOOP: while ($exp_cont) { $exp_cont = 1; $err = ""; my $rmask = ''; my $time_left = undef; if ( defined $timeout ) { $time_left = $timeout - ( time() - $start_loop_time ); $time_left = 0 if $time_left < 0; } $exp_matched = undef; # Test for a match first so we can test the current Accum w/out # worrying about an EOF. foreach my $pat (@params) { my @patterns = @{$pat}[ 1 .. $#{$pat} ]; foreach my $exp ( @{ $pat->[0] } ) { # build mask for select in next section... my $fn = $exp->fileno(); vec( $rmask, $fn, 1 ) = 1 if defined $fn; next unless ${*$exp}{exp_New_Data}; # clear error status ${*$exp}{exp_Error} = undef; ${*$exp}{exp_After} = undef; ${*$exp}{exp_Match_Number} = undef; ${*$exp}{exp_Match} = undef; # This could be huge. We should attempt to do something # about this. Because the output is used for debugging # I'm of the opinion that showing smaller amounts if the # total is huge should be ok. # Thus the 'trim_length' print STDERR ( "\r\n${*$exp}{exp_Pty_Handle}: Does `", $exp->_trim_length( _make_readable( ${*$exp}{exp_Accum} ) ), "'\r\nmatch:\r\n" ) if ${*$exp}{exp_Exp_Internal}; # we don't keep the parameter number anymore # (clashes with before & after), instead the parameter number is # stored inside the pattern; we keep the pattern ref # and look up the number later. foreach my $pattern (@patterns) { print STDERR ( " pattern", defined( $pattern->[0] ) ? ' #' . $pattern->[0] : '', ": ", $pattern->[1], " `", _make_readable( $pattern->[2] ), "'? " ) if ( ${*$exp}{exp_Exp_Internal} ); # Matching exactly if ( $pattern->[1] eq '-ex' ) { my $match_index = index( ${*$exp}{exp_Accum}, $pattern->[2] ); # We matched if $match_index > -1 if ( $match_index > -1 ) { $before = substr( ${*$exp}{exp_Accum}, 0, $match_index ); $match = substr( ${*$exp}{exp_Accum}, $match_index, length( $pattern->[2] ) ); $after = substr( ${*$exp}{exp_Accum}, $match_index + length( $pattern->[2] ) ); ${*$exp}{exp_Before} = $before; ${*$exp}{exp_Match} = $match; ${*$exp}{exp_After} = $after; ${*$exp}{exp_Match_Number} = $pattern->[0]; $exp_matched = $exp; } } elsif ( $pattern->[1] eq '-re' ) { if ($Expect::Multiline_Matching) { @matchlist = ( ${*$exp}{exp_Accum} =~ m/($pattern->[2])/m); } else { @matchlist = ( ${*$exp}{exp_Accum} =~ m/($pattern->[2])/); } if (@matchlist) { # Matching regexp $match = shift @matchlist; my $start = index ${*$exp}{exp_Accum}, $match; die 'The match could not be found' if $start == -1; $before = substr ${*$exp}{exp_Accum}, 0, $start; $after = substr ${*$exp}{exp_Accum}, $start + length($match); ${*$exp}{exp_Before} = $before; ${*$exp}{exp_Match} = $match; ${*$exp}{exp_After} = $after; #pop @matchlist; # remove kludged empty bracket from end @{ ${*$exp}{exp_Matchlist} } = @matchlist; ${*$exp}{exp_Match_Number} = $pattern->[0]; $exp_matched = $exp; } } else { # 'timeout' or 'eof' } if ($exp_matched) { ${*$exp}{exp_Accum} = $after unless ${*$exp}{exp_NoTransfer}; print STDERR "YES!!\r\n" if ${*$exp}{exp_Exp_Internal}; print STDERR ( " Before match string: `", $exp->_trim_length( _make_readable( ($before) ) ), "'\r\n", " Match string: `", _make_readable($match), "'\r\n", " After match string: `", $exp->_trim_length( _make_readable( ($after) ) ), "'\r\n", " Matchlist: (", join( ", ", map { "`" . $exp->_trim_length( _make_readable( ($_) ) ) . "'" } @matchlist, ), ")\r\n", ) if ( ${*$exp}{exp_Exp_Internal} ); # call hook function if defined if ( $pattern->[3] ) { print STDERR ( "Calling hook $pattern->[3]...\r\n", ) if ( ${*$exp}{exp_Exp_Internal} or $Expect::Debug ); if ( $#{$pattern} > 3 ) { # call with parameters if given $exp_cont = &{ $pattern->[3] }( $exp, @{$pattern}[ 4 .. $#{$pattern} ] ); } else { $exp_cont = &{ $pattern->[3] }($exp); } } if ( $exp_cont and $exp_cont eq exp_continue ) { print STDERR ("Continuing expect, restarting timeout...\r\n") if ( ${*$exp}{exp_Exp_Internal} or $Expect::Debug ); $start_loop_time = time(); # restart timeout count next READLOOP; } elsif ( $exp_cont and $exp_cont eq exp_continue_timeout ) { print STDERR ("Continuing expect...\r\n") if ( ${*$exp}{exp_Exp_Internal} or $Expect::Debug ); next READLOOP; } last READLOOP; } print STDERR "No.\r\n" if ${*$exp}{exp_Exp_Internal}; } print STDERR "\r\n" if ${*$exp}{exp_Exp_Internal}; # don't have to match again until we get new data ${*$exp}{exp_New_Data} = 0; } } # End of matching section # No match, let's see what is pending on the filehandles... print STDERR ( "Waiting for new data (", defined($time_left) ? $time_left : 'unlimited', " seconds)...\r\n", ) if ( $Expect::Exp_Internal or $Expect::Debug ); my $nfound; SELECT: { $nfound = select( $rmask, undef, undef, $time_left ); if ( $nfound < 0 ) { if ( $!{EINTR} and $Expect::IgnoreEintr ) { print STDERR ("ignoring EINTR, restarting select()...\r\n") if ( $Expect::Exp_Internal or $Expect::Debug ); next SELECT; } print STDERR ("select() returned error code '$!'\r\n") if ( $Expect::Exp_Internal or $Expect::Debug ); # returned error $err = "4:$!"; last READLOOP; } } # go until we don't find something (== timeout). if ( $nfound == 0 ) { # No pattern, no EOF. Did we time out? $err = "1:TIMEOUT"; foreach my $pat (@params) { foreach my $exp ( @{ $pat->[0] } ) { $before = ${*$exp}{exp_Before} = ${*$exp}{exp_Accum}; next if not defined $exp->fileno(); # skip already closed ${*$exp}{exp_Error} = $err unless ${*$exp}{exp_Error}; } } print STDERR ("TIMEOUT\r\n") if ( $Expect::Debug or $Expect::Exp_Internal ); if ($timeout_hook) { my $ret; print STDERR ("Calling timeout function $timeout_hook...\r\n") if ( $Expect::Debug or $Expect::Exp_Internal ); if ( ref($timeout_hook) eq 'CODE' ) { $ret = &{$timeout_hook}( $params[0]->[0] ); } else { if ( $#{$timeout_hook} > 3 ) { $ret = &{ $timeout_hook->[3] }( $params[0]->[0], @{$timeout_hook}[ 4 .. $#{$timeout_hook} ] ); } else { $ret = &{ $timeout_hook->[3] }( $params[0]->[0] ); } } if ( $ret and $ret eq exp_continue ) { $start_loop_time = time(); # restart timeout count next READLOOP; } } last READLOOP; } my @bits = split( //, unpack( 'b*', $rmask ) ); foreach my $pat (@params) { foreach my $exp ( @{ $pat->[0] } ) { next if not defined $exp->fileno(); # skip already closed if ( $bits[ $exp->fileno() ] ) { print STDERR ("${*$exp}{exp_Pty_Handle}: new data.\r\n") if $Expect::Debug; # read in what we found. my $buffer; my $nread = sysread( $exp, $buffer, 2048 ); # Make errors (nread undef) show up as EOF. $nread = 0 unless defined($nread); if ( $nread == 0 ) { print STDERR ("${*$exp}{exp_Pty_Handle}: EOF\r\n") if ($Expect::Debug); $before = ${*$exp}{exp_Before} = $exp->clear_accum(); $err = "2:EOF"; ${*$exp}{exp_Error} = $err; ${*$exp}{exp_Has_EOF} = 1; $exp_cont = undef; foreach my $eof_pat ( grep { $_->[1] eq '-eof' } @{$pat}[ 1 .. $#{$pat} ] ) { my $ret; print STDERR ( "Calling EOF hook $eof_pat->[3]...\r\n", ) if ($Expect::Debug); if ( $#{$eof_pat} > 3 ) { # call with parameters if given $ret = &{ $eof_pat->[3] }( $exp, @{$eof_pat}[ 4 .. $#{$eof_pat} ] ); } else { $ret = &{ $eof_pat->[3] }($exp); } if ($ret and ( $ret eq exp_continue or $ret eq exp_continue_timeout ) ) { $exp_cont = $ret; } } # is it dead? if ( defined( ${*$exp}{exp_Pid} ) ) { my $ret = waitpid( ${*$exp}{exp_Pid}, POSIX::WNOHANG ); if ( $ret == ${*$exp}{exp_Pid} ) { printf STDERR ( "%s: exit(0x%02X)\r\n", ${*$exp}{exp_Pty_Handle}, $? ) if ($Expect::Debug); $err = "3:Child PID ${*$exp}{exp_Pid} exited with status $?"; ${*$exp}{exp_Error} = $err; ${*$exp}{exp_Exit} = $?; delete $Expect::Spawned_PIDs{ ${*$exp}{exp_Pid} }; ${*$exp}{exp_Pid} = undef; } } print STDERR ("${*$exp}{exp_Pty_Handle}: closing...\r\n") if ($Expect::Debug); $exp->hard_close(); next; } print STDERR ("${*$exp}{exp_Pty_Handle}: read $nread byte(s).\r\n") if ($Expect::Debug); # ugly hack for broken solaris ttys that spew # into our pretty output $buffer =~ s/ \cH//g if not ${*$exp}{exp_Raw_Pty}; # Append it to the accumulator. ${*$exp}{exp_Accum} .= $buffer; if ( exists ${*$exp}{exp_Max_Accum} and ${*$exp}{exp_Max_Accum} ) { ${*$exp}{exp_Accum} = $exp->_trim_length( ${*$exp}{exp_Accum}, ${*$exp}{exp_Max_Accum} ); } ${*$exp}{exp_New_Data} = 1; # next round we try to match again $exp_cont = exp_continue if ( exists ${*$exp}{exp_Continue} and ${*$exp}{exp_Continue} ); # Now propagate what we have read to other listeners... $exp->_print_handles($buffer); # End handle reading section. } } } # end read loop $start_loop_time = time() # restart timeout count if ( $exp_cont and $exp_cont eq exp_continue ); } # End READLOOP # Post loop. Do we have anything? # Tell us status if ( $Expect::Debug or $Expect::Exp_Internal ) { if ($exp_matched) { print STDERR ( "Returning from expect ", ${*$exp_matched}{exp_Error} ? 'un' : '', "successfully.", ${*$exp_matched}{exp_Error} ? "\r\n Error: ${*$exp_matched}{exp_Error}." : '', "\r\n" ); } else { print STDERR ("Returning from expect with TIMEOUT or EOF\r\n"); } if ( $Expect::Debug and $exp_matched ) { print STDERR " ${*$exp_matched}{exp_Pty_Handle}: accumulator: `"; if ( ${*$exp_matched}{exp_Error} ) { print STDERR ( $exp_matched->_trim_length( _make_readable( ${*$exp_matched}{exp_Before} ) ), "'\r\n" ); } else { print STDERR ( $exp_matched->_trim_length( _make_readable( ${*$exp_matched}{exp_Accum} ) ), "'\r\n" ); } } } if ($exp_matched) { return wantarray ? ( ${*$exp_matched}{exp_Match_Number}, ${*$exp_matched}{exp_Error}, ${*$exp_matched}{exp_Match}, ${*$exp_matched}{exp_Before}, ${*$exp_matched}{exp_After}, $exp_matched, ) : ${*$exp_matched}{exp_Match_Number}; } return wantarray ? ( undef, $err, undef, $before, undef, undef ) : undef; } # Patterns are arrays that consist of # [ $pattern_type, $pattern, $sub, @subparms ] # optional $pattern_type is '-re' (RegExp, default) or '-ex' (exact); # $sub is optional CODE ref, which is called as &{$sub}($exp, @subparms) # if pattern matched; # the $parm_nr gets unshifted onto the array for reporting purposes. sub _add_patterns_to_list { my ($listref, $timeoutlistref,$store_parm_nr, @params) = @_; # $timeoutlistref gets timeout patterns my $parm_nr = $store_parm_nr || 1; foreach my $parm (@params) { if ( not ref($parm) eq 'ARRAY' ) { return "Parameter #$parm_nr is not an ARRAY ref."; } $parm = [@$parm]; # make copy if ( $parm->[0] =~ m/\A-/ ) { # it's an option if ( $parm->[0] ne '-re' and $parm->[0] ne '-ex' ) { return "Unknown option $parm->[0] in pattern #$parm_nr"; } } else { if ( $parm->[0] eq 'timeout' ) { if ( defined $timeoutlistref ) { splice @$parm, 0, 1, ( "-$parm->[0]", undef ); unshift @$parm, $store_parm_nr ? $parm_nr : undef; push @$timeoutlistref, $parm; } next; } elsif ( $parm->[0] eq 'eof' ) { splice @$parm, 0, 1, ( "-$parm->[0]", undef ); } else { unshift @$parm, '-re'; # defaults to RegExp } } if ( @$parm > 2 ) { if ( ref( $parm->[2] ) ne 'CODE' ) { croak( "Pattern #$parm_nr doesn't have a CODE reference", "after the pattern." ); } } else { push @$parm, undef; # make sure we have three elements } unshift @$parm, $store_parm_nr ? $parm_nr : undef; push @$listref, $parm; $parm_nr++; } return; } ###################################################################### # $process->interact([$in_handle],[$escape sequence]) # If you don't specify in_handle STDIN will be used. sub interact { my ($self, $infile, $escape_sequence) = @_; my $outfile; my @old_group = $self->set_group(); # If the handle is STDIN we'll # $infile->fileno == 0 should be stdin.. follow stdin rules. no strict 'subs'; # Allow bare word 'STDIN' unless ( defined($infile) ) { # We need a handle object Associated with STDIN. $infile = IO::File->new; $infile->IO::File::fdopen( STDIN, 'r' ); $outfile = IO::File->new; $outfile->IO::File::fdopen( STDOUT, 'w' ); } elsif ( fileno($infile) == fileno(STDIN) ) { # With STDIN we want output to go to stdout. $outfile = IO::File->new; $outfile->IO::File::fdopen( STDOUT, 'w' ); } else { undef($outfile); } # Here we assure ourselves we have an Expect object. my $in_object = Expect->exp_init($infile); if ( defined($outfile) ) { # as above.. we want output to go to stdout if we're given stdin. my $out_object = Expect->exp_init($outfile); $out_object->manual_stty(1); $self->set_group($out_object); } else { $self->set_group($in_object); } $in_object->set_group($self); $in_object->set_seq( $escape_sequence, undef ) if defined($escape_sequence); # interconnect normally sets stty -echo raw. Interact really sort # of implies we don't do that by default. If anyone wanted to they could # set it before calling interact, of use interconnect directly. my $old_manual_stty_val = $self->manual_stty(); $self->manual_stty(1); # I think this is right. Don't send stuff from in_obj to stdout by default. # in theory whatever 'self' is should echo what's going on. my $old_log_stdout_val = $self->log_stdout(); $self->log_stdout(0); $in_object->log_stdout(0); # Allow for the setting of an optional EOF escape function. # $in_object->set_seq('EOF',undef); # $self->set_seq('EOF',undef); Expect::interconnect( $self, $in_object ); $self->log_stdout($old_log_stdout_val); $self->set_group(@old_group); # If old_group was undef, make sure that occurs. This is a slight hack since # it modifies the value directly. # Normally an undef passed to set_group will return the current groups. # It is possible that it may be of worth to make it possible to undef # The current group without doing this. unless (@old_group) { @{ ${*$self}{exp_Listen_Group} } = (); } $self->manual_stty($old_manual_stty_val); return; } sub interconnect { my (@handles) = @_; # my ($handle)=(shift); call as Expect::interconnect($spawn1,$spawn2,...) my ( $nread ); my ( $rout, $emask, $eout ); my ( $escape_character_buffer ); my ( $read_mask, $temp_mask ) = ( '', '' ); # Get read/write handles foreach my $handle (@handles) { $temp_mask = ''; vec( $temp_mask, $handle->fileno(), 1 ) = 1; # Under Linux w/ 5.001 the next line comes up w/ 'Uninit var.'. # It appears to be impossible to make the warning go away. # doing something like $temp_mask='' unless defined ($temp_mask) # has no effect whatsoever. This may be a bug in 5.001. $read_mask = $read_mask | $temp_mask; } if ($Expect::Debug) { print STDERR "Read handles:\r\n"; foreach my $handle (@handles) { print STDERR "\tRead handle: "; print STDERR "'${*$handle}{exp_Pty_Handle}'\r\n"; print STDERR "\t\tListen Handles:"; foreach my $write_handle ( @{ ${*$handle}{exp_Listen_Group} } ) { print STDERR " '${*$write_handle}{exp_Pty_Handle}'"; } print STDERR ".\r\n"; } } # I think if we don't set raw/-echo here we may have trouble. We don't # want a bunch of echoing crap making all the handles jabber at each other. foreach my $handle (@handles) { unless ( ${*$handle}{"exp_Manual_Stty"} ) { # This is probably O/S specific. ${*$handle}{exp_Stored_Stty} = $handle->exp_stty('-g'); print STDERR "Setting tty for ${*$handle}{exp_Pty_Handle} to 'raw -echo'.\r\n" if ${*$handle}{"exp_Debug"}; $handle->exp_stty("raw -echo"); } foreach my $write_handle ( @{ ${*$handle}{exp_Listen_Group} } ) { unless ( ${*$write_handle}{"exp_Manual_Stty"} ) { ${*$write_handle}{exp_Stored_Stty} = $write_handle->exp_stty('-g'); print STDERR "Setting ${*$write_handle}{exp_Pty_Handle} to 'raw -echo'.\r\n" if ${*$handle}{"exp_Debug"}; $write_handle->exp_stty("raw -echo"); } } } print STDERR "Attempting interconnection\r\n" if $Expect::Debug; # Wait until the process dies or we get EOF # In the case of !${*$handle}{exp_Pid} it means # the handle was exp_inited instead of spawned. CONNECT_LOOP: # Go until we have a reason to stop while (1) { # test each handle to see if it's still alive. foreach my $read_handle (@handles) { waitpid( ${*$read_handle}{exp_Pid}, WNOHANG ) if ( exists( ${*$read_handle}{exp_Pid} ) and ${*$read_handle}{exp_Pid} ); if ( exists( ${*$read_handle}{exp_Pid} ) and ( ${*$read_handle}{exp_Pid} ) and ( !kill( 0, ${*$read_handle}{exp_Pid} ) ) ) { print STDERR "Got EOF (${*$read_handle}{exp_Pty_Handle} died) reading ${*$read_handle}{exp_Pty_Handle}\r\n" if ${*$read_handle}{"exp_Debug"}; last CONNECT_LOOP unless defined( ${ ${*$read_handle}{exp_Function} }{"EOF"} ); last CONNECT_LOOP unless &{ ${ ${*$read_handle}{exp_Function} }{"EOF"} } ( @{ ${ ${*$read_handle}{exp_Parameters} }{"EOF"} } ); } } # Every second? No, go until we get something from someone. my $nfound = select( $rout = $read_mask, undef, $eout = $emask, undef ); # Is there anything to share? May be -1 if interrupted by a signal... next CONNECT_LOOP if not defined $nfound or $nfound < 1; # Which handles have stuff? my @bits = split( //, unpack( 'b*', $rout ) ); $eout = 0 unless defined($eout); my @ebits = split( //, unpack( 'b*', $eout ) ); # print "Ebits: $eout\r\n"; foreach my $read_handle (@handles) { if ( $bits[ $read_handle->fileno() ] ) { $nread = sysread( $read_handle, ${*$read_handle}{exp_Pty_Buffer}, 1024 ); # Appease perl -w $nread = 0 unless defined($nread); print STDERR "interconnect: read $nread byte(s) from ${*$read_handle}{exp_Pty_Handle}.\r\n" if ${*$read_handle}{"exp_Debug"} > 1; # Test for escape seq. before printing. # Appease perl -w $escape_character_buffer = '' unless defined($escape_character_buffer); $escape_character_buffer .= ${*$read_handle}{exp_Pty_Buffer}; foreach my $escape_sequence ( keys( %{ ${*$read_handle}{exp_Function} } ) ) { print STDERR "Tested escape sequence $escape_sequence from ${*$read_handle}{exp_Pty_Handle}" if ${*$read_handle}{"exp_Debug"} > 1; # Make sure it doesn't grow out of bounds. $escape_character_buffer = $read_handle->_trim_length( $escape_character_buffer, ${*$read_handle}{"exp_Max_Accum"} ) if ( ${*$read_handle}{"exp_Max_Accum"} ); if ( $escape_character_buffer =~ /($escape_sequence)/ ) { my $match = $1; if ( ${*$read_handle}{"exp_Debug"} ) { print STDERR "\r\ninterconnect got escape sequence from ${*$read_handle}{exp_Pty_Handle}.\r\n"; # I'm going to make the esc. seq. pretty because it will # probably contain unprintable characters. print STDERR "\tEscape Sequence: '" . _trim_length( undef, _make_readable($escape_sequence) ) . "'\r\n"; print STDERR "\tMatched by string: '" . _trim_length( undef, _make_readable($match) ) . "'\r\n"; } # Print out stuff before the escape. # Keep in mind that the sequence may have been split up # over several reads. # Let's get rid of it from this read. If part of it was # in the last read there's not a lot we can do about it now. if ( ${*$read_handle}{exp_Pty_Buffer} =~ /([\w\W]*)($escape_sequence)/ ) { $read_handle->_print_handles($1); } else { $read_handle->_print_handles( ${*$read_handle}{exp_Pty_Buffer} ); } # Clear the buffer so no more matches can be made and it will # only be printed one time. ${*$read_handle}{exp_Pty_Buffer} = ''; $escape_character_buffer = ''; # Do the function here. Must return non-zero to continue. # More cool syntax. Maybe I should turn these in to objects. last CONNECT_LOOP unless &{ ${ ${*$read_handle}{exp_Function} }{$escape_sequence} } ( @{ ${ ${*$read_handle}{exp_Parameters} }{$escape_sequence} } ); } } $nread = 0 unless defined($nread); # Appease perl -w? waitpid( ${*$read_handle}{exp_Pid}, WNOHANG ) if ( defined( ${*$read_handle}{exp_Pid} ) && ${*$read_handle}{exp_Pid} ); if ( $nread == 0 ) { print STDERR "Got EOF reading ${*$read_handle}{exp_Pty_Handle}\r\n" if ${*$read_handle}{"exp_Debug"}; last CONNECT_LOOP unless defined( ${ ${*$read_handle}{exp_Function} }{"EOF"} ); last CONNECT_LOOP unless &{ ${ ${*$read_handle}{exp_Function} }{"EOF"} } ( @{ ${ ${*$read_handle}{exp_Parameters} }{"EOF"} } ); } last CONNECT_LOOP if ( $nread < 0 ); # This would be an error $read_handle->_print_handles( ${*$read_handle}{exp_Pty_Buffer} ); } # I'm removing this because I haven't determined what causes exceptions # consistently. if (0) #$ebits[$read_handle->fileno()]) { print STDERR "Got Exception reading ${*$read_handle}{exp_Pty_Handle}\r\n" if ${*$read_handle}{"exp_Debug"}; last CONNECT_LOOP unless defined( ${ ${*$read_handle}{exp_Function} }{"EOF"} ); last CONNECT_LOOP unless &{ ${ ${*$read_handle}{exp_Function} }{"EOF"} } ( @{ ${ ${*$read_handle}{exp_Parameters} }{"EOF"} } ); } } } foreach my $handle (@handles) { unless ( ${*$handle}{"exp_Manual_Stty"} ) { $handle->exp_stty( ${*$handle}{exp_Stored_Stty} ); } foreach my $write_handle ( @{ ${*$handle}{exp_Listen_Group} } ) { unless ( ${*$write_handle}{"exp_Manual_Stty"} ) { $write_handle->exp_stty( ${*$write_handle}{exp_Stored_Stty} ); } } } return; } # user can decide if log output gets also sent to logfile sub print_log_file { my ($self, @params) = @_; if ( ${*$self}{exp_Log_File} ) { if ( ref( ${*$self}{exp_Log_File} ) eq 'CODE' ) { ${*$self}{exp_Log_File}->(@params); } else { ${*$self}{exp_Log_File}->print(@params); } } return; } # we provide our own print so we can debug what gets sent to the # processes... sub print { my ( $self, @args ) = @_; return if not defined $self->fileno(); # skip if closed if ( ${*$self}{exp_Exp_Internal} ) { my $args = _make_readable( join( '', @args ) ); cluck "Sending '$args' to ${*$self}{exp_Pty_Handle}\r\n"; } foreach my $arg (@args) { while ( length($arg) > 80 ) { $self->SUPER::print( substr( $arg, 0, 80 ) ); $arg = substr( $arg, 80 ); } $self->SUPER::print($arg); } return; } # make an alias for Tcl/Expect users for a DWIM experience... *send = \&print; # This is an Expect standard. It's nice for talking to modems and the like # where from time to time they get unhappy if you send items too quickly. sub send_slow { my ($self, $sleep_time, @chunks) = @_; return if not defined $self->fileno(); # skip if closed # Flushing makes it so each character can be seen separately. my $chunk; while ( $chunk = shift @chunks ) { my @linechars = split( '', $chunk ); foreach my $char (@linechars) { # How slow? select( undef, undef, undef, $sleep_time ); print $self $char; print STDERR "Printed character \'" . _make_readable($char) . "\' to ${*$self}{exp_Pty_Handle}.\r\n" if ${*$self}{"exp_Debug"} > 1; # I think I can get away with this if I save it in accum if ( ${*$self}{"exp_Log_Stdout"} || ${*$self}{exp_Log_Group} ) { my $rmask = ""; vec( $rmask, $self->fileno(), 1 ) = 1; # .01 sec granularity should work. If we miss something it will # probably get flushed later, maybe in an expect call. while ( select( $rmask, undef, undef, .01 ) ) { my $ret = sysread( $self, ${*$self}{exp_Pty_Buffer}, 1024 ); last if not defined $ret or $ret == 0; # Is this necessary to keep? Probably.. # # if you need to expect it later. ${*$self}{exp_Accum} .= ${*$self}{exp_Pty_Buffer}; ${*$self}{exp_Accum} = $self->_trim_length( ${*$self}{exp_Accum}, ${*$self}{"exp_Max_Accum"} ) if ( ${*$self}{"exp_Max_Accum"} ); $self->_print_handles( ${*$self}{exp_Pty_Buffer} ); print STDERR "Received \'" . $self->_trim_length( _make_readable($char) ) . "\' from ${*$self}{exp_Pty_Handle}\r\n" if ${*$self}{"exp_Debug"} > 1; } } } } return; } sub test_handles { my ($timeout, @handle_list) = @_; # This should be called by Expect::test_handles($timeout,@objects); my ( $allmask, $rout ); foreach my $handle (@handle_list) { my $rmask = ''; vec( $rmask, $handle->fileno(), 1 ) = 1; $allmask = '' unless defined($allmask); $allmask = $allmask | $rmask; } my $nfound = select( $rout = $allmask, undef, undef, $timeout ); return () unless $nfound; # Which handles have stuff? my @bits = split( //, unpack( 'b*', $rout ) ); my $handle_num = 0; my @return_list = (); foreach my $handle (@handle_list) { # I go to great lengths to get perl -w to shut the hell up. if ( defined( $bits[ $handle->fileno() ] ) and ( $bits[ $handle->fileno() ] ) ) { push( @return_list, $handle_num ); } } continue { $handle_num++; } return @return_list; } # Be nice close. This should emulate what an interactive shell does after a # command finishes... sort of. We're not as patient as a shell. sub soft_close { my ($self) = @_; my ( $nfound, $nread, $rmask, $end_time, $temp_buffer ); # Give it 15 seconds to cough up an eof. cluck "Closing ${*$self}{exp_Pty_Handle}.\r\n" if ${*$self}{exp_Debug}; return -1 if not defined $self->fileno(); # skip if handle already closed unless ( exists ${*$self}{exp_Has_EOF} and ${*$self}{exp_Has_EOF} ) { $end_time = time() + 15; while ( $end_time > time() ) { my $select_time = $end_time - time(); # Sanity check. $select_time = 0 if $select_time < 0; $rmask = ''; vec( $rmask, $self->fileno(), 1 ) = 1; ($nfound) = select( $rmask, undef, undef, $select_time ); last unless ( defined($nfound) && $nfound ); $nread = sysread( $self, $temp_buffer, 8096 ); # 0 = EOF. unless ( defined($nread) && $nread ) { print STDERR "Got EOF from ${*$self}{exp_Pty_Handle}.\r\n" if ${*$self}{exp_Debug}; last; } $self->_print_handles($temp_buffer); } if ( ( $end_time <= time() ) && ${*$self}{exp_Debug} ) { print STDERR "Timed out waiting for an EOF from ${*$self}{exp_Pty_Handle}.\r\n"; } } my $close_status = $self->close(); if ( $close_status && ${*$self}{exp_Debug} ) { print STDERR "${*$self}{exp_Pty_Handle} closed.\r\n"; } # quit now if it isn't a process. return $close_status unless defined( ${*$self}{exp_Pid} ); # Now give it 15 seconds to die. $end_time = time() + 15; while ( $end_time > time() ) { my $returned_pid = waitpid( ${*$self}{exp_Pid}, &WNOHANG ); # Stop here if the process dies. if ( defined($returned_pid) && $returned_pid ) { delete $Expect::Spawned_PIDs{$returned_pid}; if ( ${*$self}{exp_Debug} ) { printf STDERR ( "Pid %d of %s exited, Status: 0x%02X\r\n", ${*$self}{exp_Pid}, ${*$self}{exp_Pty_Handle}, $? ); } ${*$self}{exp_Pid} = undef; ${*$self}{exp_Exit} = $?; return ${*$self}{exp_Exit}; } sleep 1; # Keep loop nice. } # Send it a term if it isn't dead. if ( ${*$self}{exp_Debug} ) { print STDERR "${*$self}{exp_Pty_Handle} not exiting, sending TERM.\r\n"; } kill TERM => ${*$self}{exp_Pid}; # Now to be anal retentive.. wait 15 more seconds for it to die. $end_time = time() + 15; while ( $end_time > time() ) { my $returned_pid = waitpid( ${*$self}{exp_Pid}, &WNOHANG ); if ( defined($returned_pid) && $returned_pid ) { delete $Expect::Spawned_PIDs{$returned_pid}; if ( ${*$self}{exp_Debug} ) { printf STDERR ( "Pid %d of %s terminated, Status: 0x%02X\r\n", ${*$self}{exp_Pid}, ${*$self}{exp_Pty_Handle}, $? ); } ${*$self}{exp_Pid} = undef; ${*$self}{exp_Exit} = $?; return $?; } sleep 1; } # Since this is a 'soft' close, sending it a -9 would be inappropriate. return; } # 'Make it go away' close. sub hard_close { my ($self) = @_; cluck "Closing ${*$self}{exp_Pty_Handle}.\r\n" if ${*$self}{exp_Debug}; # Don't wait for an EOF. my $close_status = $self->close(); if ( $close_status && ${*$self}{exp_Debug} ) { print STDERR "${*$self}{exp_Pty_Handle} closed.\r\n"; } # Return now if handle. return $close_status unless defined( ${*$self}{exp_Pid} ); # Now give it 5 seconds to die. Less patience here if it won't die. my $end_time = time() + 5; while ( $end_time > time() ) { my $returned_pid = waitpid( ${*$self}{exp_Pid}, &WNOHANG ); # Stop here if the process dies. if ( defined($returned_pid) && $returned_pid ) { delete $Expect::Spawned_PIDs{$returned_pid}; if ( ${*$self}{exp_Debug} ) { printf STDERR ( "Pid %d of %s terminated, Status: 0x%02X\r\n", ${*$self}{exp_Pid}, ${*$self}{exp_Pty_Handle}, $? ); } ${*$self}{exp_Pid} = undef; ${*$self}{exp_Exit} = $?; return ${*$self}{exp_Exit}; } sleep 1; # Keep loop nice. } # Send it a term if it isn't dead. if ( ${*$self}{exp_Debug} ) { print STDERR "${*$self}{exp_Pty_Handle} not exiting, sending TERM.\r\n"; } kill TERM => ${*$self}{exp_Pid}; # wait 15 more seconds for it to die. $end_time = time() + 15; while ( $end_time > time() ) { my $returned_pid = waitpid( ${*$self}{exp_Pid}, &WNOHANG ); if ( defined($returned_pid) && $returned_pid ) { delete $Expect::Spawned_PIDs{$returned_pid}; if ( ${*$self}{exp_Debug} ) { printf STDERR ( "Pid %d of %s terminated, Status: 0x%02X\r\n", ${*$self}{exp_Pid}, ${*$self}{exp_Pty_Handle}, $? ); } ${*$self}{exp_Pid} = undef; ${*$self}{exp_Exit} = $?; return ${*$self}{exp_Exit}; } sleep 1; } kill KILL => ${*$self}{exp_Pid}; # wait 5 more seconds for it to die. $end_time = time() + 5; while ( $end_time > time() ) { my $returned_pid = waitpid( ${*$self}{exp_Pid}, &WNOHANG ); if ( defined($returned_pid) && $returned_pid ) { delete $Expect::Spawned_PIDs{$returned_pid}; if ( ${*$self}{exp_Debug} ) { printf STDERR ( "Pid %d of %s killed, Status: 0x%02X\r\n", ${*$self}{exp_Pid}, ${*$self}{exp_Pty_Handle}, $? ); } ${*$self}{exp_Pid} = undef; ${*$self}{exp_Exit} = $?; return ${*$self}{exp_Exit}; } sleep 1; } warn "Pid ${*$self}{exp_Pid} of ${*$self}{exp_Pty_Handle} is HUNG.\r\n"; ${*$self}{exp_Pid} = undef; return; } # These should not be called externally. sub _init_vars { my ($self) = @_; # for every spawned process or filehandle. ${*$self}{exp_Log_Stdout} = $Expect::Log_Stdout if defined($Expect::Log_Stdout); ${*$self}{exp_Log_Group} = $Expect::Log_Group; ${*$self}{exp_Debug} = $Expect::Debug; ${*$self}{exp_Exp_Internal} = $Expect::Exp_Internal; ${*$self}{exp_Manual_Stty} = $Expect::Manual_Stty; ${*$self}{exp_Stored_Stty} = 'sane'; ${*$self}{exp_Do_Soft_Close} = $Expect::Do_Soft_Close; # sysread doesn't like my or local vars. ${*$self}{exp_Pty_Buffer} = ''; # Initialize accumulator. ${*$self}{exp_Max_Accum} = $Expect::Exp_Max_Accum; ${*$self}{exp_Accum} = ''; ${*$self}{exp_NoTransfer} = 0; # create empty expect_before & after lists ${*$self}{exp_expect_before_list} = []; ${*$self}{exp_expect_after_list} = []; return; } sub _make_readable { my ($s) = @_; $s = '' if not defined($s); study $s; # Speed things up? $s =~ s/\\/\\\\/g; # So we can tell easily(?) what is a backslash $s =~ s/\n/\\n/g; $s =~ s/\r/\\r/g; $s =~ s/\t/\\t/g; $s =~ s/\'/\\\'/g; # So we can tell whassa quote and whassa notta quote. $s =~ s/\"/\\\"/g; # Formfeed (does anyone use formfeed?) $s =~ s/\f/\\f/g; $s =~ s/\010/\\b/g; # escape control chars high/low, but allow ISO 8859-1 chars $s =~ s/([\000-\037\177-\237\377])/sprintf("\\%03lo",ord($1))/ge; return $s; } sub _trim_length { my ($self, $string, $length) = @_; # This is sort of a reverse truncation function # Mostly so we don't have to see the full output when we're using # Also used if Max_Accum gets set to limit the size of the accumulator # for matching functions. # exp_internal croak('No string passed') if not defined $string; # If we're not passed a length (_trim_length is being used for debugging # purposes) AND debug >= 3, don't trim. return ($string) if (defined($self) and ${*$self}{"exp_Debug"} >= 3 and ( !( defined($length) ) ) ); my $indicate_truncation = ($length ? '' : '...'); $length ||= 1021; return $string if $length >= length $string; # We wouldn't want the accumulator to begin with '...' if max_accum is passed # This is because this funct. gets called internally w/ max_accum # and is also used to print information back to the user. return $indicate_truncation . substr( $string, ( length($string) - $length ), $length ); } sub _print_handles { my ($self, $print_this) = @_; # Given crap from 'self' and the handles self wants to print to, print to # them. these are indicated by the handle's 'group' if ( ${*$self}{exp_Log_Group} ) { foreach my $handle ( @{ ${*$self}{exp_Listen_Group} } ) { $print_this = '' unless defined($print_this); # Appease perl -w print STDERR "Printed '" . $self->_trim_length( _make_readable($print_this) ) . "' to ${*$handle}{exp_Pty_Handle} from ${*$self}{exp_Pty_Handle}.\r\n" if ( ${*$handle}{"exp_Debug"} > 1 ); print $handle $print_this; } } # If ${*$self}{exp_Pty_Handle} is STDIN this would make it echo. print STDOUT $print_this if ${*$self}{"exp_Log_Stdout"}; $self->print_log_file($print_this); $| = 1; # This should not be necessary but autoflush() doesn't always work. return; } sub _get_mode { my ($handle) = @_; my ($fcntl_flags) = ''; # What mode are we opening with? use fcntl to find out. $fcntl_flags = fcntl( \*{$handle}, Fcntl::F_GETFL, $fcntl_flags ); die "fcntl returned undef during exp_init of $handle, $!\r\n" unless defined($fcntl_flags); if ( $fcntl_flags | (Fcntl::O_RDWR) ) { return 'rw'; } elsif ( $fcntl_flags | (Fcntl::O_WRONLY) ) { return 'w'; } else { # Under Solaris (among others?) O_RDONLY is implemented as 0. so |O_RDONLY would fail. return 'r'; } } sub _undef { return undef; # Seems a little retarded but &CORE::undef fails in interconnect. # This is used for the default escape sequence function. # w/out the leading & it won't compile. } # clean up child processes sub DESTROY { my ($self) = @_; my $status = $?; # save this as it gets mangled by the terminating spawned children if ( ${*$self}{exp_Do_Soft_Close} ) { $self->soft_close(); } $self->hard_close(); $? = $status; # restore it. otherwise deleting an Expect object may mangle $?, which is unintuitive return; } 1; __END__ =head1 NAME Expect - automate interactions with command line programs that expose a text terminal interface. =head1 SYNOPSIS use Expect; # create an Expect object by spawning another process my $exp = Expect->spawn($command, @params) or die "Cannot spawn $command: $!\n"; # or by using an already opened filehandle (e.g. from Net::Telnet) my $exp = Expect->exp_init(\*FILEHANDLE); # if you prefer the OO mindset: my $exp = Expect->new; $exp->raw_pty(1); $exp->spawn($command, @parameters) or die "Cannot spawn $command: $!\n"; # send some string there: $exp->send("string\n"); # or, for the filehandle mindset: print $exp "string\n"; # then do some pattern matching with either the simple interface $patidx = $exp->expect($timeout, @match_patterns); # or multi-match on several spawned commands with callbacks, # just like the Tcl version $exp->expect($timeout, [ qr/regex1/ => sub { my $exp = shift; $exp->send("response\n"); exp_continue; } ], [ "regexp2" , \&callback, @cbparms ], ); # if no longer needed, do a soft_close to nicely shut down the command $exp->soft_close(); # or be less patient with $exp->hard_close(); Expect.pm is built to either spawn a process or take an existing filehandle and interact with it such that normally interactive tasks can be done without operator assistance. This concept makes more sense if you are already familiar with the versatile Tcl version of Expect. The public functions that make up Expect.pm are: Expect->new() Expect::interconnect(@objects_to_be_read_from) Expect::test_handles($timeout, @objects_to_test) Expect::version($version_requested | undef); $object->spawn(@command) $object->clear_accum() $object->set_accum($value) $object->debug($debug_level) $object->exp_internal(0 | 1) $object->notransfer(0 | 1) $object->raw_pty(0 | 1) $object->stty(@stty_modes) # See the IO::Stty docs $object->slave() $object->before(); $object->match(); $object->after(); $object->matchlist(); $object->match_number(); $object->error(); $object->command(); $object->exitstatus(); $object->pty_handle(); $object->do_soft_close(); $object->restart_timeout_upon_receive(0 | 1); $object->interact($other_object, $escape_sequence) $object->log_group(0 | 1 | undef) $object->log_user(0 | 1 | undef) $object->log_file("filename" | $filehandle | \&coderef | undef) $object->manual_stty(0 | 1 | undef) $object->match_max($max_buffersize or undef) $object->pid(); $object->send_slow($delay, @strings_to_send) $object->set_group(@listen_group_objects | undef) $object->set_seq($sequence,\&function,\@parameters); There are several configurable package variables that affect the behavior of Expect. They are: $Expect::Debug; $Expect::Exp_Internal; $Expect::IgnoreEintr; $Expect::Log_Group; $Expect::Log_Stdout; $Expect::Manual_Stty; $Expect::Multiline_Matching; $Expect::Do_Soft_Close; =head1 DESCRIPTION See an explanation of L The Expect module is a successor of Comm.pl and a descendent of Chat.pl. It more closely resembles the Tcl Expect language than its predecessors. It does not contain any of the networking code found in Comm.pl. I suspect this would be obsolete anyway given the advent of IO::Socket and external tools such as netcat. Expect.pm is an attempt to have more of a switch() & case feeling to make decision processing more fluid. Three separate types of debugging have been implemented to make code production easier. It is possible to interconnect multiple file handles (and processes) much like Tcl's Expect. An attempt was made to enable all the features of Tcl's Expect without forcing Tcl on the victim programmer :-) . Please, before you consider using Expect, read the FAQs about L and L =head1 USAGE =over 4 =item new Creates a new Expect object, i.e. a pty. You can change parameters on it before actually spawning a command. This is important if you want to modify the terminal settings for the slave. See slave() below. The object returned is actually a reblessed IO::Pty filehandle, so see there for additional methods. =item Expect->exp_init(\*FILEHANDLE) I =item Expect->init(\*FILEHANDLE) Initializes $new_handle_object for use with other Expect functions. It must be passed a B<_reference_> to FILEHANDLE if you want it to work properly. IO::File objects are preferable. Returns a reference to the newly created object. You can use only real filehandles, certain tied filehandles (e.g. Net::SSH2) that lack a fileno() will not work. Net::Telnet objects can be used but have been reported to work only for certain hosts. YMMV. =item Expect->spawn($command, @parameters) I =item $object->spawn($command, @parameters) I =item Expect->new($command, @parameters) Forks and execs $command. Returns an Expect object upon success or C if the fork was unsuccessful or the command could not be found. spawn() passes its parameters unchanged to Perls exec(), so look there for detailed semantics. Note that if spawn cannot exec() the given command, the Expect object is still valid and the next expect() will see "Cannot exec", so you can use that for error handling. Also note that you cannot reuse an object with an already spawned command, even if that command has exited. Sorry, but you have to allocate a new object... =item $object->debug(0 | 1 | 2 | 3 | undef) Sets debug level for $object. 1 refers to general debugging information, 2 refers to verbose debugging and 0 refers to no debugging. If you call debug() with no parameters it will return the current debugging level. When the object is created the debugging level will match that $Expect::Debug, normally 0. The '3' setting is new with 1.05, and adds the additional functionality of having the _full_ accumulated buffer printed every time data is read from an Expect object. This was implemented by request. I recommend against using this unless you think you need it as it can create quite a quantity of output under some circumstances.. =item $object->exp_internal(1 | 0) Sets/unsets 'exp_internal' debugging. This is similar in nature to its Tcl counterpart. It is extremely valuable when debugging expect() sequences. When the object is created the exp_internal setting will match the value of $Expect::Exp_Internal, normally 0. Returns the current setting if called without parameters. It is highly recommended that you make use of the debugging features lest you have angry code. =item $object->raw_pty(1 | 0) Set pty to raw mode before spawning. This disables echoing, CR->LF translation and an ugly hack for broken Solaris TTYs (which send to slow things down) and thus gives a more pipe-like behaviour (which is important if you want to transfer binary content). Note that this must be set I spawning the program. =item $object->stty(qw(mode1 mode2...)) Sets the tty mode for $object's associated terminal to the given modes. Note that on many systems the master side of the pty is not a tty, so you have to modify the slave pty instead, see next item. This needs IO::Stty installed, which is no longer required. =item $object->slave() Returns a filehandle to the slave part of the pty. Very useful in modifying the terminal settings: $object->slave->stty(qw(raw -echo)); Typical values are 'sane', 'raw', and 'raw -echo'. Note that I recommend setting the terminal to 'raw' or 'raw -echo', as this avoids a lot of hassle and gives pipe-like (i.e. transparent) behaviour (without the buffering issue). =item $object->print(@strings) I =item $object->send(@strings) Sends the given strings to the spawned command. Note that the strings are not logged in the logfile (see print_log_file) but will probably be echoed back by the pty, depending on pty settings (default is echo) and thus end up there anyway. This must also be taken into account when expect()ing for an answer: the next string will be the command just sent. I suggest setting the pty to raw, which disables echo and makes the pty transparently act like a bidirectional pipe. =item $object->expect($timeout, @match_patterns) =over 4 =item Simple interface Given $timeout in seconds Expect will wait for $object's handle to produce one of the match_patterns, which are matched exactly by default. If you want a regexp match, use a regexp object (C) or prefix the pattern with '-re'. $object->expect(15, 'match me exactly', qr/match\s+me\s+exactly/); $object->expect(15, 'match me exactly','-re','match\s+me\s+exactly'); Due to o/s limitations $timeout should be a round number. If $timeout is 0 Expect will check one time to see if $object's handle contains any of the match_patterns. If $timeout is undef Expect will wait forever for a pattern to match. If you don't want to explicitly put the timeout on all calls to C, you can set it via the C method . If the first argument of C doesn't look like a number, that value will be used. $object->timeout(15); $object->expect('match me exactly','-re','match\s+me\s+exactly'); If called in a scalar context, expect() will return the position of the matched pattern within @matched_patterns, or undef if no pattern was matched. This is a position starting from 1, so if you want to know which of an array of @matched_patterns matched you should subtract one from the return value. If called in an array context expect() will return ($matched_pattern_position, $error, $successfully_matching_string, $before_match, and $after_match). C<$matched_pattern_position> will contain the value that would have been returned if expect() had been called in a scalar context. C<$error> is the error that occurred that caused expect() to return. $error will contain a number followed by a string equivalent expressing the nature of the error. Possible values are undef, indicating no error, '1:TIMEOUT' indicating that $timeout seconds had elapsed without a match, '2:EOF' indicating an eof was read from $object, '3: spawn id($fileno) died' indicating that the process exited before matching and '4:$!' indicating whatever error was set in $ERRNO during the last read on $object's handle or during select(). All handles indicated by set_group plus STDOUT will have all data to come out of $object printed to them during expect() if log_group and log_stdout are set. C<$successfully_matching_string> C<$before_match> C<$after_match> Changed from older versions is the regular expression handling. By default now all strings passed to expect() are treated as literals. To match a regular expression pass '-re' as a parameter in front of the pattern you want to match as a regexp. This change makes it possible to match literals and regular expressions in the same expect() call. Also new is multiline matching. ^ will now match the beginning of lines. Unfortunately, because perl doesn't use $/ in determining where lines break using $ to find the end of a line frequently doesn't work. This is because your terminal is returning "\r\n" at the end of every line. One way to check for a pattern at the end of a line would be to use \r?$ instead of $. Example: Spawning telnet to a host, you might look for the escape character. telnet would return to you "\r\nEscape character is '^]'.\r\n". To find this you might use $match='^Escape char.*\.\r?$'; $telnet->expect(10,'-re',$match); =item New more Tcl/Expect-like interface expect($timeout, '-i', [ $obj1, $obj2, ... ], [ $re_pattern, sub { ...; exp_continue; }, @subparms, ], [ 'eof', sub { ... } ], [ 'timeout', sub { ... }, \$subparm1 ], '-i', [ $objn, ...], '-ex', $exact_pattern, sub { ... }, $exact_pattern, sub { ...; exp_continue_timeout; }, '-re', $re_pattern, sub { ... }, '-i', \@object_list, @pattern_list, ...); It's now possible to expect on more than one connection at a time by specifying 'C<-i>' and a single Expect object or a ref to an array containing Expect objects, e.g. expect($timeout, '-i', $exp1, @patterns_1, '-i', [ $exp2, $exp3 ], @patterns_2_3, ) Furthermore, patterns can now be specified as array refs containing [$regexp, sub { ...}, @optional_subprams] . When the pattern matches, the subroutine is called with parameters ($matched_expect_obj, @optional_subparms). The subroutine can return the symbol `exp_continue' to continue the expect matching with timeout starting anew or return the symbol `exp_continue_timeout' for continuing expect without resetting the timeout count. $exp->expect($timeout, [ qr/username: /i, sub { my $self = shift; $self->send("$username\n"); exp_continue; }], [ qr/password: /i, sub { my $self = shift; $self->send("$password\n"); exp_continue; }], $shell_prompt); `expect' is now exported by default. =back =item $object->exp_before() I =item $object->before() before() returns the 'before' part of the last expect() call. If the last expect() call didn't match anything, exp_before() will return the entire output of the object accumulated before the expect() call finished. Note that this is something different than Tcl Expects before()!! =item $object->exp_after() I =item $object->after() returns the 'after' part of the last expect() call. If the last expect() call didn't match anything, exp_after() will return undef(). =item $object->exp_match() I =item $object->match() returns the string matched by the last expect() call, undef if no string was matched. =item $object->exp_match_number() I =item $object->match_number() exp_match_number() returns the number of the pattern matched by the last expect() call. Keep in mind that the first pattern in a list of patterns is 1, not 0. Returns undef if no pattern was matched. =item $object->exp_matchlist() I =item $object->matchlist() exp_matchlist() returns a list of matched substrings from the brackets () inside the regexp that last matched. ($object->matchlist)[0] thus corresponds to $1, ($object->matchlist)[1] to $2, etc. =item $object->exp_error() I =item $object->error() exp_error() returns the error generated by the last expect() call if no pattern was matched. It is typically useful to examine the value returned by before() to find out what the output of the object was in determining why it didn't match any of the patterns. =item $object->clear_accum() Clear the contents of the accumulator for $object. This gets rid of any residual contents of a handle after expect() or send_slow() such that the next expect() call will only see new data from $object. The contents of the accumulator are returned. =item $object->set_accum($value) Sets the content of the accumulator for $object to $value. The previous content of the accumulator is returned. =item $object->exp_command() I =item $object->command() exp_command() returns the string that was used to spawn the command. Helpful for debugging and for reused patternmatch subroutines. =item $object->exp_exitstatus() I =item $object->exitstatus() Returns the exit status of $object (if it already exited). =item $object->exp_pty_handle() I =item $object->pty_handle() Returns a string representation of the attached pty, for example: `spawn id(5)' (pty has fileno 5), `handle id(7)' (pty was initialized from fileno 7) or `STDIN'. Useful for debugging. =item $object->restart_timeout_upon_receive(0 | 1) If this is set to 1, the expect timeout is retriggered whenever something is received from the spawned command. This allows to perform some aliveness testing and still expect for patterns. $exp->restart_timeout_upon_receive(1); $exp->expect($timeout, [ timeout => \&report_timeout ], [ qr/pattern/ => \&handle_pattern], ); Now the timeout isn't triggered if the command produces any kind of output, i.e. is still alive, but you can act upon patterns in the output. =item $object->notransfer(1 | 0) Do not truncate the content of the accumulator after a match. Normally, the accumulator is set to the remains that come after the matched string. Note that this setting is per object and not per pattern, so if you want to have normal acting patterns that truncate the accumulator, you have to add a $exp->set_accum($exp->after); to their callback, e.g. $exp->notransfer(1); $exp->expect($timeout, # accumulator not truncated, pattern1 will match again [ "pattern1" => sub { my $self = shift; ... } ], # accumulator truncated, pattern2 will not match again [ "pattern2" => sub { my $self = shift; ... $self->set_accum($self->after()); } ], ); This is only a temporary fix until I can rewrite the pattern matching part so it can take that additional -notransfer argument. =item Expect::interconnect(@objects); Read from @objects and print to their @listen_groups until an escape sequence is matched from one of @objects and the associated function returns 0 or undef. The special escape sequence 'EOF' is matched when an object's handle returns an end of file. Note that it is not necessary to include objects that only accept data in @objects since the escape sequence is _read_ from an object. Further note that the listen_group for a write-only object is always empty. Why would you want to have objects listening to STDOUT (for example)? By default every member of @objects _as well as every member of its listen group_ will be set to 'raw -echo' for the duration of interconnection. Setting $object->manual_stty() will stop this behavior per object. The original tty settings will be restored as interconnect exits. For a generic way to interconnect processes, take a look at L. =item Expect::test_handles(@objects) Given a set of objects determines which objects' handles have data ready to be read. B who's members are positions in @objects that have ready handles. Returns undef if there are no such handles ready. =item Expect::version($version_requested or undef); Returns current version of Expect. As of .99 earlier versions are not supported. Too many things were changed to make versioning possible. =item $object->interact( C<\*FILEHANDLE, $escape_sequence>) interact() is essentially a macro for calling interconnect() for connecting 2 processes together. \*FILEHANDLE defaults to \*STDIN and $escape_sequence defaults to undef. Interaction ceases when $escape_sequence is read from B, not $object. $object's listen group will consist solely of \*FILEHANDLE for the duration of the interaction. \*FILEHANDLE will not be echoed on STDOUT. =item $object->log_group(0 | 1 | undef) Set/unset logging of $object to its 'listen group'. If set all objects in the listen group will have output from $object printed to them during $object->expect(), $object->send_slow(), and C. Default value is on. During creation of $object the setting will match the value of $Expect::Log_Group, normally 1. =item $object->log_user(0 | 1 | undef) I =item $object->log_stdout(0 | 1 | undef) Set/unset logging of object's handle to STDOUT. This corresponds to Tcl's log_user variable. Returns current setting if called without parameters. Default setting is off for initialized handles. When a process object is created (not a filehandle initialized with exp_init) the log_stdout setting will match the value of $Expect::Log_Stdout variable, normally 1. If/when you initialize STDIN it is usually associated with a tty which will by default echo to STDOUT anyway, so be careful or you will have multiple echoes. =item $object->log_file("filename" | $filehandle | \&coderef | undef) Log session to a file. All characters send to or received from the spawned process are written to the file. Normally appends to the logfile, but you can pass an additional mode of "w" to truncate the file upon open(): $object->log_file("filename", "w"); Returns the logfilehandle. If called with an undef value, stops logging and closes logfile: $object->log_file(undef); If called without argument, returns the logfilehandle: $fh = $object->log_file(); Can be set to a code ref, which will be called instead of printing to the logfile: $object->log_file(\&myloggerfunc); =item $object->print_log_file(@strings) Prints to logfile (if opened) or calls the logfile hook function. This allows the user to add arbitrary text to the logfile. Note that this could also be done as $object->log_file->print() but would only work for log files, not code hooks. =item $object->set_seq($sequence, \&function, \@function_parameters) During Expect->interconnect() if $sequence is read from $object &function will be executed with parameters @function_parameters. It is B<_highly recommended_> that the escape sequence be a single character since the likelihood is great that the sequence will be broken into to separate reads from the $object's handle, making it impossible to strip $sequence from getting printed to $object's listen group. \&function should be something like 'main::control_w_function' and @function_parameters should be an array defined by the caller, passed by reference to set_seq(). Your function should return a non-zero value if execution of interconnect() is to resume after the function returns, zero or undefined if interconnect() should return after your function returns. The special sequence 'EOF' matches the end of file being reached by $object. See interconnect() for details. =item $object->set_group(@listener_objects) @listener_objects is the list of objects that should have their handles printed to by $object when Expect::interconnect, $object->expect() or $object->send_slow() are called. Calling w/out parameters will return the current list of the listener objects. =item $object->manual_stty(0 | 1 | undef) Sets/unsets whether or not Expect should make reasonable guesses as to when and how to set tty parameters for $object. Will match $Expect::Manual_Stty value (normally 0) when $object is created. If called without parameters manual_stty() will return the current manual_stty setting. =item $object->match_max($maximum_buffer_length | undef) I =item $object->max_accum($maximum_buffer_length | undef) Set the maximum accumulator size for object. This is useful if you think that the accumulator will grow out of hand during expect() calls. Since the buffer will be matched by every match_pattern it may get slow if the buffer gets too large. Returns current value if called without parameters. Not defined by default. =item $object->notransfer(0 | 1) If set, matched strings will not be deleted from the accumulator. Returns current value if called without parameters. False by default. =item $object->exp_pid() I =item $object->pid() Return pid of $object, if one exists. Initialized filehandles will not have pids (of course). =item $object->send_slow($delay, @strings); print each character from each string of @strings one at a time with $delay seconds before each character. This is handy for devices such as modems that can be annoying if you send them data too fast. After each character $object will be checked to determine whether or not it has any new data ready and if so update the accumulator for future expect() calls and print the output to STDOUT and @listen_group if log_stdout and log_group are appropriately set. =back =head2 Configurable Package Variables: =over 4 =item $Expect::Debug Defaults to 0. Newly created objects have a $object->debug() value of $Expect::Debug. See $object->debug(); =item $Expect::Do_Soft_Close Defaults to 0. When destroying objects, soft_close may take up to half a minute to shut everything down. From now on, only hard_close will be called, which is less polite but still gives the process a chance to terminate properly. Set this to '1' for old behaviour. =item $Expect::Exp_Internal Defaults to 0. Newly created objects have a $object->exp_internal() value of $Expect::Exp_Internal. See $object->exp_internal(). =item $Expect::IgnoreEintr Defaults to 0. If set to 1, when waiting for new data, Expect will ignore EINTR errors and restart the select() call instead. =item $Expect::Log_Group Defaults to 1. Newly created objects have a $object->log_group() value of $Expect::Log_Group. See $object->log_group(). =item $Expect::Log_Stdout Defaults to 1 for spawned commands, 0 for file handles attached with exp_init(). Newly created objects have a $object->log_stdout() value of $Expect::Log_Stdout. See $object->log_stdout(). =item $Expect::Manual_Stty Defaults to 0. Newly created objects have a $object->manual_stty() value of $Expect::Manual_Stty. See $object->manual_stty(). =item $Expect::Multiline_Matching Defaults to 1. Affects whether or not expect() uses the /m flag for doing regular expression matching. If set to 1 /m is used. This makes a difference when you are trying to match ^ and $. If you have this on you can match lines in the middle of a page of output using ^ and $ instead of it matching the beginning and end of the entire expression. I think this is handy. The $Expect::Multiline_Matching turns on and off Expect's multi-line matching mode. But this only has an effect if you pass in a string, and then use '-re' mode. If you pass in a regular expression value (via qr//), then the qr//'s own flags are preserved irrespective of what it gets interpolated into. There was a bug in Perl 5.8.x where interpolating a regex without /m into a match with /m would incorrectly apply the /m to the inner regex too, but this was fixed in Perl 5.10. The correct behavior, as seen in Perl 5.10, is that if you pass in a regex (via qr//), then $Expect::Multiline_Matching has no effect. So if you pass in a regex, then you must use the qr's flags to control whether it is multiline (which by default it is not, opposite of the default behavior of Expect). =back =head1 CONTRIBUTIONS Lee Eakin has ported the kibitz script from Tcl/Expect to Perl/Expect. Jeff Carr provided a simple example of how handle terminal window resize events (transmitted via the WINCH signal) in a ssh session. You can find both scripts in the examples/ subdir. Thanks to both! Historical notes: There are still a few lines of code dating back to the inspirational Comm.pl and Chat.pl modules without which this would not have been possible. Kudos to Eric Arnold and Randal 'Nuke your NT box with one line of perl code' Schwartz for making these available to the perl public. As of .98 I think all the old code is toast. No way could this have been done without it though. Special thanks to Graham Barr for helping make sense of the IO::Handle stuff as well as providing the highly recommended IO::Tty module. =head1 REFERENCES Mark Rogaski wrote: "I figured that you'd like to know that Expect.pm has been very useful to AT&T Labs over the past couple of years (since I first talked to Austin about design decisions). We use Expect.pm for managing the switches in our network via the telnet interface, and such automation has significantly increased our reliability. So, you can honestly say that one of the largest digital networks in existence (AT&T Frame Relay) uses Expect.pm quite extensively." =head1 FAQ - Frequently Asked Questions This is a growing collection of things that might help. Please send you questions that are not answered here to RGiersig@cpan.org =head2 What systems does Expect run on? Expect itself doesn't have real system dependencies, but the underlying IO::Tty needs pseudoterminals. IO::Stty uses POSIX.pm and Fcntl.pm. I have used it on Solaris, Linux and AIX, others report *BSD and OSF as working. Generally, any modern POSIX Unix should do, but there are exceptions to every rule. Feedback is appreciated. See L for a list of verified systems. =head2 Can I use this module with ActivePerl on Windows? Up to now, the answer was 'No', but this has changed. You still cannot use ActivePerl, but if you use the Cygwin environment (http://sources.redhat.com), which brings its own perl, and have the latest IO::Tty (v0.05 or later) installed, it should work (feedback appreciated). =head2 The examples in the tutorial don't work! The tutorial is hopelessly out of date and needs a serious overhaul. I apologize for this, I have concentrated my efforts mainly on the functionality. Volunteers welcomed. =head2 How can I find out what Expect is doing? If you set $Expect::Exp_Internal = 1; Expect will tell you very verbosely what it is receiving and sending, what matching it is trying and what it found. You can do this on a per-command base with $exp->exp_internal(1); You can also set $Expect::Debug = 1; # or 2, 3 for more verbose output or $exp->debug(1); which gives you even more output. =head2 I am seeing the output of the command I spawned. Can I turn that off? Yes, just set $Expect::Log_Stdout = 0; to globally disable it or $exp->log_stdout(0); for just that command. 'log_user' is provided as an alias so Tcl/Expect user get a DWIM experience... :-) =head2 No, I mean that when I send some text to the spawned process, it gets echoed back and I have to deal with it in the next expect. This is caused by the pty, which has probably 'echo' enabled. A solution would be to set the pty to raw mode, which in general is cleaner for communication between two programs (no more unexpected character translations). Unfortunately this would break a lot of old code that sends "\r" to the program instead of "\n" (translating this is also handled by the pty), so I won't add this to Expect just like that. But feel free to experiment with C<$exp-Eraw_pty(1)>. =head2 How do I send control characters to a process? A: You can send any characters to a process with the print command. To represent a control character in Perl, use \c followed by the letter. For example, control-G can be represented with "\cG" . Note that this will not work if you single-quote your string. So, to send control-C to a process in $exp, do: print $exp "\cC"; Or, if you prefer: $exp->send("\cC"); The ability to include control characters in a string like this is provided by Perl, not by Expect.pm . Trying to learn Expect.pm without a thorough grounding in Perl can be very daunting. We suggest you look into some of the excellent Perl learning material, such as the books _Programming Perl_ and _Learning Perl_ by O'Reilly, as well as the extensive online Perl documentation available through the perldoc command. =head2 My script fails from time to time without any obvious reason. It seems that I am sometimes loosing output from the spawned program. You could be exiting too fast without giving the spawned program enough time to finish. Try adding $exp->soft_close() to terminate the program gracefully or do an expect() for 'eof'. Alternatively, try adding a 'sleep 1' after you spawn() the program. It could be that pty creation on your system is just slow (but this is rather improbable if you are using the latest IO-Tty). =head2 I want to automate password entry for su/ssh/scp/rsh/... You shouldn't use Expect for this. Putting passwords, especially root passwords, into scripts in clear text can mean severe security problems. I strongly recommend using other means. For 'su', consider switching to 'sudo', which gives you root access on a per-command and per-user basis without the need to enter passwords. 'ssh'/'scp' can be set up with RSA authentication without passwords. 'rsh' can use the .rhost mechanism, but I'd strongly suggest to switch to 'ssh'; to mention 'rsh' and 'security' in the same sentence makes an oxymoron. It will work for 'telnet', though, and there are valid uses for it, but you still might want to consider using 'ssh', as keeping cleartext passwords around is very insecure. =head2 I want to use Expect to automate [anything with a buzzword]... Are you sure there is no other, easier way? As a rule of thumb, Expect is useful for automating things that expect to talk to a human, where no formal standard applies. For other tasks that do follow a well-defined protocol, there are often better-suited modules that already can handle those protocols. Don't try to do HTTP requests by spawning telnet to port 80, use LWP instead. To automate FTP, take a look at L or C (http://www.ncftp.org). You don't use a screwdriver to hammer in your nails either, or do you? =head2 Is it possible to use threads with Expect? Basically yes, with one restriction: you must spawn() your programs in the main thread and then pass the Expect objects to the handling threads. The reason is that spawn() uses fork(), and L: "Thinking of mixing fork() and threads? Please lie down and wait until the feeling passes." =head2 I want to log the whole session to a file. Use $exp->log_file("filename"); or $exp->log_file($filehandle); or even $exp->log_file(\&log_procedure); for maximum flexibility. Note that the logfile is appended to by default, but you can specify an optional mode "w" to truncate the logfile: $exp->log_file("filename", "w"); To stop logging, just call it with a false argument: $exp->log_file(undef); =head2 How can I turn off multi-line matching for my regexps? To globally unset multi-line matching for all regexps: $Expect::Multiline_Matching = 0; You can do that on a per-regexp basis by stating C<(?-m)> inside the regexp (you need perl5.00503 or later for that). =head2 How can I expect on multiple spawned commands? You can use the B<-i> parameter to specify a single object or a list of Expect objects. All following patterns will be evaluated against that list. You can specify B<-i> multiple times to create groups of objects and patterns to match against within the same expect statement. This works just like in Tcl/Expect. See the source example below. =head2 I seem to have problems with ptys! Well, pty handling is really a black magic, as it is extremely system dependent. I have extensively revised IO-Tty, so these problems should be gone. If your system is listed in the "verified" list of IO::Tty, you probably have some non-standard setup, e.g. you compiled your Linux-kernel yourself and disabled ptys. Please ask your friendly sysadmin for help. If your system is not listed, unpack the latest version of IO::Tty, do a 'perl Makefile.PL; make; make test; uname C<-a>' and send me the results and I'll see what I can deduce from that. =head2 I just want to read the output of a process without expect()ing anything. How can I do this? [ Are you sure you need Expect for this? How about qx() or open("prog|")? ] By using expect without any patterns to match. $process->expect(undef); # Forever until EOF $process->expect($timeout); # For a few seconds $process->expect(0); # Is there anything ready on the handle now? =head2 Ok, so now how do I get what was read on the handle? $read = $process->before(); =head2 Where's IO::Pty? Find it on CPAN as IO-Tty, which provides both. =head2 How come when I automate the passwd program to change passwords for me passwd dies before changing the password sometimes/every time? What's happening is you are closing the handle before passwd exits. When you close the handle to a process, it is sent a signal (SIGPIPE?) telling it that STDOUT has gone away. The default behavior for processes is to die in this circumstance. Two ways you can make this not happen are: $process->soft_close(); This will wait 15 seconds for a process to come up with an EOF by itself before killing it. $process->expect(undef); This will wait forever for the process to match an empty set of patterns. It will return when the process hits an EOF. As a rule, you should always expect() the result of your transaction before you continue with processing. =head2 How come when I try to make a logfile with log_file() or set_group() it doesn't print anything after the last time I run expect()? Output is only printed to the logfile/group when Expect reads from the process, during expect(), send_slow() and interconnect(). One way you can force this is to make use of $process->expect(undef); and $process->expect(0); which will make expect() run with an empty pattern set forever or just for an instant to capture the output of $process. The output is available in the accumulator, so you can grab it using $process->before(). =head2 I seem to have problems with terminal settings, double echoing, etc. Tty settings are a major pain to keep track of. If you find unexpected behavior such as double-echoing or a frozen session, doublecheck the documentation for default settings. When in doubt, handle them yourself using $exp->stty() and manual_stty() functions. As of .98 you shouldn't have to worry about stty settings getting fouled unless you use interconnect or intentionally change them (like doing -echo to get a password). If you foul up your terminal's tty settings, kill any hung processes and enter 'stty sane' at a shell prompt. This should make your terminal manageable again. Note that IO::Tty returns ptys with your systems default setting regarding echoing, CRLF translation etc. and Expect does not change them. I have considered setting the ptys to 'raw' without any translation whatsoever, but this would break a lot of existing things, as '\r' translation would not work anymore. On the other hand, a raw pty works much like a pipe and is more WYGIWYE (what you get is what you expect), so I suggest you set it to 'raw' by yourself: $exp = Expect->new; $exp->raw_pty(1); $exp->spawn(...); To disable echo: $exp->slave->stty(qw(-echo)); =head2 I'm spawning a telnet/ssh session and then let the user interact with it. But screen-oriented applications on the other side don't work properly. You have to set the terminal screen size for that. Luckily, IO::Pty already has a method for that, so modify your code to look like this: my $exp = Expect->new; $exp->slave->clone_winsize_from(\*STDIN); $exp->spawn("telnet somehost); Also, some applications need the TERM shell variable set so they know how to move the cursor across the screen. When logging in, the remote shell sends a query (Ctrl-Z I think) and expects the terminal to answer with a string, e.g. 'xterm'. If you really want to go that way (be aware, madness lies at its end), you can handle that and send back the value in $ENV{TERM}. This is only a hand-waving explanation, please figure out the details by yourself. =head2 I set the terminal size as explained above, but if I resize the window, the application does not notice this. You have to catch the signal WINCH ("window size changed"), change the terminal size and propagate the signal to the spawned application: my $exp = Expect->new; $exp->slave->clone_winsize_from(\*STDIN); $exp->spawn("ssh somehost); $SIG{WINCH} = \&winch; sub winch { $exp->slave->clone_winsize_from(\*STDIN); kill WINCH => $exp->pid if $exp->pid; $SIG{WINCH} = \&winch; } $exp->interact(); There is an example file ssh.pl in the examples/ subdir that shows how this works with ssh. Please note that I do strongly object against using Expect to automate ssh login, as there are better way to do that (see L). =head2 I noticed that the test uses a string that resembles, but not exactly matches, a well-known sentence that contains every character. What does that mean? That means you are anal-retentive. :-) [Gotcha there!] =head2 I get a "Could not assign a pty" error when running as a non-root user on an IRIX box? The OS may not be configured to grant additional pty's (pseudo terminals) to non-root users. /usr/sbin/mkpts should be 4755, not 700 for this to work. I don't know about security implications if you do this. =head2 How come I don't notice when the spawned process closes its stdin/out/err?? You are probably on one of the systems where the master doesn't get an EOF when the slave closes stdin/out/err. One possible solution is when you spawn a process, follow it with a unique string that would indicate the process is finished. $process = Expect->spawn('telnet somehost; echo ____END____'); And then $process->expect($timeout,'____END____','other','patterns'); =head1 Source Examples =head2 How to automate login my $telnet = Net::Telnet->new("remotehost") # see Net::Telnet or die "Cannot telnet to remotehost: $!\n";; my $exp = Expect->exp_init($telnet); # deprecated use of spawned telnet command # my $exp = Expect->spawn("telnet localhost") # or die "Cannot spawn telnet: $!\n";; my $spawn_ok; $exp->expect($timeout, [ qr'login: $', sub { $spawn_ok = 1; my $fh = shift; $fh->send("$username\n"); exp_continue; } ], [ 'Password: $', sub { my $fh = shift; print $fh "$password\n"; exp_continue; } ], [ eof => sub { if ($spawn_ok) { die "ERROR: premature EOF in login.\n"; } else { die "ERROR: could not spawn telnet.\n"; } } ], [ timeout => sub { die "No login.\n"; } ], '-re', qr'[#>:] $', #' wait for shell prompt, then exit expect ); =head2 How to expect on multiple spawned commands foreach my $cmd (@list_of_commands) { push @commands, Expect->spawn($cmd); } expect($timeout, '-i', \@commands, [ qr"pattern", # find this pattern in output of all commands sub { my $obj = shift; # object that matched print $obj "something\n"; exp_continue; # we don't want to terminate the expect call } ], '-i', $some_other_command, [ "some other pattern", sub { my ($obj, $parmref) = @_; # ... # now we exit the expect command }, \$parm ], ); =head2 How to propagate terminal sizes my $exp = Expect->new; $exp->slave->clone_winsize_from(\*STDIN); $exp->spawn("ssh somehost); $SIG{WINCH} = \&winch; sub winch { $exp->slave->clone_winsize_from(\*STDIN); kill WINCH => $exp->pid if $exp->pid; $SIG{WINCH} = \&winch; } $exp->interact(); =head1 HOMEPAGE L though the source code is now in GitHub: L =head1 MAILING LISTS There are two mailing lists available, expectperl-announce and expectperl-discuss, at http://lists.sourceforge.net/lists/listinfo/expectperl-announce and http://lists.sourceforge.net/lists/listinfo/expectperl-discuss =head1 BUG TRACKING You can use the CPAN Request Tracker http://rt.cpan.org/ and submit new bugs under http://rt.cpan.org/Ticket/Create.html?Queue=Expect =head1 AUTHORS (c) 1997 Austin Schutz EFE (retired) expect() interface & functionality enhancements (c) 1999-2006 Roland Giersig. This module is now maintained by Dave Jacoby EFE =head1 LICENSE This module can be used under the same terms as Perl. =head1 DISCLAIMER THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. In other words: Use at your own risk. Provided as is. Your mileage may vary. Read the source, Luke! And finally, just to be sure: Any Use of This Product, in Any Manner Whatsoever, Will Increase the Amount of Disorder in the Universe. Although No Liability Is Implied Herein, the Consumer Is Warned That This Process Will Ultimately Lead to the Heat Death of the Universe. =cut PKA0]n3y..perl5/CPAN/Version.pmnu6$package CPAN::Version; use strict; use vars qw($VERSION); $VERSION = "5.5003"; # CPAN::Version::vcmp courtesy Jost Krieger sub vcmp { my($self,$l,$r) = @_; local($^W) = 0; CPAN->debug("l[$l] r[$r]") if $CPAN::DEBUG; # treat undef as zero $l = 0 if $l eq 'undef'; $r = 0 if $r eq 'undef'; return 0 if $l eq $r; # short circuit for quicker success for ($l,$r) { s/_//g; } CPAN->debug("l[$l] r[$r]") if $CPAN::DEBUG; for ($l,$r) { next unless tr/.// > 1 || /^v/; s/^v?/v/; 1 while s/\.0+(\d)/.$1/; # remove leading zeroes per group } CPAN->debug("l[$l] r[$r]") if $CPAN::DEBUG; if ($l=~/^v/ <=> $r=~/^v/) { for ($l,$r) { next if /^v/; $_ = $self->float2vv($_); } } CPAN->debug("l[$l] r[$r]") if $CPAN::DEBUG; my $lvstring = "v0"; my $rvstring = "v0"; if ($] >= 5.006 && $l =~ /^v/ && $r =~ /^v/) { $lvstring = $self->vstring($l); $rvstring = $self->vstring($r); CPAN->debug(sprintf "lv[%vd] rv[%vd]", $lvstring, $rvstring) if $CPAN::DEBUG; } return ( ($l ne "undef") <=> ($r ne "undef") || $lvstring cmp $rvstring || $l <=> $r || $l cmp $r ); } sub vgt { my($self,$l,$r) = @_; $self->vcmp($l,$r) > 0; } sub vlt { my($self,$l,$r) = @_; $self->vcmp($l,$r) < 0; } sub vge { my($self,$l,$r) = @_; $self->vcmp($l,$r) >= 0; } sub vle { my($self,$l,$r) = @_; $self->vcmp($l,$r) <= 0; } sub vstring { my($self,$n) = @_; $n =~ s/^v// or die "CPAN::Version::vstring() called with invalid arg [$n]"; pack "U*", split /\./, $n; } # vv => visible vstring sub float2vv { my($self,$n) = @_; my($rev) = int($n); $rev ||= 0; my($mantissa) = $n =~ /\.(\d{1,12})/; # limit to 12 digits to limit # architecture influence $mantissa ||= 0; $mantissa .= "0" while length($mantissa)%3; my $ret = "v" . $rev; while ($mantissa) { $mantissa =~ s/(\d{1,3})// or die "Panic: length>0 but not a digit? mantissa[$mantissa]"; $ret .= ".".int($1); } # warn "n[$n]ret[$ret]"; $ret =~ s/(\.0)+/.0/; # v1.0.0 => v1.0 $ret; } sub readable { my($self,$n) = @_; $n =~ /^([\w\-\+\.]+)/; return $1 if defined $1 && length($1)>0; # if the first user reaches version v43, he will be treated as "+". # We'll have to decide about a new rule here then, depending on what # will be the prevailing versioning behavior then. if ($] < 5.006) { # or whenever v-strings were introduced # we get them wrong anyway, whatever we do, because 5.005 will # have already interpreted 0.2.4 to be "0.24". So even if he # indexer sends us something like "v0.2.4" we compare wrongly. # And if they say v1.2, then the old perl takes it as "v12" if (defined $CPAN::Frontend) { $CPAN::Frontend->mywarn("Suspicious version string seen [$n]\n"); } else { warn("Suspicious version string seen [$n]\n"); } return $n; } my $better = sprintf "v%vd", $n; CPAN->debug("n[$n] better[$better]") if $CPAN::DEBUG; return $better; } 1; __END__ =head1 NAME CPAN::Version - utility functions to compare CPAN versions =head1 SYNOPSIS use CPAN::Version; CPAN::Version->vgt("1.1","1.1.1"); # 1 bc. 1.1 > 1.001001 CPAN::Version->vlt("1.1","1.1"); # 0 bc. 1.1 not < 1.1 CPAN::Version->vcmp("1.1","1.1.1"); # 1 bc. first is larger CPAN::Version->vcmp("1.1.1","1.1"); # -1 bc. first is smaller CPAN::Version->readable(v1.2.3); # "v1.2.3" CPAN::Version->vstring("v1.2.3"); # v1.2.3 CPAN::Version->float2vv(1.002003); # "v1.2.3" =head1 DESCRIPTION This module mediates between some version that perl sees in a package and the version that is published by the CPAN indexer. It's only written as a helper module for both CPAN.pm and CPANPLUS.pm. As it stands it predates version.pm but has the same goal: make version strings visible and comparable. =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut # Local Variables: # mode: cperl # cperl-indent-level: 4 # End: PKA0]C perl5/CPAN/Plugin.pmnu6$package CPAN::Plugin; use strict; use warnings; our $VERSION = '0.97'; require CPAN; ###################################################################### sub new { # ; my ($class, %params) = @_; my $self = +{ (ref $class ? (%$class) : ()), %params, }; $self = bless $self, ref $class ? ref $class : $class; unless (ref $class) { local $_; no warnings 'once'; $CPAN::META->use_inst ($_) for $self->plugin_requires; } $self; } ###################################################################### sub plugin_requires { # ; } ###################################################################### sub distribution_object { # ; my ($self) = @_; $self->{distribution_object}; } ###################################################################### sub distribution { # ; my ($self) = @_; my $distribution = $self->distribution_object->id; CPAN::Shell->expand("Distribution",$distribution) or $self->frontend->mydie("Unknowns distribution '$distribution'\n"); } ###################################################################### sub distribution_info { # ; my ($self) = @_; CPAN::DistnameInfo->new ($self->distribution->id); } ###################################################################### sub build_dir { # ; my ($self) = @_; my $build_dir = $self->distribution->{build_dir} or $self->frontend->mydie("Distribution has not been built yet, cannot proceed"); } ###################################################################### sub is_xs { # my ($self) = @_; my @xs = glob File::Spec->catfile ($self->build_dir, '*.xs'); # quick try unless (@xs) { require ExtUtils::Manifest; my $manifest_file = File::Spec->catfile ($self->build_dir, "MANIFEST"); my $manifest = ExtUtils::Manifest::maniread($manifest_file); @xs = grep /\.xs$/, keys %$manifest; } scalar @xs; } ###################################################################### package CPAN::Plugin; 1; __END__ =pod =head1 NAME CPAN::Plugin - Base class for CPAN shell extensions =head1 SYNOPSIS package CPAN::Plugin::Flurb; use parent 'CPAN::Plugin'; sub post_test { my ($self, $distribution_object) = @_; $self = $self->new (distribution_object => $distribution_object); ...; } =head1 DESCRIPTION =head2 Alpha Status The plugin system in the CPAN shell was introduced in version 2.07 and is still considered experimental. =head2 How Plugins work? See L. =head1 METHODS =head2 plugin_requires returns list of packages given plugin requires for functionality. This list is evaluated using C<< CPAN->use_inst >> method. =head2 distribution_object Get current distribution object. =head2 distribution =head2 distribution_info =head2 build_dir Simple delegatees for misc parameters derived from distribution =head2 is_xs Predicate to detect whether package contains XS. =head1 AUTHOR Branislav Zahradnik =cut PKA0]< !perl5/CPAN/Kwalify/distroprefs.ddnu6$$VAR1 = { "mapping" => { "comment" => { "type" => "text" }, "cpanconfig" => { "mapping" => { "=" => { "type" => "text" } }, "type" => "map" }, "depends" => { "mapping" => { "build_requires" => { "mapping" => { "=" => { "type" => "text" } }, "type" => "map" }, "configure_requires" => {}, "requires" => {} }, "type" => "map" }, "disabled" => { "enum" => [ 0, 1 ], "type" => "int" }, "features" => { "sequence" => [ { "type" => "text" } ], "type" => "seq" }, "goto" => { "type" => "text" }, "install" => { "mapping" => { "args" => { "sequence" => [ { "type" => "text" } ], "type" => "seq" }, "commandline" => { "type" => "text" }, "eexpect" => { "mapping" => { "mode" => { "enum" => [ "deterministic", "anyorder" ], "type" => "text" }, "reuse" => { "type" => "int" }, "talk" => { "sequence" => [ { "type" => "text" } ], "type" => "seq" }, "timeout" => { "type" => "number" } }, "type" => "map" }, "env" => { "mapping" => { "=" => { "type" => "text" } }, "type" => "map" }, "expect" => { "sequence" => [ { "type" => "text" } ], "type" => "seq" } }, "type" => "map" }, "make" => {}, "match" => { "mapping" => { "distribution" => { "type" => "text" }, "env" => { "mapping" => { "=" => { "type" => "text" } }, "type" => "map" }, "module" => { "type" => "text" }, "perl" => { "type" => "text" }, "perlconfig" => {} }, "type" => "map" }, "patches" => { "sequence" => [ { "type" => "text" } ], "type" => "seq" }, "pl" => {}, "reminder" => { "type" => "text" }, "test" => {} }, "type" => "map" }; $VAR1->{"mapping"}{"depends"}{"mapping"}{"configure_requires"} = $VAR1->{"mapping"}{"depends"}{"mapping"}{"build_requires"}; $VAR1->{"mapping"}{"depends"}{"mapping"}{"requires"} = $VAR1->{"mapping"}{"depends"}{"mapping"}{"build_requires"}; $VAR1->{"mapping"}{"make"} = $VAR1->{"mapping"}{"install"}; $VAR1->{"mapping"}{"match"}{"mapping"}{"perlconfig"} = $VAR1->{"mapping"}{"match"}{"mapping"}{"env"}; $VAR1->{"mapping"}{"pl"} = $VAR1->{"mapping"}{"install"}; $VAR1->{"mapping"}{"test"} = $VAR1->{"mapping"}{"install"}; PKA0]w)#99"perl5/CPAN/Kwalify/distroprefs.ymlnu6$--- type: map mapping: comment: type: text depends: type: map mapping: configure_requires: &requires_common type: map mapping: =: type: text build_requires: *requires_common requires: *requires_common match: type: map mapping: distribution: type: text module: type: text perl: type: text perlconfig: &matchhash_common type: map mapping: =: type: text env: *matchhash_common install: &args_env_expect type: map mapping: args: type: seq sequence: - type: text commandline: type: text env: type: map mapping: =: type: text expect: type: seq sequence: - type: text eexpect: type: map mapping: mode: type: text enum: - deterministic - anyorder timeout: type: number reuse: type: int talk: type: seq sequence: - type: text make: *args_env_expect pl: *args_env_expect test: *args_env_expect patches: type: seq sequence: - type: text disabled: type: int enum: - 0 - 1 goto: type: text cpanconfig: type: map mapping: =: type: text features: type: seq sequence: - type: text reminder: type: text PKA0]z¤!!perl5/CPAN/Plugin/Specfile.pmnu6$=head1 NAME CPAN::Plugin::Specfile - Proof of concept implementation of a trivial CPAN::Plugin =head1 SYNOPSIS # once in the cpan shell o conf plugin_list push CPAN::Plugin::Specfile # make permanent o conf commit # any time in the cpan shell to write a spec file test Acme::Meta # disable # if it is the last in plugin_list: o conf plugin_list pop # otherwise, determine the index to splice: o conf plugin_list # and then use splice, e.g. to splice position 3: o conf plugin_list splice 3 1 =head1 DESCRIPTION Implemented as a post-test hook, this plugin writes a specfile after every successful test run. The content is also written to the terminal. As a side effect, the timestamps of the written specfiles reflect the linear order of all dependencies. B This code is just a small demo how to use the plugin system of the CPAN shell, not a full fledged spec file writer. Do not expect new features in this plugin. =head2 OPTIONS The target directory to store the spec files in can be set using C as in o conf plugin_list push CPAN::Plugin::Specfile=dir,/tmp/specfiles-000042 The default directory for this is the C directory in the I directory. =head1 AUTHOR Andreas Koenig , Branislav Zahradnik =cut package CPAN::Plugin::Specfile; our $VERSION = '0.02'; use File::Path; use File::Spec; sub __accessor { my ($class, $key) = @_; no strict 'refs'; *{$class . '::' . $key} = sub { my $self = shift; if (@_) { $self->{$key} = shift; } return $self->{$key}; }; } BEGIN { __PACKAGE__->__accessor($_) for qw(dir dir_default) } sub new { my($class, @rest) = @_; my $self = bless {}, $class; while (my($arg,$val) = splice @rest, 0, 2) { $self->$arg($val); } $self->dir_default(File::Spec->catdir($CPAN::Config->{cpan_home},"plugins",__PACKAGE__)); $self; } sub post_test { my $self = shift; my $distribution_object = shift; my $distribution = $distribution_object->pretty_id; unless ($CPAN::META->has_inst("CPAN::DistnameInfo")){ $CPAN::Frontend->mydie("CPAN::DistnameInfo not installed; cannot continue"); } my $d = CPAN::Shell->expand("Distribution",$distribution) or $CPAN::Frontend->mydie("Unknowns distribution '$distribution'\n"); my $build_dir = $d->{build_dir} or $CPAN::Frontend->mydie("Distribution has not been built yet, cannot proceed"); my %contains = map {($_ => undef)} $d->containsmods; my @m; my $width = 16; my $header = sub { my($header,$value) = @_; push @m, sprintf("%-s:%*s%s\n", $header, $width-length($header), "", $value); }; my $dni = CPAN::DistnameInfo->new($distribution); my $dist = $dni->dist; my $summary = CPAN::Shell->_guess_manpage($d,\%contains,$dist); $header->("Name", "perl-$dist"); my $version = $dni->version; $header->("Version", $version); $header->("Release", "1%{?dist}"); #Summary: Template processing system #Group: Development/Libraries #License: GPL+ or Artistic #URL: http://www.template-toolkit.org/ #Source0: http://search.cpan.org/CPAN/authors/id/A/AB/ABW/Template-Toolkit-%{version}.tar.gz #Patch0: Template-2.22-SREZIC-01.patch #BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) for my $h_tuple ([Summary => $summary], [Group => "Development/Libraries"], [License =>], [URL =>], [BuildRoot => "%{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)"], [Requires => "perl(:MODULE_COMPAT_%(eval \"`%{__perl} -V:version`\"; echo \$version))"], ) { my($h,$v) = @$h_tuple; $v = "unknown" unless defined $v; $header->($h, $v); } $header->("Source0", sprintf( "http://search.cpan.org/CPAN/authors/id/%s/%s/%s", substr($distribution,0,1), substr($distribution,0,2), $distribution )); require POSIX; my @xs = glob "$build_dir/*.xs"; # quick try unless (@xs) { require ExtUtils::Manifest; my $manifest_file = "$build_dir/MANIFEST"; my $manifest = ExtUtils::Manifest::maniread($manifest_file); @xs = grep /\.xs$/, keys %$manifest; } if (! @xs) { $header->('BuildArch', 'noarch'); } for my $k (sort keys %contains) { my $m = CPAN::Shell->expand("Module",$k); my $v = $contains{$k} = $m->cpan_version; my $vspec = $v eq "undef" ? "" : " = $v"; $header->("Provides", "perl($k)$vspec"); } if (my $prereq_pm = $d->{prereq_pm}) { my %req; for my $reqkey (keys %$prereq_pm) { while (my($k,$v) = each %{$prereq_pm->{$reqkey}}) { $req{$k} = $v; } } if (-e "$build_dir/Build.PL" && ! exists $req{"Module::Build"}) { $req{"Module::Build"} = 0; } for my $k (sort keys %req) { next if $k eq "perl"; my $v = $req{$k}; my $vspec = defined $v && length $v && $v > 0 ? " >= $v" : ""; $header->(BuildRequires => "perl($k)$vspec"); next if $k =~ /^(Module::Build)$/; # MB is always only a # BuildRequires; if we # turn it into a # Requires, then we # would have to make it # a BuildRequires # everywhere we depend # on *one* MB built # module. $header->(Requires => "perl($k)$vspec"); } } push @m, "\n%define _use_internal_dependency_generator 0 %define __find_requires %{nil} %define __find_provides %{nil} "; push @m, "\n%description\n%{summary}.\n"; push @m, "\n%prep\n%setup -q -n $dist-%{version}\n"; if (-e "$build_dir/Build.PL") { # see http://www.redhat.com/archives/rpm-list/2002-July/msg00110.html about RPM_BUILD_ROOT vs %{buildroot} push @m, <<'EOF'; %build %{__perl} Build.PL --installdirs=vendor --libdoc installvendorman3dir ./Build %install rm -rf $RPM_BUILD_ROOT ./Build install destdir=$RPM_BUILD_ROOT create_packlist=0 find $RPM_BUILD_ROOT -depth -type d -exec rmdir {} 2>/dev/null \; %{_fixperms} $RPM_BUILD_ROOT/* %check ./Build test EOF } elsif (-e "$build_dir/Makefile.PL") { push @m, <<'EOF'; %build %{__perl} Makefile.PL INSTALLDIRS=vendor make %{?_smp_mflags} %install rm -rf $RPM_BUILD_ROOT make pure_install DESTDIR=$RPM_BUILD_ROOT find $RPM_BUILD_ROOT -type f -name .packlist -exec rm -f {} ';' find $RPM_BUILD_ROOT -depth -type d -exec rmdir {} 2>/dev/null ';' %{_fixperms} $RPM_BUILD_ROOT/* %check make test EOF } else { $CPAN::Frontend->mydie("'$distribution' has neither a Build.PL nor a Makefile.PL\n"); } push @m, "\n%clean\nrm -rf \$RPM_BUILD_ROOT\n"; my $vendorlib = @xs ? "vendorarch" : "vendorlib"; my $date = POSIX::strftime("%a %b %d %Y", gmtime); my @doc = grep { -e "$build_dir/$_" } qw(README Changes); my $exe_stanza = "\n"; if (my $exe_files = $d->_exe_files) { if (@$exe_files) { $exe_stanza = "%{_mandir}/man1/*.1*\n"; for my $e (@$exe_files) { unless (CPAN->has_inst("File::Basename")) { $CPAN::Frontend->mydie("File::Basename not installed, cannot continue"); } my $basename = File::Basename::basename($e); $exe_stanza .= "/usr/bin/$basename\n"; } } } push @m, < - $version-1 - autogenerated by CPAN::Plugin::Specfile() EOF my $ret = join "", @m; $CPAN::Frontend->myprint($ret); my $target_dir = $self->dir || $self->dir_default; File::Path::mkpath($target_dir); my $outfile = File::Spec->catfile($target_dir, "perl-$dist.spec"); open my $specout, ">", $outfile or $CPAN::Frontend->mydie("Could not open >$outfile: $!"); print $specout $ret; $CPAN::Frontend->myprint("Wrote $outfile"); $ret; } 1; PKA0]2]perl5/CPAN/Nox.pmnu6$package CPAN::Nox; use strict; use vars qw($VERSION @EXPORT); BEGIN{ $CPAN::Suppress_readline=1 unless defined $CPAN::term; } use Exporter (); @CPAN::ISA = ('Exporter'); use CPAN; $VERSION = "5.5001"; $CPAN::META->has_inst('Digest::MD5','no'); $CPAN::META->has_inst('LWP','no'); $CPAN::META->has_inst('Compress::Zlib','no'); @EXPORT = @CPAN::EXPORT; *AUTOLOAD = \&CPAN::AUTOLOAD; 1; __END__ =head1 NAME CPAN::Nox - Wrapper around CPAN.pm without using any XS module =head1 SYNOPSIS Interactive mode: perl -MCPAN::Nox -e shell; =head1 DESCRIPTION This package has the same functionality as CPAN.pm, but tries to prevent the usage of compiled extensions during its own execution. Its primary purpose is a rescue in case you upgraded perl and broke binary compatibility somehow. =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L =cut PKA0]((perl5/CPAN/HTTP/Client.pmnu6$# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::HTTP::Client; use strict; use vars qw(@ISA); use CPAN::HTTP::Credentials; use HTTP::Tiny 0.005; $CPAN::HTTP::Client::VERSION = $CPAN::HTTP::Client::VERSION = "1.9602"; # CPAN::HTTP::Client is adapted from parts of cpanm by Tatsuhiko Miyagawa # and parts of LWP by Gisle Aas sub new { my $class = shift; my %args = @_; for my $k ( keys %args ) { $args{$k} = '' unless defined $args{$k}; } $args{no_proxy} = [split(",", $args{no_proxy}) ] if $args{no_proxy}; return bless \%args, $class; } # This executes a request with redirection (up to 5) and returns the # response structure generated by HTTP::Tiny # # If authentication fails, it will attempt to get new authentication # information and repeat up to 5 times sub mirror { my($self, $uri, $path) = @_; my $want_proxy = $self->_want_proxy($uri); my $http = HTTP::Tiny->new( verify_SSL => 1, $want_proxy ? (proxy => $self->{proxy}) : () ); my ($response, %headers); my $retries = 0; while ( $retries++ < 5 ) { $response = $http->mirror( $uri, $path, {headers => \%headers} ); if ( $response->{status} eq '401' ) { last unless $self->_get_auth_params( $response, 'non_proxy' ); } elsif ( $response->{status} eq '407' ) { last unless $self->_get_auth_params( $response, 'proxy' ); } else { last; # either success or failure } my %headers = ( $self->_auth_headers( $uri, 'non_proxy' ), ( $want_proxy ? $self->_auth_headers($uri, 'proxy') : () ), ); } return $response; } sub _want_proxy { my ($self, $uri) = @_; return unless $self->{proxy}; my($host) = $uri =~ m|://([^/:]+)|; return ! grep { $host =~ /\Q$_\E$/ } @{ $self->{no_proxy} || [] }; } # Generates the authentication headers for a given mode # C is 'proxy' or 'non_proxy' # C<_${mode}_type> is 'basic' or 'digest' # C<_${mode}_params> will be the challenge parameters from the 401/407 headers sub _auth_headers { my ($self, $uri, $mode) = @_; # Get names for our mode-specific attributes my ($type_key, $param_key) = map {"_" . $mode . $_} qw/_type _params/; # If _prepare_auth has not been called, we can't prepare headers return unless $self->{$type_key}; # Get user credentials for mode my $cred_method = "get_" . ($mode ? "proxy" : "non_proxy") ."_credentials"; my ($user, $pass) = CPAN::HTTP::Credentials->$cred_method; # Generate the header for the mode & type my $header = $mode eq 'proxy' ? 'Proxy-Authorization' : 'Authorization'; my $value_method = "_" . $self->{$type_key} . "_auth"; my $value = $self->$value_method($user, $pass, $self->{$param_key}, $uri); # If we didn't get a value, we didn't have the right modules available return $value ? ( $header, $value ) : (); } # Extract authentication parameters from headers, but clear any prior # credentials if we failed (so we might prompt user for password again) sub _get_auth_params { my ($self, $response, $mode) = @_; my $prefix = $mode eq 'proxy' ? 'Proxy' : 'WWW'; my ($type_key, $param_key) = map {"_" . $mode . $_} qw/_type _params/; if ( ! $response->{success} ) { # auth failed my $method = "clear_${mode}_credentials"; CPAN::HTTP::Credentials->$method; delete $self->{$_} for $type_key, $param_key; } ($self->{$type_key}, $self->{$param_key}) = $self->_get_challenge( $response, "${prefix}-Authenticate"); return $self->{$type_key}; } # Extract challenge type and parameters for a challenge list sub _get_challenge { my ($self, $response, $auth_header) = @_; my $auth_list = $response->{headers}(lc $auth_header); return unless defined $auth_list; $auth_list = [$auth_list] unless ref $auth_list; for my $challenge (@$auth_list) { $challenge =~ tr/,/;/; # "," is used to separate auth-params!! ($challenge) = $self->split_header_words($challenge); my $scheme = shift(@$challenge); shift(@$challenge); # no value $challenge = { @$challenge }; # make rest into a hash unless ($scheme =~ /^(basic|digest)$/) { next; # bad scheme } $scheme = $1; # untainted now return ($scheme, $challenge); } return; } # Generate a basic authentication header value sub _basic_auth { my ($self, $user, $pass) = @_; unless ( $CPAN::META->has_usable('MIME::Base64') ) { $CPAN::Frontend->mywarn( "MIME::Base64 is required for 'Basic' style authentication" ); return; } return "Basic " . MIME::Base64::encode_base64("$user\:$pass", q{}); } # Generate a digest authentication header value sub _digest_auth { my ($self, $user, $pass, $auth_param, $uri) = @_; unless ( $CPAN::META->has_usable('Digest::MD5') ) { $CPAN::Frontend->mywarn( "Digest::MD5 is required for 'Digest' style authentication" ); return; } my $nc = sprintf "%08X", ++$self->{_nonce_count}{$auth_param->{nonce}}; my $cnonce = sprintf "%8x", time; my ($path) = $uri =~ m{^\w+?://[^/]+(/.*)$}; $path = "/" unless defined $path; my $md5 = Digest::MD5->new; my(@digest); $md5->add(join(":", $user, $auth_param->{realm}, $pass)); push(@digest, $md5->hexdigest); $md5->reset; push(@digest, $auth_param->{nonce}); if ($auth_param->{qop}) { push(@digest, $nc, $cnonce, ($auth_param->{qop} =~ m|^auth[,;]auth-int$|) ? 'auth' : $auth_param->{qop}); } $md5->add(join(":", 'GET', $path)); push(@digest, $md5->hexdigest); $md5->reset; $md5->add(join(":", @digest)); my($digest) = $md5->hexdigest; $md5->reset; my %resp = map { $_ => $auth_param->{$_} } qw(realm nonce opaque); @resp{qw(username uri response algorithm)} = ($user, $path, $digest, "MD5"); if (($auth_param->{qop} || "") =~ m|^auth([,;]auth-int)?$|) { @resp{qw(qop cnonce nc)} = ("auth", $cnonce, $nc); } my(@order) = qw(username realm qop algorithm uri nonce nc cnonce response opaque); my @pairs; for (@order) { next unless defined $resp{$_}; push(@pairs, "$_=" . qq("$resp{$_}")); } my $auth_value = "Digest " . join(", ", @pairs); return $auth_value; } # split_header_words adapted from HTTP::Headers::Util sub split_header_words { my ($self, @words) = @_; my @res = $self->_split_header_words(@words); for my $arr (@res) { for (my $i = @$arr - 2; $i >= 0; $i -= 2) { $arr->[$i] = lc($arr->[$i]); } } return @res; } sub _split_header_words { my($self, @val) = @_; my @res; for (@val) { my @cur; while (length) { if (s/^\s*(=*[^\s=;,]+)//) { # 'token' or parameter 'attribute' push(@cur, $1); # a quoted value if (s/^\s*=\s*\"([^\"\\]*(?:\\.[^\"\\]*)*)\"//) { my $val = $1; $val =~ s/\\(.)/$1/g; push(@cur, $val); # some unquoted value } elsif (s/^\s*=\s*([^;,\s]*)//) { my $val = $1; $val =~ s/\s+$//; push(@cur, $val); # no value, a lone token } else { push(@cur, undef); } } elsif (s/^\s*,//) { push(@res, [@cur]) if @cur; @cur = (); } elsif (s/^\s*;// || s/^\s+//) { # continue } else { die "This should not happen: '$_'"; } } push(@res, \@cur) if @cur; } @res; } 1; PKA0]3yI I perl5/CPAN/HTTP/Credentials.pmnu6$# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::HTTP::Credentials; use strict; use vars qw($USER $PASSWORD $PROXY_USER $PROXY_PASSWORD); $CPAN::HTTP::Credentials::VERSION = $CPAN::HTTP::Credentials::VERSION = "1.9601"; sub clear_credentials { clear_non_proxy_credentials(); clear_proxy_credentials(); } sub clear_non_proxy_credentials { undef $USER; undef $PASSWORD; } sub clear_proxy_credentials { undef $PROXY_USER; undef $PROXY_PASSWORD; } sub get_proxy_credentials { my $self = shift; if ($PROXY_USER && $PROXY_PASSWORD) { return ($PROXY_USER, $PROXY_PASSWORD); } if ( defined $CPAN::Config->{proxy_user} && $CPAN::Config->{proxy_user} ) { $PROXY_USER = $CPAN::Config->{proxy_user}; $PROXY_PASSWORD = $CPAN::Config->{proxy_pass} || ""; return ($PROXY_USER, $PROXY_PASSWORD); } my $username_prompt = "\nProxy authentication needed! (Note: to permanently configure username and password run o conf proxy_user your_username o conf proxy_pass your_password )\nUsername:"; ($PROXY_USER, $PROXY_PASSWORD) = _get_username_and_password_from_user($username_prompt); return ($PROXY_USER,$PROXY_PASSWORD); } sub get_non_proxy_credentials { my $self = shift; if ($USER && $PASSWORD) { return ($USER, $PASSWORD); } if ( defined $CPAN::Config->{username} ) { $USER = $CPAN::Config->{username}; $PASSWORD = $CPAN::Config->{password} || ""; return ($USER, $PASSWORD); } my $username_prompt = "\nAuthentication needed! (Note: to permanently configure username and password run o conf username your_username o conf password your_password )\nUsername:"; ($USER, $PASSWORD) = _get_username_and_password_from_user($username_prompt); return ($USER,$PASSWORD); } sub _get_username_and_password_from_user { my $username_message = shift; my ($username,$password); ExtUtils::MakeMaker->import(qw(prompt)); $username = prompt($username_message); if ($CPAN::META->has_inst("Term::ReadKey")) { Term::ReadKey::ReadMode("noecho"); } else { $CPAN::Frontend->mywarn( "Warning: Term::ReadKey seems not to be available, your password will be echoed to the terminal!\n" ); } $password = prompt("Password:"); if ($CPAN::META->has_inst("Term::ReadKey")) { Term::ReadKey::ReadMode("restore"); } $CPAN::Frontend->myprint("\n\n"); return ($username,$password); } 1; PKA0]bJGJGperl5/CPAN/Mirrors.pmnu6$# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: =head1 NAME CPAN::Mirrors - Get CPAN mirror information and select a fast one =head1 SYNOPSIS use CPAN::Mirrors; my $mirrors = CPAN::Mirrors->new( $mirrored_by_file ); my $seen = {}; my $best_continent = $mirrors->find_best_continents( { seen => $seen } ); my @mirrors = $mirrors->get_mirrors_by_continents( $best_continent ); my $callback = sub { my( $m ) = @_; printf "%s = %s\n", $m->hostname, $m->rtt }; $mirrors->get_mirrors_timings( \@mirrors, $seen, $callback, %args ); @mirrors = sort { $a->rtt <=> $b->rtt } @mirrors; print "Best mirrors are ", map( { $_->rtt } @mirrors[0..3] ), "\n"; =head1 DESCRIPTION =over =cut package CPAN::Mirrors; use strict; use vars qw($VERSION $urllist $silent); $VERSION = "2.27"; use Carp; use FileHandle; use Fcntl ":flock"; use Net::Ping (); use CPAN::Version; =item new( LOCAL_FILE_NAME ) Create a new CPAN::Mirrors object from LOCAL_FILE_NAME. This file should look like that in http://www.cpan.org/MIRRORED.BY . =cut sub new { my ($class, $file) = @_; croak "CPAN::Mirrors->new requires a filename" unless defined $file; croak "The file [$file] was not found" unless -e $file; my $self = bless { mirrors => [], geography => {}, }, $class; $self->parse_mirrored_by( $file ); return $self; } sub parse_mirrored_by { my ($self, $file) = @_; my $handle = FileHandle->new; $handle->open($file) or croak "Couldn't open $file: $!"; flock $handle, LOCK_SH; $self->_parse($file,$handle); flock $handle, LOCK_UN; $handle->close; } =item continents() Return a list of continents based on those defined in F. =cut sub continents { my ($self) = @_; return sort keys %{$self->{geography} || {}}; } =item countries( [CONTINENTS] ) Return a list of countries based on those defined in F. It only returns countries for the continents you specify (as defined in C). If you don't specify any continents, it returns all of the countries listed in F. =cut sub countries { my ($self, @continents) = @_; @continents = $self->continents unless @continents; my @countries; for my $c (@continents) { push @countries, sort keys %{ $self->{geography}{$c} || {} }; } return @countries; } =item mirrors( [COUNTRIES] ) Return a list of mirrors based on those defined in F. It only returns mirrors for the countries you specify (as defined in C). If you don't specify any countries, it returns all of the mirrors listed in F. =cut sub mirrors { my ($self, @countries) = @_; return @{$self->{mirrors}} unless @countries; my %wanted = map { $_ => 1 } @countries; my @found; for my $m (@{$self->{mirrors}}) { push @found, $m if exists $wanted{$m->country}; } return @found; } =item get_mirrors_by_countries( [COUNTRIES] ) A more sensible synonym for mirrors. =cut sub get_mirrors_by_countries { &mirrors } =item get_mirrors_by_continents( [CONTINENTS] ) Return a list of mirrors for all of continents you specify. If you don't specify any continents, it returns all of the mirrors. You can specify a single continent or an array reference of continents. =cut sub get_mirrors_by_continents { my ($self, $continents ) = @_; $continents = [ $continents ] unless ref $continents; eval { $self->mirrors( $self->get_countries_by_continents( @$continents ) ); }; } =item get_countries_by_continents( [CONTINENTS] ) A more sensible synonym for countries. =cut sub get_countries_by_continents { &countries } =item default_mirror Returns the default mirror, http://www.cpan.org/ . This mirror uses dynamic DNS to give a close mirror. =cut sub default_mirror { CPAN::Mirrored::By->new({ http => 'http://www.cpan.org/'}); } =item best_mirrors C checks for the best mirrors based on the list of continents you pass, or, without that, all continents, as defined by C. It pings each mirror, up to the value of C. In list context, it returns up to C mirrors. In scalar context, it returns the single best mirror. Arguments how_many - the number of mirrors to return. Default: 1 callback - a callback for find_best_continents verbose - true or false on all the whining and moaning. Default: false continents - an array ref of the continents to check external_ping - if true, use external ping via Net::Ping::External. Default: false If you don't specify the continents, C calls C to get the list of continents to check. If you don't have L v2.13 or later, needed for timings, this returns the default mirror. C should be set and then C needs to be installed, if the local network has a transparent proxy. =cut sub best_mirrors { my ($self, %args) = @_; my $how_many = $args{how_many} || 1; my $callback = $args{callback}; my $verbose = defined $args{verbose} ? $args{verbose} : 0; my $continents = $args{continents} || []; $continents = [$continents] unless ref $continents; $args{external_ping} = 0 unless defined $args{external_ping}; my $external_ping = $args{external_ping}; # Old Net::Ping did not do timings at all my $min_version = '2.13'; unless( CPAN::Version->vgt(Net::Ping->VERSION, $min_version) ) { carp sprintf "Net::Ping version is %s (< %s). Returning %s", Net::Ping->VERSION, $min_version, $self->default_mirror; return $self->default_mirror; } my $seen = {}; if ( ! @$continents ) { print "Searching for the best continent ...\n" if $verbose; my @best_continents = $self->find_best_continents( seen => $seen, verbose => $verbose, callback => $callback, external_ping => $external_ping, ); # Only add enough continents to find enough mirrors my $count = 0; for my $continent ( @best_continents ) { push @$continents, $continent; $count += $self->mirrors( $self->countries($continent) ); last if $count >= $how_many; } } return $self->default_mirror unless @$continents; print "Scanning " . join(", ", @$continents) . " ...\n" if $verbose; my $trial_mirrors = $self->get_n_random_mirrors_by_continents( 3 * $how_many, $continents->[0] ); my $timings = $self->get_mirrors_timings( $trial_mirrors, $seen, $callback, %args, ); return $self->default_mirror unless @$timings; $how_many = @$timings if $how_many > @$timings; return wantarray ? @{$timings}[0 .. $how_many-1] : $timings->[0]; } =item get_n_random_mirrors_by_continents( N, [CONTINENTS] ) Returns up to N random mirrors for the specified continents. Specify the continents as an array reference. =cut sub get_n_random_mirrors_by_continents { my( $self, $n, $continents ) = @_; $n ||= 3; $continents = [ $continents ] unless ref $continents; if ( $n <= 0 ) { return wantarray ? () : []; } my @long_list = $self->get_mirrors_by_continents( $continents ); if ( $n eq '*' or $n > @long_list ) { return wantarray ? @long_list : \@long_list; } @long_list = map {$_->[0]} sort {$a->[1] <=> $b->[1]} map {[$_, rand]} @long_list; splice @long_list, $n; # truncate \@long_list; } =item get_mirrors_timings( MIRROR_LIST, SEEN, CALLBACK, %ARGS ); Pings the listed mirrors and returns a list of mirrors sorted in ascending ping times. C is an anonymous array of C objects to ping. The optional argument C is a hash reference used to track the mirrors you've already pinged. The optional argument C is a subroutine reference to call after each ping. It gets the C object after each ping. =cut sub get_mirrors_timings { my( $self, $mirror_list, $seen, $callback, %args ) = @_; $seen = {} unless defined $seen; croak "The mirror list argument must be an array reference" unless ref $mirror_list eq ref []; croak "The seen argument must be a hash reference" unless ref $seen eq ref {}; croak "callback must be a subroutine" if( defined $callback and ref $callback ne ref sub {} ); my $timings = []; for my $m ( @$mirror_list ) { $seen->{$m->hostname} = $m; next unless eval{ $m->http }; if( $self->_try_a_ping( $seen, $m, ) ) { my $ping = $m->ping(%args); next unless defined $ping; # printf "m %s ping %s\n", $m, $ping; push @$timings, $m; $callback->( $m ) if $callback; } else { push @$timings, $seen->{$m->hostname} if defined $seen->{$m->hostname}->rtt; } } my @best = sort { if( defined $a->rtt and defined $b->rtt ) { $a->rtt <=> $b->rtt } elsif( defined $a->rtt and ! defined $b->rtt ) { return -1; } elsif( ! defined $a->rtt and defined $b->rtt ) { return 1; } elsif( ! defined $a->rtt and ! defined $b->rtt ) { return 0; } } @$timings; return wantarray ? @best : \@best; } =item find_best_continents( HASH_REF ); C goes through each continent and pings C random mirrors on that continent. It then orders the continents by ascending median ping time. In list context, it returns the ordered list of continent. In scalar context, it returns the same list as an anonymous array. Arguments: n - the number of hosts to ping for each continent. Default: 3 seen - a hashref of cached hostname ping times verbose - true or false for noisy or quiet. Default: false callback - a subroutine to run after each ping. ping_cache_limit - how long, in seconds, to reuse previous ping times. Default: 1 day The C hash has hostnames as keys and anonymous arrays as values. The anonymous array is a triplet of a C object, a ping time, and the epoch time for the measurement. The callback subroutine gets the C object, the ping time, and measurement time (the same things in the C hashref) as arguments. C doesn't care what the callback does and ignores the return value. With a low value for C, a single mirror might skew the results enough to choose a worse continent. If you have that problem, try a larger value. =cut sub find_best_continents { my ($self, %args) = @_; $args{n} ||= 3; $args{verbose} = 0 unless defined $args{verbose}; $args{seen} = {} unless defined $args{seen}; croak "The seen argument must be a hash reference" unless ref $args{seen} eq ref {}; $args{ping_cache_limit} = 24 * 60 * 60 unless defined $args{ping_cache_limit}; croak "callback must be a subroutine" if( defined $args{callback} and ref $args{callback} ne ref sub {} ); my %medians; CONT: for my $c ( $self->continents ) { my @mirrors = $self->mirrors( $self->countries($c) ); printf "Testing %s (%d mirrors)\n", $c, scalar @mirrors if $args{verbose}; next CONT unless @mirrors; my $n = (@mirrors < $args{n}) ? @mirrors : $args{n}; my @tests; my $tries = 0; RANDOM: while ( @mirrors && @tests < $n && $tries++ < 15 ) { my $m = splice( @mirrors, int(rand(@mirrors)), 1 ); if( $self->_try_a_ping( $args{seen}, $m, $args{ping_cache_limit} )) { $self->get_mirrors_timings( [ $m ], $args{seen}, $args{callback}, %args, ); next RANDOM unless defined $args{seen}{$m->hostname}->rtt; } printf "(%s -> %0.2f ms)", $m->hostname, join ' ', 1000 * $args{seen}{$m->hostname}->rtt if $args{verbose}; push @tests, $args{seen}{$m->hostname}->rtt; } my $median = $self->_get_median_ping_time( \@tests, $args{verbose} ); $medians{$c} = $median if defined $median; } my @best_cont = sort { $medians{$a} <=> $medians{$b} } keys %medians; if ( $args{verbose} ) { print "Median result by continent:\n"; if ( @best_cont ) { for my $c ( @best_cont ) { printf( " %7.2f ms %s\n", $medians{$c}*1000, $c ); } } else { print " **** No results found ****\n" } } return wantarray ? @best_cont : $best_cont[0]; } # retry if sub _try_a_ping { my ($self, $seen, $mirror, $ping_cache_limit ) = @_; ( ! exists $seen->{$mirror->hostname} or ! defined $seen->{$mirror->hostname}->rtt or ! defined $ping_cache_limit or time - $seen->{$mirror->hostname}->ping_time > $ping_cache_limit ) } sub _get_median_ping_time { my ($self, $tests, $verbose ) = @_; my @sorted = sort { $a <=> $b } @$tests; my $median = do { if ( @sorted == 0 ) { undef } elsif ( @sorted == 1 ) { $sorted[0] } elsif ( @sorted % 2 ) { $sorted[ int(@sorted / 2) ] } else { my $mid_high = int(@sorted/2); ($sorted[$mid_high-1] + $sorted[$mid_high])/2; } }; if ($verbose){ if ($median) { printf " => median time: %.2f ms\n", $median * 1000 } else { printf " => **** no median time ****\n"; } } return $median; } # Adapted from Parse::CPAN::MirroredBy by Adam Kennedy sub _parse { my ($self, $file, $handle) = @_; my $output = $self->{mirrors}; my $geo = $self->{geography}; local $/ = "\012"; my $line = 0; my $mirror = undef; while ( 1 ) { # Next line my $string = <$handle>; last if ! defined $string; $line = $line + 1; # Remove the useless lines chomp( $string ); next if $string =~ /^\s*$/; next if $string =~ /^\s*#/; # Hostname or property? if ( $string =~ /^\s/ ) { # Property unless ( $string =~ /^\s+(\w+)\s+=\s+\"(.*)\"$/ ) { croak("Invalid property on line $line"); } my ($prop, $value) = ($1,$2); $mirror ||= {}; if ( $prop eq 'dst_location' ) { my (@location,$continent,$country); @location = (split /\s*,\s*/, $value) and ($continent, $country) = @location[-1,-2]; $continent =~ s/\s\(.*//; $continent =~ s/\W+$//; # if Jarkko doesn't know latitude/longitude $geo->{$continent}{$country} = 1 if $continent && $country; $mirror->{continent} = $continent || "unknown"; $mirror->{country} = $country || "unknown"; } elsif ( $prop eq 'dst_http' ) { $mirror->{http} = $value; } elsif ( $prop eq 'dst_ftp' ) { $mirror->{ftp} = $value; } elsif ( $prop eq 'dst_rsync' ) { $mirror->{rsync} = $value; } else { $prop =~ s/^dst_//; $mirror->{$prop} = $value; } } else { # Hostname unless ( $string =~ /^([\w\.-]+)\:\s*$/ ) { croak("Invalid host name on line $line"); } my $current = $mirror; $mirror = { hostname => "$1" }; if ( $current ) { push @$output, CPAN::Mirrored::By->new($current); } } } if ( $mirror ) { push @$output, CPAN::Mirrored::By->new($mirror); } return; } #--------------------------------------------------------------------------# package CPAN::Mirrored::By; use strict; use Net::Ping (); sub new { my($self,$arg) = @_; $arg ||= {}; bless $arg, $self; } sub hostname { shift->{hostname} } sub continent { shift->{continent} } sub country { shift->{country} } sub http { shift->{http} || '' } sub ftp { shift->{ftp} || '' } sub rsync { shift->{rsync} || '' } sub rtt { shift->{rtt} } sub ping_time { shift->{ping_time} } sub url { my $self = shift; return $self->{http} || $self->{ftp}; } sub ping { my($self, %args) = @_; my $external_ping = $args{external_ping}; if ($external_ping) { eval { require Net::Ping::External } or die "Net::Ping::External required to use external ping command"; } my $ping = Net::Ping->new( $external_ping ? 'external' : $^O eq 'VMS' ? 'icmp' : 'tcp', 1 ); my ($proto) = $self->url =~ m{^([^:]+)}; my $port = $proto eq 'http' ? 80 : 21; return unless $port; if ( $ping->can('port_number') ) { $ping->port_number($port); } else { $ping->{'port_num'} = $port; } $ping->hires(1) if $ping->can('hires'); my ($alive,$rtt) = eval { $ping->ping($self->hostname); }; my $verbose = $args{verbose}; if ($verbose && !$alive) { printf "(host %s not alive)", $self->hostname; } $self->{rtt} = $alive ? $rtt : undef; $self->{ping_time} = time; $self->rtt; } 1; =back =head1 AUTHOR Andreas Koenig C<< >>, David Golden C<< >>, brian d foy C<< >> =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L =cut PKA0]!+perl5/CPAN/Shell.pmnu6$package CPAN::Shell; use strict; # -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: use vars qw( $ADVANCED_QUERY $AUTOLOAD $COLOR_REGISTERED $Help $autoload_recursion $reload @ISA @relo $VERSION ); @relo = ( "CPAN.pm", "CPAN/Author.pm", "CPAN/CacheMgr.pm", "CPAN/Complete.pm", "CPAN/Debug.pm", "CPAN/DeferredCode.pm", "CPAN/Distribution.pm", "CPAN/Distroprefs.pm", "CPAN/Distrostatus.pm", "CPAN/Exception/RecursiveDependency.pm", "CPAN/Exception/yaml_not_installed.pm", "CPAN/FirstTime.pm", "CPAN/FTP.pm", "CPAN/FTP/netrc.pm", "CPAN/HandleConfig.pm", "CPAN/Index.pm", "CPAN/InfoObj.pm", "CPAN/Kwalify.pm", "CPAN/LWP/UserAgent.pm", "CPAN/Module.pm", "CPAN/Prompt.pm", "CPAN/Queue.pm", "CPAN/Reporter/Config.pm", "CPAN/Reporter/History.pm", "CPAN/Reporter/PrereqCheck.pm", "CPAN/Reporter.pm", "CPAN/Shell.pm", "CPAN/SQLite.pm", "CPAN/Tarzip.pm", "CPAN/Version.pm", ); $VERSION = "5.5009"; # record the initial timestamp for reload. $reload = { map {$INC{$_} ? ($_,(stat $INC{$_})[9]) : ()} @relo }; @CPAN::Shell::ISA = qw(CPAN::Debug); use Cwd qw(chdir); use Carp (); $COLOR_REGISTERED ||= 0; $Help = { '?' => \"help", '!' => "eval the rest of the line as perl", a => "whois author", autobundle => "write inventory into a bundle file", b => "info about bundle", bye => \"quit", clean => "clean up a distribution's build directory", # cvs_import d => "info about a distribution", # dump exit => \"quit", failed => "list all failed actions within current session", fforce => "redo a command from scratch", force => "redo a command", get => "download a distribution", h => \"help", help => "overview over commands; 'help ...' explains specific commands", hosts => "statistics about recently used hosts", i => "info about authors/bundles/distributions/modules", install => "install a distribution", install_tested => "install all distributions tested OK", is_tested => "list all distributions tested OK", look => "open a subshell in a distribution's directory", ls => "list distributions matching a fileglob", m => "info about a module", make => "make/build a distribution", mkmyconfig => "write current config into a CPAN/MyConfig.pm file", notest => "run a (usually install) command but leave out the test phase", o => "'o conf ...' for config stuff; 'o debug ...' for debugging", perldoc => "try to get a manpage for a module", q => \"quit", quit => "leave the cpan shell", r => "review upgradable modules", readme => "display the README of a distro with a pager", recent => "show recent uploads to the CPAN", # recompile reload => "'reload cpan' or 'reload index'", report => "test a distribution and send a test report to cpantesters", reports => "info about reported tests from cpantesters", # scripts # smoke test => "test a distribution", u => "display uninstalled modules", upgrade => "combine 'r' command with immediate installation", }; { $autoload_recursion ||= 0; #-> sub CPAN::Shell::AUTOLOAD ; sub AUTOLOAD { ## no critic $autoload_recursion++; my($l) = $AUTOLOAD; my $class = shift(@_); # warn "autoload[$l] class[$class]"; $l =~ s/.*:://; if ($CPAN::Signal) { warn "Refusing to autoload '$l' while signal pending"; $autoload_recursion--; return; } if ($autoload_recursion > 1) { my $fullcommand = join " ", map { "'$_'" } $l, @_; warn "Refusing to autoload $fullcommand in recursion\n"; $autoload_recursion--; return; } if ($l =~ /^w/) { # XXX needs to be reconsidered if ($CPAN::META->has_inst('CPAN::WAIT')) { CPAN::WAIT->$l(@_); } else { $CPAN::Frontend->mywarn(qq{ Commands starting with "w" require CPAN::WAIT to be installed. Please consider installing CPAN::WAIT to use the fulltext index. For this you just need to type install CPAN::WAIT }); } } else { $CPAN::Frontend->mywarn(qq{Unknown shell command '$l'. }. qq{Type ? for help. }); } $autoload_recursion--; } } #-> sub CPAN::Shell::h ; sub h { my($class,$about) = @_; if (defined $about) { my $help; if (exists $Help->{$about}) { if (ref $Help->{$about}) { # aliases $about = ${$Help->{$about}}; } $help = $Help->{$about}; } else { $help = "No help available"; } $CPAN::Frontend->myprint("$about\: $help\n"); } else { my $filler = " " x (80 - 28 - length($CPAN::VERSION)); $CPAN::Frontend->myprint(qq{ Display Information $filler (ver $CPAN::VERSION) command argument description a,b,d,m WORD or /REGEXP/ about authors, bundles, distributions, modules i WORD or /REGEXP/ about any of the above ls AUTHOR or GLOB about files in the author's directory (with WORD being a module, bundle or author name or a distribution name of the form AUTHOR/DISTRIBUTION) Download, Test, Make, Install... get download clean make clean make make (implies get) look open subshell in dist directory test make test (implies make) readme display these README files install make install (implies test) perldoc display POD documentation Upgrade installed modules r WORDs or /REGEXP/ or NONE report updates for some/matching/all upgrade WORDs or /REGEXP/ or NONE upgrade some/matching/all modules Pragmas force CMD try hard to do command fforce CMD try harder notest CMD skip testing Other h,? display this menu ! perl-code eval a perl command o conf [opt] set and query options q quit the cpan shell reload cpan load CPAN.pm again reload index load newer indices autobundle Snapshot recent latest CPAN uploads}); } } *help = \&h; #-> sub CPAN::Shell::a ; sub a { my($self,@arg) = @_; # authors are always UPPERCASE for (@arg) { $_ = uc $_ unless /=/; } $CPAN::Frontend->myprint($self->format_result('Author',@arg)); } #-> sub CPAN::Shell::globls ; sub globls { my($self,$s,$pragmas) = @_; # ls is really very different, but we had it once as an ordinary # command in the Shell (up to rev. 321) and we could not handle # force well then my(@accept,@preexpand); if ($s =~ /[\*\?\/]/) { if ($CPAN::META->has_inst("Text::Glob")) { if (my($au,$pathglob) = $s =~ m|(.*?)/(.*)|) { my $rau = Text::Glob::glob_to_regex(uc $au); CPAN::Shell->debug("au[$au]pathglob[$pathglob]rau[$rau]") if $CPAN::DEBUG; push @preexpand, map { $_->id . "/" . $pathglob } CPAN::Shell->expand_by_method('CPAN::Author',['id'],"/$rau/"); } else { my $rau = Text::Glob::glob_to_regex(uc $s); push @preexpand, map { $_->id } CPAN::Shell->expand_by_method('CPAN::Author', ['id'], "/$rau/"); } } else { $CPAN::Frontend->mydie("Text::Glob not installed, cannot proceed"); } } else { push @preexpand, uc $s; } for (@preexpand) { unless (/^[A-Z0-9\-]+(\/|$)/i) { $CPAN::Frontend->mywarn("ls command rejects argument $_: not an author\n"); next; } push @accept, $_; } my $silent = @accept>1; my $last_alpha = ""; my @results; for my $a (@accept) { my($author,$pathglob); if ($a =~ m|(.*?)/(.*)|) { my $a2 = $1; $pathglob = $2; $author = CPAN::Shell->expand_by_method('CPAN::Author', ['id'], $a2) or $CPAN::Frontend->mydie("No author found for $a2\n"); } else { $author = CPAN::Shell->expand_by_method('CPAN::Author', ['id'], $a) or $CPAN::Frontend->mydie("No author found for $a\n"); } if ($silent) { my $alpha = substr $author->id, 0, 1; my $ad; if ($alpha eq $last_alpha) { $ad = ""; } else { $ad = "[$alpha]"; $last_alpha = $alpha; } $CPAN::Frontend->myprint($ad); } for my $pragma (@$pragmas) { if ($author->can($pragma)) { $author->$pragma(); } } CPAN->debug("author[$author]pathglob[$pathglob]silent[$silent]") if $CPAN::DEBUG; push @results, $author->ls($pathglob,$silent); # silent if # more than one # author for my $pragma (@$pragmas) { my $unpragma = "un$pragma"; if ($author->can($unpragma)) { $author->$unpragma(); } } } @results; } #-> sub CPAN::Shell::local_bundles ; sub local_bundles { my($self,@which) = @_; my($incdir,$bdir,$dh); foreach $incdir ($CPAN::Config->{'cpan_home'},@INC) { my @bbase = "Bundle"; while (my $bbase = shift @bbase) { $bdir = File::Spec->catdir($incdir,split /::/, $bbase); CPAN->debug("bdir[$bdir]\@bbase[@bbase]") if $CPAN::DEBUG; if ($dh = DirHandle->new($bdir)) { # may fail my($entry); for $entry ($dh->read) { next if $entry =~ /^\./; next unless $entry =~ /^\w+(\.pm)?(?!\n)\Z/; if (-d File::Spec->catdir($bdir,$entry)) { push @bbase, "$bbase\::$entry"; } else { next unless $entry =~ s/\.pm(?!\n)\Z//; $CPAN::META->instance('CPAN::Bundle',"$bbase\::$entry"); } } } } } } #-> sub CPAN::Shell::b ; sub b { my($self,@which) = @_; CPAN->debug("which[@which]") if $CPAN::DEBUG; $self->local_bundles; $CPAN::Frontend->myprint($self->format_result('Bundle',@which)); } #-> sub CPAN::Shell::d ; sub d { $CPAN::Frontend->myprint(shift->format_result('Distribution',@_));} #-> sub CPAN::Shell::m ; sub m { # emacs confused here }; sub mimimimimi { # emacs in sync here my $self = shift; my @m = @_; for (@m) { if (m|(?:\w+/)*\w+\.pm$|) { # same regexp in expandany s/.pm$//; s|/|::|g; } } $CPAN::Frontend->myprint($self->format_result('Module',@m)); } #-> sub CPAN::Shell::i ; sub i { my($self) = shift; my(@args) = @_; @args = '/./' unless @args; my(@result); for my $type (qw/Bundle Distribution Module/) { push @result, $self->expand($type,@args); } # Authors are always uppercase. push @result, $self->expand("Author", map { uc $_ } @args); my $result = @result == 1 ? $result[0]->as_string : @result == 0 ? "No objects found of any type for argument @args\n" : join("", (map {$_->as_glimpse} @result), scalar @result, " items found\n", ); $CPAN::Frontend->myprint($result); } #-> sub CPAN::Shell::o ; # CPAN::Shell::o and CPAN::HandleConfig::edit are closely related. 'o # conf' calls through to CPAN::HandleConfig::edit. 'o conf' should # probably have been called 'set' and 'o debug' maybe 'set debug' or # 'debug'; 'o conf ARGS' calls ->edit in CPAN/HandleConfig.pm sub o { my($self,$o_type,@o_what) = @_; $o_type ||= ""; CPAN->debug("o_type[$o_type] o_what[".join(" | ",@o_what)."]\n"); if ($o_type eq 'conf') { my($cfilter); ($cfilter) = $o_what[0] =~ m|^/(.*)/$| if @o_what; if (!@o_what or $cfilter) { # print all things, "o conf" $cfilter ||= ""; my $qrfilter = eval 'qr/$cfilter/'; if ($@) { $CPAN::Frontend->mydie("Cannot parse commandline: $@"); } my($k,$v); my $configpm = CPAN::HandleConfig->require_myconfig_or_config; $CPAN::Frontend->myprint("\$CPAN::Config options from $configpm\:\n"); for $k (sort keys %CPAN::HandleConfig::can) { next unless $k =~ /$qrfilter/; $v = $CPAN::HandleConfig::can{$k}; $CPAN::Frontend->myprint(sprintf " %-18s [%s]\n", $k, $v); } $CPAN::Frontend->myprint("\n"); for $k (sort keys %CPAN::HandleConfig::keys) { next unless $k =~ /$qrfilter/; CPAN::HandleConfig->prettyprint($k); } $CPAN::Frontend->myprint("\n"); } else { if (CPAN::HandleConfig->edit(@o_what)) { } else { $CPAN::Frontend->myprint(qq{Type 'o conf' to view all configuration }. qq{items\n\n}); } } } elsif ($o_type eq 'debug') { my(%valid); @o_what = () if defined $o_what[0] && $o_what[0] =~ /help/i; if (@o_what) { while (@o_what) { my($what) = shift @o_what; if ($what =~ s/^-// && exists $CPAN::DEBUG{$what}) { $CPAN::DEBUG &= $CPAN::DEBUG ^ $CPAN::DEBUG{$what}; next; } if ( exists $CPAN::DEBUG{$what} ) { $CPAN::DEBUG |= $CPAN::DEBUG{$what}; } elsif ($what =~ /^\d/) { $CPAN::DEBUG = $what; } elsif (lc $what eq 'all') { my($max) = 0; for (values %CPAN::DEBUG) { $max += $_; } $CPAN::DEBUG = $max; } else { my($known) = 0; for (keys %CPAN::DEBUG) { next unless lc($_) eq lc($what); $CPAN::DEBUG |= $CPAN::DEBUG{$_}; $known = 1; } $CPAN::Frontend->myprint("unknown argument [$what]\n") unless $known; } } } else { my $raw = "Valid options for debug are ". join(", ",sort(keys %CPAN::DEBUG), 'all'). qq{ or a number. Completion works on the options. }. qq{Case is ignored.}; require Text::Wrap; $CPAN::Frontend->myprint(Text::Wrap::fill("","",$raw)); $CPAN::Frontend->myprint("\n\n"); } if ($CPAN::DEBUG) { $CPAN::Frontend->myprint("Options set for debugging ($CPAN::DEBUG):\n"); my($k,$v); for $k (sort {$CPAN::DEBUG{$a} <=> $CPAN::DEBUG{$b}} keys %CPAN::DEBUG) { $v = $CPAN::DEBUG{$k}; $CPAN::Frontend->myprint(sprintf " %-14s(%s)\n", $k, $v) if $v & $CPAN::DEBUG; } } else { $CPAN::Frontend->myprint("Debugging turned off completely.\n"); } } else { $CPAN::Frontend->myprint(qq{ Known options: conf set or get configuration variables debug set or get debugging options }); } } # CPAN::Shell::paintdots_onreload sub paintdots_onreload { my($ref) = shift; sub { if ( $_[0] =~ /[Ss]ubroutine ([\w:]+) redefined/ ) { my($subr) = $1; ++$$ref; local($|) = 1; # $CPAN::Frontend->myprint(".($subr)"); $CPAN::Frontend->myprint("."); if ($subr =~ /\bshell\b/i) { # warn "debug[$_[0]]"; # It would be nice if we could detect that a # subroutine has actually changed, but for now we # practically always set the GOTOSHELL global $CPAN::GOTOSHELL=1; } return; } warn @_; }; } #-> sub CPAN::Shell::hosts ; sub hosts { my($self) = @_; my $fullstats = CPAN::FTP->_ftp_statistics(); my $history = $fullstats->{history} || []; my %S; # statistics while (my $last = pop @$history) { my $attempts = $last->{attempts} or next; my $start; if (@$attempts) { $start = $attempts->[-1]{start}; if ($#$attempts > 0) { for my $i (0..$#$attempts-1) { my $url = $attempts->[$i]{url} or next; $S{no}{$url}++; } } } else { $start = $last->{start}; } next unless $last->{thesiteurl}; # C-C? bad filenames? $S{start} = $start; $S{end} ||= $last->{end}; my $dltime = $last->{end} - $start; my $dlsize = $last->{filesize} || 0; my $url = ref $last->{thesiteurl} ? $last->{thesiteurl}->text : $last->{thesiteurl}; my $s = $S{ok}{$url} ||= {}; $s->{n}++; $s->{dlsize} ||= 0; $s->{dlsize} += $dlsize/1024; $s->{dltime} ||= 0; $s->{dltime} += $dltime; } my $res; for my $url (sort keys %{$S{ok}}) { next if $S{ok}{$url}{dltime} == 0; # div by zero push @{$res->{ok}}, [@{$S{ok}{$url}}{qw(n dlsize dltime)}, $S{ok}{$url}{dlsize}/$S{ok}{$url}{dltime}, $url, ]; } for my $url (sort keys %{$S{no}}) { push @{$res->{no}}, [$S{no}{$url}, $url, ]; } my $R = ""; # report if ($S{start} && $S{end}) { $R .= sprintf "Log starts: %s\n", $S{start} ? scalar(localtime $S{start}) : "unknown"; $R .= sprintf "Log ends : %s\n", $S{end} ? scalar(localtime $S{end}) : "unknown"; } if ($res->{ok} && @{$res->{ok}}) { $R .= sprintf "\nSuccessful downloads: N kB secs kB/s url\n"; my $i = 20; for (sort { $b->[3] <=> $a->[3] } @{$res->{ok}}) { $R .= sprintf "%4d %8d %5d %9.1f %s\n", @$_; last if --$i<=0; } } if ($res->{no} && @{$res->{no}}) { $R .= sprintf "\nUnsuccessful downloads:\n"; my $i = 20; for (sort { $b->[0] <=> $a->[0] } @{$res->{no}}) { $R .= sprintf "%4d %s\n", @$_; last if --$i<=0; } } $CPAN::Frontend->myprint($R); } # here is where 'reload cpan' is done #-> sub CPAN::Shell::reload ; sub reload { my($self,$command,@arg) = @_; $command ||= ""; $self->debug("self[$self]command[$command]arg[@arg]") if $CPAN::DEBUG; if ($command =~ /^cpan$/i) { my $redef = 0; chdir "$CPAN::iCwd" if $CPAN::iCwd; # may fail my $failed; MFILE: for my $f (@relo) { next unless exists $INC{$f}; my $p = $f; $p =~ s/\.pm$//; $p =~ s|/|::|g; $CPAN::Frontend->myprint("($p"); local($SIG{__WARN__}) = paintdots_onreload(\$redef); $self->_reload_this($f) or $failed++; my $v = eval "$p\::->VERSION"; $CPAN::Frontend->myprint("v$v)"); } $CPAN::Frontend->myprint("\n$redef subroutines redefined\n"); if ($failed) { my $errors = $failed == 1 ? "error" : "errors"; $CPAN::Frontend->mywarn("\n$failed $errors during reload. You better quit ". "this session.\n"); } } elsif ($command =~ /^index$/i) { CPAN::Index->force_reload; } else { $CPAN::Frontend->myprint(qq{cpan re-evals the CPAN modules index re-reads the index files\n}); } } # reload means only load again what we have loaded before #-> sub CPAN::Shell::_reload_this ; sub _reload_this { my($self,$f,$args) = @_; CPAN->debug("f[$f]") if $CPAN::DEBUG; return 1 unless $INC{$f}; # we never loaded this, so we do not # reload but say OK my $pwd = CPAN::anycwd(); CPAN->debug("pwd[$pwd]") if $CPAN::DEBUG; my($file); for my $inc (@INC) { $file = File::Spec->catfile($inc,split /\//, $f); last if -f $file; $file = ""; } CPAN->debug("file[$file]") if $CPAN::DEBUG; my @inc = @INC; unless ($file && -f $file) { # this thingy is not in the INC path, maybe CPAN/MyConfig.pm? $file = $INC{$f}; unless (CPAN->has_inst("File::Basename")) { @inc = File::Basename::dirname($file); } else { # do we ever need this? @inc = substr($file,0,-length($f)-1); # bring in back to me! } } CPAN->debug("file[$file]inc[@inc]") if $CPAN::DEBUG; unless (-f $file) { $CPAN::Frontend->mywarn("Found no file to reload for '$f'\n"); return; } my $mtime = (stat $file)[9]; $reload->{$f} ||= -1; my $must_reload = $mtime != $reload->{$f}; $args ||= {}; $must_reload ||= $args->{reloforce}; # o conf defaults needs this if ($must_reload) { my $fh = FileHandle->new($file) or $CPAN::Frontend->mydie("Could not open $file: $!"); my $content; { local($/); local $^W = 1; $content = <$fh>; } CPAN->debug(sprintf("reload file[%s] content[%s...]",$file,substr($content,0,128))) if $CPAN::DEBUG; my $includefile; if ($includefile = $INC{$f} and -e $includefile) { $f = $includefile; } delete $INC{$f}; local @INC = @inc; eval "require '$f'"; if ($@) { warn $@; return; } $reload->{$f} = $mtime; } else { $CPAN::Frontend->myprint("__unchanged__"); } return 1; } #-> sub CPAN::Shell::mkmyconfig ; sub mkmyconfig { my($self) = @_; if ( my $configpm = $INC{'CPAN/MyConfig.pm'} ) { $CPAN::Frontend->myprint( "CPAN::MyConfig already exists as $configpm.\n" . "Running configuration again...\n" ); require CPAN::FirstTime; CPAN::FirstTime::init($configpm); } else { # force some missing values to be filled in with defaults delete $CPAN::Config->{$_} for qw/build_dir cpan_home keep_source_where histfile/; CPAN::HandleConfig->load( make_myconfig => 1 ); } } #-> sub CPAN::Shell::_binary_extensions ; sub _binary_extensions { my($self) = shift @_; my(@result,$module,%seen,%need,$headerdone); for $module ($self->expand('Module','/./')) { my $file = $module->cpan_file; next if $file eq "N/A"; next if $file =~ /^Contact Author/; my $dist = $CPAN::META->instance('CPAN::Distribution',$file); next if $dist->isa_perl; next unless $module->xs_file; local($|) = 1; $CPAN::Frontend->myprint("."); push @result, $module; } # print join " | ", @result; $CPAN::Frontend->myprint("\n"); return @result; } #-> sub CPAN::Shell::recompile ; sub recompile { my($self) = shift @_; my($module,@module,$cpan_file,%dist); @module = $self->_binary_extensions(); for $module (@module) { # we force now and compile later, so we # don't do it twice $cpan_file = $module->cpan_file; my $pack = $CPAN::META->instance('CPAN::Distribution',$cpan_file); $pack->force; $dist{$cpan_file}++; } for $cpan_file (sort keys %dist) { $CPAN::Frontend->myprint(" CPAN: Recompiling $cpan_file\n\n"); my $pack = $CPAN::META->instance('CPAN::Distribution',$cpan_file); $pack->install; $CPAN::Signal = 0; # it's tempting to reset Signal, so we can # stop a package from recompiling, # e.g. IO-1.12 when we have perl5.003_10 } } #-> sub CPAN::Shell::scripts ; sub scripts { my($self, $arg) = @_; $CPAN::Frontend->mywarn(">>>> experimental command, currently unsupported <<<<\n\n"); for my $req (qw( HTML::LinkExtor Sort::Versions List::Util )) { unless ($CPAN::META->has_inst($req)) { $CPAN::Frontend->mywarn(" $req not available\n"); } } my $p = HTML::LinkExtor->new(); my $indexfile = "/home/ftp/pub/PAUSE/scripts/new/index.html"; unless (-f $indexfile) { $CPAN::Frontend->mydie("found no indexfile[$indexfile]\n"); } $p->parse_file($indexfile); my @hrefs; my $qrarg; if ($arg =~ s|^/(.+)/$|$1|) { $qrarg = eval 'qr/$arg/'; # hide construct from 5.004 } for my $l ($p->links) { my $tag = shift @$l; next unless $tag eq "a"; my %att = @$l; my $href = $att{href}; next unless $href =~ s|^\.\./authors/id/./../||; if ($arg) { if ($qrarg) { if ($href =~ $qrarg) { push @hrefs, $href; } } else { if ($href =~ /\Q$arg\E/) { push @hrefs, $href; } } } else { push @hrefs, $href; } } # now filter for the latest version if there is more than one of a name my %stems; for (sort @hrefs) { my $href = $_; s/-v?\d.*//; my $stem = $_; $stems{$stem} ||= []; push @{$stems{$stem}}, $href; } for (sort keys %stems) { my $highest; if (@{$stems{$_}} > 1) { $highest = List::Util::reduce { Sort::Versions::versioncmp($a,$b) > 0 ? $a : $b } @{$stems{$_}}; } else { $highest = $stems{$_}[0]; } $CPAN::Frontend->myprint("$highest\n"); } } sub _guess_manpage { my($self,$d,$contains,$dist) = @_; $dist =~ s/-/::/g; my $module; if (exists $contains->{$dist}) { $module = $dist; } elsif (1 == keys %$contains) { ($module) = keys %$contains; } my $manpage; if ($module) { my $m = $self->expand("Module",$module); $m->as_string; # called for side-effects, shame $manpage = $m->{MANPAGE}; } else { $manpage = "unknown"; } return $manpage; } #-> sub CPAN::Shell::_specfile ; sub _specfile { die "CPAN::Shell::_specfile() has been moved to CPAN::Plugin::Specfile::post_test()"; } #-> sub CPAN::Shell::report ; sub report { my($self,@args) = @_; unless ($CPAN::META->has_inst("CPAN::Reporter")) { $CPAN::Frontend->mydie("CPAN::Reporter not installed; cannot continue"); } local $CPAN::Config->{test_report} = 1; $self->force("test",@args); # force is there so that the test be # re-run (as documented) } # compare with is_tested #-> sub CPAN::Shell::install_tested sub install_tested { my($self,@some) = @_; $CPAN::Frontend->mywarn("install_tested() must not be called with arguments.\n"), return if @some; CPAN::Index->reload; for my $b (reverse $CPAN::META->_list_sorted_descending_is_tested) { my $yaml = "$b.yml"; unless (-f $yaml) { $CPAN::Frontend->mywarn("No YAML file for $b available, skipping\n"); next; } my $yaml_content = CPAN->_yaml_loadfile($yaml); my $id = $yaml_content->[0]{distribution}{ID}; unless ($id) { $CPAN::Frontend->mywarn("No ID found in '$yaml', skipping\n"); next; } my $do = CPAN::Shell->expandany($id); unless ($do) { $CPAN::Frontend->mywarn("Could not expand ID '$id', skipping\n"); next; } unless ($do->{build_dir}) { $CPAN::Frontend->mywarn("Distro '$id' has no build_dir, skipping\n"); next; } unless ($do->{build_dir} eq $b) { $CPAN::Frontend->mywarn("Distro '$id' has build_dir '$do->{build_dir}' but expected '$b', skipping\n"); next; } push @some, $do; } $CPAN::Frontend->mywarn("No tested distributions found.\n"), return unless @some; @some = grep { $_->{make_test} && ! $_->{make_test}->failed } @some; $CPAN::Frontend->mywarn("No distributions tested with this build of perl found.\n"), return unless @some; # @some = grep { not $_->uptodate } @some; # $CPAN::Frontend->mywarn("No non-uptodate distributions tested with this build of perl found.\n"), # return unless @some; CPAN->debug("some[@some]"); for my $d (@some) { my $id = $d->can("pretty_id") ? $d->pretty_id : $d->id; $CPAN::Frontend->myprint("install_tested: Running for $id\n"); $CPAN::Frontend->mysleep(1); $self->install($d); } } #-> sub CPAN::Shell::upgrade ; sub upgrade { my($self,@args) = @_; $self->install($self->r(@args)); } #-> sub CPAN::Shell::_u_r_common ; sub _u_r_common { my($self) = shift @_; my($what) = shift @_; CPAN->debug("self[$self] what[$what] args[@_]") if $CPAN::DEBUG; Carp::croak "Usage: \$obj->_u_r_common(a|r|u)" unless $what && $what =~ /^[aru]$/; my(@args) = @_; @args = '/./' unless @args; my(@result,$module,%seen,%need,$headerdone, $version_undefs,$version_zeroes, @version_undefs,@version_zeroes); $version_undefs = $version_zeroes = 0; my $sprintf = "%s%-25s%s %9s %9s %s\n"; my @expand = $self->expand('Module',@args); if ($CPAN::DEBUG) { # Looks like noise to me, was very useful for debugging # for metadata cache my $expand = scalar @expand; $CPAN::Frontend->myprint(sprintf "%d matches in the database, time[%d]\n", $expand, time); } my @sexpand; if ($] < 5.008) { # hard to believe that the more complex sorting can lead to # stack curruptions on older perl @sexpand = sort {$a->id cmp $b->id} @expand; } else { @sexpand = map { $_->[1] } sort { $b->[0] <=> $a->[0] || $a->[1]{ID} cmp $b->[1]{ID}, } map { [$_->_is_representative_module, $_ ] } @expand; } if ($CPAN::DEBUG) { $CPAN::Frontend->myprint(sprintf "sorted at time[%d]\n", time); sleep 1; } MODULE: for $module (@sexpand) { my $file = $module->cpan_file; next MODULE unless defined $file; # ?? $file =~ s!^./../!!; my($latest) = $module->cpan_version; my($inst_file) = $module->inst_file; CPAN->debug("file[$file]latest[$latest]") if $CPAN::DEBUG; my($have); return if $CPAN::Signal; my($next_MODULE); eval { # version.pm involved! if ($inst_file) { if ($what eq "a") { $have = $module->inst_version; } elsif ($what eq "r") { $have = $module->inst_version; local($^W) = 0; if ($have eq "undef") { $version_undefs++; push @version_undefs, $module->as_glimpse; } elsif (CPAN::Version->vcmp($have,0)==0) { $version_zeroes++; push @version_zeroes, $module->as_glimpse; } ++$next_MODULE unless CPAN::Version->vgt($latest, $have); # to be pedantic we should probably say: # && !($have eq "undef" && $latest ne "undef" && $latest gt ""); # to catch the case where CPAN has a version 0 and we have a version undef } elsif ($what eq "u") { ++$next_MODULE; } } else { if ($what eq "a") { ++$next_MODULE; } elsif ($what eq "r") { ++$next_MODULE; } elsif ($what eq "u") { $have = "-"; } } }; next MODULE if $next_MODULE; if ($@) { $CPAN::Frontend->mywarn (sprintf("Error while comparing cpan/installed versions of '%s': INST_FILE: %s INST_VERSION: %s %s CPAN_VERSION: %s %s ", $module->id, $inst_file || "", (defined $have ? $have : "[UNDEFINED]"), (ref $have ? ref $have : ""), $latest, (ref $latest ? ref $latest : ""), )); next MODULE; } return if $CPAN::Signal; # this is sometimes lengthy $seen{$file} ||= 0; if ($what eq "a") { push @result, sprintf "%s %s\n", $module->id, $have; } elsif ($what eq "r") { push @result, $module->id; next MODULE if $seen{$file}++; } elsif ($what eq "u") { push @result, $module->id; next MODULE if $seen{$file}++; next MODULE if $file =~ /^Contact/; } unless ($headerdone++) { $CPAN::Frontend->myprint("\n"); $CPAN::Frontend->myprint(sprintf( $sprintf, "", "Package namespace", "", "installed", "latest", "in CPAN file" )); } my $color_on = ""; my $color_off = ""; if ( $COLOR_REGISTERED && $CPAN::META->has_inst("Term::ANSIColor") && $module->description ) { $color_on = Term::ANSIColor::color("green"); $color_off = Term::ANSIColor::color("reset"); } $CPAN::Frontend->myprint(sprintf $sprintf, $color_on, $module->id, $color_off, $have, $latest, $file); $need{$module->id}++; } unless (%need) { if (!@expand || $what eq "u") { $CPAN::Frontend->myprint("No modules found for @args\n"); } elsif ($what eq "r") { $CPAN::Frontend->myprint("All modules are up to date for @args\n"); } } if ($what eq "r") { if ($version_zeroes) { my $s_has = $version_zeroes > 1 ? "s have" : " has"; $CPAN::Frontend->myprint(qq{$version_zeroes installed module$s_has }. qq{a version number of 0\n}); if ($CPAN::Config->{show_zero_versions}) { local $" = "\t"; $CPAN::Frontend->myprint(qq{ they are\n\t@version_zeroes\n}); $CPAN::Frontend->myprint(qq{(use 'o conf show_zero_versions 0' }. qq{to hide them)\n}); } else { $CPAN::Frontend->myprint(qq{(use 'o conf show_zero_versions 1' }. qq{to show them)\n}); } } if ($version_undefs) { my $s_has = $version_undefs > 1 ? "s have" : " has"; $CPAN::Frontend->myprint(qq{$version_undefs installed module$s_has no }. qq{parsable version number\n}); if ($CPAN::Config->{show_unparsable_versions}) { local $" = "\t"; $CPAN::Frontend->myprint(qq{ they are\n\t@version_undefs\n}); $CPAN::Frontend->myprint(qq{(use 'o conf show_unparsable_versions 0' }. qq{to hide them)\n}); } else { $CPAN::Frontend->myprint(qq{(use 'o conf show_unparsable_versions 1' }. qq{to show them)\n}); } } } @result; } #-> sub CPAN::Shell::r ; sub r { shift->_u_r_common("r",@_); } #-> sub CPAN::Shell::u ; sub u { shift->_u_r_common("u",@_); } #-> sub CPAN::Shell::failed ; sub failed { my($self,$only_id,$silent) = @_; my @failed = $self->find_failed($only_id); my $scope; if ($only_id) { $scope = "this command"; } elsif ($CPAN::Index::HAVE_REANIMATED) { $scope = "this or a previous session"; # it might be nice to have a section for previous session and # a second for this } else { $scope = "this session"; } if (@failed) { my $print; my $debug = 0; if ($debug) { $print = join "", map { sprintf "%5d %-45s: %s %s\n", @$_ } sort { $a->[0] <=> $b->[0] } @failed; } else { $print = join "", map { sprintf " %-45s: %s %s\n", @$_[1..3] } sort { $a->[0] <=> $b->[0] || $a->[4] <=> $b->[4] } @failed; } $CPAN::Frontend->myprint("Failed during $scope:\n$print"); } elsif (!$only_id || !$silent) { $CPAN::Frontend->myprint("Nothing failed in $scope\n"); } } sub find_failed { my($self,$only_id) = @_; my @failed; DIST: for my $d (sort { $a->id cmp $b->id } $CPAN::META->all_objects("CPAN::Distribution")) { my $failed = ""; NAY: for my $nosayer ( # order matters! "unwrapped", "writemakefile", "signature_verify", "make", "make_test", "install", "make_clean", ) { next unless exists $d->{$nosayer}; next unless defined $d->{$nosayer}; next unless ( UNIVERSAL::can($d->{$nosayer},"failed") ? $d->{$nosayer}->failed : $d->{$nosayer} =~ /^NO/ ); next NAY if $only_id && $only_id != ( UNIVERSAL::can($d->{$nosayer},"commandid") ? $d->{$nosayer}->commandid : $CPAN::CurrentCommandId ); $failed = $nosayer; last; } next DIST unless $failed; my $id = $d->id; $id =~ s|^./../||; ### XXX need to flag optional modules as '(optional)' if they are # from recommends/suggests -- i.e. *show* failure, but make it clear # it was failure of optional module -- xdg, 2012-04-01 $id = "(optional) $id" if ! $d->{mandatory}; #$print .= sprintf( # " %-45s: %s %s\n", push @failed, ( UNIVERSAL::can($d->{$failed},"failed") ? [ $d->{$failed}->commandid, $id, $failed, $d->{$failed}->text, $d->{$failed}{TIME}||0, !! $d->{mandatory}, ] : [ 1, $id, $failed, $d->{$failed}, 0, !! $d->{mandatory}, ] ); } return @failed; } sub mandatory_dist_failed { my ($self) = @_; return grep { $_->[5] } $self->find_failed($CPAN::CurrentCommandID); } # XXX intentionally undocumented because completely bogus, unportable, # useless, etc. #-> sub CPAN::Shell::status ; sub status { my($self) = @_; require Devel::Size; my $ps = FileHandle->new; open $ps, "/proc/$$/status"; my $vm = 0; while (<$ps>) { next unless /VmSize:\s+(\d+)/; $vm = $1; last; } $CPAN::Frontend->mywarn(sprintf( "%-27s %6d\n%-27s %6d\n", "vm", $vm, "CPAN::META", Devel::Size::total_size($CPAN::META)/1024, )); for my $k (sort keys %$CPAN::META) { next unless substr($k,0,4) eq "read"; warn sprintf " %-26s %6d\n", $k, Devel::Size::total_size($CPAN::META->{$k})/1024; for my $k2 (sort keys %{$CPAN::META->{$k}}) { warn sprintf " %-25s %6d (keys: %6d)\n", $k2, Devel::Size::total_size($CPAN::META->{$k}{$k2})/1024, scalar keys %{$CPAN::META->{$k}{$k2}}; } } } # compare with install_tested #-> sub CPAN::Shell::is_tested sub is_tested { my($self) = @_; CPAN::Index->reload; for my $b (reverse $CPAN::META->_list_sorted_descending_is_tested) { my $time; if ($CPAN::META->{is_tested}{$b}) { $time = scalar(localtime $CPAN::META->{is_tested}{$b}); } else { $time = scalar localtime; $time =~ s/\S/?/g; } $CPAN::Frontend->myprint(sprintf "%s %s\n", $time, $b); } } #-> sub CPAN::Shell::autobundle ; sub autobundle { my($self) = shift; CPAN::HandleConfig->load unless $CPAN::Config_loaded++; my(@bundle) = $self->_u_r_common("a",@_); my($todir) = File::Spec->catdir($CPAN::Config->{'cpan_home'},"Bundle"); File::Path::mkpath($todir); unless (-d $todir) { $CPAN::Frontend->myprint("Couldn't mkdir $todir for some reason\n"); return; } my($y,$m,$d) = (localtime)[5,4,3]; $y+=1900; $m++; my($c) = 0; my($me) = sprintf "Snapshot_%04d_%02d_%02d_%02d", $y, $m, $d, $c; my($to) = File::Spec->catfile($todir,"$me.pm"); while (-f $to) { $me = sprintf "Snapshot_%04d_%02d_%02d_%02d", $y, $m, $d, ++$c; $to = File::Spec->catfile($todir,"$me.pm"); } my($fh) = FileHandle->new(">$to") or Carp::croak "Can't open >$to: $!"; $fh->print( "package Bundle::$me;\n\n", "\$","VERSION = '0.01';\n\n", # hide from perl-reversion "1;\n\n", "__END__\n\n", "=head1 NAME\n\n", "Bundle::$me - Snapshot of installation on ", $Config::Config{'myhostname'}, " on ", scalar(localtime), "\n\n=head1 SYNOPSIS\n\n", "perl -MCPAN -e 'install Bundle::$me'\n\n", "=head1 CONTENTS\n\n", join("\n", @bundle), "\n\n=head1 CONFIGURATION\n\n", Config->myconfig, "\n\n=head1 AUTHOR\n\n", "This Bundle has been generated automatically ", "by the autobundle routine in CPAN.pm.\n", ); $fh->close; $CPAN::Frontend->myprint("\nWrote bundle file $to\n\n"); return $to; } #-> sub CPAN::Shell::expandany ; sub expandany { my($self,$s) = @_; CPAN->debug("s[$s]") if $CPAN::DEBUG; my $module_as_path = ""; if ($s =~ m|(?:\w+/)*\w+\.pm$|) { # same regexp in sub m $module_as_path = $s; $module_as_path =~ s/.pm$//; $module_as_path =~ s|/|::|g; } if ($module_as_path) { if ($module_as_path =~ m|^Bundle::|) { $self->local_bundles; return $self->expand('Bundle',$module_as_path); } else { return $self->expand('Module',$module_as_path) if $CPAN::META->exists('CPAN::Module',$module_as_path); } } elsif ($s =~ m|/| or substr($s,-1,1) eq ".") { # looks like a file or a directory $s = CPAN::Distribution->normalize($s); return $CPAN::META->instance('CPAN::Distribution',$s); # Distributions spring into existence, not expand } elsif ($s =~ m|^Bundle::|) { $self->local_bundles; # scanning so late for bundles seems # both attractive and crumpy: always # current state but easy to forget # somewhere return $self->expand('Bundle',$s); } else { return $self->expand('Module',$s) if $CPAN::META->exists('CPAN::Module',$s); } return; } #-> sub CPAN::Shell::expand ; sub expand { my $self = shift; my($type,@args) = @_; CPAN->debug("type[$type]args[@args]") if $CPAN::DEBUG; my $class = "CPAN::$type"; my $methods = ['id']; for my $meth (qw(name)) { next unless $class->can($meth); push @$methods, $meth; } $self->expand_by_method($class,$methods,@args); } #-> sub CPAN::Shell::expand_by_method ; sub expand_by_method { my $self = shift; my($class,$methods,@args) = @_; my($arg,@m); for $arg (@args) { my($regex,$command); if ($arg =~ m|^/(.*)/$|) { $regex = $1; # FIXME: there seem to be some ='s in the author data, which trigger # a failure here. This needs to be contemplated. # } elsif ($arg =~ m/=/) { # $command = 1; } my $obj; CPAN->debug(sprintf "class[%s]regex[%s]command[%s]", $class, defined $regex ? $regex : "UNDEFINED", defined $command ? $command : "UNDEFINED", ) if $CPAN::DEBUG; if (defined $regex) { if (CPAN::_sqlite_running()) { CPAN::Index->reload; $CPAN::SQLite->search($class, $regex); } for $obj ( $CPAN::META->all_objects($class) ) { unless ($obj && UNIVERSAL::can($obj,"id") && $obj->id) { # BUG, we got an empty object somewhere require Data::Dumper; CPAN->debug(sprintf( "Bug in CPAN: Empty id on obj[%s][%s]", $obj, Data::Dumper::Dumper($obj) )) if $CPAN::DEBUG; next; } for my $method (@$methods) { my $match = eval {$obj->$method() =~ /$regex/i}; if ($@) { my($err) = $@ =~ /^(.+) at .+? line \d+\.$/; $err ||= $@; # if we were too restrictive above $CPAN::Frontend->mydie("$err\n"); } elsif ($match) { push @m, $obj; last; } } } } elsif ($command) { die "equal sign in command disabled (immature interface), ". "you can set ! \$CPAN::Shell::ADVANCED_QUERY=1 to enable it. But please note, this is HIGHLY EXPERIMENTAL code that may go away anytime.\n" unless $ADVANCED_QUERY; my($method,$criterion) = $arg =~ /(.+?)=(.+)/; my($matchcrit) = $criterion =~ m/^~(.+)/; for my $self ( sort {$a->id cmp $b->id} $CPAN::META->all_objects($class) ) { my $lhs = $self->$method() or next; # () for 5.00503 if ($matchcrit) { push @m, $self if $lhs =~ m/$matchcrit/; } else { push @m, $self if $lhs eq $criterion; } } } else { my($xarg) = $arg; if ( $class eq 'CPAN::Bundle' ) { $xarg =~ s/^(Bundle::)?(.*)/Bundle::$2/; } elsif ($class eq "CPAN::Distribution") { $xarg = CPAN::Distribution->normalize($arg); } else { $xarg =~ s/:+/::/g; } if ($CPAN::META->exists($class,$xarg)) { $obj = $CPAN::META->instance($class,$xarg); } elsif ($CPAN::META->exists($class,$arg)) { $obj = $CPAN::META->instance($class,$arg); } else { next; } push @m, $obj; } } @m = sort {$a->id cmp $b->id} @m; if ( $CPAN::DEBUG ) { my $wantarray = wantarray; my $join_m = join ",", map {$_->id} @m; # $self->debug("wantarray[$wantarray]join_m[$join_m]"); my $count = scalar @m; $self->debug("class[$class]wantarray[$wantarray]count m[$count]"); } return wantarray ? @m : $m[0]; } #-> sub CPAN::Shell::format_result ; sub format_result { my($self) = shift; my($type,@args) = @_; @args = '/./' unless @args; my(@result) = $self->expand($type,@args); my $result = @result == 1 ? $result[0]->as_string : @result == 0 ? "No objects of type $type found for argument @args\n" : join("", (map {$_->as_glimpse} @result), scalar @result, " items found\n", ); $result; } #-> sub CPAN::Shell::report_fh ; { my $installation_report_fh; my $previously_noticed = 0; sub report_fh { return $installation_report_fh if $installation_report_fh; if ($CPAN::META->has_usable("File::Temp")) { $installation_report_fh = File::Temp->new( dir => File::Spec->tmpdir, template => 'cpan_install_XXXX', suffix => '.txt', unlink => 0, ); } unless ( $installation_report_fh ) { warn("Couldn't open installation report file; " . "no report file will be generated." ) unless $previously_noticed++; } } } # The only reason for this method is currently to have a reliable # debugging utility that reveals which output is going through which # channel. No, I don't like the colors ;-) # to turn colordebugging on, write # cpan> o conf colorize_output 1 #-> sub CPAN::Shell::colorize_output ; { my $print_ornamented_have_warned = 0; sub colorize_output { my $colorize_output = $CPAN::Config->{colorize_output}; if ($colorize_output && $^O eq 'MSWin32' && !$CPAN::META->has_inst("Win32::Console::ANSI")) { unless ($print_ornamented_have_warned++) { # no myprint/mywarn within myprint/mywarn! warn "Colorize_output is set to true but Win32::Console::ANSI is not installed. To activate colorized output, please install Win32::Console::ANSI.\n\n"; } $colorize_output = 0; } if ($colorize_output && !$CPAN::META->has_inst("Term::ANSIColor")) { unless ($print_ornamented_have_warned++) { # no myprint/mywarn within myprint/mywarn! warn "Colorize_output is set to true but Term::ANSIColor is not installed. To activate colorized output, please install Term::ANSIColor.\n\n"; } $colorize_output = 0; } return $colorize_output; } } #-> sub CPAN::Shell::print_ornamented ; sub print_ornamented { my($self,$what,$ornament) = @_; return unless defined $what; local $| = 1; # Flush immediately if ( $CPAN::Be_Silent ) { # WARNING: variable Be_Silent is poisoned and must be eliminated. print {report_fh()} $what; return; } my $swhat = "$what"; # stringify if it is an object if ($CPAN::Config->{term_is_latin}) { # note: deprecated, need to switch to $LANG and $LC_* # courtesy jhi: $swhat =~ s{([\xC0-\xDF])([\x80-\xBF])}{chr(ord($1)<<6&0xC0|ord($2)&0x3F)}eg; #}; } if ($self->colorize_output) { if ( $CPAN::DEBUG && $swhat =~ /^Debug\(/ ) { # if you want to have this configurable, please file a bug report $ornament = $CPAN::Config->{colorize_debug} || "black on_cyan"; } my $color_on = eval { Term::ANSIColor::color($ornament) } || ""; if ($@) { print "Term::ANSIColor rejects color[$ornament]: $@\n Please choose a different color (Hint: try 'o conf init /color/')\n"; } # GGOLDBACH/Test-GreaterVersion-0.008 broke without this # $trailer construct. We want the newline be the last thing if # there is a newline at the end ensuring that the next line is # empty for other players my $trailer = ""; $trailer = $1 if $swhat =~ s/([\r\n]+)\z//; print $color_on, $swhat, Term::ANSIColor::color("reset"), $trailer; } else { print $swhat; } } #-> sub CPAN::Shell::myprint ; # where is myprint/mywarn/Frontend/etc. documented? Where to use what? # I think, we send everything to STDOUT and use print for normal/good # news and warn for news that need more attention. Yes, this is our # working contract for now. sub myprint { my($self,$what) = @_; $self->print_ornamented($what, $CPAN::Config->{colorize_print}||'bold blue on_white', ); } my %already_printed; #-> sub CPAN::Shell::mywarnonce ; sub myprintonce { my($self,$what) = @_; $self->myprint($what) unless $already_printed{$what}++; } sub optprint { my($self,$category,$what) = @_; my $vname = $category . "_verbosity"; CPAN::HandleConfig->load unless $CPAN::Config_loaded++; if (!$CPAN::Config->{$vname} || $CPAN::Config->{$vname} =~ /^v/ ) { $CPAN::Frontend->myprint($what); } } #-> sub CPAN::Shell::myexit ; sub myexit { my($self,$what) = @_; $self->myprint($what); exit; } #-> sub CPAN::Shell::mywarn ; sub mywarn { my($self,$what) = @_; $self->print_ornamented($what, $CPAN::Config->{colorize_warn}||'bold red on_white'); } my %already_warned; #-> sub CPAN::Shell::mywarnonce ; sub mywarnonce { my($self,$what) = @_; $self->mywarn($what) unless $already_warned{$what}++; } # only to be used for shell commands #-> sub CPAN::Shell::mydie ; sub mydie { my($self,$what) = @_; $self->mywarn($what); # If it is the shell, we want the following die to be silent, # but if it is not the shell, we would need a 'die $what'. We need # to take care that only shell commands use mydie. Is this # possible? die "\n"; } # sub CPAN::Shell::colorable_makemaker_prompt ; sub colorable_makemaker_prompt { my($foo,$bar,$ornament) = @_; $ornament ||= "colorize_print"; if (CPAN::Shell->colorize_output) { my $ornament = $CPAN::Config->{$ornament}||'bold blue on_white'; my $color_on = eval { Term::ANSIColor::color($ornament); } || ""; print $color_on; } my $ans = ExtUtils::MakeMaker::prompt($foo,$bar); if (CPAN::Shell->colorize_output) { print Term::ANSIColor::color('reset'); } return $ans; } # use this only for unrecoverable errors! #-> sub CPAN::Shell::unrecoverable_error ; sub unrecoverable_error { my($self,$what) = @_; my @lines = split /\n/, $what; my $longest = 0; for my $l (@lines) { $longest = length $l if length $l > $longest; } $longest = 62 if $longest > 62; for my $l (@lines) { if ($l =~ /^\s*$/) { $l = "\n"; next; } $l = "==> $l"; if (length $l < 66) { $l = pack "A66 A*", $l, "<=="; } $l .= "\n"; } unshift @lines, "\n"; $self->mydie(join "", @lines); } #-> sub CPAN::Shell::mysleep ; sub mysleep { return if $ENV{AUTOMATED_TESTING} || ! -t STDOUT; my($self, $sleep) = @_; if (CPAN->has_inst("Time::HiRes")) { Time::HiRes::sleep($sleep); } else { sleep($sleep < 1 ? 1 : int($sleep + 0.5)); } } #-> sub CPAN::Shell::setup_output ; sub setup_output { return if -t STDOUT; my $odef = select STDERR; $| = 1; select STDOUT; $| = 1; select $odef; } #-> sub CPAN::Shell::rematein ; # RE-adme||MA-ke||TE-st||IN-stall : nearly everything runs through here sub rematein { my $self = shift; # this variable was global and disturbed programmers, so localize: local $CPAN::Distrostatus::something_has_failed_at; my($meth,@some) = @_; my @pragma; while($meth =~ /^(ff?orce|notest)$/) { push @pragma, $meth; $meth = shift @some or $CPAN::Frontend->mydie("Pragma $pragma[-1] used without method: ". "cannot continue"); } setup_output(); CPAN->debug("pragma[@pragma]meth[$meth]some[@some]") if $CPAN::DEBUG; # Here is the place to set "test_count" on all involved parties to # 0. We then can pass this counter on to the involved # distributions and those can refuse to test if test_count > X. In # the first stab at it we could use a 1 for "X". # But when do I reset the distributions to start with 0 again? # Jost suggested to have a random or cycling interaction ID that # we pass through. But the ID is something that is just left lying # around in addition to the counter, so I'd prefer to set the # counter to 0 now, and repeat at the end of the loop. But what # about dependencies? They appear later and are not reset, they # enter the queue but not its copy. How do they get a sensible # test_count? # With configure_requires, "get" is vulnerable in recursion. my $needs_recursion_protection = "get|make|test|install"; # construct the queue my($s,@s,@qcopy); STHING: foreach $s (@some) { my $obj; if (ref $s) { CPAN->debug("s is an object[$s]") if $CPAN::DEBUG; $obj = $s; } elsif ($s =~ m|[\$\@\%]|) { # looks like a perl variable } elsif ($s =~ m|^/|) { # looks like a regexp if (substr($s,-1,1) eq ".") { $obj = CPAN::Shell->expandany($s); } else { my @obj; CLASS: for my $class (qw(Distribution Bundle Module)) { if (@obj = $self->expand($class,$s)) { last CLASS; } } if (@obj) { if (1==@obj) { $obj = $obj[0]; } else { $CPAN::Frontend->mywarn("Sorry, $meth with a regular expression is ". "only supported when unambiguous.\nRejecting argument '$s'\n"); $CPAN::Frontend->mysleep(2); next STHING; } } } } elsif ($meth eq "ls") { $self->globls($s,\@pragma); next STHING; } else { CPAN->debug("calling expandany [$s]") if $CPAN::DEBUG; $obj = CPAN::Shell->expandany($s); } if (0) { } elsif (ref $obj) { if ($meth =~ /^($needs_recursion_protection)$/) { # it would be silly to check for recursion for look or dump # (we are in CPAN::Shell::rematein) CPAN->debug("Testing against recursion") if $CPAN::DEBUG; eval { $obj->color_cmd_tmps(0,1); }; if ($@) { if (ref $@ and $@->isa("CPAN::Exception::RecursiveDependency")) { $CPAN::Frontend->mywarn($@); } else { if (0) { require Carp; Carp::confess(sprintf "DEBUG: \$\@[%s]ref[%s]", $@, ref $@); } die; } } } CPAN::Queue->queue_item(qmod => $obj->id, reqtype => "c", optional => ''); push @qcopy, $obj; } elsif ($CPAN::META->exists('CPAN::Author',uc($s))) { $obj = $CPAN::META->instance('CPAN::Author',uc($s)); if ($meth =~ /^(dump|ls|reports)$/) { $obj->$meth(); } else { $CPAN::Frontend->mywarn( join "", "Don't be silly, you can't $meth ", $obj->fullname, " ;-)\n" ); $CPAN::Frontend->mysleep(2); } } elsif ($s =~ m|[\$\@\%]| && $meth eq "dump") { CPAN::InfoObj->dump($s); } else { $CPAN::Frontend ->mywarn(qq{Warning: Cannot $meth $s, }. qq{don't know what it is. Try the command i /$s/ to find objects with matching identifiers. }); $CPAN::Frontend->mysleep(2); } } # queuerunner (please be warned: when I started to change the # queue to hold objects instead of names, I made one or two # mistakes and never found which. I reverted back instead) QITEM: while (my $q = CPAN::Queue->first) { my $obj; my $s = $q->as_string; my $reqtype = $q->reqtype || ""; my $optional = $q->optional || ""; $obj = CPAN::Shell->expandany($s); unless ($obj) { # don't know how this can happen, maybe we should panic, # but maybe we get a solution from the first user who hits # this unfortunate exception? $CPAN::Frontend->mywarn("Warning: Could not expand string '$s' ". "to an object. Skipping.\n"); $CPAN::Frontend->mysleep(5); CPAN::Queue->delete_first($s); next QITEM; } $obj->{reqtype} ||= ""; my $type = ref $obj; if ( $type eq 'CPAN::Distribution' || $type eq 'CPAN::Bundle' ) { $obj->{mandatory} ||= ! $optional; # once mandatory, always mandatory } elsif ( $type eq 'CPAN::Module' ) { $obj->{mandatory} ||= ! $optional; # once mandatory, always mandatory if (my $d = $obj->distribution) { $d->{mandatory} ||= ! $optional; # once mandatory, always mandatory } elsif ($optional) { # the queue object does not know who was recommending/suggesting us:( # So we only vaguely write "optional". $CPAN::Frontend->mywarn("Warning: optional module '$s' ". "not known. Skipping.\n"); CPAN::Queue->delete_first($s); next QITEM; } } { # force debugging because CPAN::SQLite somehow delivers us # an empty object; # local $CPAN::DEBUG = 1024; # Shell; probably fixed now CPAN->debug("s[$s]obj-reqtype[$obj->{reqtype}]". "q-reqtype[$reqtype]") if $CPAN::DEBUG; } if ($obj->{reqtype}) { if ($obj->{reqtype} eq "b" && $reqtype =~ /^[rc]$/) { $obj->{reqtype} = $reqtype; if ( exists $obj->{install} && ( UNIVERSAL::can($obj->{install},"failed") ? $obj->{install}->failed : $obj->{install} =~ /^NO/ ) ) { delete $obj->{install}; $CPAN::Frontend->mywarn ("Promoting $obj->{ID} from 'build_requires' to 'requires'"); } } } else { $obj->{reqtype} = $reqtype; } for my $pragma (@pragma) { if ($pragma && $obj->can($pragma)) { $obj->$pragma($meth); } } if (UNIVERSAL::can($obj, 'called_for')) { $obj->called_for($s) unless $obj->called_for; } CPAN->debug(qq{pragma[@pragma]meth[$meth]}. qq{ID[$obj->{ID}]}) if $CPAN::DEBUG; push @qcopy, $obj; if ($meth =~ /^(report)$/) { # they came here with a pragma? $self->$meth($obj); } elsif (! UNIVERSAL::can($obj,$meth)) { # Must never happen my $serialized = ""; if (0) { } elsif ($CPAN::META->has_inst("YAML::Syck")) { $serialized = YAML::Syck::Dump($obj); } elsif ($CPAN::META->has_inst("YAML")) { $serialized = YAML::Dump($obj); } elsif ($CPAN::META->has_inst("Data::Dumper")) { $serialized = Data::Dumper::Dumper($obj); } else { require overload; $serialized = overload::StrVal($obj); } CPAN->debug("Going to panic. meth[$meth]s[$s]") if $CPAN::DEBUG; $CPAN::Frontend->mydie("Panic: obj[$serialized] cannot meth[$meth]"); } else { my $upgraded_meth = $meth; if ( $meth eq "make" and $obj->{reqtype} eq "b" ) { # rt 86915 $upgraded_meth = "test"; } if ($obj->$upgraded_meth()) { CPAN::Queue->delete($s); CPAN->debug("Succeeded and deleted from queue. pragma[@pragma]meth[$meth][s][$s]") if $CPAN::DEBUG; } else { CPAN->debug("Failed. pragma[@pragma]meth[$meth]s[$s]") if $CPAN::DEBUG; } } $obj->undelay; for my $pragma (@pragma) { my $unpragma = "un$pragma"; if ($obj->can($unpragma)) { $obj->$unpragma(); } } # if any failures occurred and the current object is mandatory, we # still don't know if *it* failed or if it was another (optional) # module, so we have to check that explicitly (and expensively) if ( $CPAN::Config->{halt_on_failure} && $obj->{mandatory} && CPAN::Distrostatus::something_has_just_failed() && $self->mandatory_dist_failed() ) { $CPAN::Frontend->mywarn("Stopping: '$meth' failed for '$s'.\n"); CPAN::Queue->nullify_queue; last QITEM; } CPAN::Queue->delete_first($s); } if ($meth =~ /^($needs_recursion_protection)$/) { for my $obj (@qcopy) { $obj->color_cmd_tmps(0,0); } } } #-> sub CPAN::Shell::recent ; sub recent { my($self) = @_; if ($CPAN::META->has_inst("XML::LibXML")) { my $url = $CPAN::Defaultrecent; $CPAN::Frontend->myprint("Fetching '$url'\n"); unless ($CPAN::META->has_usable("LWP")) { $CPAN::Frontend->mydie("LWP not installed; cannot continue"); } CPAN::LWP::UserAgent->config; my $Ua; eval { $Ua = CPAN::LWP::UserAgent->new; }; if ($@) { $CPAN::Frontend->mydie("CPAN::LWP::UserAgent->new dies with $@\n"); } my $resp = $Ua->get($url); unless ($resp->is_success) { $CPAN::Frontend->mydie(sprintf "Could not download '%s': %s\n", $url, $resp->code); } $CPAN::Frontend->myprint("DONE\n\n"); my $xml = XML::LibXML->new->parse_string($resp->content); if (0) { my $s = $xml->serialize(2); $s =~ s/\n\s*\n/\n/g; $CPAN::Frontend->myprint($s); return; } my @distros; if ($url =~ /winnipeg/) { my $pubdate = $xml->findvalue("/rss/channel/pubDate"); $CPAN::Frontend->myprint(" pubDate: $pubdate\n\n"); for my $eitem ($xml->findnodes("/rss/channel/item")) { my $distro = $eitem->findvalue("enclosure/\@url"); $distro =~ s|.*?/authors/id/./../||; my $size = $eitem->findvalue("enclosure/\@length"); my $desc = $eitem->findvalue("description"); $desc =~ s/.+? - //; $CPAN::Frontend->myprint("$distro [$size b]\n $desc\n"); push @distros, $distro; } } elsif ($url =~ /search.*uploads.rdf/) { # xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" # xmlns="http://purl.org/rss/1.0/" # xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" # xmlns:dc="http://purl.org/dc/elements/1.1/" # xmlns:syn="http://purl.org/rss/1.0/modules/syndication/" # xmlns:admin="http://webns.net/mvcb/" my $dc_date = $xml->findvalue("//*[local-name(.) = 'RDF']/*[local-name(.) = 'channel']/*[local-name(.) = 'date']"); $CPAN::Frontend->myprint(" dc:date: $dc_date\n\n"); my $finish_eitem = 0; local $SIG{INT} = sub { $finish_eitem = 1 }; EITEM: for my $eitem ($xml->findnodes("//*[local-name(.) = 'RDF']/*[local-name(.) = 'item']")) { my $distro = $eitem->findvalue("\@rdf:about"); $distro =~ s|.*~||; # remove up to the tilde before the name $distro =~ s|/$||; # remove trailing slash $distro =~ s|([^/]+)|\U$1\E|; # upcase the name my $author = uc $1 or die "distro[$distro] without author, cannot continue"; my $desc = $eitem->findvalue("*[local-name(.) = 'description']"); my $i = 0; SUBDIRTEST: while () { last SUBDIRTEST if ++$i >= 6; # half a dozen must do! if (my @ret = $self->globls("$distro*")) { @ret = grep {$_->[2] !~ /meta/} @ret; @ret = grep {length $_->[2]} @ret; if (@ret) { $distro = "$author/$ret[0][2]"; last SUBDIRTEST; } } $distro =~ s|/|/*/|; # allow it to reside in a subdirectory } next EITEM if $distro =~ m|\*|; # did not find the thing $CPAN::Frontend->myprint("____$desc\n"); push @distros, $distro; last EITEM if $finish_eitem; } } return \@distros; } else { # deprecated old version $CPAN::Frontend->mydie("no XML::LibXML installed, cannot continue\n"); } } #-> sub CPAN::Shell::smoke ; sub smoke { my($self) = @_; my $distros = $self->recent; DISTRO: for my $distro (@$distros) { next if $distro =~ m|/Bundle-|; # XXX crude heuristic to skip bundles $CPAN::Frontend->myprint(sprintf "Downloading and testing '$distro'\n"); { my $skip = 0; local $SIG{INT} = sub { $skip = 1 }; for (0..9) { $CPAN::Frontend->myprint(sprintf "\r%2d (Hit ^C to skip)", 10-$_); sleep 1; if ($skip) { $CPAN::Frontend->myprint(" skipped\n"); next DISTRO; } } } $CPAN::Frontend->myprint("\r \n"); # leave the dirty line with a newline $self->test($distro); } } { # set up the dispatching methods no strict "refs"; for my $command (qw( clean cvs_import dump force fforce get install look ls make notest perldoc readme reports test )) { *$command = sub { shift->rematein($command, @_); }; } } 1; PKA0]naX677perl5/CPAN/Prompt.pmnu6$# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::Prompt; use overload '""' => "as_string"; use vars qw($prompt); use vars qw( $VERSION ); $VERSION = "5.5"; $prompt = "cpan> "; $CPAN::CurrentCommandId ||= 0; sub new { bless {}, shift; } sub as_string { my $word = "cpan"; unless ($CPAN::META->{LOCK}) { $word = "nolock_cpan"; } if ($CPAN::Config->{commandnumber_in_prompt}) { sprintf "$word\[%d]> ", $CPAN::CurrentCommandId; } else { "$word> "; } } 1; PKA0]:perl5/CPAN/Complete.pmnu6$# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::Complete; use strict; @CPAN::Complete::ISA = qw(CPAN::Debug); # Q: where is the "How do I add a new command" HOWTO? # A: git log -p -1 355c44e9caaec857e4b12f51afb96498833c3e36 where andk added the report command @CPAN::Complete::COMMANDS = sort qw( ? ! a b d h i m o q r u autobundle bye clean cvs_import dump exit failed force fforce hosts install install_tested is_tested look ls make mkmyconfig notest perldoc quit readme recent recompile reload report reports scripts smoke test upgrade ); use vars qw( $VERSION ); $VERSION = "5.5001"; package CPAN::Complete; use strict; sub gnu_cpl { my($text, $line, $start, $end) = @_; my(@perlret) = cpl($text, $line, $start); # find longest common match. Can anybody show me how to peruse # T::R::Gnu to have this done automatically? Seems expensive. return () unless @perlret; my($newtext) = $text; for (my $i = length($text)+1;;$i++) { last unless length($perlret[0]) && length($perlret[0]) >= $i; my $try = substr($perlret[0],0,$i); my @tries = grep {substr($_,0,$i) eq $try} @perlret; # warn "try[$try]tries[@tries]"; if (@tries == @perlret) { $newtext = $try; } else { last; } } ($newtext,@perlret); } #-> sub CPAN::Complete::cpl ; sub cpl { my($word,$line,$pos) = @_; $word ||= ""; $line ||= ""; $pos ||= 0; CPAN->debug("word [$word] line[$line] pos[$pos]") if $CPAN::DEBUG; $line =~ s/^\s*//; if ($line =~ s/^((?:notest|f?force)\s*)//) { $pos -= length($1); } my @return; if ($pos == 0 || $line =~ /^(?:h(?:elp)?|\?)\s/) { @return = grep /^\Q$word\E/, @CPAN::Complete::COMMANDS; } elsif ( $line !~ /^[\!abcdghimorutl]/ ) { @return = (); } elsif ($line =~ /^a\s/) { @return = cplx('CPAN::Author',uc($word)); } elsif ($line =~ /^ls\s/) { my($author,$rest) = $word =~ m|([^/]+)/?(.*)|; @return = $rest ? () : map {"$_/"} cplx('CPAN::Author',uc($author||"")); if (0 && 1==@return) { # XXX too slow and even wrong when there is a * already @return = grep /^\Q$word\E/, map {"$author/$_->[2]"} CPAN::Shell->expand("Author",$author)->ls("$rest*","2"); } } elsif ($line =~ /^b\s/) { CPAN::Shell->local_bundles; @return = cplx('CPAN::Bundle',$word); } elsif ($line =~ /^d\s/) { @return = cplx('CPAN::Distribution',$word); } elsif ($line =~ m/^( [mru]|make|clean|dump|get|test|install|readme|look|cvs_import|perldoc|recent )\s/x ) { if ($word =~ /^Bundle::/) { CPAN::Shell->local_bundles; } @return = (cplx('CPAN::Module',$word),cplx('CPAN::Bundle',$word)); } elsif ($line =~ /^i\s/) { @return = cpl_any($word); } elsif ($line =~ /^reload\s/) { @return = cpl_reload($word,$line,$pos); } elsif ($line =~ /^o\s/) { @return = cpl_option($word,$line,$pos); } elsif ($line =~ m/^\S+\s/ ) { # fallback for future commands and what we have forgotten above @return = (cplx('CPAN::Module',$word),cplx('CPAN::Bundle',$word)); } else { @return = (); } return @return; } #-> sub CPAN::Complete::cplx ; sub cplx { my($class, $word) = @_; if (CPAN::_sqlite_running()) { $CPAN::SQLite->search($class, "^\Q$word\E"); } my $method = "id"; $method = "pretty_id" if $class eq "CPAN::Distribution"; sort grep /^\Q$word\E/, map { $_->$method() } $CPAN::META->all_objects($class); } #-> sub CPAN::Complete::cpl_any ; sub cpl_any { my($word) = shift; return ( cplx('CPAN::Author',$word), cplx('CPAN::Bundle',$word), cplx('CPAN::Distribution',$word), cplx('CPAN::Module',$word), ); } #-> sub CPAN::Complete::cpl_reload ; sub cpl_reload { my($word,$line,$pos) = @_; $word ||= ""; my(@words) = split " ", $line; CPAN->debug("word[$word] line[$line] pos[$pos]") if $CPAN::DEBUG; my(@ok) = qw(cpan index); return @ok if @words == 1; return grep /^\Q$word\E/, @ok if @words == 2 && $word; } #-> sub CPAN::Complete::cpl_option ; sub cpl_option { my($word,$line,$pos) = @_; $word ||= ""; my(@words) = split " ", $line; CPAN->debug("word[$word] line[$line] pos[$pos]") if $CPAN::DEBUG; my(@ok) = qw(conf debug); return @ok if @words == 1; return grep /^\Q$word\E/, @ok if @words == 2 && length($word); if (0) { } elsif ($words[1] eq 'index') { return (); } elsif ($words[1] eq 'conf') { return CPAN::HandleConfig::cpl(@_); } elsif ($words[1] eq 'debug') { return sort grep /^\Q$word\E/i, sort keys %CPAN::DEBUG, 'all'; } } 1; PKA0]Ʈޒw^w^perl5/CPAN/HandleConfig.pmnu6$package CPAN::HandleConfig; use strict; use vars qw(%can %keys $loading $VERSION); use File::Path (); use File::Spec (); use File::Basename (); use Carp (); =head1 NAME CPAN::HandleConfig - internal configuration handling for CPAN.pm =cut $VERSION = "5.5012"; # see also CPAN::Config::VERSION at end of file %can = ( commit => "Commit changes to disk", defaults => "Reload defaults from disk", help => "Short help about 'o conf' usage", init => "Interactive setting of all options", ); # Q: where is the "How do I add a new config option" HOWTO? # A1: svn diff -r 757:758 # where dagolden added test_report [git e997b71de88f1019a1472fc13cb97b1b7f96610f] # A2: svn diff -r 985:986 # where andk added yaml_module [git 312b6d9b12b1bdec0b6e282d853482145475021f] # A3: 1. add new config option to %keys below # 2. add a Pod description in CPAN::FirstTime in the DESCRIPTION # section; it should include a prompt line; see others for # examples # 3. add a "matcher" section in CPAN::FirstTime::init that includes # a prompt function; see others for examples # 4. add config option to documentation section in CPAN.pm %keys = map { $_ => undef } ( "allow_installing_module_downgrades", "allow_installing_outdated_dists", "applypatch", "auto_commit", "build_cache", "build_dir", "build_dir_reuse", "build_requires_install_policy", "bzip2", "cache_metadata", "check_sigs", "cleanup_after_install", "colorize_debug", "colorize_output", "colorize_print", "colorize_warn", "commandnumber_in_prompt", "commands_quote", "connect_to_internet_ok", "cpan_home", "curl", "dontload_hash", # deprecated after 1.83_68 (rev. 581) "dontload_list", "ftp", "ftp_passive", "ftp_proxy", "ftpstats_size", "ftpstats_period", "getcwd", "gpg", "gzip", "halt_on_failure", "histfile", "histsize", "http_proxy", "inactivity_timeout", "index_expire", "inhibit_startup_message", "keep_source_where", "load_module_verbosity", "lynx", "make", "make_arg", "make_install_arg", "make_install_make_command", "makepl_arg", "mbuild_arg", "mbuild_install_arg", "mbuild_install_build_command", "mbuildpl_arg", "ncftp", "ncftpget", "no_proxy", "pager", "password", "patch", "patches_dir", "perl5lib_verbosity", "plugin_list", "prefer_external_tar", "prefer_installer", "prefs_dir", "prerequisites_policy", "proxy_pass", "proxy_user", "pushy_https", "randomize_urllist", "recommends_policy", "scan_cache", "shell", "show_unparsable_versions", "show_upload_date", "show_zero_versions", "suggests_policy", "tar", "tar_verbosity", "term_is_latin", "term_ornaments", "test_report", "trust_test_report_history", "unzip", "urllist", "urllist_ping_verbose", "urllist_ping_external", "use_prompt_default", "use_sqlite", "username", "version_timeout", "wait_list", "wget", "yaml_load_code", "yaml_module", ); my %prefssupport = map { $_ => 1 } ( "allow_installing_module_downgrades", "allow_installing_outdated_dists", "build_requires_install_policy", "check_sigs", "make", "make_install_make_command", "prefer_installer", "test_report", ); # returns true on successful action sub edit { my($self,@args) = @_; return unless @args; CPAN->debug("self[$self]args[".join(" | ",@args)."]"); my($o,$str,$func,$args,$key_exists); $o = shift @args; if($can{$o}) { my $success = $self->$o(args => \@args); # o conf init => sub init => sub load unless ($success) { die "Panic: could not configure CPAN.pm for args [@args]. Giving up."; } } else { CPAN->debug("o[$o]") if $CPAN::DEBUG; unless (exists $keys{$o}) { $CPAN::Frontend->mywarn("Warning: unknown configuration variable '$o'\n"); } my $changed; # one day I used randomize_urllist for a boolean, so we must # list them explicitly --ak if (0) { } elsif ($o =~ /^(wait_list|urllist|dontload_list|plugin_list)$/) { # # ARRAYS # $func = shift @args; $func ||= ""; CPAN->debug("func[$func]args[@args]") if $CPAN::DEBUG; # Let's avoid eval, it's easier to comprehend without. if ($func eq "push") { push @{$CPAN::Config->{$o}}, @args; $changed = 1; } elsif ($func eq "pop") { pop @{$CPAN::Config->{$o}}; $changed = 1; } elsif ($func eq "shift") { shift @{$CPAN::Config->{$o}}; $changed = 1; } elsif ($func eq "unshift") { unshift @{$CPAN::Config->{$o}}, @args; $changed = 1; } elsif ($func eq "splice") { my $offset = shift @args || 0; my $length = shift @args || 0; splice @{$CPAN::Config->{$o}}, $offset, $length, @args; # may warn $changed = 1; } elsif ($func) { $CPAN::Config->{$o} = [$func, @args]; $changed = 1; } else { $self->prettyprint($o); } if ($changed) { if ($o eq "urllist") { # reset the cached values undef $CPAN::FTP::Thesite; undef $CPAN::FTP::Themethod; $CPAN::Index::LAST_TIME = 0; } elsif ($o eq "dontload_list") { # empty it, it will be built up again $CPAN::META->{dontload_hash} = {}; } } } elsif ($o =~ /_hash$/) { # # HASHES # if (@args==1 && $args[0] eq "") { @args = (); } elsif (@args % 2) { push @args, ""; } $CPAN::Config->{$o} = { @args }; $changed = 1; } else { # # SCALARS # if (defined $args[0]) { $CPAN::CONFIG_DIRTY = 1; $CPAN::Config->{$o} = $args[0]; $changed = 1; } $self->prettyprint($o) if exists $keys{$o} or defined $CPAN::Config->{$o}; } if ($changed) { if ($CPAN::Config->{auto_commit}) { $self->commit; } else { $CPAN::CONFIG_DIRTY = 1; $CPAN::Frontend->myprint("Please use 'o conf commit' to ". "make the config permanent!\n\n"); } } } } sub prettyprint { my($self,$k) = @_; my $v = $CPAN::Config->{$k}; if (ref $v) { my(@report); if (ref $v eq "ARRAY") { @report = map {"\t$_ \[$v->[$_]]\n"} 0..$#$v; } else { @report = map { sprintf "\t%-18s => %s\n", "[$_]", defined $v->{$_} ? "[$v->{$_}]" : "undef" } sort keys %$v; } $CPAN::Frontend->myprint( join( "", sprintf( " %-18s\n", $k ), @report ) ); } elsif (defined $v) { $CPAN::Frontend->myprint(sprintf " %-18s [%s]\n", $k, $v); } else { $CPAN::Frontend->myprint(sprintf " %-18s undef\n", $k); } } # generally, this should be called without arguments so that the currently # loaded config file is where changes are committed. sub commit { my($self,@args) = @_; CPAN->debug("args[@args]") if $CPAN::DEBUG; if ($CPAN::RUN_DEGRADED) { $CPAN::Frontend->mydie( "'o conf commit' disabled in ". "degraded mode. Maybe try\n". " !undef \$CPAN::RUN_DEGRADED\n" ); } my ($configpm, $must_reload); # XXX does anything do this? can it be simplified? -- dagolden, 2011-01-19 if (@args) { if ($args[0] eq "args") { # we have not signed that contract } else { $configpm = $args[0]; } } # use provided name or the current config or create a new MyConfig $configpm ||= require_myconfig_or_config() || make_new_config(); # commit to MyConfig if we can't write to Config if ( ! -w $configpm && $configpm =~ m{CPAN/Config\.pm} ) { my $myconfig = _new_config_name(); $CPAN::Frontend->mywarn( "Your $configpm file\n". "is not writable. I will attempt to write your configuration to\n" . "$myconfig instead.\n\n" ); $configpm = make_new_config(); $must_reload++; # so it gets loaded as $INC{'CPAN/MyConfig.pm'} } # XXX why not just "-w $configpm"? -- dagolden, 2011-01-19 my($mode); if (-f $configpm) { $mode = (stat $configpm)[2]; if ($mode && ! -w _) { _die_cant_write_config($configpm); } } $self->_write_config_file($configpm); require_myconfig_or_config() if $must_reload; #$mode = 0444 | ( $mode & 0111 ? 0111 : 0 ); #chmod $mode, $configpm; ###why was that so? $self->defaults; $CPAN::Frontend->myprint("commit: wrote '$configpm'\n"); $CPAN::CONFIG_DIRTY = 0; 1; } sub _write_config_file { my ($self, $configpm) = @_; my $msg; $msg = <new; rename $configpm, "$configpm~" if -f $configpm; open $fh, ">$configpm" or $CPAN::Frontend->mydie("Couldn't open >$configpm: $!"); $fh->print(qq[$msg\$CPAN::Config = \{\n]); foreach (sort keys %$CPAN::Config) { unless (exists $keys{$_}) { # do not drop them: forward compatibility! $CPAN::Frontend->mywarn("Unknown config variable '$_'\n"); next; } $fh->print( " '$_' => ", $self->neatvalue($CPAN::Config->{$_}), ",\n" ); } $fh->print("};\n1;\n__END__\n"); close $fh; return; } # stolen from MakeMaker; not taking the original because it is buggy; # bugreport will have to say: keys of hashes remain unquoted and can # produce syntax errors sub neatvalue { my($self, $v) = @_; return "undef" unless defined $v; my($t) = ref $v; unless ($t) { $v =~ s/\\/\\\\/g; return "q[$v]"; } if ($t eq 'ARRAY') { my(@m, @neat); push @m, "["; foreach my $elem (@$v) { push @neat, "q[$elem]"; } push @m, join ", ", @neat; push @m, "]"; return join "", @m; } return "$v" unless $t eq 'HASH'; my @m; foreach my $key (sort keys %$v) { my $val = $v->{$key}; push(@m,"q[$key]=>".$self->neatvalue($val)) ; } return "{ ".join(', ',@m)." }"; } sub defaults { my($self) = @_; if ($CPAN::RUN_DEGRADED) { $CPAN::Frontend->mydie( "'o conf defaults' disabled in ". "degraded mode. Maybe try\n". " !undef \$CPAN::RUN_DEGRADED\n" ); } my $done; for my $config (qw(CPAN/MyConfig.pm CPAN/Config.pm)) { if ($INC{$config}) { CPAN->debug("INC{'$config'}[$INC{$config}]") if $CPAN::DEBUG; CPAN::Shell->_reload_this($config,{reloforce => 1}); $CPAN::Frontend->myprint("'$INC{$config}' reread\n"); last; } } $CPAN::CONFIG_DIRTY = 0; 1; } =head2 C<< CLASS->safe_quote ITEM >> Quotes an item to become safe against spaces in shell interpolation. An item is enclosed in double quotes if: - the item contains spaces in the middle - the item does not start with a quote This happens to avoid shell interpolation problems when whitespace is present in directory names. This method uses C to determine the correct quote. If C is a space, no quoting will take place. if it starts and ends with the same quote character: leave it as it is if it contains no whitespace: leave it as it is if it contains whitespace, then if it contains quotes: better leave it as it is else: quote it with the correct quote type for the box we're on =cut { # Instead of patching the guess, set commands_quote # to the right value my ($quotes,$use_quote) = $^O eq 'MSWin32' ? ('"', '"') : (q{"'}, "'") ; sub safe_quote { my ($self, $command) = @_; # Set up quote/default quote my $quote = $CPAN::Config->{commands_quote} || $quotes; if ($quote ne ' ' and defined($command ) and $command =~ /\s/ and $command !~ /[$quote]/) { return qq<$use_quote$command$use_quote> } return $command; } } sub init { my($self,@args) = @_; CPAN->debug("self[$self]args[".join(",",@args)."]"); $self->load(do_init => 1, @args); 1; } # Loads CPAN::MyConfig or fall-back to CPAN::Config. Will not reload a file # if already loaded. Returns the path to the file %INC or else the empty string # # Note -- if CPAN::Config were loaded and CPAN::MyConfig subsequently # created, calling this again will leave *both* in %INC sub require_myconfig_or_config () { if ( $INC{"CPAN/MyConfig.pm"} || _try_loading("CPAN::MyConfig", cpan_home())) { return $INC{"CPAN/MyConfig.pm"}; } elsif ( $INC{"CPAN/Config.pm"} || _try_loading("CPAN::Config") ) { return $INC{"CPAN/Config.pm"}; } else { return q{}; } } # Load a module, but ignore "can't locate..." errors # Optionally take a list of directories to add to @INC for the load sub _try_loading { my ($module, @dirs) = @_; (my $file = $module) =~ s{::}{/}g; $file .= ".pm"; local @INC = @INC; for my $dir ( @dirs ) { if ( -f File::Spec->catfile($dir, $file) ) { unshift @INC, $dir; last; } } eval { require $file }; my $err_myconfig = $@; if ($err_myconfig and $err_myconfig !~ m#locate \Q$file\E#) { die "Error while requiring ${module}:\n$err_myconfig"; } return $INC{$file}; } # prioritized list of possible places for finding "CPAN/MyConfig.pm" sub cpan_home_dir_candidates { my @dirs; my $old_v = $CPAN::Config->{load_module_verbosity}; $CPAN::Config->{load_module_verbosity} = q[none]; if ($CPAN::META->has_usable('File::HomeDir')) { if ($^O ne 'darwin') { push @dirs, File::HomeDir->my_data; # my_data is ~/Library/Application Support on darwin, # which causes issues in the toolchain. } push @dirs, File::HomeDir->my_home; } # Windows might not have HOME, so check it first push @dirs, $ENV{HOME} if $ENV{HOME}; # Windows might have these instead push( @dirs, File::Spec->catpath($ENV{HOMEDRIVE}, $ENV{HOMEPATH}, '') ) if $ENV{HOMEDRIVE} && $ENV{HOMEPATH}; push @dirs, $ENV{USERPROFILE} if $ENV{USERPROFILE}; $CPAN::Config->{load_module_verbosity} = $old_v; my $dotcpan = $^O eq 'VMS' ? '_cpan' : '.cpan'; @dirs = map { File::Spec->catdir($_, $dotcpan) } grep { defined } @dirs; return wantarray ? @dirs : $dirs[0]; } sub load { my($self, %args) = @_; $CPAN::Be_Silent+=0; # protect against 'used only once' $CPAN::Be_Silent++ if $args{be_silent}; # do not use; planned to be removed in 2011 my $do_init = delete $args{do_init} || 0; my $make_myconfig = delete $args{make_myconfig}; $loading = 0 unless defined $loading; my $configpm = require_myconfig_or_config; my @miss = $self->missing_config_data; CPAN->debug("do_init[$do_init]loading[$loading]miss[@miss]") if $CPAN::DEBUG; return unless $do_init || @miss; if (@miss==1 and $miss[0] eq "pushy_https" && !$do_init) { $CPAN::Frontend->myprint(<<'END'); Starting with version 2.29 of the cpan shell, a new download mechanism is the default which exclusively uses cpan.org as the host to download from. The configuration variable pushy_https can be used to (de)select the new mechanism. Please read more about it and make your choice between the old and the new mechanism by running o conf init pushy_https Once you have done that and stored the config variable this dialog will disappear. END return; } # I'm not how we'd ever wind up in a recursive loop, but I'm leaving # this here for safety's sake -- dagolden, 2011-01-19 return if $loading; local $loading = ($loading||0) + 1; # Warn if we have a config file, but things were found missing if ($configpm && @miss && !$do_init) { if ($make_myconfig || ( ! -w $configpm && $configpm =~ m{CPAN/Config\.pm})) { $configpm = make_new_config(); $CPAN::Frontend->myprint(<> END } else { $CPAN::Frontend->myprint(<> END } } require CPAN::FirstTime; return CPAN::FirstTime::init($configpm || make_new_config(), %args); } # Creates a new, empty config file at the preferred location # Any existing will be renamed with a ".bak" suffix if possible # If the file cannot be created, an exception is thrown sub make_new_config { my $configpm = _new_config_name(); my $configpmdir = File::Basename::dirname( $configpm ); File::Path::mkpath($configpmdir) unless -d $configpmdir; if ( -w $configpmdir ) { #_#_# following code dumped core on me with 5.003_11, a.k. if( -f $configpm ) { my $configpm_bak = "$configpm.bak"; unlink $configpm_bak if -f $configpm_bak; if( rename $configpm, $configpm_bak ) { $CPAN::Frontend->mywarn(<new; if ($fh->open(">$configpm")) { $fh->print("1;\n"); return $configpm; } } _die_cant_write_config($configpm); } sub _die_cant_write_config { my ($configpm) = @_; $CPAN::Frontend->mydie(<<"END"); WARNING: CPAN.pm is unable to write a configuration file. You must be able to create and write to '$configpm'. Aborting configuration. END } # From candidate directories, we would like (in descending preference order): # * the one that contains a MyConfig file # * one that exists (even without MyConfig) # * the first one on the list sub cpan_home { my @dirs = cpan_home_dir_candidates(); for my $d (@dirs) { return $d if -f "$d/CPAN/MyConfig.pm"; } for my $d (@dirs) { return $d if -d $d; } return $dirs[0]; } sub _new_config_name { return File::Spec->catfile(cpan_home(), 'CPAN', 'MyConfig.pm'); } # returns mandatory but missing entries in the Config sub missing_config_data { my(@miss); for ( "auto_commit", "build_cache", "build_dir", "cache_metadata", "cpan_home", "ftp_proxy", #"gzip", "http_proxy", "index_expire", #"inhibit_startup_message", "keep_source_where", #"make", "make_arg", "make_install_arg", "makepl_arg", "mbuild_arg", "mbuild_install_arg", ($^O eq "MSWin32" ? "" : "mbuild_install_build_command"), "mbuildpl_arg", "no_proxy", #"pager", "prerequisites_policy", "pushy_https", "scan_cache", #"tar", #"unzip", "urllist", ) { next unless exists $keys{$_}; push @miss, $_ unless defined $CPAN::Config->{$_}; } return @miss; } sub help { $CPAN::Frontend->myprint(q[ Known options: commit commit session changes to disk defaults reload default config values from disk help this help init enter a dialog to set all or a set of parameters Edit key values as in the following (the "o" is a literal letter o): o conf build_cache 15 o conf build_dir "/foo/bar" o conf urllist shift o conf urllist unshift ftp://ftp.foo.bar/ o conf inhibit_startup_message 1 ]); 1; #don't reprint CPAN::Config } sub cpl { my($word,$line,$pos) = @_; $word ||= ""; CPAN->debug("word[$word] line[$line] pos[$pos]") if $CPAN::DEBUG; my(@words) = split " ", substr($line,0,$pos+1); if ( defined($words[2]) and $words[2] =~ /list$/ and ( @words == 3 || @words == 4 && length($word) ) ) { return grep /^\Q$word\E/, qw(splice shift unshift pop push); } elsif (defined($words[2]) and $words[2] eq "init" and ( @words == 3 || @words >= 4 && length($word) )) { return sort grep /^\Q$word\E/, keys %keys; } elsif (@words >= 4) { return (); } my %seen; my(@o_conf) = sort grep { !$seen{$_}++ } keys %can, keys %$CPAN::Config, keys %keys; return grep /^\Q$word\E/, @o_conf; } sub prefs_lookup { my($self,$distro,$what) = @_; if ($prefssupport{$what}) { return $CPAN::Config->{$what} unless $distro and $distro->prefs and $distro->prefs->{cpanconfig} and defined $distro->prefs->{cpanconfig}{$what}; return $distro->prefs->{cpanconfig}{$what}; } else { $CPAN::Frontend->mywarn("Warning: $what not yet officially ". "supported for distroprefs, doing a normal lookup\n"); return $CPAN::Config->{$what}; } } { package CPAN::Config; ####::###### #hide from indexer # note: J. Nick Koston wrote me that they are using # CPAN::Config->commit although undocumented. I suggested # CPAN::Shell->o("conf","commit") even when ugly it is at least # documented # that's why I added the CPAN::Config class with autoload and # deprecated warning use strict; use vars qw($AUTOLOAD $VERSION); $VERSION = "5.5012"; # formerly CPAN::HandleConfig was known as CPAN::Config sub AUTOLOAD { ## no critic my $class = shift; # e.g. in dh-make-perl: CPAN::Config my($l) = $AUTOLOAD; $CPAN::Frontend->mywarn("Dispatching deprecated method '$l' to CPAN::HandleConfig\n"); $l =~ s/.*:://; CPAN::HandleConfig->$l(@_); } } 1; __END__ =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut # Local Variables: # mode: cperl # cperl-indent-level: 4 # End: # vim: ts=4 sts=4 sw=4: PKA0]operl5/CPAN/CacheMgr.pmnu6$# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::CacheMgr; use strict; use CPAN::InfoObj; @CPAN::CacheMgr::ISA = qw(CPAN::InfoObj CPAN); use Cwd qw(chdir); use File::Find; use vars qw( $VERSION ); $VERSION = "5.5002"; package CPAN::CacheMgr; use strict; #-> sub CPAN::CacheMgr::as_string ; sub as_string { eval { require Data::Dumper }; if ($@) { return shift->SUPER::as_string; } else { return Data::Dumper::Dumper(shift); } } #-> sub CPAN::CacheMgr::cachesize ; sub cachesize { shift->{DU}; } #-> sub CPAN::CacheMgr::tidyup ; sub tidyup { my($self) = @_; return unless $CPAN::META->{LOCK}; return unless -d $self->{ID}; my @toremove = grep { $self->{SIZE}{$_}==0 } @{$self->{FIFO}}; for my $current (0..$#toremove) { my $toremove = $toremove[$current]; $CPAN::Frontend->myprint(sprintf( "DEL(%d/%d): %s \n", $current+1, scalar @toremove, $toremove, ) ); return if $CPAN::Signal; $self->_clean_cache($toremove); return if $CPAN::Signal; } $self->{FIFO} = []; } #-> sub CPAN::CacheMgr::dir ; sub dir { shift->{ID}; } #-> sub CPAN::CacheMgr::entries ; sub entries { my($self,$dir) = @_; return unless defined $dir; $self->debug("reading dir[$dir]") if $CPAN::DEBUG; $dir ||= $self->{ID}; my($cwd) = CPAN::anycwd(); chdir $dir or Carp::croak("Can't chdir to $dir: $!"); my $dh = DirHandle->new(File::Spec->curdir) or Carp::croak("Couldn't opendir $dir: $!"); my(@entries); for ($dh->read) { next if $_ eq "." || $_ eq ".."; if (-f $_) { push @entries, File::Spec->catfile($dir,$_); } elsif (-d _) { push @entries, File::Spec->catdir($dir,$_); } else { $CPAN::Frontend->mywarn("Warning: weird direntry in $dir: $_\n"); } } chdir $cwd or Carp::croak("Can't chdir to $cwd: $!"); sort { -M $a <=> -M $b} @entries; } #-> sub CPAN::CacheMgr::disk_usage ; sub disk_usage { my($self,$dir,$fast) = @_; return if exists $self->{SIZE}{$dir}; return if $CPAN::Signal; my($Du) = 0; if (-e $dir) { if (-d $dir) { unless (-x $dir) { unless (chmod 0755, $dir) { $CPAN::Frontend->mywarn("I have neither the -x permission nor the ". "permission to change the permission; cannot ". "estimate disk usage of '$dir'\n"); $CPAN::Frontend->mysleep(5); return; } } } elsif (-f $dir) { # nothing to say, no matter what the permissions } } else { $CPAN::Frontend->mywarn("File or directory '$dir' has gone, ignoring\n"); return; } if ($fast) { $Du = 0; # placeholder } else { find( sub { $File::Find::prune++ if $CPAN::Signal; return if -l $_; if ($^O eq 'MacOS') { require Mac::Files; my $cat = Mac::Files::FSpGetCatInfo($_); $Du += $cat->ioFlLgLen() + $cat->ioFlRLgLen() if $cat; } else { if (-d _) { unless (-x _) { unless (chmod 0755, $_) { $CPAN::Frontend->mywarn("I have neither the -x permission nor ". "the permission to change the permission; ". "can only partially estimate disk usage ". "of '$_'\n"); $CPAN::Frontend->mysleep(5); return; } } } else { $Du += (-s _); } } }, $dir ); } return if $CPAN::Signal; $self->{SIZE}{$dir} = $Du/1024/1024; unshift @{$self->{FIFO}}, $dir; $self->debug("measured $dir is $Du") if $CPAN::DEBUG; $self->{DU} += $Du/1024/1024; $self->{DU}; } #-> sub CPAN::CacheMgr::_clean_cache ; sub _clean_cache { my($self,$dir) = @_; return unless -e $dir; unless (File::Spec->canonpath(File::Basename::dirname($dir)) eq File::Spec->canonpath($CPAN::Config->{build_dir})) { $CPAN::Frontend->mywarn("Directory '$dir' not below $CPAN::Config->{build_dir}, ". "will not remove\n"); $CPAN::Frontend->mysleep(5); return; } $self->debug("have to rmtree $dir, will free $self->{SIZE}{$dir}") if $CPAN::DEBUG; File::Path::rmtree($dir); my $id_deleted = 0; if ($dir !~ /\.yml$/ && -f "$dir.yml") { my $yaml_module = CPAN::_yaml_module(); if ($CPAN::META->has_inst($yaml_module)) { my($peek_yaml) = eval { CPAN->_yaml_loadfile("$dir.yml"); }; if ($@) { $CPAN::Frontend->mywarn("(parse error on '$dir.yml' removing anyway)"); unlink "$dir.yml" or $CPAN::Frontend->mywarn("(Could not unlink '$dir.yml': $!)"); return; } elsif (my $id = $peek_yaml->[0]{distribution}{ID}) { $CPAN::META->delete("CPAN::Distribution", $id); # XXX we should restore the state NOW, otherwise this # distro does not exist until we read an index. BUG ALERT(?) # $CPAN::Frontend->mywarn (" +++\n"); $id_deleted++; } } unlink "$dir.yml"; # may fail unless ($id_deleted) { CPAN->debug("no distro found associated with '$dir'"); } } $self->{DU} -= $self->{SIZE}{$dir}; delete $self->{SIZE}{$dir}; } #-> sub CPAN::CacheMgr::new ; sub new { my($class,$phase) = @_; $phase ||= "atstart"; my $time = time; my($debug,$t2); $debug = ""; my $self = { ID => $CPAN::Config->{build_dir}, MAX => $CPAN::Config->{'build_cache'}, SCAN => $CPAN::Config->{'scan_cache'} || 'atstart', DU => 0 }; $CPAN::Frontend->mydie("Unknown scan_cache argument: $self->{SCAN}") unless $self->{SCAN} =~ /never|atstart|atexit/; File::Path::mkpath($self->{ID}); my $dh = DirHandle->new($self->{ID}); bless $self, $class; $self->scan_cache($phase); $t2 = time; $debug .= "timing of CacheMgr->new: ".($t2 - $time); $time = $t2; CPAN->debug($debug) if $CPAN::DEBUG; $self; } #-> sub CPAN::CacheMgr::scan_cache ; sub scan_cache { my ($self, $phase) = @_; $phase = '' unless defined $phase; return unless $phase eq $self->{SCAN}; return unless $CPAN::META->{LOCK}; $CPAN::Frontend->myprint( sprintf("Scanning cache %s for sizes\n", $self->{ID})); my $e; my @entries = $self->entries($self->{ID}); my $i = 0; my $painted = 0; for $e (@entries) { my $symbol = "."; if ($self->{DU} > $self->{MAX}) { $symbol = "-"; $self->disk_usage($e,1); } else { $self->disk_usage($e); } $i++; while (($painted/76) < ($i/@entries)) { $CPAN::Frontend->myprint($symbol); $painted++; } return if $CPAN::Signal; } $CPAN::Frontend->myprint("DONE\n"); $self->tidyup; } 1; PKA0]M :..perl5/CPAN/Distroprefs.pmnu6$# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: use 5.006; use strict; package CPAN::Distroprefs; use vars qw($VERSION); $VERSION = '6.0001'; package CPAN::Distroprefs::Result; use File::Spec; sub new { bless $_[1] || {} => $_[0] } sub abs { File::Spec->catfile($_[0]->dir, $_[0]->file) } sub __cloner { my ($class, $name, $newclass) = @_; $newclass = 'CPAN::Distroprefs::Result::' . $newclass; no strict 'refs'; *{$class . '::' . $name} = sub { $newclass->new({ %{ $_[0] }, %{ $_[1] }, }); }; } BEGIN { __PACKAGE__->__cloner(as_warning => 'Warning') } BEGIN { __PACKAGE__->__cloner(as_fatal => 'Fatal') } BEGIN { __PACKAGE__->__cloner(as_success => 'Success') } sub __accessor { my ($class, $key) = @_; no strict 'refs'; *{$class . '::' . $key} = sub { $_[0]->{$key} }; } BEGIN { __PACKAGE__->__accessor($_) for qw(type file ext dir) } sub is_warning { 0 } sub is_fatal { 0 } sub is_success { 0 } package CPAN::Distroprefs::Result::Error; use vars qw(@ISA); BEGIN { @ISA = 'CPAN::Distroprefs::Result' } ## no critic BEGIN { __PACKAGE__->__accessor($_) for qw(msg) } sub as_string { my ($self) = @_; if ($self->msg) { return sprintf $self->fmt_reason, $self->file, $self->msg; } else { return sprintf $self->fmt_unknown, $self->file; } } package CPAN::Distroprefs::Result::Warning; use vars qw(@ISA); BEGIN { @ISA = 'CPAN::Distroprefs::Result::Error' } ## no critic sub is_warning { 1 } sub fmt_reason { "Error reading distroprefs file %s, skipping: %s" } sub fmt_unknown { "Unknown error reading distroprefs file %s, skipping." } package CPAN::Distroprefs::Result::Fatal; use vars qw(@ISA); BEGIN { @ISA = 'CPAN::Distroprefs::Result::Error' } ## no critic sub is_fatal { 1 } sub fmt_reason { "Error reading distroprefs file %s: %s" } sub fmt_unknown { "Unknown error reading distroprefs file %s." } package CPAN::Distroprefs::Result::Success; use vars qw(@ISA); BEGIN { @ISA = 'CPAN::Distroprefs::Result' } ## no critic BEGIN { __PACKAGE__->__accessor($_) for qw(prefs extension) } sub is_success { 1 } package CPAN::Distroprefs::Iterator; sub new { bless $_[1] => $_[0] } sub next { $_[0]->() } package CPAN::Distroprefs; use Carp (); use DirHandle; sub _load_method { my ($self, $loader, $result) = @_; return '_load_yaml' if $loader eq 'CPAN' or $loader =~ /^YAML(::|$)/; return '_load_' . $result->ext; } sub _load_yaml { my ($self, $loader, $result) = @_; my $data = eval { $loader eq 'CPAN' ? $loader->_yaml_loadfile($result->abs) : [ $loader->can('LoadFile')->($result->abs) ] }; if (my $err = $@) { die $result->as_warning({ msg => $err, }); } elsif (!$data) { die $result->as_warning; } else { return @$data; } } sub _load_dd { my ($self, $loader, $result) = @_; my @data; { package CPAN::Eval; # this caused a die in CPAN.pm, and I am leaving it 'fatal', though I'm # not sure why we wouldn't just skip the file as we do for all other # errors. -- hdp my $abs = $result->abs; open FH, "<$abs" or die $result->as_fatal(msg => "$!"); local $/; my $eval = ; close FH; no strict; eval $eval; if (my $err = $@) { die $result->as_warning({ msg => $err }); } my $i = 1; while (${"VAR$i"}) { push @data, ${"VAR$i"}; $i++; } } return @data; } sub _load_st { my ($self, $loader, $result) = @_; # eval because Storable is never forward compatible my @data = eval { @{scalar $loader->can('retrieve')->($result->abs) } }; if (my $err = $@) { die $result->as_warning({ msg => $err }); } return @data; } sub _build_file_list { if (@_ > 3) { die "_build_file_list should be called with 3 arguments, was called with more. First argument is '$_[0]'."; } my ($dir, $dir1, $ext_re) = @_; my @list; my $dh; unless (opendir($dh, $dir)) { $CPAN::Frontend->mywarn("ignoring prefs directory '$dir': $!"); return @list; } while (my $fn = readdir $dh) { next if $fn eq '.' || $fn eq '..'; if (-d "$dir/$fn") { next if $fn =~ /^[._]/; # prune .svn, .git, .hg, _darcs and what the user wants to hide push @list, _build_file_list("$dir/$fn", "$dir1$fn/", $ext_re); } else { if ($fn =~ $ext_re) { push @list, "$dir1$fn"; } } } return @list; } sub find { my ($self, $dir, $ext_map) = @_; return CPAN::Distroprefs::Iterator->new(sub { return }) unless %$ext_map; my $possible_ext = join "|", map { quotemeta } keys %$ext_map; my $ext_re = qr/\.($possible_ext)$/; my @files = _build_file_list($dir, '', $ext_re); @files = sort @files if @files; # label the block so that we can use redo in the middle return CPAN::Distroprefs::Iterator->new(sub { LOOP: { my $fn = shift @files; return unless defined $fn; my ($ext) = $fn =~ $ext_re; my $loader = $ext_map->{$ext}; my $result = CPAN::Distroprefs::Result->new({ file => $fn, ext => $ext, dir => $dir }); # copied from CPAN.pm; is this ever actually possible? redo unless -f $result->abs; my $load_method = $self->_load_method($loader, $result); my @prefs = eval { $self->$load_method($loader, $result) }; if (my $err = $@) { if (ref($err) && eval { $err->isa('CPAN::Distroprefs::Result') }) { return $err; } # rethrow any exceptions that we did not generate die $err; } elsif (!@prefs) { # the loader should have handled this, but just in case: return $result->as_warning; } return $result->as_success({ prefs => [ map { CPAN::Distroprefs::Pref->new({ data => $_ }) } @prefs ], }); } }); } package CPAN::Distroprefs::Pref; use Carp (); sub new { bless $_[1] => $_[0] } sub data { shift->{data} } sub has_any_match { $_[0]->data->{match} ? 1 : 0 } sub has_match { my $match = $_[0]->data->{match} || return 0; exists $match->{$_[1]} || exists $match->{"not_$_[1]"} } sub has_valid_subkeys { grep { exists $_[0]->data->{match}{$_} } map { $_, "not_$_" } $_[0]->match_attributes } sub _pattern { my $re = shift; my $p = eval sprintf 'qr{%s}', $re; if ($@) { $@ =~ s/\n$//; die "Error in Distroprefs pattern qr{$re}\n$@"; } return $p; } sub _match_scalar { my ($match, $data) = @_; my $qr = _pattern($match); return $data =~ /$qr/; } sub _match_hash { my ($match, $data) = @_; for my $mkey (keys %$match) { (my $dkey = $mkey) =~ s/^not_//; my $val = defined $data->{$dkey} ? $data->{$dkey} : ''; if (_match_scalar($match->{$mkey}, $val)) { return 0 if $mkey =~ /^not_/; } else { return 0 if $mkey !~ /^not_/; } } return 1; } sub _match { my ($self, $key, $data, $matcher) = @_; my $m = $self->data->{match}; if (exists $m->{$key}) { return 0 unless $matcher->($m->{$key}, $data); } if (exists $m->{"not_$key"}) { return 0 if $matcher->($m->{"not_$key"}, $data); } return 1; } sub _scalar_match { my ($self, $key, $data) = @_; return $self->_match($key, $data, \&_match_scalar); } sub _hash_match { my ($self, $key, $data) = @_; return $self->_match($key, $data, \&_match_hash); } # do not take the order of C because "module" is by far the # slowest sub match_attributes { qw(env distribution perl perlconfig module) } sub match_module { my ($self, $modules) = @_; return $self->_match("module", $modules, sub { my($match, $data) = @_; my $qr = _pattern($match); for my $module (@$data) { return 1 if $module =~ /$qr/; } return 0; }); } sub match_distribution { shift->_scalar_match(distribution => @_) } sub match_perl { shift->_scalar_match(perl => @_) } sub match_perlconfig { shift->_hash_match(perlconfig => @_) } sub match_env { shift->_hash_match(env => @_) } sub matches { my ($self, $arg) = @_; my $default_match = 0; for my $key (grep { $self->has_match($_) } $self->match_attributes) { unless (exists $arg->{$key}) { Carp::croak "Can't match pref: missing argument key $key"; } $default_match = 1; my $val = $arg->{$key}; # make it possible to avoid computing things until we have to if (ref($val) eq 'CODE') { $val = $val->() } my $meth = "match_$key"; return 0 unless $self->$meth($val); } return $default_match; } 1; __END__ =head1 NAME CPAN::Distroprefs -- read and match distroprefs =head1 SYNOPSIS use CPAN::Distroprefs; my %info = (... distribution/environment info ...); my $finder = CPAN::Distroprefs->find($prefs_dir, \%ext_map); while (my $result = $finder->next) { die $result->as_string if $result->is_fatal; warn($result->as_string), next if $result->is_warning; for my $pref (@{ $result->prefs }) { if ($pref->matches(\%info)) { return $pref; } } } =head1 DESCRIPTION This module encapsulates reading L and matching them against CPAN distributions. =head1 INTERFACE my $finder = CPAN::Distroprefs->find($dir, \%ext_map); while (my $result = $finder->next) { ... } Build an iterator which finds distroprefs files in the tree below the given directory. Within the tree directories matching C are pruned. C<%ext_map> is a hashref whose keys are file extensions and whose values are modules used to load matching files: { 'yml' => 'YAML::Syck', 'dd' => 'Data::Dumper', ... } Each time C<< $finder->next >> is called, the iterator returns one of two possible values: =over =item * a CPAN::Distroprefs::Result object =item * C, indicating that no prefs files remain to be found =back =head1 RESULTS L|/INTERFACE> returns CPAN::Distroprefs::Result objects to indicate success or failure when reading a prefs file. =head2 Common All results share some common attributes: =head3 type C, C, or C =head3 file the file from which these prefs were read, or to which this error refers (relative filename) =head3 ext the file's extension, which determines how to load it =head3 dir the directory the file was read from =head3 abs the absolute path to the file =head2 Errors Error results (warning and fatal) contain: =head3 msg the error message (usually either C<$!> or a YAML error) =head2 Successes Success results contain: =head3 prefs an arrayref of CPAN::Distroprefs::Pref objects =head1 PREFS CPAN::Distroprefs::Pref objects represent individual distroprefs documents. They are constructed automatically as part of C results from C. =head3 data the pref information as a hashref, suitable for e.g. passing to Kwalify =head3 match_attributes returns a list of the valid match attributes (see the Distroprefs section in L) currently: C =head3 has_any_match true if this pref has a 'match' attribute at all =head3 has_valid_subkeys true if this pref has a 'match' attribute and at least one valid match attribute =head3 matches if ($pref->matches(\%arg)) { ... } true if this pref matches the passed-in hashref, which must have a value for each of the C (above) =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut PKA0]䍽perl5/CPAN/DeferredCode.pmnu6$package CPAN::DeferredCode; use strict; use vars qw/$VERSION/; use overload fallback => 1, map { ($_ => 'run') } qw/ bool "" 0+ /; $VERSION = "5.50"; sub run { $_[0]->(); } 1; PKA0]7Sperl5/CPAN/Author.pmnu6$# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::Author; use strict; use CPAN::InfoObj; @CPAN::Author::ISA = qw(CPAN::InfoObj); use vars qw( $VERSION ); $VERSION = "5.5002"; package CPAN::Author; use strict; #-> sub CPAN::Author::force sub force { my $self = shift; $self->{force}++; } #-> sub CPAN::Author::force sub unforce { my $self = shift; delete $self->{force}; } #-> sub CPAN::Author::id sub id { my $self = shift; my $id = $self->{ID}; $CPAN::Frontend->mydie("Illegal author id[$id]") unless $id =~ /^[A-Z]/; $id; } #-> sub CPAN::Author::as_glimpse ; sub as_glimpse { my($self) = @_; my(@m); my $class = ref($self); $class =~ s/^CPAN:://; push @m, sprintf(qq{%-15s %s ("%s" <%s>)\n}, $class, $self->{ID}, $self->fullname, $self->email); join "", @m; } #-> sub CPAN::Author::fullname ; sub fullname { shift->ro->{FULLNAME}; } *name = \&fullname; #-> sub CPAN::Author::email ; sub email { shift->ro->{EMAIL}; } #-> sub CPAN::Author::ls ; sub ls { my $self = shift; my $glob = shift || ""; my $silent = shift || 0; my $id = $self->id; # adapted from CPAN::Distribution::verifyCHECKSUM ; my(@csf); # chksumfile @csf = $self->id =~ /(.)(.)(.*)/; $csf[1] = join "", @csf[0,1]; $csf[2] = join "", @csf[1,2]; # ("A","AN","ANDK") my(@dl); @dl = $self->dir_listing([$csf[0],"CHECKSUMS"], 0, 1); unless (grep {$_->[2] eq $csf[1]} @dl) { $CPAN::Frontend->myprint("Directory $csf[1]/ does not exist\n") unless $silent ; return; } @dl = $self->dir_listing([@csf[0,1],"CHECKSUMS"], 0, 1); unless (grep {$_->[2] eq $csf[2]} @dl) { $CPAN::Frontend->myprint("Directory $id/ does not exist\n") unless $silent; return; } @dl = $self->dir_listing([@csf,"CHECKSUMS"], 1, 1); if ($glob) { if ($CPAN::META->has_inst("Text::Glob")) { $glob =~ s|/$|/*|; my $rglob = Text::Glob::glob_to_regex($glob); CPAN->debug("glob[$glob]rglob[$rglob]dl[@dl]") if $CPAN::DEBUG; my @tmpdl = grep { $_->[2] =~ /$rglob/ } @dl; if (1==@tmpdl && $tmpdl[0][0]==0) { $rglob = Text::Glob::glob_to_regex("$glob/*"); @dl = grep { $_->[2] =~ /$rglob/ } @dl; } else { @dl = @tmpdl; } CPAN->debug("rglob[$rglob]dl[@dl]") if $CPAN::DEBUG; } else { $CPAN::Frontend->mydie("Text::Glob not installed, cannot proceed"); } } unless ($silent >= 2) { $CPAN::Frontend->myprint ( join "", map { sprintf ( "%8d %10s %s/%s%s\n", $_->[0], $_->[1], $id, $_->[2], 0==$_->[0]?"/":"", ) } sort { $a->[2] cmp $b->[2] } @dl ); } @dl; } # returns an array of arrays, the latter contain (size,mtime,filename) #-> sub CPAN::Author::dir_listing ; sub dir_listing { my $self = shift; my $chksumfile = shift; my $recursive = shift; my $may_ftp = shift; my $lc_want = File::Spec->catfile($CPAN::Config->{keep_source_where}, "authors", "id", @$chksumfile); my $fh; CPAN->debug("chksumfile[@$chksumfile]recursive[$recursive]may_ftp[$may_ftp]") if $CPAN::DEBUG; # Purge and refetch old (pre-PGP) CHECKSUMS; they are a security # hazard. (Without GPG installed they are not that much better, # though.) $fh = FileHandle->new; if (open($fh, $lc_want)) { my $line = <$fh>; close $fh; unlink($lc_want) unless $line =~ /PGP/; } local($") = "/"; # connect "force" argument with "index_expire". my $force = $self->{force}; if (my @stat = stat $lc_want) { $force ||= $stat[9] + $CPAN::Config->{index_expire}*86400 <= time; } my $lc_file; if ($may_ftp) { $lc_file = eval { CPAN::FTP->localize ( "authors/id/@$chksumfile", $lc_want, $force, ); }; unless ($lc_file) { $CPAN::Frontend->myprint("Trying $lc_want.gz\n"); $chksumfile->[-1] .= ".gz"; $lc_file = eval { CPAN::FTP->localize ("authors/id/@$chksumfile", "$lc_want.gz", 1, ); }; if ($lc_file) { $lc_file =~ s{\.gz(?!\n)\Z}{}; #}; eval{CPAN::Tarzip->new("$lc_file.gz")->gunzip($lc_file)}; } else { return; } } } else { $lc_file = $lc_want; # we *could* second-guess and if the user has a file: URL, # then we could look there. But on the other hand, if they do # have a file: URL, why did they choose to set # $CPAN::Config->{show_upload_date} to false? } # adapted from CPAN::Distribution::CHECKSUM_check_file ; $fh = FileHandle->new; my($cksum); if (open $fh, $lc_file) { local($/); my $eval = <$fh>; $eval =~ s/\015?\012/\n/g; close $fh; my($compmt) = Safe->new(); $cksum = $compmt->reval($eval); if ($@) { rename $lc_file, "$lc_file.bad"; Carp::confess($@) if $@; } } elsif ($may_ftp) { Carp::carp ("Could not open '$lc_file' for reading."); } else { # Maybe should warn: "You may want to set show_upload_date to a true value" return; } my(@result,$f); for $f (sort keys %$cksum) { if (exists $cksum->{$f}{isdir}) { if ($recursive) { my(@dir) = @$chksumfile; pop @dir; push @dir, $f, "CHECKSUMS"; push @result, [ 0, "-", $f ]; push @result, map { [$_->[0], $_->[1], "$f/$_->[2]"] } $self->dir_listing(\@dir,1,$may_ftp); } else { push @result, [ 0, "-", $f ]; } } else { push @result, [ ($cksum->{$f}{"size"}||0), $cksum->{$f}{"mtime"}||"---", $f ]; } } @result; } #-> sub CPAN::Author::reports sub reports { $CPAN::Frontend->mywarn("reports on authors not implemented. Please file a bugreport if you need this.\n"); } 1; PKA0]Y3AAperl5/CPAN/Tarzip.pmnu6$# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- package CPAN::Tarzip; use strict; use vars qw($VERSION @ISA $BUGHUNTING); use CPAN::Debug; use File::Basename qw(basename); $VERSION = "5.5013"; # module is internal to CPAN.pm @ISA = qw(CPAN::Debug); ## no critic $BUGHUNTING ||= 0; # released code must have turned off # it's ok if file doesn't exist, it just matters if it is .gz or .bz2 sub new { my($class,$file) = @_; $CPAN::Frontend->mydie("CPAN::Tarzip->new called without arg") unless defined $file; my $me = { FILE => $file }; if ($file =~ /\.(bz2|gz|zip|tbz|tgz)$/i) { $me->{ISCOMPRESSED} = 1; } else { $me->{ISCOMPRESSED} = 0; } if (0) { } elsif ($file =~ /\.(?:bz2|tbz)$/i) { unless ($me->{UNGZIPPRG} = $CPAN::Config->{bzip2}) { my $bzip2 = _my_which("bzip2"); if ($bzip2) { $me->{UNGZIPPRG} = $bzip2; } else { $CPAN::Frontend->mydie(qq{ CPAN.pm needs the external program bzip2 in order to handle '$file'. Please install it now and run 'o conf init bzip2' from the CPAN shell prompt to register it as external program. }); } } } else { $me->{UNGZIPPRG} = _my_which("gzip"); } $me->{TARPRG} = _my_which("tar") || _my_which("gtar"); bless $me, $class; } sub _zlib_ok () { $CPAN::META->has_inst("Compress::Zlib") or return; Compress::Zlib->can('gzopen'); } sub _my_which { my($what) = @_; if ($CPAN::Config->{$what}) { return $CPAN::Config->{$what}; } if ($CPAN::META->has_inst("File::Which")) { return File::Which::which($what); } my @cand = MM->maybe_command($what); return $cand[0] if @cand; require File::Spec; my $component; PATH_COMPONENT: foreach $component (File::Spec->path()) { next unless defined($component) && $component; my($abs) = File::Spec->catfile($component,$what); if (MM->maybe_command($abs)) { return $abs; } } return; } sub gzip { my($self,$read) = @_; my $write = $self->{FILE}; if (_zlib_ok) { my($buffer,$fhw); $fhw = FileHandle->new($read) or $CPAN::Frontend->mydie("Could not open $read: $!"); my $cwd = `pwd`; my $gz = Compress::Zlib::gzopen($write, "wb") or $CPAN::Frontend->mydie("Cannot gzopen $write: $! (pwd is $cwd)\n"); binmode($fhw); $gz->gzwrite($buffer) while read($fhw,$buffer,4096) > 0 ; $gz->gzclose() ; $fhw->close; return 1; } else { my $command = CPAN::HandleConfig->safe_quote($self->{UNGZIPPRG}); system(qq{$command -c "$read" > "$write"})==0; } } sub gunzip { my($self,$write) = @_; my $read = $self->{FILE}; if (_zlib_ok) { my($buffer,$fhw); $fhw = FileHandle->new(">$write") or $CPAN::Frontend->mydie("Could not open >$write: $!"); my $gz = Compress::Zlib::gzopen($read, "rb") or $CPAN::Frontend->mydie("Cannot gzopen $read: $!\n"); binmode($fhw); $fhw->print($buffer) while $gz->gzread($buffer) > 0 ; $CPAN::Frontend->mydie("Error reading from $read: $!\n") if $gz->gzerror != Compress::Zlib::Z_STREAM_END(); $gz->gzclose() ; $fhw->close; return 1; } else { my $command = CPAN::HandleConfig->safe_quote($self->{UNGZIPPRG}); system(qq{$command -d -c "$read" > "$write"})==0; } } sub gtest { my($self) = @_; return $self->{GTEST} if exists $self->{GTEST}; defined $self->{FILE} or $CPAN::Frontend->mydie("gtest called but no FILE specified"); my $read = $self->{FILE}; my $success; if ($read=~/\.(?:bz2|tbz)$/ && $CPAN::META->has_inst("Compress::Bzip2")) { my($buffer,$len); $len = 0; my $gz = Compress::Bzip2::bzopen($read, "rb") or $CPAN::Frontend->mydie(sprintf("Cannot bzopen %s: %s\n", $read, $Compress::Bzip2::bzerrno)); while ($gz->bzread($buffer) > 0 ) { $len += length($buffer); $buffer = ""; } my $err = $gz->bzerror; $success = ! $err || $err == Compress::Bzip2::BZ_STREAM_END(); if ($len == -s $read) { $success = 0; CPAN->debug("hit an uncompressed file") if $CPAN::DEBUG; } $gz->gzclose(); CPAN->debug("err[$err]success[$success]") if $CPAN::DEBUG; } elsif ( $read=~/\.(?:gz|tgz)$/ && _zlib_ok ) { # After I had reread the documentation in zlib.h, I discovered that # uncompressed files do not lead to an gzerror (anymore?). my($buffer,$len); $len = 0; my $gz = Compress::Zlib::gzopen($read, "rb") or $CPAN::Frontend->mydie(sprintf("Cannot gzopen %s: %s\n", $read, $Compress::Zlib::gzerrno)); while ($gz->gzread($buffer) > 0 ) { $len += length($buffer); $buffer = ""; } my $err = $gz->gzerror; $success = ! $err || $err == Compress::Zlib::Z_STREAM_END(); if ($len == -s $read) { $success = 0; CPAN->debug("hit an uncompressed file") if $CPAN::DEBUG; } $gz->gzclose(); CPAN->debug("err[$err]success[$success]") if $CPAN::DEBUG; } elsif (!$self->{ISCOMPRESSED}) { $success = 0; } else { my $command = CPAN::HandleConfig->safe_quote($self->{UNGZIPPRG}); $success = 0==system(qq{$command -qdt "$read"}); } return $self->{GTEST} = $success; } sub TIEHANDLE { my($class,$file) = @_; my $ret; $class->debug("file[$file]"); my $self = $class->new($file); if (0) { } elsif (!$self->gtest) { my $fh = FileHandle->new($file) or $CPAN::Frontend->mydie("Could not open file[$file]: $!"); binmode $fh; $self->{FH} = $fh; $class->debug("via uncompressed FH"); } elsif ($file =~ /\.(?:bz2|tbz)$/ && $CPAN::META->has_inst("Compress::Bzip2")) { my $gz = Compress::Bzip2::bzopen($file,"rb") or $CPAN::Frontend->mydie("Could not bzopen $file"); $self->{GZ} = $gz; $class->debug("via Compress::Bzip2"); } elsif ($file =~/\.(?:gz|tgz)$/ && _zlib_ok) { my $gz = Compress::Zlib::gzopen($file,"rb") or $CPAN::Frontend->mydie("Could not gzopen $file"); $self->{GZ} = $gz; $class->debug("via Compress::Zlib"); } else { my $gzip = CPAN::HandleConfig->safe_quote($self->{UNGZIPPRG}); my $pipe = "$gzip -d -c $file |"; my $fh = FileHandle->new($pipe) or $CPAN::Frontend->mydie("Could not pipe[$pipe]: $!"); binmode $fh; $self->{FH} = $fh; $class->debug("via external $gzip"); } $self; } sub READLINE { my($self) = @_; if (exists $self->{GZ}) { my $gz = $self->{GZ}; my($line,$bytesread); $bytesread = $gz->gzreadline($line); return undef if $bytesread <= 0; return $line; } else { my $fh = $self->{FH}; return scalar <$fh>; } } sub READ { my($self,$ref,$length,$offset) = @_; $CPAN::Frontend->mydie("read with offset not implemented") if defined $offset; if (exists $self->{GZ}) { my $gz = $self->{GZ}; my $byteread = $gz->gzread($$ref,$length);# 30eaf79e8b446ef52464b5422da328a8 return $byteread; } else { my $fh = $self->{FH}; return read($fh,$$ref,$length); } } sub DESTROY { my($self) = @_; if (exists $self->{GZ}) { my $gz = $self->{GZ}; $gz->gzclose() if defined $gz; # hard to say if it is allowed # to be undef ever. AK, 2000-09 } else { my $fh = $self->{FH}; $fh->close if defined $fh; } undef $self; } sub untar { my($self) = @_; my $file = $self->{FILE}; my($prefer) = 0; my $exttar = $self->{TARPRG} || ""; $exttar = "" if $exttar =~ /^\s+$/; # user refuses to use it my $extgzip = $self->{UNGZIPPRG} || ""; $extgzip = "" if $extgzip =~ /^\s+$/; # user refuses to use it if (0) { # makes changing order easier } elsif ($BUGHUNTING) { $prefer=2; } elsif ($CPAN::Config->{prefer_external_tar}) { $prefer = 1; } elsif ( $CPAN::META->has_usable("Archive::Tar") && _zlib_ok ) { my $prefer_external_tar = $CPAN::Config->{prefer_external_tar}; unless (defined $prefer_external_tar) { if ($^O =~ /(MSWin32|solaris)/) { $prefer_external_tar = 0; } else { $prefer_external_tar = 1; } } $prefer = $prefer_external_tar ? 1 : 2; } elsif ($exttar && $extgzip) { # no modules and not bz2 $prefer = 1; # but solaris binary tar is a problem if ($^O eq 'solaris' && qx($exttar --version 2>/dev/null) !~ /gnu/i) { $CPAN::Frontend->mywarn(<< 'END_WARN'); WARNING: Many CPAN distributions were archived with GNU tar and some of them may be incompatible with Solaris tar. We respectfully suggest you configure CPAN to use a GNU tar instead ("o conf init tar") or install a recent Archive::Tar instead; END_WARN } } else { my $foundtar = $exttar ? "'$exttar'" : "nothing"; my $foundzip = $extgzip ? "'$extgzip'" : $foundtar ? "nothing" : "also nothing"; my $foundAT; if ($CPAN::META->has_usable("Archive::Tar")) { $foundAT = sprintf "'%s'", "Archive::Tar::"->VERSION; } else { $foundAT = "nothing"; } my $foundCZ; if (_zlib_ok) { $foundCZ = sprintf "'%s'", "Compress::Zlib::"->VERSION; } elsif ($foundAT) { $foundCZ = "nothing"; } else { $foundCZ = "also nothing"; } $CPAN::Frontend->mydie(qq{ CPAN.pm needs either the external programs tar and gzip -or- both modules Archive::Tar and Compress::Zlib installed. For tar I found $foundtar, for gzip $foundzip. For Archive::Tar I found $foundAT, for Compress::Zlib $foundCZ; Can't continue cutting file '$file'. }); } my $tar_verb = "v"; if (defined $CPAN::Config->{tar_verbosity}) { $tar_verb = $CPAN::Config->{tar_verbosity} eq "none" ? "" : $CPAN::Config->{tar_verbosity}; } if ($prefer==1) { # 1 => external gzip+tar my($system); my $is_compressed = $self->gtest(); my $tarcommand = CPAN::HandleConfig->safe_quote($exttar); if ($is_compressed) { my $command = CPAN::HandleConfig->safe_quote($extgzip); $system = qq{$command -d -c }. qq{< "$file" | $tarcommand x${tar_verb}f -}; } else { $system = qq{$tarcommand x${tar_verb}f "$file"}; } if (system($system) != 0) { # people find the most curious tar binaries that cannot handle # pipes if ($is_compressed) { (my $ungzf = $file) =~ s/\.gz(?!\n)\Z//; $ungzf = basename $ungzf; my $ct = CPAN::Tarzip->new($file); if ($ct->gunzip($ungzf)) { $CPAN::Frontend->myprint(qq{Uncompressed $file successfully\n}); } else { $CPAN::Frontend->mydie(qq{Couldn\'t uncompress $file\n}); } $file = $ungzf; } $system = qq{$tarcommand x${tar_verb}f "$file"}; $CPAN::Frontend->myprint(qq{Using Tar:$system:\n}); my $ret = system($system); if ($ret==0) { $CPAN::Frontend->myprint(qq{Untarred $file successfully\n}); } else { if ($? == -1) { $CPAN::Frontend->mydie(sprintf qq{Couldn\'t untar %s: '%s'\n}, $file, $!); } elsif ($? & 127) { $CPAN::Frontend->mydie(sprintf qq{Couldn\'t untar %s: child died with signal %d, %s coredump\n}, $file, ($? & 127), ($? & 128) ? 'with' : 'without'); } else { $CPAN::Frontend->mydie(sprintf qq{Couldn\'t untar %s: child exited with value %d\n}, $file, $? >> 8); } } return 1; } else { return 1; } } elsif ($prefer==2) { # 2 => modules unless ($CPAN::META->has_usable("Archive::Tar")) { $CPAN::Frontend->mydie("Archive::Tar not installed, please install it to continue"); } # Make sure AT does not use uid/gid/permissions in the archive # This leaves it to the user's umask instead local $Archive::Tar::CHMOD = 1; local $Archive::Tar::SAME_PERMISSIONS = 0; # Make sure AT leaves current user as owner local $Archive::Tar::CHOWN = 0; my $tar = Archive::Tar->new($file,1); my $af; # archive file my @af; if ($BUGHUNTING) { # RCS 1.337 had this code, it turned out unacceptable slow but # it revealed a bug in Archive::Tar. Code is only here to hunt # the bug again. It should never be enabled in published code. # GDGraph3d-0.53 was an interesting case according to Larry # Virden. warn(">>>Bughunting code enabled<<< " x 20); for $af ($tar->list_files) { if ($af =~ m!^(/|\.\./)!) { $CPAN::Frontend->mydie("ALERT: Archive contains ". "illegal member [$af]"); } $CPAN::Frontend->myprint("$af\n"); $tar->extract($af); # slow but effective for finding the bug return if $CPAN::Signal; } } else { for $af ($tar->list_files) { if ($af =~ m!^(/|\.\./)!) { $CPAN::Frontend->mydie("ALERT: Archive contains ". "illegal member [$af]"); } if ($tar_verb eq "v" || $tar_verb eq "vv") { $CPAN::Frontend->myprint("$af\n"); } push @af, $af; return if $CPAN::Signal; } $tar->extract(@af) or $CPAN::Frontend->mydie("Could not untar with Archive::Tar."); } Mac::BuildTools::convert_files([$tar->list_files], 1) if ($^O eq 'MacOS'); return 1; } } sub unzip { my($self) = @_; my $file = $self->{FILE}; if ($CPAN::META->has_inst("Archive::Zip")) { # blueprint of the code from Archive::Zip::Tree::extractTree(); my $zip = Archive::Zip->new(); my $status; $status = $zip->read($file); $CPAN::Frontend->mydie("Read of file[$file] failed\n") if $status != Archive::Zip::AZ_OK(); $CPAN::META->debug("Successfully read file[$file]") if $CPAN::DEBUG; my @members = $zip->members(); for my $member ( @members ) { my $af = $member->fileName(); if ($af =~ m!^(/|\.\./)!) { $CPAN::Frontend->mydie("ALERT: Archive contains ". "illegal member [$af]"); } $status = $member->extractToFileNamed( $af ); $CPAN::META->debug("af[$af]status[$status]") if $CPAN::DEBUG; $CPAN::Frontend->mydie("Extracting of file[$af] from zipfile[$file] failed\n") if $status != Archive::Zip::AZ_OK(); return if $CPAN::Signal; } return 1; } elsif ( my $unzip = $CPAN::Config->{unzip} ) { my @system = ($unzip, $file); return system(@system) == 0; } else { $CPAN::Frontend->mydie(<<"END"); Can't unzip '$file': You have not configured an 'unzip' program and do not have Archive::Zip installed. Please either install Archive::Zip or else configure 'unzip' by running the command 'o conf init unzip' from the CPAN shell prompt. END } } 1; __END__ =head1 NAME CPAN::Tarzip - internal handling of tar archives for CPAN.pm =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut PKA0]iۨDperl5/CPAN/Distrostatus.pmnu6$# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::Distrostatus; use overload '""' => "as_string", fallback => 1; use vars qw($something_has_failed_at); use vars qw( $VERSION ); $VERSION = "5.5"; sub new { my($class,$arg) = @_; my $failed = substr($arg,0,2) eq "NO"; if ($failed) { $something_has_failed_at = $CPAN::CurrentCommandId; } bless { TEXT => $arg, FAILED => $failed, COMMANDID => $CPAN::CurrentCommandId, TIME => time, }, $class; } sub something_has_just_failed () { defined $something_has_failed_at && $something_has_failed_at == $CPAN::CurrentCommandId; } sub commandid { shift->{COMMANDID} } sub failed { shift->{FAILED} } sub text { my($self,$set) = @_; if (defined $set) { $self->{TEXT} = $set; } $self->{TEXT}; } sub as_string { my($self) = @_; $self->text; } 1; PKA0]*^'perl5/CPAN/Exception/blocked_urllist.pmnu6$# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::Exception::blocked_urllist; use strict; use overload '""' => "as_string"; use vars qw( $VERSION ); $VERSION = "1.001"; sub new { my($class) = @_; bless {}, $class; } sub as_string { my($self) = shift; if ($CPAN::Config->{connect_to_internet_ok}) { return qq{ You have not configured a urllist for CPAN mirrors. Configure it with o conf init urllist }; } else { return qq{ You have not configured a urllist and do not allow connections to the internet to get a list of mirrors. If you wish to get a list of CPAN mirrors to pick from, use this command o conf init connect_to_internet_ok urllist If you do not wish to get a list of mirrors and would prefer to set your urllist manually, use just this command instead o conf init urllist }; } } 1; PKA0]1..+perl5/CPAN/Exception/RecursiveDependency.pmnu6$# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::Exception::RecursiveDependency; use strict; use overload '""' => "as_string"; use vars qw( $VERSION ); $VERSION = "5.5001"; { package CPAN::Exception::RecursiveDependency::na; use overload '""' => "as_string"; sub new { bless {}, shift }; sub as_string { "N/A" }; } my $NA = CPAN::Exception::RecursiveDependency::na->new; # a module sees its distribution (no version) # a distribution sees its prereqs (which are module names) (usually with versions) # a bundle sees its module names and/or its distributions (no version) sub new { my($class) = shift; my($deps_arg) = shift; my (@deps,%seen,$loop_starts_with); DCHAIN: for my $dep (@$deps_arg) { push @deps, {name => $dep, display_as => $dep}; if ($seen{$dep}++) { $loop_starts_with = $dep; last DCHAIN; } } my $in_loop = 0; my %mark; DWALK: for my $i (0..$#deps) { my $x = $deps[$i]{name}; $in_loop ||= $loop_starts_with && $x eq $loop_starts_with; my $xo = CPAN::Shell->expandany($x) or next; if ($xo->isa("CPAN::Module")) { my $have = $xo->inst_version || $NA; my($want,$d,$want_type); if ($i>0 and $d = $deps[$i-1]{name}) { my $do = CPAN::Shell->expandany($d); $want = $do->{prereq_pm}{requires}{$x}; if (defined $want) { $want_type = "requires: "; } else { $want = $do->{prereq_pm}{build_requires}{$x}; if (defined $want) { $want_type = "build_requires: "; } else { $want_type = "unknown status"; $want = "???"; } } } else { $want = $xo->cpan_version; $want_type = "want: "; } $deps[$i]{have} = $have; $deps[$i]{want_type} = $want_type; $deps[$i]{want} = $want; $deps[$i]{display_as} = "$x (have: $have; $want_type$want)"; if ((! ref $have || !$have->isa('CPAN::Exception::RecursiveDependency::na')) && CPAN::Version->vge($have, $want)) { # https://rt.cpan.org/Ticket/Display.html?id=115340 undef $loop_starts_with; last DWALK; } } elsif ($xo->isa("CPAN::Distribution")) { my $pretty = $deps[$i]{display_as} = $xo->pretty_id; my $mark_as; if ($in_loop) { $mark_as = CPAN::Distrostatus->new("NO cannot resolve circular dependency"); } else { $mark_as = CPAN::Distrostatus->new("NO one dependency ($loop_starts_with) is a circular dependency"); } $mark{$pretty} = { xo => $xo, mark_as => $mark_as }; } } if ($loop_starts_with) { while (my($k,$v) = each %mark) { my $xo = $v->{xo}; $xo->{make} = $v->{mark_as}; $xo->store_persistent_state; # otherwise I will not reach # all involved parties for # the next session } } bless { deps => \@deps, loop_starts_with => $loop_starts_with }, $class; } sub is_resolvable { ! defined shift->{loop_starts_with}; } sub as_string { my($self) = shift; my $deps = $self->{deps}; my $loop_starts_with = $self->{loop_starts_with}; unless ($loop_starts_with) { return "--not a recursive/circular dependency--"; } my $ret = "\nRecursive dependency detected:\n "; $ret .= join("\n => ", map {$_->{display_as}} @$deps); $ret .= ".\nCannot resolve.\n"; $ret; } 1; PKA0]T*perl5/CPAN/Exception/yaml_not_installed.pmnu6$# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::Exception::yaml_not_installed; use strict; use overload '""' => "as_string"; use vars qw( $VERSION ); $VERSION = "5.5"; sub new { my($class,$module,$file,$during) = @_; bless { module => $module, file => $file, during => $during }, $class; } sub as_string { my($self) = shift; "'$self->{module}' not installed, cannot $self->{during} '$self->{file}'\n"; } 1; PKA0]ܑH*perl5/CPAN/Exception/yaml_process_error.pmnu6$# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::Exception::yaml_process_error; use strict; use overload '""' => "as_string"; use vars qw( $VERSION ); $VERSION = "5.5"; sub new { my($class,$module,$file,$during,$error) = @_; # my $at = Carp::longmess(""); # XXX find something more beautiful bless { module => $module, file => $file, during => $during, error => $error, # at => $at, }, $class; } sub as_string { my($self) = shift; if ($self->{during}) { if ($self->{file}) { if ($self->{module}) { if ($self->{error}) { return "Alert: While trying to '$self->{during}' YAML file\n". " '$self->{file}'\n". "with '$self->{module}' the following error was encountered:\n". " $self->{error}\n"; } else { return "Alert: While trying to '$self->{during}' YAML file\n". " '$self->{file}'\n". "with '$self->{module}' some unknown error was encountered\n"; } } else { return "Alert: While trying to '$self->{during}' YAML file\n". " '$self->{file}'\n". "some unknown error was encountered\n"; } } else { return "Alert: While trying to '$self->{during}' some YAML file\n". "some unknown error was encountered\n"; } } else { return "Alert: unknown error encountered\n"; } } 1; PKA0]ך9LLperl5/CPAN/URL.pmnu6$# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::URL; use overload '""' => "as_string", fallback => 1; # accessors: TEXT(the url string), FROM(DEF=>defaultlist,USER=>urllist), # planned are things like age or quality use vars qw( $VERSION ); $VERSION = "5.5"; sub new { my($class,%args) = @_; bless { %args }, $class; } sub as_string { my($self) = @_; $self->text; } sub text { my($self,$set) = @_; if (defined $set) { $self->{TEXT} = $set; } $self->{TEXT}; } 1; PKA0]>+perl5/CPAN/LWP/UserAgent.pmnu6$# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::LWP::UserAgent; use strict; use vars qw(@ISA $USER $PASSWD $SETUPDONE); use CPAN::HTTP::Credentials; # we delay requiring LWP::UserAgent and setting up inheritance until we need it $CPAN::LWP::UserAgent::VERSION = $CPAN::LWP::UserAgent::VERSION = "1.9601"; sub config { return if $SETUPDONE; if ($CPAN::META->has_usable('LWP::UserAgent')) { require LWP::UserAgent; @ISA = qw(Exporter LWP::UserAgent); ## no critic $SETUPDONE++; } else { $CPAN::Frontend->mywarn(" LWP::UserAgent not available\n"); } } sub get_basic_credentials { my($self, $realm, $uri, $proxy) = @_; if ( $proxy ) { return CPAN::HTTP::Credentials->get_proxy_credentials(); } else { return CPAN::HTTP::Credentials->get_non_proxy_credentials(); } } sub no_proxy { my ( $self, $no_proxy ) = @_; return $self->SUPER::no_proxy( split(',',$no_proxy) ); } # mirror(): Its purpose is to deal with proxy authentication. When we # call SUPER::mirror, we really call the mirror method in # LWP::UserAgent. LWP::UserAgent will then call # $self->get_basic_credentials or some equivalent and this will be # $self->dispatched to our own get_basic_credentials method. # Our own get_basic_credentials sets $USER and $PASSWD, two globals. # 407 stands for HTTP_PROXY_AUTHENTICATION_REQUIRED. Which means # although we have gone through our get_basic_credentials, the proxy # server refuses to connect. This could be a case where the username or # password has changed in the meantime, so I'm trying once again without # $USER and $PASSWD to give the get_basic_credentials routine another # chance to set $USER and $PASSWD. sub mirror { my($self,$url,$aslocal) = @_; my $result = $self->SUPER::mirror($url,$aslocal); if ($result->code == 407) { CPAN::HTTP::Credentials->clear_credentials; $result = $self->SUPER::mirror($url,$aslocal); } $result; } 1; PKA0]somFUFUperl5/CPAN/Meta/Requirements.pmnu6$use v5.10; use strict; use warnings; package CPAN::Meta::Requirements; # ABSTRACT: a set of version requirements for a CPAN dist our $VERSION = '2.143'; use CPAN::Meta::Requirements::Range; #pod =head1 SYNOPSIS #pod #pod use CPAN::Meta::Requirements; #pod #pod my $build_requires = CPAN::Meta::Requirements->new; #pod #pod $build_requires->add_minimum('Library::Foo' => 1.208); #pod #pod $build_requires->add_minimum('Library::Foo' => 2.602); #pod #pod $build_requires->add_minimum('Module::Bar' => 'v1.2.3'); #pod #pod $METAyml->{build_requires} = $build_requires->as_string_hash; #pod #pod =head1 DESCRIPTION #pod #pod A CPAN::Meta::Requirements object models a set of version constraints like #pod those specified in the F or F files in CPAN distributions, #pod and as defined by L. #pod It can be built up by adding more and more constraints, and it will reduce them #pod to the simplest representation. #pod #pod Logically impossible constraints will be identified immediately by thrown #pod exceptions. #pod #pod =cut use Carp (); #pod =method new #pod #pod my $req = CPAN::Meta::Requirements->new; #pod #pod This returns a new CPAN::Meta::Requirements object. It takes an optional #pod hash reference argument. Currently, only one key is supported: #pod #pod =for :list #pod * C -- if provided, when a version cannot be parsed into #pod a version object, this code reference will be called with the invalid #pod version string as first argument, and the module name as second #pod argument. It must return a valid version object. #pod #pod All other keys are ignored. #pod #pod =cut my @valid_options = qw( bad_version_hook ); sub new { my ($class, $options) = @_; $options ||= {}; Carp::croak "Argument to $class\->new() must be a hash reference" unless ref $options eq 'HASH'; my %self = map {; $_ => $options->{$_}} @valid_options; return bless \%self => $class; } #pod =method add_minimum #pod #pod $req->add_minimum( $module => $version ); #pod #pod This adds a new minimum version requirement. If the new requirement is #pod redundant to the existing specification, this has no effect. #pod #pod Minimum requirements are inclusive. C<$version> is required, along with any #pod greater version number. #pod #pod This method returns the requirements object. #pod #pod =method add_maximum #pod #pod $req->add_maximum( $module => $version ); #pod #pod This adds a new maximum version requirement. If the new requirement is #pod redundant to the existing specification, this has no effect. #pod #pod Maximum requirements are inclusive. No version strictly greater than the given #pod version is allowed. #pod #pod This method returns the requirements object. #pod #pod =method add_exclusion #pod #pod $req->add_exclusion( $module => $version ); #pod #pod This adds a new excluded version. For example, you might use these three #pod method calls: #pod #pod $req->add_minimum( $module => '1.00' ); #pod $req->add_maximum( $module => '1.82' ); #pod #pod $req->add_exclusion( $module => '1.75' ); #pod #pod Any version between 1.00 and 1.82 inclusive would be acceptable, except for #pod 1.75. #pod #pod This method returns the requirements object. #pod #pod =method exact_version #pod #pod $req->exact_version( $module => $version ); #pod #pod This sets the version required for the given module to I the given #pod version. No other version would be considered acceptable. #pod #pod This method returns the requirements object. #pod #pod =cut BEGIN { for my $type (qw(maximum exclusion exact_version)) { my $method = "with_$type"; my $to_add = $type eq 'exact_version' ? $type : "add_$type"; my $code = sub { my ($self, $name, $version) = @_; $self->__modify_entry_for($name, $method, $version); return $self; }; no strict 'refs'; *$to_add = $code; } } # add_minimum is optimized compared to generated subs above because # it is called frequently and with "0" or equivalent input sub add_minimum { my ($self, $name, $version) = @_; # stringify $version so that version->new("0.00")->stringify ne "0" # which preserves the user's choice of "0.00" as the requirement if (not defined $version or "$version" eq '0') { return $self if $self->__entry_for($name); Carp::croak("can't add new requirements to finalized requirements") if $self->is_finalized; $self->{requirements}{ $name } = CPAN::Meta::Requirements::Range->with_minimum('0', $name); } else { $self->__modify_entry_for($name, 'with_minimum', $version); } return $self; } #pod =method version_range_for_module #pod #pod $req->version_range_for_module( $another_req_object ); #pod #pod =cut sub version_range_for_module { my ($self, $module) = @_; return $self->{requirements}{$module}; } #pod =method add_requirements #pod #pod $req->add_requirements( $another_req_object ); #pod #pod This method adds all the requirements in the given CPAN::Meta::Requirements #pod object to the requirements object on which it was called. If there are any #pod conflicts, an exception is thrown. #pod #pod This method returns the requirements object. #pod #pod =cut sub add_requirements { my ($self, $req) = @_; for my $module ($req->required_modules) { my $new_range = $req->version_range_for_module($module); $self->__modify_entry_for($module, 'with_range', $new_range); } return $self; } #pod =method accepts_module #pod #pod my $bool = $req->accepts_module($module => $version); #pod #pod Given an module and version, this method returns true if the version #pod specification for the module accepts the provided version. In other words, #pod given: #pod #pod Module => '>= 1.00, < 2.00' #pod #pod We will accept 1.00 and 1.75 but not 0.50 or 2.00. #pod #pod For modules that do not appear in the requirements, this method will return #pod true. #pod #pod =cut sub accepts_module { my ($self, $module, $version) = @_; return 1 unless my $range = $self->__entry_for($module); return $range->accepts($version); } #pod =method clear_requirement #pod #pod $req->clear_requirement( $module ); #pod #pod This removes the requirement for a given module from the object. #pod #pod This method returns the requirements object. #pod #pod =cut sub clear_requirement { my ($self, $module) = @_; return $self unless $self->__entry_for($module); Carp::croak("can't clear requirements on finalized requirements") if $self->is_finalized; delete $self->{requirements}{ $module }; return $self; } #pod =method requirements_for_module #pod #pod $req->requirements_for_module( $module ); #pod #pod This returns a string containing the version requirements for a given module in #pod the format described in L or undef if the given module has no #pod requirements. This should only be used for informational purposes such as error #pod messages and should not be interpreted or used for comparison (see #pod L instead). #pod #pod =cut sub requirements_for_module { my ($self, $module) = @_; my $entry = $self->__entry_for($module); return unless $entry; return $entry->as_string; } #pod =method structured_requirements_for_module #pod #pod $req->structured_requirements_for_module( $module ); #pod #pod This returns a data structure containing the version requirements for a given #pod module or undef if the given module has no requirements. This should #pod not be used for version checks (see L instead). #pod #pod Added in version 2.134. #pod #pod =cut sub structured_requirements_for_module { my ($self, $module) = @_; my $entry = $self->__entry_for($module); return unless $entry; return $entry->as_struct; } #pod =method required_modules #pod #pod This method returns a list of all the modules for which requirements have been #pod specified. #pod #pod =cut sub required_modules { keys %{ $_[0]{requirements} } } #pod =method clone #pod #pod $req->clone; #pod #pod This method returns a clone of the invocant. The clone and the original object #pod can then be changed independent of one another. #pod #pod =cut sub clone { my ($self) = @_; my $new = (ref $self)->new; return $new->add_requirements($self); } sub __entry_for { $_[0]{requirements}{ $_[1] } } sub __modify_entry_for { my ($self, $name, $method, $version) = @_; my $fin = $self->is_finalized; my $old = $self->__entry_for($name); Carp::croak("can't add new requirements to finalized requirements") if $fin and not $old; my $new = ($old || 'CPAN::Meta::Requirements::Range') ->$method($version, $name, $self->{bad_version_hook}); Carp::croak("can't modify finalized requirements") if $fin and $old->as_string ne $new->as_string; $self->{requirements}{ $name } = $new; } #pod =method is_simple #pod #pod This method returns true if and only if all requirements are inclusive minimums #pod -- that is, if their string expression is just the version number. #pod #pod =cut sub is_simple { my ($self) = @_; for my $module ($self->required_modules) { # XXX: This is a complete hack, but also entirely correct. return if not $self->__entry_for($module)->is_simple; } return 1; } #pod =method is_finalized #pod #pod This method returns true if the requirements have been finalized by having the #pod C method called on them. #pod #pod =cut sub is_finalized { $_[0]{finalized} } #pod =method finalize #pod #pod This method marks the requirements finalized. Subsequent attempts to change #pod the requirements will be fatal, I they would result in a change. If they #pod would not alter the requirements, they have no effect. #pod #pod If a finalized set of requirements is cloned, the cloned requirements are not #pod also finalized. #pod #pod =cut sub finalize { $_[0]{finalized} = 1 } #pod =method as_string_hash #pod #pod This returns a reference to a hash describing the requirements using the #pod strings in the L specification. #pod #pod For example after the following program: #pod #pod my $req = CPAN::Meta::Requirements->new; #pod #pod $req->add_minimum('CPAN::Meta::Requirements' => 0.102); #pod #pod $req->add_minimum('Library::Foo' => 1.208); #pod #pod $req->add_maximum('Library::Foo' => 2.602); #pod #pod $req->add_minimum('Module::Bar' => 'v1.2.3'); #pod #pod $req->add_exclusion('Module::Bar' => 'v1.2.8'); #pod #pod $req->exact_version('Xyzzy' => '6.01'); #pod #pod my $hashref = $req->as_string_hash; #pod #pod C<$hashref> would contain: #pod #pod { #pod 'CPAN::Meta::Requirements' => '0.102', #pod 'Library::Foo' => '>= 1.208, <= 2.206', #pod 'Module::Bar' => '>= v1.2.3, != v1.2.8', #pod 'Xyzzy' => '== 6.01', #pod } #pod #pod =cut sub as_string_hash { my ($self) = @_; my %hash = map {; $_ => $self->{requirements}{$_}->as_string } $self->required_modules; return \%hash; } #pod =method add_string_requirement #pod #pod $req->add_string_requirement('Library::Foo' => '>= 1.208, <= 2.206'); #pod $req->add_string_requirement('Library::Foo' => v1.208); #pod #pod This method parses the passed in string and adds the appropriate requirement #pod for the given module. A version can be a Perl "v-string". It understands #pod version ranges as described in the L. For #pod example: #pod #pod =over 4 #pod #pod =item 1.3 #pod #pod =item >= 1.3 #pod #pod =item <= 1.3 #pod #pod =item == 1.3 #pod #pod =item != 1.3 #pod #pod =item > 1.3 #pod #pod =item < 1.3 #pod #pod =item >= 1.3, != 1.5, <= 2.0 #pod #pod A version number without an operator is equivalent to specifying a minimum #pod (C=>). Extra whitespace is allowed. #pod #pod =back #pod #pod =cut sub add_string_requirement { my ($self, $module, $req) = @_; $self->__modify_entry_for($module, 'with_string_requirement', $req); } #pod =method from_string_hash #pod #pod my $req = CPAN::Meta::Requirements->from_string_hash( \%hash ); #pod my $req = CPAN::Meta::Requirements->from_string_hash( \%hash, \%opts ); #pod #pod This is an alternate constructor for a CPAN::Meta::Requirements #pod object. It takes a hash of module names and version requirement #pod strings and returns a new CPAN::Meta::Requirements object. As with #pod add_string_requirement, a version can be a Perl "v-string". Optionally, #pod you can supply a hash-reference of options, exactly as with the L #pod method. #pod #pod =cut sub from_string_hash { my ($class, $hash, $options) = @_; my $self = $class->new($options); for my $module (keys %$hash) { my $req = $hash->{$module}; $self->add_string_requirement($module, $req); } return $self; } 1; # vim: ts=2 sts=2 sw=2 et: __END__ =pod =encoding UTF-8 =head1 NAME CPAN::Meta::Requirements - a set of version requirements for a CPAN dist =head1 VERSION version 2.143 =head1 SYNOPSIS use CPAN::Meta::Requirements; my $build_requires = CPAN::Meta::Requirements->new; $build_requires->add_minimum('Library::Foo' => 1.208); $build_requires->add_minimum('Library::Foo' => 2.602); $build_requires->add_minimum('Module::Bar' => 'v1.2.3'); $METAyml->{build_requires} = $build_requires->as_string_hash; =head1 DESCRIPTION A CPAN::Meta::Requirements object models a set of version constraints like those specified in the F or F files in CPAN distributions, and as defined by L. It can be built up by adding more and more constraints, and it will reduce them to the simplest representation. Logically impossible constraints will be identified immediately by thrown exceptions. =head1 METHODS =head2 new my $req = CPAN::Meta::Requirements->new; This returns a new CPAN::Meta::Requirements object. It takes an optional hash reference argument. Currently, only one key is supported: =over 4 =item * C -- if provided, when a version cannot be parsed into a version object, this code reference will be called with the invalid version string as first argument, and the module name as second argument. It must return a valid version object. =back All other keys are ignored. =head2 add_minimum $req->add_minimum( $module => $version ); This adds a new minimum version requirement. If the new requirement is redundant to the existing specification, this has no effect. Minimum requirements are inclusive. C<$version> is required, along with any greater version number. This method returns the requirements object. =head2 add_maximum $req->add_maximum( $module => $version ); This adds a new maximum version requirement. If the new requirement is redundant to the existing specification, this has no effect. Maximum requirements are inclusive. No version strictly greater than the given version is allowed. This method returns the requirements object. =head2 add_exclusion $req->add_exclusion( $module => $version ); This adds a new excluded version. For example, you might use these three method calls: $req->add_minimum( $module => '1.00' ); $req->add_maximum( $module => '1.82' ); $req->add_exclusion( $module => '1.75' ); Any version between 1.00 and 1.82 inclusive would be acceptable, except for 1.75. This method returns the requirements object. =head2 exact_version $req->exact_version( $module => $version ); This sets the version required for the given module to I the given version. No other version would be considered acceptable. This method returns the requirements object. =head2 version_range_for_module $req->version_range_for_module( $another_req_object ); =head2 add_requirements $req->add_requirements( $another_req_object ); This method adds all the requirements in the given CPAN::Meta::Requirements object to the requirements object on which it was called. If there are any conflicts, an exception is thrown. This method returns the requirements object. =head2 accepts_module my $bool = $req->accepts_module($module => $version); Given an module and version, this method returns true if the version specification for the module accepts the provided version. In other words, given: Module => '>= 1.00, < 2.00' We will accept 1.00 and 1.75 but not 0.50 or 2.00. For modules that do not appear in the requirements, this method will return true. =head2 clear_requirement $req->clear_requirement( $module ); This removes the requirement for a given module from the object. This method returns the requirements object. =head2 requirements_for_module $req->requirements_for_module( $module ); This returns a string containing the version requirements for a given module in the format described in L or undef if the given module has no requirements. This should only be used for informational purposes such as error messages and should not be interpreted or used for comparison (see L instead). =head2 structured_requirements_for_module $req->structured_requirements_for_module( $module ); This returns a data structure containing the version requirements for a given module or undef if the given module has no requirements. This should not be used for version checks (see L instead). Added in version 2.134. =head2 required_modules This method returns a list of all the modules for which requirements have been specified. =head2 clone $req->clone; This method returns a clone of the invocant. The clone and the original object can then be changed independent of one another. =head2 is_simple This method returns true if and only if all requirements are inclusive minimums -- that is, if their string expression is just the version number. =head2 is_finalized This method returns true if the requirements have been finalized by having the C method called on them. =head2 finalize This method marks the requirements finalized. Subsequent attempts to change the requirements will be fatal, I they would result in a change. If they would not alter the requirements, they have no effect. If a finalized set of requirements is cloned, the cloned requirements are not also finalized. =head2 as_string_hash This returns a reference to a hash describing the requirements using the strings in the L specification. For example after the following program: my $req = CPAN::Meta::Requirements->new; $req->add_minimum('CPAN::Meta::Requirements' => 0.102); $req->add_minimum('Library::Foo' => 1.208); $req->add_maximum('Library::Foo' => 2.602); $req->add_minimum('Module::Bar' => 'v1.2.3'); $req->add_exclusion('Module::Bar' => 'v1.2.8'); $req->exact_version('Xyzzy' => '6.01'); my $hashref = $req->as_string_hash; C<$hashref> would contain: { 'CPAN::Meta::Requirements' => '0.102', 'Library::Foo' => '>= 1.208, <= 2.206', 'Module::Bar' => '>= v1.2.3, != v1.2.8', 'Xyzzy' => '== 6.01', } =head2 add_string_requirement $req->add_string_requirement('Library::Foo' => '>= 1.208, <= 2.206'); $req->add_string_requirement('Library::Foo' => v1.208); This method parses the passed in string and adds the appropriate requirement for the given module. A version can be a Perl "v-string". It understands version ranges as described in the L. For example: =over 4 =item 1.3 =item >= 1.3 =item <= 1.3 =item == 1.3 =item != 1.3 =item > 1.3 =item < 1.3 =item >= 1.3, != 1.5, <= 2.0 A version number without an operator is equivalent to specifying a minimum (C=>). Extra whitespace is allowed. =back =head2 from_string_hash my $req = CPAN::Meta::Requirements->from_string_hash( \%hash ); my $req = CPAN::Meta::Requirements->from_string_hash( \%hash, \%opts ); This is an alternate constructor for a CPAN::Meta::Requirements object. It takes a hash of module names and version requirement strings and returns a new CPAN::Meta::Requirements object. As with add_string_requirement, a version can be a Perl "v-string". Optionally, you can supply a hash-reference of options, exactly as with the L method. =for :stopwords cpan testmatrix url bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan =head1 SUPPORT =head2 Bugs / Feature Requests Please report any bugs or feature requests through the issue tracker at L. You will be notified automatically of any progress on your issue. =head2 Source Code This is open source software. The code repository is available for public review and contribution under the terms of the license. L git clone https://github.com/Perl-Toolchain-Gang/CPAN-Meta-Requirements.git =head1 AUTHORS =over 4 =item * David Golden =item * Ricardo Signes =back =head1 CONTRIBUTORS =for stopwords Ed J Graham Knop Karen Etheridge Leon Timmermans Paul Howarth Ricardo Signes robario Tatsuhiko Miyagawa =over 4 =item * Ed J =item * Graham Knop =item * Karen Etheridge =item * Leon Timmermans =item * Paul Howarth =item * Ricardo Signes =item * robario =item * Tatsuhiko Miyagawa =item * Tatsuhiko Miyagawa =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2010 by David Golden and Ricardo Signes. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut PKA0]< $l:M:M%perl5/CPAN/Meta/Requirements/Range.pmnu6$use v5.10; use strict; use warnings; package CPAN::Meta::Requirements::Range; # ABSTRACT: a set of version requirements for a CPAN dist our $VERSION = '2.143'; use Carp (); #pod =head1 SYNOPSIS #pod #pod use CPAN::Meta::Requirements::Range; #pod #pod my $range = CPAN::Meta::Requirements::Range->with_minimum(1); #pod #pod $range = $range->with_maximum('v2.2'); #pod #pod my $stringified = $range->as_string; #pod #pod =head1 DESCRIPTION #pod #pod A CPAN::Meta::Requirements::Range object models a set of version constraints like #pod those specified in the F or F files in CPAN distributions, #pod and as defined by L; #pod It can be built up by adding more and more constraints, and it will reduce them #pod to the simplest representation. #pod #pod Logically impossible constraints will be identified immediately by thrown #pod exceptions. #pod #pod =cut use Carp (); package CPAN::Meta::Requirements::Range::_Base; # To help ExtUtils::MakeMaker bootstrap CPAN::Meta::Requirements on perls # before 5.10, we fall back to the EUMM bundled compatibility version module if # that's the only thing available. This shouldn't ever happen in a normal CPAN # install of CPAN::Meta::Requirements, as version.pm will be picked up from # prereqs and be available at runtime. BEGIN { eval "use version ()"; ## no critic if ( my $err = $@ ) { eval "use ExtUtils::MakeMaker::version" or die $err; ## no critic } } # from version::vpp sub _find_magic_vstring { my $value = shift; my $tvalue = ''; require B; my $sv = B::svref_2object(\$value); my $magic = ref($sv) eq 'B::PVMG' ? $sv->MAGIC : undef; while ( $magic ) { if ( $magic->TYPE eq 'V' ) { $tvalue = $magic->PTR; $tvalue =~ s/^v?(.+)$/v$1/; last; } else { $magic = $magic->MOREMAGIC; } } return $tvalue; } # Perl 5.10.0 didn't have "is_qv" in version.pm *_is_qv = version->can('is_qv') ? sub { $_[0]->is_qv } : sub { exists $_[0]->{qv} }; # construct once, reuse many times my $V0 = version->new(0); # safe if given an unblessed reference sub _isa_version { UNIVERSAL::isa( $_[0], 'UNIVERSAL' ) && $_[0]->isa('version') } sub _version_object { my ($self, $version, $module, $bad_version_hook) = @_; my ($vobj, $err); if (not defined $version or (!ref($version) && $version eq '0')) { return $V0; } elsif ( ref($version) eq 'version' || ( ref($version) && _isa_version($version) ) ) { $vobj = $version; } else { # hack around version::vpp not handling <3 character vstring literals if ( $INC{'version/vpp.pm'} || $INC{'ExtUtils/MakeMaker/version/vpp.pm'} ) { my $magic = _find_magic_vstring( $version ); $version = $magic if length $magic; } # pad to 3 characters if before 5.8.1 and appears to be a v-string if ( $] < 5.008001 && $version !~ /\A[0-9]/ && substr($version,0,1) ne 'v' && length($version) < 3 ) { $version .= "\0" x (3 - length($version)); } eval { local $SIG{__WARN__} = sub { die "Invalid version: $_[0]" }; # avoid specific segfault on some older version.pm versions die "Invalid version: $version" if $version eq 'version'; $vobj = version->new($version); }; if ( my $err = $@ ) { $vobj = eval { $bad_version_hook->($version, $module) } if ref $bad_version_hook eq 'CODE'; unless (eval { $vobj->isa("version") }) { $err =~ s{ at .* line \d+.*$}{}; die "Can't convert '$version': $err"; } } } # ensure no leading '.' if ( $vobj =~ m{\A\.} ) { $vobj = version->new("0$vobj"); } # ensure normal v-string form if ( _is_qv($vobj) ) { $vobj = version->new($vobj->normal); } return $vobj; } #pod =method with_string_requirement #pod #pod $req->with_string_requirement('>= 1.208, <= 2.206'); #pod $req->with_string_requirement(v1.208); #pod #pod This method parses the passed in string and adds the appropriate requirement. #pod A version can be a Perl "v-string". It understands version ranges as described #pod in the L. For example: #pod #pod =over 4 #pod #pod =item 1.3 #pod #pod =item >= 1.3 #pod #pod =item <= 1.3 #pod #pod =item == 1.3 #pod #pod =item != 1.3 #pod #pod =item > 1.3 #pod #pod =item < 1.3 #pod #pod =item >= 1.3, != 1.5, <= 2.0 #pod #pod A version number without an operator is equivalent to specifying a minimum #pod (C=>). Extra whitespace is allowed. #pod #pod =back #pod #pod =cut my %methods_for_op = ( '==' => [ qw(with_exact_version) ], '!=' => [ qw(with_exclusion) ], '>=' => [ qw(with_minimum) ], '<=' => [ qw(with_maximum) ], '>' => [ qw(with_minimum with_exclusion) ], '<' => [ qw(with_maximum with_exclusion) ], ); sub with_string_requirement { my ($self, $req, $module, $bad_version_hook) = @_; $module //= 'module'; unless ( defined $req && length $req ) { $req = 0; Carp::carp("Undefined requirement for $module treated as '0'"); } my $magic = _find_magic_vstring( $req ); if (length $magic) { return $self->with_minimum($magic, $module, $bad_version_hook); } my @parts = split qr{\s*,\s*}, $req; for my $part (@parts) { my ($op, $ver) = $part =~ m{\A\s*(==|>=|>|<=|<|!=)\s*(.*)\z}; if (! defined $op) { $self = $self->with_minimum($part, $module, $bad_version_hook); } else { Carp::croak("illegal requirement string: $req") unless my $methods = $methods_for_op{ $op }; $self = $self->$_($ver, $module, $bad_version_hook) for @$methods; } } return $self; } #pod =method with_range #pod #pod $range->with_range($other_range) #pod #pod This creates a new range object that is a merge two others. #pod #pod =cut sub with_range { my ($self, $other, $module, $bad_version_hook) = @_; for my $modifier($other->_as_modifiers) { my ($method, $arg) = @$modifier; $self = $self->$method($arg, $module, $bad_version_hook); } return $self; } package CPAN::Meta::Requirements::Range; our @ISA = 'CPAN::Meta::Requirements::Range::_Base'; sub _clone { return (bless { } => $_[0]) unless ref $_[0]; my ($s) = @_; my %guts = ( (exists $s->{minimum} ? (minimum => version->new($s->{minimum})) : ()), (exists $s->{maximum} ? (maximum => version->new($s->{maximum})) : ()), (exists $s->{exclusions} ? (exclusions => [ map { version->new($_) } @{ $s->{exclusions} } ]) : ()), ); bless \%guts => ref($s); } #pod =method with_exact_version #pod #pod $range->with_exact_version( $version ); #pod #pod This sets the version required to I the given #pod version. No other version would be considered acceptable. #pod #pod This method returns the version range object. #pod #pod =cut sub with_exact_version { my ($self, $version, $module, $bad_version_hook) = @_; $module //= 'module'; $self = $self->_clone; $version = $self->_version_object($version, $module, $bad_version_hook); unless ($self->accepts($version)) { $self->_reject_requirements( $module, "exact specification $version outside of range " . $self->as_string ); } return CPAN::Meta::Requirements::Range::_Exact->_new($version); } sub _simplify { my ($self, $module) = @_; if (defined $self->{minimum} and defined $self->{maximum}) { if ($self->{minimum} == $self->{maximum}) { if (grep { $_ == $self->{minimum} } @{ $self->{exclusions} || [] }) { $self->_reject_requirements( $module, "minimum and maximum are both $self->{minimum}, which is excluded", ); } return CPAN::Meta::Requirements::Range::_Exact->_new($self->{minimum}); } if ($self->{minimum} > $self->{maximum}) { $self->_reject_requirements( $module, "minimum $self->{minimum} exceeds maximum $self->{maximum}", ); } } # eliminate irrelevant exclusions if ($self->{exclusions}) { my %seen; @{ $self->{exclusions} } = grep { (! defined $self->{minimum} or $_ >= $self->{minimum}) and (! defined $self->{maximum} or $_ <= $self->{maximum}) and ! $seen{$_}++ } @{ $self->{exclusions} }; } return $self; } #pod =method with_minimum #pod #pod $range->with_minimum( $version ); #pod #pod This adds a new minimum version requirement. If the new requirement is #pod redundant to the existing specification, this has no effect. #pod #pod Minimum requirements are inclusive. C<$version> is required, along with any #pod greater version number. #pod #pod This method returns the version range object. #pod #pod =cut sub with_minimum { my ($self, $minimum, $module, $bad_version_hook) = @_; $module //= 'module'; $self = $self->_clone; $minimum = $self->_version_object( $minimum, $module, $bad_version_hook ); if (defined (my $old_min = $self->{minimum})) { $self->{minimum} = (sort { $b cmp $a } ($minimum, $old_min))[0]; } else { $self->{minimum} = $minimum; } return $self->_simplify($module); } #pod =method with_maximum #pod #pod $range->with_maximum( $version ); #pod #pod This adds a new maximum version requirement. If the new requirement is #pod redundant to the existing specification, this has no effect. #pod #pod Maximum requirements are inclusive. No version strictly greater than the given #pod version is allowed. #pod #pod This method returns the version range object. #pod #pod =cut sub with_maximum { my ($self, $maximum, $module, $bad_version_hook) = @_; $module //= 'module'; $self = $self->_clone; $maximum = $self->_version_object( $maximum, $module, $bad_version_hook ); if (defined (my $old_max = $self->{maximum})) { $self->{maximum} = (sort { $a cmp $b } ($maximum, $old_max))[0]; } else { $self->{maximum} = $maximum; } return $self->_simplify($module); } #pod =method with_exclusion #pod #pod $range->with_exclusion( $version ); #pod #pod This adds a new excluded version. For example, you might use these three #pod method calls: #pod #pod $range->with_minimum( '1.00' ); #pod $range->with_maximum( '1.82' ); #pod #pod $range->with_exclusion( '1.75' ); #pod #pod Any version between 1.00 and 1.82 inclusive would be acceptable, except for #pod 1.75. #pod #pod This method returns the requirements object. #pod #pod =cut sub with_exclusion { my ($self, $exclusion, $module, $bad_version_hook) = @_; $module //= 'module'; $self = $self->_clone; $exclusion = $self->_version_object( $exclusion, $module, $bad_version_hook ); push @{ $self->{exclusions} ||= [] }, $exclusion; return $self->_simplify($module); } sub _as_modifiers { my ($self) = @_; my @mods; push @mods, [ with_minimum => $self->{minimum} ] if exists $self->{minimum}; push @mods, [ with_maximum => $self->{maximum} ] if exists $self->{maximum}; push @mods, map {; [ with_exclusion => $_ ] } @{$self->{exclusions} || []}; return @mods; } #pod =method as_struct #pod #pod $range->as_struct( $module ); #pod #pod This returns a data structure containing the version requirements. This should #pod not be used for version checks (see L instead). #pod #pod =cut sub as_struct { my ($self) = @_; return 0 if ! keys %$self; my @exclusions = @{ $self->{exclusions} || [] }; my @parts; for my $tuple ( [ qw( >= > minimum ) ], [ qw( <= < maximum ) ], ) { my ($op, $e_op, $k) = @$tuple; if (exists $self->{$k}) { my @new_exclusions = grep { $_ != $self->{ $k } } @exclusions; if (@new_exclusions == @exclusions) { push @parts, [ $op, "$self->{ $k }" ]; } else { push @parts, [ $e_op, "$self->{ $k }" ]; @exclusions = @new_exclusions; } } } push @parts, map {; [ "!=", "$_" ] } @exclusions; return \@parts; } #pod =method as_string #pod #pod $range->as_string; #pod #pod This returns a string containing the version requirements in the format #pod described in L. This should only be used for informational #pod purposes such as error messages and should not be interpreted or used for #pod comparison (see L instead). #pod #pod =cut sub as_string { my ($self) = @_; my @parts = @{ $self->as_struct }; return $parts[0][1] if @parts == 1 and $parts[0][0] eq '>='; return join q{, }, map {; join q{ }, @$_ } @parts; } sub _reject_requirements { my ($self, $module, $error) = @_; Carp::croak("illegal requirements for $module: $error") } #pod =method accepts #pod #pod my $bool = $range->accepts($version); #pod #pod Given a version, this method returns true if the version specification #pod accepts the provided version. In other words, given: #pod #pod '>= 1.00, < 2.00' #pod #pod We will accept 1.00 and 1.75 but not 0.50 or 2.00. #pod #pod =cut sub accepts { my ($self, $version) = @_; return if defined $self->{minimum} and $version < $self->{minimum}; return if defined $self->{maximum} and $version > $self->{maximum}; return if defined $self->{exclusions} and grep { $version == $_ } @{ $self->{exclusions} }; return 1; } #pod =method is_simple #pod #pod This method returns true if and only if the range is an inclusive minimum #pod -- that is, if their string expression is just the version number. #pod #pod =cut sub is_simple { my ($self) = @_; # XXX: This is a complete hack, but also entirely correct. return if $self->as_string =~ /\s/; return 1; } package CPAN::Meta::Requirements::Range::_Exact; our @ISA = 'CPAN::Meta::Requirements::Range::_Base'; our $VERSION = '2.141'; BEGIN { eval "use version ()"; ## no critic if ( my $err = $@ ) { eval "use ExtUtils::MakeMaker::version" or die $err; ## no critic } } sub _new { bless { version => $_[1] } => $_[0] } sub accepts { return $_[0]{version} == $_[1] } sub _reject_requirements { my ($self, $module, $error) = @_; Carp::croak("illegal requirements for $module: $error") } sub _clone { (ref $_[0])->_new( version->new( $_[0]{version} ) ) } sub with_exact_version { my ($self, $version, $module, $bad_version_hook) = @_; $module //= 'module'; $version = $self->_version_object($version, $module, $bad_version_hook); return $self->_clone if $self->accepts($version); $self->_reject_requirements( $module, "can't be exactly $version when exact requirement is already $self->{version}", ); } sub with_minimum { my ($self, $minimum, $module, $bad_version_hook) = @_; $module //= 'module'; $minimum = $self->_version_object( $minimum, $module, $bad_version_hook ); return $self->_clone if $self->{version} >= $minimum; $self->_reject_requirements( $module, "minimum $minimum exceeds exact specification $self->{version}", ); } sub with_maximum { my ($self, $maximum, $module, $bad_version_hook) = @_; $module //= 'module'; $maximum = $self->_version_object( $maximum, $module, $bad_version_hook ); return $self->_clone if $self->{version} <= $maximum; $self->_reject_requirements( $module, "maximum $maximum below exact specification $self->{version}", ); } sub with_exclusion { my ($self, $exclusion, $module, $bad_version_hook) = @_; $module //= 'module'; $exclusion = $self->_version_object( $exclusion, $module, $bad_version_hook ); return $self->_clone unless $exclusion == $self->{version}; $self->_reject_requirements( $module, "tried to exclude $exclusion, which is already exactly specified", ); } sub as_string { return "== $_[0]{version}" } sub as_struct { return [ [ '==', "$_[0]{version}" ] ] } sub _as_modifiers { return [ with_exact_version => $_[0]{version} ] } 1; # vim: ts=2 sts=2 sw=2 et: __END__ =pod =encoding UTF-8 =head1 NAME CPAN::Meta::Requirements::Range - a set of version requirements for a CPAN dist =head1 VERSION version 2.143 =head1 SYNOPSIS use CPAN::Meta::Requirements::Range; my $range = CPAN::Meta::Requirements::Range->with_minimum(1); $range = $range->with_maximum('v2.2'); my $stringified = $range->as_string; =head1 DESCRIPTION A CPAN::Meta::Requirements::Range object models a set of version constraints like those specified in the F or F files in CPAN distributions, and as defined by L; It can be built up by adding more and more constraints, and it will reduce them to the simplest representation. Logically impossible constraints will be identified immediately by thrown exceptions. =head1 METHODS =head2 with_string_requirement $req->with_string_requirement('>= 1.208, <= 2.206'); $req->with_string_requirement(v1.208); This method parses the passed in string and adds the appropriate requirement. A version can be a Perl "v-string". It understands version ranges as described in the L. For example: =over 4 =item 1.3 =item >= 1.3 =item <= 1.3 =item == 1.3 =item != 1.3 =item > 1.3 =item < 1.3 =item >= 1.3, != 1.5, <= 2.0 A version number without an operator is equivalent to specifying a minimum (C=>). Extra whitespace is allowed. =back =head2 with_range $range->with_range($other_range) This creates a new range object that is a merge two others. =head2 with_exact_version $range->with_exact_version( $version ); This sets the version required to I the given version. No other version would be considered acceptable. This method returns the version range object. =head2 with_minimum $range->with_minimum( $version ); This adds a new minimum version requirement. If the new requirement is redundant to the existing specification, this has no effect. Minimum requirements are inclusive. C<$version> is required, along with any greater version number. This method returns the version range object. =head2 with_maximum $range->with_maximum( $version ); This adds a new maximum version requirement. If the new requirement is redundant to the existing specification, this has no effect. Maximum requirements are inclusive. No version strictly greater than the given version is allowed. This method returns the version range object. =head2 with_exclusion $range->with_exclusion( $version ); This adds a new excluded version. For example, you might use these three method calls: $range->with_minimum( '1.00' ); $range->with_maximum( '1.82' ); $range->with_exclusion( '1.75' ); Any version between 1.00 and 1.82 inclusive would be acceptable, except for 1.75. This method returns the requirements object. =head2 as_struct $range->as_struct( $module ); This returns a data structure containing the version requirements. This should not be used for version checks (see L instead). =head2 as_string $range->as_string; This returns a string containing the version requirements in the format described in L. This should only be used for informational purposes such as error messages and should not be interpreted or used for comparison (see L instead). =head2 accepts my $bool = $range->accepts($version); Given a version, this method returns true if the version specification accepts the provided version. In other words, given: '>= 1.00, < 2.00' We will accept 1.00 and 1.75 but not 0.50 or 2.00. =head2 is_simple This method returns true if and only if the range is an inclusive minimum -- that is, if their string expression is just the version number. =head1 AUTHORS =over 4 =item * David Golden =item * Ricardo Signes =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2010 by David Golden and Ricardo Signes. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut PKA0]$d d perl5/CPAN/Kwalify.pmnu6$=head1 NAME CPAN::Kwalify - Interface between CPAN.pm and Kwalify.pm =head1 SYNOPSIS use CPAN::Kwalify; validate($schema_name, $data, $file, $doc); =head1 DESCRIPTION =over =item _validate($schema_name, $data, $file, $doc) $schema_name is the name of a supported schema. Currently only C is supported. $data is the data to be validated. $file is the absolute path to the file the data are coming from. $doc is the index of the document within $doc that is to be validated. The last two arguments are only there for better error reporting. Relies on being called from within CPAN.pm. Dies if something fails. Does not return anything useful. =item yaml($schema_name) Returns the YAML text of that schema. Dies if something fails. =back =head1 AUTHOR Andreas Koenig C<< >> =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L =cut use strict; package CPAN::Kwalify; use vars qw($VERSION $VAR1); $VERSION = "5.50"; use File::Spec (); my %vcache = (); my $schema_loaded = {}; sub _validate { my($schema_name,$data,$abs,$y) = @_; my $yaml_module = CPAN->_yaml_module; if ( $CPAN::META->has_inst($yaml_module) && $CPAN::META->has_inst("Kwalify") ) { my $load = UNIVERSAL::can($yaml_module,"Load"); unless ($schema_loaded->{$schema_name}) { eval { my $schema_yaml = yaml($schema_name); $schema_loaded->{$schema_name} = $load->($schema_yaml); }; if ($@) { # we know that YAML.pm 0.62 cannot parse the schema, # so we try a fallback my $content = do { my $path = __FILE__; $path =~ s/\.pm$//; $path = File::Spec->catfile($path, "$schema_name.dd"); local *FH; open FH, $path or die "Could not open '$path': $!"; local $/; ; }; $VAR1 = undef; eval $content; if (my $err = $@) { die "parsing of '$schema_name.dd' failed: $err"; } $schema_loaded->{$schema_name} = $VAR1; } } } if (my $schema = $schema_loaded->{$schema_name}) { my $mtime = (stat $abs)[9]; for my $k (keys %{$vcache{$abs}}) { delete $vcache{$abs}{$k} unless $k eq $mtime; } return if $vcache{$abs}{$mtime}{$y}++; eval { Kwalify::validate($schema, $data) }; if (my $err = $@) { my $info = {}; yaml($schema_name, info => $info); die "validation of distropref '$abs'[$y] against schema '$info->{path}' failed: $err"; } } } sub _clear_cache { %vcache = (); } sub yaml { my($schema_name, %opt) = @_; my $content = do { my $path = __FILE__; $path =~ s/\.pm$//; $path = File::Spec->catfile($path, "$schema_name.yml"); if ($opt{info}) { $opt{info}{path} = $path; } local *FH; open FH, $path or die "Could not open '$path': $!"; local $/; ; }; return $content; } 1; # Local Variables: # mode: cperl # cperl-indent-level: 4 # End: PKA0]Чperl5/CPAN/InfoObj.pmnu6$# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::InfoObj; use strict; use CPAN::Debug; @CPAN::InfoObj::ISA = qw(CPAN::Debug); use Cwd qw(chdir); use vars qw( $VERSION ); $VERSION = "5.5"; sub ro { my $self = shift; exists $self->{RO} and return $self->{RO}; } #-> sub CPAN::InfoObj::cpan_userid sub cpan_userid { my $self = shift; my $ro = $self->ro; if ($ro) { return $ro->{CPAN_USERID} || "N/A"; } else { $self->debug("ID[$self->{ID}]"); # N/A for bundles found locally return "N/A"; } } sub id { shift->{ID}; } #-> sub CPAN::InfoObj::new ; sub new { my $this = bless {}, shift; %$this = @_; $this } # The set method may only be used by code that reads index data or # otherwise "objective" data from the outside world. All session # related material may do anything else with instance variables but # must not touch the hash under the RO attribute. The reason is that # the RO hash gets written to Metadata file and is thus persistent. #-> sub CPAN::InfoObj::safe_chdir ; sub safe_chdir { my($self,$todir) = @_; # we die if we cannot chdir and we are debuggable Carp::confess("safe_chdir called without todir argument") unless defined $todir and length $todir; if (chdir $todir) { $self->debug(sprintf "changed directory to %s", CPAN::anycwd()) if $CPAN::DEBUG; } else { if (-e $todir) { unless (-x $todir) { unless (chmod 0755, $todir) { my $cwd = CPAN::anycwd(); $CPAN::Frontend->mywarn("I have neither the -x permission nor the ". "permission to change the permission; cannot ". "chdir to '$todir'\n"); $CPAN::Frontend->mysleep(5); $CPAN::Frontend->mydie(qq{Could not chdir from cwd[$cwd] }. qq{to todir[$todir]: $!}); } } } else { $CPAN::Frontend->mydie("Directory '$todir' has gone. Cannot continue.\n"); } if (chdir $todir) { $self->debug(sprintf "changed directory to %s", CPAN::anycwd()) if $CPAN::DEBUG; } else { my $cwd = CPAN::anycwd(); $CPAN::Frontend->mydie(qq{Could not chdir from cwd[$cwd] }. qq{to todir[$todir] (a chmod has been issued): $!}); } } } #-> sub CPAN::InfoObj::set ; sub set { my($self,%att) = @_; my $class = ref $self; # This must be ||=, not ||, because only if we write an empty # reference, only then the set method will write into the readonly # area. But for Distributions that spring into existence, maybe # because of a typo, we do not like it that they are written into # the readonly area and made permanent (at least for a while) and # that is why we do not "allow" other places to call ->set. unless ($self->id) { CPAN->debug("Bug? Empty ID, rejecting"); return; } my $ro = $self->{RO} = $CPAN::META->{readonly}{$class}{$self->id} ||= {}; while (my($k,$v) = each %att) { $ro->{$k} = $v; } } #-> sub CPAN::InfoObj::as_glimpse ; sub as_glimpse { my($self) = @_; my(@m); my $class = ref($self); $class =~ s/^CPAN:://; my $id = $self->can("pretty_id") ? $self->pretty_id : $self->{ID}; push @m, sprintf "%-15s %s\n", $class, $id; join "", @m; } #-> sub CPAN::InfoObj::as_string ; sub as_string { my($self) = @_; my(@m); my $class = ref($self); $class =~ s/^CPAN:://; push @m, $class, " id = $self->{ID}\n"; my $ro; unless ($ro = $self->ro) { if (substr($self->{ID},-1,1) eq ".") { # directory $ro = +{}; } else { $CPAN::Frontend->mywarn("Unknown object $self->{ID}\n"); $CPAN::Frontend->mysleep(5); return; } } for (sort keys %$ro) { # next if m/^(ID|RO)$/; my $extra = ""; if ($_ eq "CPAN_USERID") { $extra .= " ("; $extra .= $self->fullname; my $email; # old perls! if ($email = $CPAN::META->instance("CPAN::Author", $self->cpan_userid )->email) { $extra .= " <$email>"; } else { $extra .= " "; } $extra .= ")"; } elsif ($_ eq "FULLNAME") { # potential UTF-8 conversion push @m, sprintf " %-12s %s\n", $_, $self->fullname; next; } next unless defined $ro->{$_}; push @m, sprintf " %-12s %s%s\n", $_, $ro->{$_}, $extra; } KEY: for (sort keys %$self) { next if m/^(ID|RO)$/; unless (defined $self->{$_}) { delete $self->{$_}; next KEY; } if (ref($self->{$_}) eq "ARRAY") { push @m, sprintf " %-12s %s\n", $_, "@{$self->{$_}}"; } elsif (ref($self->{$_}) eq "HASH") { my $value; if (/^CONTAINSMODS$/) { $value = join(" ",sort keys %{$self->{$_}}); } elsif (/^prereq_pm$/) { my @value; my $v = $self->{$_}; for my $x (sort keys %$v) { my @svalue; for my $y (sort keys %{$v->{$x}}) { push @svalue, "$y=>$v->{$x}{$y}"; } push @value, "$x\:" . join ",", @svalue if @svalue; } $value = join ";", @value; } else { $value = $self->{$_}; } push @m, sprintf( " %-12s %s\n", $_, $value, ); } else { push @m, sprintf " %-12s %s\n", $_, $self->{$_}; } } join "", @m, "\n"; } #-> sub CPAN::InfoObj::fullname ; sub fullname { my($self) = @_; $CPAN::META->instance("CPAN::Author",$self->cpan_userid)->fullname; } #-> sub CPAN::InfoObj::dump ; sub dump { my($self, $what) = @_; unless ($CPAN::META->has_inst("Data::Dumper")) { $CPAN::Frontend->mydie("dump command requires Data::Dumper installed"); } local $Data::Dumper::Sortkeys; $Data::Dumper::Sortkeys = 1; my $out = Data::Dumper::Dumper($what ? eval $what : $self); if (length $out > 100000) { my $fh_pager = FileHandle->new; local($SIG{PIPE}) = "IGNORE"; my $pager = $CPAN::Config->{'pager'} || "cat"; $fh_pager->open("|$pager") or die "Could not open pager $pager\: $!"; $fh_pager->print($out); close $fh_pager; } else { $CPAN::Frontend->myprint($out); } } 1; PKA0]Hperl5/CPAN/FTP/netrc.pmnu6$package CPAN::FTP::netrc; use strict; $CPAN::FTP::netrc::VERSION = $CPAN::FTP::netrc::VERSION = "1.01"; # package CPAN::FTP::netrc; sub new { my($class) = @_; my $file = File::Spec->catfile($ENV{HOME},".netrc"); my($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat($file); $mode ||= 0; my $protected = 0; my($fh,@machines,$hasdefault); $hasdefault = 0; $fh = FileHandle->new or die "Could not create a filehandle"; if($fh->open($file)) { $protected = ($mode & 077) == 0; local($/) = ""; NETRC: while (<$fh>) { my(@tokens) = split " ", $_; TOKEN: while (@tokens) { my($t) = shift @tokens; if ($t eq "default") { $hasdefault++; last NETRC; } last TOKEN if $t eq "macdef"; if ($t eq "machine") { push @machines, shift @tokens; } } } } else { $file = $hasdefault = $protected = ""; } bless { 'mach' => [@machines], 'netrc' => $file, 'hasdefault' => $hasdefault, 'protected' => $protected, }, $class; } # CPAN::FTP::netrc::hasdefault; sub hasdefault { shift->{'hasdefault'} } sub netrc { shift->{'netrc'} } sub protected { shift->{'protected'} } sub contains { my($self,$mach) = @_; for ( @{$self->{'mach'}} ) { return 1 if $_ eq $mach; } return 0; } 1; PKA0]4A''perl5/CPAN/Bundle.pmnu6$# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::Bundle; use strict; use CPAN::Module; @CPAN::Bundle::ISA = qw(CPAN::Module); use vars qw( $VERSION ); $VERSION = "5.5005"; sub look { my $self = shift; $CPAN::Frontend->myprint($self->as_string); } #-> CPAN::Bundle::undelay sub undelay { my $self = shift; delete $self->{later}; for my $c ( $self->contains ) { my $obj = CPAN::Shell->expandany($c) or next; if ($obj->id eq $self->id){ my $id = $obj->id; $CPAN::Frontend->mywarn("$id seems to contain itself, skipping\n"); next; } $obj->undelay; } } # mark as dirty/clean #-> sub CPAN::Bundle::color_cmd_tmps ; sub color_cmd_tmps { my($self) = shift; my($depth) = shift || 0; my($color) = shift || 0; my($ancestors) = shift || []; # a module needs to recurse to its cpan_file, a distribution needs # to recurse into its prereq_pms, a bundle needs to recurse into its modules return if exists $self->{incommandcolor} && $color==1 && $self->{incommandcolor}==$color; if ($depth>=$CPAN::MAX_RECURSION) { my $e = CPAN::Exception::RecursiveDependency->new($ancestors); if ($e->is_resolvable) { return $self->{incommandcolor}=2; } else { die $e; } } # warn "color_cmd_tmps $depth $color " . $self->id; # sleep 1; for my $c ( $self->contains ) { my $obj = CPAN::Shell->expandany($c) or next; CPAN->debug("c[$c]obj[$obj]") if $CPAN::DEBUG; $obj->color_cmd_tmps($depth+1,$color,[@$ancestors, $self->id]); } # never reached code? #if ($color==0) { #delete $self->{badtestcnt}; #} $self->{incommandcolor} = $color; } #-> sub CPAN::Bundle::as_string ; sub as_string { my($self) = @_; $self->contains; # following line must be "=", not "||=" because we have a moving target $self->{INST_VERSION} = $self->inst_version; return $self->SUPER::as_string; } #-> sub CPAN::Bundle::contains ; sub contains { my($self) = @_; my($inst_file) = $self->inst_file || ""; my($id) = $self->id; $self->debug("inst_file[$inst_file]id[$id]") if $CPAN::DEBUG; if ($inst_file && CPAN::Version->vlt($self->inst_version,$self->cpan_version)) { undef $inst_file; } unless ($inst_file) { # Try to get at it in the cpan directory $self->debug("no inst_file") if $CPAN::DEBUG; my $cpan_file; $CPAN::Frontend->mydie("I don't know a bundle with ID '$id'\n") unless $cpan_file = $self->cpan_file; if ($cpan_file eq "N/A") { $CPAN::Frontend->mywarn("Bundle '$id' not found on disk and not on CPAN. Maybe stale symlink? Maybe removed during session?\n"); return; } my $dist = $CPAN::META->instance('CPAN::Distribution', $self->cpan_file); $self->debug("before get id[$dist->{ID}]") if $CPAN::DEBUG; $dist->get; $self->debug("after get id[$dist->{ID}]") if $CPAN::DEBUG; my($todir) = $CPAN::Config->{'cpan_home'}; my(@me,$from,$to,$me); @me = split /::/, $self->id; $me[-1] .= ".pm"; $me = File::Spec->catfile(@me); my $build_dir; unless ($build_dir = $dist->{build_dir}) { $CPAN::Frontend->mywarn("Warning: cannot determine bundle content without a build_dir.\n"); return; } $from = $self->find_bundle_file($build_dir,join('/',@me)); $to = File::Spec->catfile($todir,$me); File::Path::mkpath(File::Basename::dirname($to)); File::Copy::copy($from, $to) or Carp::confess("Couldn't copy $from to $to: $!"); $inst_file = $to; } my @result; my $fh = FileHandle->new; local $/ = "\n"; open($fh,$inst_file) or die "Could not open '$inst_file': $!"; my $in_cont = 0; $self->debug("inst_file[$inst_file]") if $CPAN::DEBUG; while (<$fh>) { $in_cont = m/^=(?!head1\s+(?i-xsm:CONTENTS))/ ? 0 : m/^=head1\s+(?i-xsm:CONTENTS)/ ? 1 : $in_cont; next unless $in_cont; next if /^=/; s/\#.*//; next if /^\s+$/; chomp; push @result, (split " ", $_, 2)[0]; } close $fh; delete $self->{STATUS}; $self->{CONTAINS} = \@result; $self->debug("CONTAINS[@result]") if $CPAN::DEBUG; unless (@result) { $CPAN::Frontend->mywarn(qq{ The bundle file "$inst_file" may be a broken bundlefile. It seems not to contain any bundle definition. Please check the file and if it is bogus, please delete it. Sorry for the inconvenience. }); } @result; } #-> sub CPAN::Bundle::find_bundle_file # $where is in local format, $what is in unix format sub find_bundle_file { my($self,$where,$what) = @_; $self->debug("where[$where]what[$what]") if $CPAN::DEBUG; ### The following two lines let CPAN.pm become Bundle/CPAN.pm :-( ### my $bu = File::Spec->catfile($where,$what); ### return $bu if -f $bu; my $manifest = File::Spec->catfile($where,"MANIFEST"); unless (-f $manifest) { require ExtUtils::Manifest; my $cwd = CPAN::anycwd(); $self->safe_chdir($where); ExtUtils::Manifest::mkmanifest(); $self->safe_chdir($cwd); } my $fh = FileHandle->new($manifest) or Carp::croak("Couldn't open $manifest: $!"); local($/) = "\n"; my $bundle_filename = $what; $bundle_filename =~ s|Bundle.*/||; my $bundle_unixpath; while (<$fh>) { next if /^\s*\#/; my($file) = /(\S+)/; if ($file =~ m|\Q$what\E$|) { $bundle_unixpath = $file; # return File::Spec->catfile($where,$bundle_unixpath); # bad last; } # retry if she managed to have no Bundle directory $bundle_unixpath = $file if $file =~ m|\Q$bundle_filename\E$|; } return File::Spec->catfile($where, split /\//, $bundle_unixpath) if $bundle_unixpath; Carp::croak("Couldn't find a Bundle file in $where"); } # needs to work quite differently from Module::inst_file because of # cpan_home/Bundle/ directory and the possibility that we have # shadowing effect. As it makes no sense to take the first in @INC for # Bundles, we parse them all for $VERSION and take the newest. #-> sub CPAN::Bundle::inst_file ; sub inst_file { my($self) = @_; my($inst_file); my(@me); @me = split /::/, $self->id; $me[-1] .= ".pm"; my($incdir,$bestv); foreach $incdir ($CPAN::Config->{'cpan_home'},@INC) { my $parsefile = File::Spec->catfile($incdir, @me); CPAN->debug("parsefile[$parsefile]") if $CPAN::DEBUG; next unless -f $parsefile; my $have = eval { MM->parse_version($parsefile); }; if ($@) { $CPAN::Frontend->mywarn("Error while parsing version number in file '$parsefile'\n"); } if (!$bestv || CPAN::Version->vgt($have,$bestv)) { $self->{INST_FILE} = $parsefile; $self->{INST_VERSION} = $bestv = $have; } } $self->{INST_FILE}; } #-> sub CPAN::Bundle::inst_version ; sub inst_version { my($self) = @_; $self->inst_file; # finds INST_VERSION as side effect $self->{INST_VERSION}; } #-> sub CPAN::Bundle::rematein ; sub rematein { my($self,$meth) = @_; $self->debug("self[$self] meth[$meth]") if $CPAN::DEBUG; my($id) = $self->id; Carp::croak( "Can't $meth $id, don't have an associated bundle file. :-(\n" ) unless $self->inst_file || $self->cpan_file; my($s,%fail); for $s ($self->contains) { my($type) = $s =~ m|/| ? 'CPAN::Distribution' : $s =~ m|^Bundle::| ? 'CPAN::Bundle' : 'CPAN::Module'; if ($type eq 'CPAN::Distribution') { $CPAN::Frontend->mywarn(qq{ The Bundle }.$self->id.qq{ contains explicitly a file '$s'. Going to $meth that. }); $CPAN::Frontend->mysleep(5); } # possibly noisy action: $self->debug("type[$type] s[$s]") if $CPAN::DEBUG; my $obj = $CPAN::META->instance($type,$s); $obj->{reqtype} = $self->{reqtype}; $obj->{viabundle} ||= { id => $id, reqtype => $self->{reqtype}, optional => !$self->{mandatory}}; # $obj->$meth(); # XXX should optional be based on whether bundle was optional? -- xdg, 2012-04-01 # A: Sure, what could demand otherwise? --andk, 2013-11-25 CPAN::Queue->queue_item(qmod => $obj->id, reqtype => $self->{reqtype}, optional => !$self->{mandatory}); } } # If a bundle contains another that contains an xs_file we have here, # we just don't bother I suppose #-> sub CPAN::Bundle::xs_file sub xs_file { return 0; } #-> sub CPAN::Bundle::force ; sub fforce { shift->rematein('fforce',@_); } #-> sub CPAN::Bundle::force ; sub force { shift->rematein('force',@_); } #-> sub CPAN::Bundle::notest ; sub notest { shift->rematein('notest',@_); } #-> sub CPAN::Bundle::get ; sub get { shift->rematein('get',@_); } #-> sub CPAN::Bundle::make ; sub make { shift->rematein('make',@_); } #-> sub CPAN::Bundle::test ; sub test { my $self = shift; # $self->{badtestcnt} ||= 0; $self->rematein('test',@_); } #-> sub CPAN::Bundle::install ; sub install { my $self = shift; $self->rematein('install',@_); } #-> sub CPAN::Bundle::clean ; sub clean { shift->rematein('clean',@_); } #-> sub CPAN::Bundle::uptodate ; sub uptodate { my($self) = @_; return 0 unless $self->SUPER::uptodate; # we must have the current Bundle def my $c; foreach $c ($self->contains) { my $obj = CPAN::Shell->expandany($c); return 0 unless $obj->uptodate; } return 1; } #-> sub CPAN::Bundle::readme ; sub readme { my($self) = @_; my($file) = $self->cpan_file or $CPAN::Frontend->myprint(qq{ No File found for bundle } . $self->id . qq{\n}), return; $self->debug("self[$self] file[$file]") if $CPAN::DEBUG; $CPAN::META->instance('CPAN::Distribution',$file)->readme; } 1; PKA0]7=$$perl5/CPAN/FirstTime.pmnu6$# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::FirstTime; use strict; use ExtUtils::MakeMaker (); use FileHandle (); use File::Basename (); use File::Path (); use File::Spec (); use CPAN::Mirrors (); use CPAN::Version (); use vars qw($VERSION $auto_config); $VERSION = "5.5317"; =head1 NAME CPAN::FirstTime - Utility for CPAN::Config file Initialization =head1 SYNOPSIS CPAN::FirstTime::init() =head1 DESCRIPTION The init routine asks a few questions and writes a CPAN/Config.pm or CPAN/MyConfig.pm file (depending on what it is currently using). In the following all questions and explanations regarding config variables are collected. =cut # down until the next =back the manpage must be parsed by the program # because the text is used in the init dialogues. my @podpara = split /\n\n/, <<'=back'; =over 2 =item allow_installing_module_downgrades The CPAN shell can watch the C directories that are built up before running C to determine whether the current distribution will end up with modules being overwritten with decreasing module version numbers. It can then let the build of this distro fail when it discovers a downgrade. Do you want to allow installing distros with decreasing module versions compared to what you have installed (yes, no, ask/yes, ask/no)? =item allow_installing_outdated_dists The CPAN shell can watch the C directories that are built up before running C to determine whether the current distribution contains modules that are indexed with a distro with a higher distro-version number than the current one. It can then let the build of this distro fail when it would not represent the most up-to-date version of the distro. Note: choosing anything but 'yes' for this option will need CPAN::DistnameInfo being installed for taking effect. Do you want to allow installing distros that are not indexed as the highest distro-version for all contained modules (yes, no, ask/yes, ask/no)? =item auto_commit Normally CPAN.pm keeps config variables in memory and changes need to be saved in a separate 'o conf commit' command to make them permanent between sessions. If you set the 'auto_commit' option to true, changes to a config variable are always automatically committed to disk. Always commit changes to config variables to disk? =item build_cache CPAN.pm can limit the size of the disk area for keeping the build directories with all the intermediate files. Cache size for build directory (in MB)? =item build_dir Directory where the build process takes place? =item build_dir_reuse Until version 1.88 CPAN.pm never trusted the contents of the build_dir directory between sessions. Since 1.88_58 CPAN.pm has a YAML-based mechanism that makes it possible to share the contents of the build_dir/ directory between different sessions with the same version of perl. People who prefer to test things several days before installing will like this feature because it saves a lot of time. If you say yes to the following question, CPAN will try to store enough information about the build process so that it can pick up in future sessions at the same state of affairs as it left a previous session. Store and re-use state information about distributions between CPAN.pm sessions? =item build_requires_install_policy When a module declares another one as a 'build_requires' prerequisite this means that the other module is only needed for building or testing the module but need not be installed permanently. In this case you may wish to install that other module nonetheless or just keep it in the 'build_dir' directory to have it available only temporarily. Installing saves time on future installations but makes the perl installation bigger. You can choose if you want to always install (yes), never install (no) or be always asked. In the latter case you can set the default answer for the question to yes (ask/yes) or no (ask/no). Policy on installing 'build_requires' modules (yes, no, ask/yes, ask/no)? =item cache_metadata To considerably speed up the initial CPAN shell startup, it is possible to use Storable to create a cache of metadata. If Storable is not available, the normal index mechanism will be used. Note: this mechanism is not used when use_sqlite is on and SQLite is running. Cache metadata (yes/no)? =item check_sigs CPAN packages can be digitally signed by authors and thus verified with the security provided by strong cryptography. The exact mechanism is defined in the Module::Signature module. While this is generally considered a good thing, it is not always convenient to the end user to install modules that are signed incorrectly or where the key of the author is not available or where some prerequisite for Module::Signature has a bug and so on. With the check_sigs parameter you can turn signature checking on and off. The default is off for now because the whole tool chain for the functionality is not yet considered mature by some. The author of CPAN.pm would recommend setting it to true most of the time and turning it off only if it turns out to be annoying. Note that if you do not have Module::Signature installed, no signature checks will be performed at all. Always try to check and verify signatures if a SIGNATURE file is in the package and Module::Signature is installed (yes/no)? =item cleanup_after_install Users who install modules and do not intend to look back, can free occupied disk space quickly by letting CPAN.pm cleanup each build directory immediately after a successful install. Remove build directory after a successful install? (yes/no)? =item colorize_output When you have Term::ANSIColor installed, you can turn on colorized output to have some visual differences between normal CPAN.pm output, warnings, debugging output, and the output of the modules being installed. Set your favorite colors after some experimenting with the Term::ANSIColor module. Please note that on Windows platforms colorized output also requires the Win32::Console::ANSI module. Do you want to turn on colored output? =item colorize_print Color for normal output? =item colorize_warn Color for warnings? =item colorize_debug Color for debugging messages? =item commandnumber_in_prompt The prompt of the cpan shell can contain the current command number for easier tracking of the session or be a plain string. Do you want the command number in the prompt (yes/no)? =item connect_to_internet_ok If you have never defined your own C in your configuration then C will be hesitant to use the built in default sites for downloading. It will ask you once per session if a connection to the internet is OK and only if you say yes, it will try to connect. But to avoid this question, you can choose your favorite download sites once and get away with it. Or, if you have no favorite download sites answer yes to the following question. If no urllist has been chosen yet, would you prefer CPAN.pm to connect to the built-in default sites without asking? (yes/no)? =item ftp_passive Shall we always set the FTP_PASSIVE environment variable when dealing with ftp download (yes/no)? =item ftpstats_period Statistics about downloads are truncated by size and period simultaneously. How many days shall we keep statistics about downloads? =item ftpstats_size Statistics about downloads are truncated by size and period simultaneously. Setting this to zero or negative disables download statistics. How many items shall we keep in the statistics about downloads? =item getcwd CPAN.pm changes the current working directory often and needs to determine its own current working directory. Per default it uses Cwd::cwd but if this doesn't work on your system for some reason, alternatives can be configured according to the following table: cwd Cwd::cwd getcwd Cwd::getcwd fastcwd Cwd::fastcwd getdcwd Cwd::getdcwd backtickcwd external command cwd Preferred method for determining the current working directory? =item halt_on_failure Normally, CPAN.pm continues processing the full list of targets and dependencies, even if one of them fails. However, you can specify that CPAN should halt after the first failure. (Note that optional recommended or suggested modules that fail will not cause a halt.) Do you want to halt on failure (yes/no)? =item histfile If you have one of the readline packages (Term::ReadLine::Perl, Term::ReadLine::Gnu, possibly others) installed, the interactive CPAN shell will have history support. The next two questions deal with the filename of the history file and with its size. If you do not want to set this variable, please hit SPACE ENTER to the following question. File to save your history? =item histsize Number of lines to save? =item inactivity_timeout Sometimes you may wish to leave the processes run by CPAN alone without caring about them. Because the Makefile.PL or the Build.PL sometimes contains question you're expected to answer, you can set a timer that will kill a 'perl Makefile.PL' process after the specified time in seconds. If you set this value to 0, these processes will wait forever. This is the default and recommended setting. Timeout for inactivity during {Makefile,Build}.PL? =item index_expire The CPAN indexes are usually rebuilt once or twice per hour, but the typical CPAN mirror mirrors only once or twice per day. Depending on the quality of your mirror and your desire to be on the bleeding edge, you may want to set the following value to more or less than one day (which is the default). It determines after how many days CPAN.pm downloads new indexes. Let the index expire after how many days? =item inhibit_startup_message When the CPAN shell is started it normally displays a greeting message that contains the running version and the status of readline support. Do you want to turn this message off? =item keep_source_where Unless you are accessing the CPAN on your filesystem via a file: URL, CPAN.pm needs to keep the source files it downloads somewhere. Please supply a directory where the downloaded files are to be kept. Download target directory? =item load_module_verbosity When CPAN.pm loads a module it needs for some optional feature, it usually reports about module name and version. Choose 'v' to get this message, 'none' to suppress it. Verbosity level for loading modules (none or v)? =item makepl_arg Every Makefile.PL is run by perl in a separate process. Likewise we run 'make' and 'make install' in separate processes. If you have any parameters (e.g. PREFIX, UNINST or the like) you want to pass to the calls, please specify them here. If you don't understand this question, just press ENTER. Typical frequently used settings: PREFIX=~/perl # non-root users (please see manual for more hints) Parameters for the 'perl Makefile.PL' command? =item make_arg Parameters for the 'make' command? Typical frequently used setting: -j3 # dual processor system (on GNU make) Your choice: =item make_install_arg Parameters for the 'make install' command? Typical frequently used setting: UNINST=1 # to always uninstall potentially conflicting files # (but do NOT use with local::lib or INSTALL_BASE) Your choice: =item make_install_make_command Do you want to use a different make command for 'make install'? Cautious people will probably prefer: su root -c make or sudo make or /path1/to/sudo -u admin_account /path2/to/make or some such. Your choice: =item mbuildpl_arg A Build.PL is run by perl in a separate process. Likewise we run './Build' and './Build install' in separate processes. If you have any parameters you want to pass to the calls, please specify them here. Typical frequently used settings: --install_base /home/xxx # different installation directory Parameters for the 'perl Build.PL' command? =item mbuild_arg Parameters for the './Build' command? Setting might be: --extra_linker_flags -L/usr/foo/lib # non-standard library location Your choice: =item mbuild_install_arg Parameters for the './Build install' command? Typical frequently used setting: --uninst 1 # uninstall conflicting files # (but do NOT use with local::lib or INSTALL_BASE) Your choice: =item mbuild_install_build_command Do you want to use a different command for './Build install'? Sudo users will probably prefer: su root -c ./Build or sudo ./Build or /path1/to/sudo -u admin_account ./Build or some such. Your choice: =item pager What is your favorite pager program? =item prefer_installer When you have Module::Build installed and a module comes with both a Makefile.PL and a Build.PL, which shall have precedence? The main two standard installer modules are the old and well established ExtUtils::MakeMaker (for short: EUMM) which uses the Makefile.PL. And the next generation installer Module::Build (MB) which works with the Build.PL (and often comes with a Makefile.PL too). If a module comes only with one of the two we will use that one but if both are supplied then a decision must be made between EUMM and MB. See also http://rt.cpan.org/Ticket/Display.html?id=29235 for a discussion about the right default. Or, as a third option you can choose RAND which will make a random decision (something regular CPAN testers will enjoy). In case you can choose between running a Makefile.PL or a Build.PL, which installer would you prefer (EUMM or MB or RAND)? =item prefs_dir CPAN.pm can store customized build environments based on regular expressions for distribution names. These are YAML files where the default options for CPAN.pm and the environment can be overridden and dialog sequences can be stored that can later be executed by an Expect.pm object. The CPAN.pm distribution comes with some prefab YAML files that cover sample distributions that can be used as blueprints to store your own prefs. Please check out the distroprefs/ directory of the CPAN.pm distribution to get a quick start into the prefs system. Directory where to store default options/environment/dialogs for building modules that need some customization? =item prerequisites_policy The CPAN module can detect when a module which you are trying to build depends on prerequisites. If this happens, it can build the prerequisites for you automatically ('follow'), ask you for confirmation ('ask'), or just ignore them ('ignore'). Choosing 'follow' also sets PERL_AUTOINSTALL and PERL_EXTUTILS_AUTOINSTALL for "--defaultdeps" if not already set. Please set your policy to one of the three values. Policy on building prerequisites (follow, ask or ignore)? =item pushy_https Boolean. Defaults to true. If this option is true, the cpan shell will use https://cpan.org/ to download stuff from the CPAN. It will fall back to http://cpan.org/ if it can't handle https for some reason (missing modules, missing programs). Whenever it falls back to the http protocol, it will issue a warning. If this option is true, the option C will be ignored. Consequently, if you want to work with local mirrors via your own configured list of URLs, you will have to choose no below. Do you want to turn the pushy_https behaviour on? =item randomize_urllist CPAN.pm can introduce some randomness when using hosts for download that are configured in the urllist parameter. Enter a numeric value between 0 and 1 to indicate how often you want to let CPAN.pm try a random host from the urllist. A value of one specifies to always use a random host as the first try. A value of zero means no randomness at all. Anything in between specifies how often, on average, a random host should be tried first. Randomize parameter =item recommends_policy (Experimental feature!) Some CPAN modules recommend additional, optional dependencies. These should generally be installed except in resource constrained environments. When this policy is true, recommended modules will be included with required modules. Include recommended modules? =item scan_cache By default, each time the CPAN module is started, cache scanning is performed to keep the cache size in sync ('atstart'). Alternatively, scanning and cleanup can happen when CPAN exits ('atexit'). To prevent any cache cleanup, answer 'never'. Perform cache scanning ('atstart', 'atexit' or 'never')? =item shell What is your favorite shell? =item show_unparsable_versions During the 'r' command CPAN.pm finds modules without version number. When the command finishes, it prints a report about this. If you want this report to be very verbose, say yes to the following variable. Show all individual modules that have no $VERSION? =item show_upload_date The 'd' and the 'm' command normally only show you information they have in their in-memory database and thus will never connect to the internet. If you set the 'show_upload_date' variable to true, 'm' and 'd' will additionally show you the upload date of the module or distribution. Per default this feature is off because it may require a net connection to get at the upload date. Always try to show upload date with 'd' and 'm' command (yes/no)? =item show_zero_versions During the 'r' command CPAN.pm finds modules with a version number of zero. When the command finishes, it prints a report about this. If you want this report to be very verbose, say yes to the following variable. Show all individual modules that have a $VERSION of zero? =item suggests_policy (Experimental feature!) Some CPAN modules suggest additional, optional dependencies. These 'suggest' dependencies provide enhanced operation. When this policy is true, suggested modules will be included with required modules. Include suggested modules? =item tar_verbosity When CPAN.pm uses the tar command, which switch for the verbosity shall be used? Choose 'none' for quiet operation, 'v' for file name listing, 'vv' for full listing. Tar command verbosity level (none or v or vv)? =item term_is_latin The next option deals with the charset (a.k.a. character set) your terminal supports. In general, CPAN is English speaking territory, so the charset does not matter much but some CPAN have names that are outside the ASCII range. If your terminal supports UTF-8, you should say no to the next question. If it expects ISO-8859-1 (also known as LATIN1) then you should say yes. If it supports neither, your answer does not matter because you will not be able to read the names of some authors anyway. If you answer no, names will be output in UTF-8. Your terminal expects ISO-8859-1 (yes/no)? =item term_ornaments When using Term::ReadLine, you can turn ornaments on so that your input stands out against the output from CPAN.pm. Do you want to turn ornaments on? =item test_report The goal of the CPAN Testers project (http://testers.cpan.org/) is to test as many CPAN packages as possible on as many platforms as possible. This provides valuable feedback to module authors and potential users to identify bugs or platform compatibility issues and improves the overall quality and value of CPAN. One way you can contribute is to send test results for each module that you install. If you install the CPAN::Reporter module, you have the option to automatically generate and deliver test reports to CPAN Testers whenever you run tests on a CPAN package. See the CPAN::Reporter documentation for additional details and configuration settings. If your firewall blocks outgoing traffic, you may need to configure CPAN::Reporter before sending reports. Generate test reports if CPAN::Reporter is installed (yes/no)? =item perl5lib_verbosity When CPAN.pm extends @INC via PERL5LIB, it prints a list of directories added (or a summary of how many directories are added). Choose 'v' to get this message, 'none' to suppress it. Verbosity level for PERL5LIB changes (none or v)? =item prefer_external_tar Per default all untar operations are done with the perl module Archive::Tar; by setting this variable to true the external tar command is used if available; on Unix this is usually preferred because they have a reliable and fast gnutar implementation. Use the external tar program instead of Archive::Tar? =item trust_test_report_history When a distribution has already been tested by CPAN::Reporter on this machine, CPAN can skip the test phase and just rely on the test report history instead. Note that this will not apply to distributions that failed tests because of missing dependencies. Also, tests can be run regardless of the history using "force". Do you want to rely on the test report history (yes/no)? =item urllist_ping_external When automatic selection of the nearest cpan mirrors is performed, turn on the use of the external ping via Net::Ping::External. This is recommended in the case the local network has a transparent proxy. Do you want to use the external ping command when autoselecting mirrors? =item urllist_ping_verbose When automatic selection of the nearest cpan mirrors is performed, this option can be used to turn on verbosity during the selection process. Do you want to see verbosity turned on when autoselecting mirrors? =item use_prompt_default When this is true, CPAN will set PERL_MM_USE_DEFAULT to a true value. This causes ExtUtils::MakeMaker (and compatible) prompts to use default values instead of stopping to prompt you to answer questions. It also sets NONINTERACTIVE_TESTING to a true value to signal more generally that distributions should not try to interact with you. Do you want to use prompt defaults (yes/no)? =item use_sqlite CPAN::SQLite is a layer between the index files that are downloaded from the CPAN and CPAN.pm that speeds up metadata queries and reduces memory consumption of CPAN.pm considerably. Use CPAN::SQLite if available? (yes/no)? =item version_timeout This timeout prevents CPAN from hanging when trying to parse a pathologically coded $VERSION from a module. The default is 15 seconds. If you set this value to 0, no timeout will occur, but this is not recommended. Timeout for parsing module versions? =item yaml_load_code Both YAML.pm and YAML::Syck are capable of deserialising code. As this requires a string eval, which might be a security risk, you can use this option to enable or disable the deserialisation of code via CPAN::DeferredCode. (Note: This does not work under perl 5.6) Do you want to enable code deserialisation (yes/no)? =item yaml_module At the time of this writing (2009-03) there are three YAML implementations working: YAML, YAML::Syck, and YAML::XS. The latter two are faster but need a C compiler installed on your system. There may be more alternative YAML conforming modules. When I tried two other players, YAML::Tiny and YAML::Perl, they seemed not powerful enough to work with CPAN.pm. This may have changed in the meantime. Which YAML implementation would you prefer? =back =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut use vars qw( %prompts ); { my @prompts = ( auto_config => qq{ CPAN.pm requires configuration, but most of it can be done automatically. If you answer 'no' below, you will enter an interactive dialog for each configuration option instead. Would you like to configure as much as possible automatically?}, auto_pick => qq{ Would you like me to automatically choose some CPAN mirror sites for you? (This means connecting to the Internet)}, config_intro => qq{ The following questions are intended to help you with the configuration. The CPAN module needs a directory of its own to cache important index files and maybe keep a temporary mirror of CPAN files. This may be a site-wide or a personal directory. }, # cpan_home => qq{ }, cpan_home_where => qq{ First of all, I'd like to create this directory. Where? }, external_progs => qq{ The CPAN module will need a few external programs to work properly. Please correct me, if I guess the wrong path for a program. Don't panic if you do not have some of them, just press ENTER for those. To disable the use of a program, you can type a space followed by ENTER. }, proxy_intro => qq{ If you're accessing the net via proxies, you can specify them in the CPAN configuration or via environment variables. The variable in the \$CPAN::Config takes precedence. }, proxy_user => qq{ If your proxy is an authenticating proxy, you can store your username permanently. If you do not want that, just press ENTER. You will then be asked for your username in every future session. }, proxy_pass => qq{ Your password for the authenticating proxy can also be stored permanently on disk. If this violates your security policy, just press ENTER. You will then be asked for the password in every future session. }, urls_intro => qq{ Now you need to choose your CPAN mirror sites. You can let me pick mirrors for you, you can select them from a list or you can enter them by hand. }, urls_picker_intro => qq{First, pick a nearby continent and country by typing in the number(s) in front of the item(s) you want to select. You can pick several of each, separated by spaces. Then, you will be presented with a list of URLs of CPAN mirrors in the countries you selected, along with previously selected URLs. Select some of those URLs, or just keep the old list. Finally, you will be prompted for any extra URLs -- file:, ftp:, or http: -- that host a CPAN mirror. You should select more than one (just in case the first isn't available). }, password_warn => qq{ Warning: Term::ReadKey seems not to be available, your password will be echoed to the terminal! }, install_help => qq{ Warning: You do not have write permission for Perl library directories. To install modules, you need to configure a local Perl library directory or escalate your privileges. CPAN can help you by bootstrapping the local::lib module or by configuring itself to use 'sudo' (if available). You may also resolve this problem manually if you need to customize your setup. What approach do you want? (Choose 'local::lib', 'sudo' or 'manual') }, local_lib_installed => qq{ local::lib is installed. You must now add the following environment variables to your shell configuration files (or registry, if you are on Windows) and then restart your command line shell and CPAN before installing modules: }, ); die "Coding error in \@prompts declaration. Odd number of elements, above" if (@prompts % 2); %prompts = @prompts; if (scalar(keys %prompts) != scalar(@prompts)/2) { my %already; for my $item (0..$#prompts) { next if $item % 2; die "$prompts[$item] is duplicated\n" if $already{$prompts[$item]}++; } } shift @podpara; while (@podpara) { warn "Alert: cannot parse my own manpage for init dialog" unless $podpara[0] =~ s/^=item\s+//; my $name = shift @podpara; my @para; while (@podpara && $podpara[0] !~ /^=item/) { push @para, shift @podpara; } $prompts{$name} = pop @para; if (@para) { $prompts{$name . "_intro"} = join "", map { "$_\n\n" } @para; } } } sub init { my($configpm, %args) = @_; use Config; # extra args after 'o conf init' my $matcher = $args{args} && @{$args{args}} ? $args{args}[0] : ''; if ($matcher =~ /^\/(.*)\/$/) { # case /regex/ => take the first, ignore the rest $matcher = $1; shift @{$args{args}}; if (@{$args{args}}) { local $" = " "; $CPAN::Frontend->mywarn("Ignoring excessive arguments '@{$args{args}}'"); $CPAN::Frontend->mysleep(2); } } elsif (0 == length $matcher) { } elsif (0 && $matcher eq "~") { # extremely buggy, but a nice idea my @unconfigured = sort grep { not exists $CPAN::Config->{$_} or not defined $CPAN::Config->{$_} or not length $CPAN::Config->{$_} } keys %$CPAN::Config; $matcher = "\\b(".join("|", @unconfigured).")\\b"; $CPAN::Frontend->mywarn("matcher[$matcher]"); } else { # case WORD... => all arguments must be valid for my $arg (@{$args{args}}) { unless (exists $CPAN::HandleConfig::keys{$arg}) { $CPAN::Frontend->mywarn("'$arg' is not a valid configuration variable\n"); return; } } $matcher = "\\b(".join("|",@{$args{args}}).")\\b"; } CPAN->debug("matcher[$matcher]") if $CPAN::DEBUG; unless ($CPAN::VERSION) { require CPAN::Nox; } require CPAN::HandleConfig; CPAN::HandleConfig::require_myconfig_or_config(); $CPAN::Config ||= {}; local($/) = "\n"; local($\) = ""; local($|) = 1; my($ans,$default); # why so half global? # #= Files, directories # local *_real_prompt; if ( $args{autoconfig} ) { $auto_config = 1; } elsif ($matcher) { $auto_config = 0; } else { my $_conf = prompt($prompts{auto_config}, "yes"); $auto_config = ($_conf and $_conf =~ /^y/i) ? 1 : 0; } CPAN->debug("auto_config[$auto_config]") if $CPAN::DEBUG; if ( $auto_config ) { local $^W = 0; # prototype should match that of &MakeMaker::prompt my $current_second = time; my $current_second_count = 0; my $i_am_mad = 0; # silent prompting -- just quietly use default *_real_prompt = sub { return $_[1] }; } # # bootstrap local::lib or sudo # unless ( $matcher || _can_write_to_libdirs() || _using_installbase() || _using_sudo() ) { local $auto_config = 0; # We *must* ask, even under autoconfig local *_real_prompt; # We *must* show prompt my_prompt_loop(install_help => 'local::lib', $matcher, 'local::lib|sudo|manual'); } $CPAN::Config->{install_help} ||= ''; # Temporary to suppress warnings if (!$matcher or q{ build_dir build_dir_reuse cpan_home keep_source_where prefs_dir } =~ /$matcher/) { $CPAN::Frontend->myprint($prompts{config_intro}) unless $auto_config; init_cpan_home($matcher); my_dflt_prompt("keep_source_where", File::Spec->catdir($CPAN::Config->{cpan_home},"sources"), $matcher, ); my_dflt_prompt("build_dir", File::Spec->catdir($CPAN::Config->{cpan_home},"build"), $matcher ); my_yn_prompt(build_dir_reuse => 0, $matcher); my_dflt_prompt("prefs_dir", File::Spec->catdir($CPAN::Config->{cpan_home},"prefs"), $matcher ); } # #= Config: auto_commit # my_yn_prompt(auto_commit => 0, $matcher); # #= Cache size, Index expire # my_dflt_prompt(build_cache => 100, $matcher); my_dflt_prompt(index_expire => 1, $matcher); my_prompt_loop(scan_cache => 'atstart', $matcher, 'atstart|atexit|never'); my_yn_prompt(cleanup_after_install => 0, $matcher); # #= cache_metadata # my_yn_prompt(cache_metadata => 1, $matcher); my_yn_prompt(use_sqlite => 0, $matcher); # #= Do we follow PREREQ_PM? # my_prompt_loop(prerequisites_policy => 'follow', $matcher, 'follow|ask|ignore'); my_prompt_loop(build_requires_install_policy => 'yes', $matcher, 'yes|no|ask/yes|ask/no'); my_yn_prompt(recommends_policy => 1, $matcher); my_yn_prompt(suggests_policy => 0, $matcher); # #= Module::Signature # my_yn_prompt(check_sigs => 0, $matcher); # #= CPAN::Reporter # if (!$matcher or 'test_report' =~ /$matcher/) { my_yn_prompt(test_report => 0, $matcher); if ( $matcher && $CPAN::Config->{test_report} && $CPAN::META->has_inst("CPAN::Reporter") && CPAN::Reporter->can('configure') ) { my $_conf = prompt("Would you like me configure CPAN::Reporter now?", "yes"); if ($_conf =~ /^y/i) { $CPAN::Frontend->myprint("\nProceeding to configure CPAN::Reporter.\n"); CPAN::Reporter::configure(); $CPAN::Frontend->myprint("\nReturning to CPAN configuration.\n"); } } } my_yn_prompt(trust_test_report_history => 0, $matcher); # #= YAML vs. YAML::Syck # if (!$matcher or "yaml_module" =~ /$matcher/) { my_dflt_prompt(yaml_module => "YAML", $matcher); my $old_v = $CPAN::Config->{load_module_verbosity}; $CPAN::Config->{load_module_verbosity} = q[none]; if (!$auto_config && !$CPAN::META->has_inst($CPAN::Config->{yaml_module})) { $CPAN::Frontend->mywarn ("Warning (maybe harmless): '$CPAN::Config->{yaml_module}' not installed.\n"); $CPAN::Frontend->mysleep(3); } $CPAN::Config->{load_module_verbosity} = $old_v; } # #= YAML code deserialisation # my_yn_prompt(yaml_load_code => 0, $matcher); # #= External programs # my(@path) = split /$Config{'path_sep'}/, $ENV{'PATH'}; $CPAN::Frontend->myprint($prompts{external_progs}) if !$matcher && !$auto_config; _init_external_progs($matcher, { path => \@path, progs => [ qw/make bzip2 gzip tar unzip gpg patch applypatch/ ], shortcut => 0 }); _init_external_progs($matcher, { path => \@path, progs => [ qw/wget curl lynx ncftpget ncftp ftp/ ], shortcut => 1 }); { my $path = $CPAN::Config->{'pager'} || $ENV{PAGER} || find_exe("less",\@path) || find_exe("more",\@path) || ($^O eq 'MacOS' ? $ENV{EDITOR} : 0 ) || "more"; my_dflt_prompt(pager => $path, $matcher); } { my $path = $CPAN::Config->{'shell'}; if ($path && File::Spec->file_name_is_absolute($path)) { $CPAN::Frontend->mywarn("Warning: configured $path does not exist\n") unless -e $path; $path = ""; } $path ||= $ENV{SHELL}; $path ||= $ENV{COMSPEC} if $^O eq "MSWin32"; if ($^O eq 'MacOS') { $CPAN::Config->{'shell'} = 'not_here'; } else { $path ||= 'sh', $path =~ s,\\,/,g if $^O eq 'os2'; # Cosmetic only my_dflt_prompt(shell => $path, $matcher); } } { my $tar = $CPAN::Config->{tar}; my $prefer_external_tar = $CPAN::Config->{prefer_external_tar}; # XXX not yet supported unless (defined $prefer_external_tar) { if ($^O =~ /(MSWin32|solaris)/) { # both have a record of broken tars $prefer_external_tar = 0; } elsif ($tar) { $prefer_external_tar = 1; } else { $prefer_external_tar = 0; } } my_yn_prompt(prefer_external_tar => $prefer_external_tar, $matcher); } # # verbosity # my_prompt_loop(tar_verbosity => 'none', $matcher, 'none|v|vv'); my_prompt_loop(load_module_verbosity => 'none', $matcher, 'none|v'); my_prompt_loop(perl5lib_verbosity => 'none', $matcher, 'none|v'); my_yn_prompt(inhibit_startup_message => 0, $matcher); # #= Installer, arguments to make etc. # my_prompt_loop(prefer_installer => 'MB', $matcher, 'MB|EUMM|RAND'); if (!$matcher or 'makepl_arg make_arg' =~ /$matcher/) { my_dflt_prompt(makepl_arg => "", $matcher); my_dflt_prompt(make_arg => "", $matcher); if ( $CPAN::Config->{makepl_arg} =~ /LIBS=|INC=/ ) { $CPAN::Frontend->mywarn( "Warning: Using LIBS or INC in makepl_arg will likely break distributions\n" . "that specify their own LIBS or INC options in Makefile.PL.\n" ); } } require CPAN::HandleConfig; if (exists $CPAN::HandleConfig::keys{make_install_make_command}) { # as long as Windows needs $self->_build_command, we cannot # support sudo on windows :-) my $default = $CPAN::Config->{make} || ""; if ( $default && $CPAN::Config->{install_help} eq 'sudo' ) { if ( find_exe('sudo') ) { $default = "sudo $default"; delete $CPAN::Config->{make_install_make_command} unless $CPAN::Config->{make_install_make_command} =~ /sudo/; } else { $CPAN::Frontend->mywarnonce("Could not find 'sudo' in PATH\n"); } } my_dflt_prompt(make_install_make_command => $default, $matcher); } my_dflt_prompt(make_install_arg => $CPAN::Config->{make_arg} || "", $matcher); my_dflt_prompt(mbuildpl_arg => "", $matcher); my_dflt_prompt(mbuild_arg => "", $matcher); if (exists $CPAN::HandleConfig::keys{mbuild_install_build_command} and $^O ne "MSWin32") { # as long as Windows needs $self->_build_command, we cannot # support sudo on windows :-) my $default = $^O eq 'VMS' ? '@Build.com' : "./Build"; if ( $CPAN::Config->{install_help} eq 'sudo' ) { if ( find_exe('sudo') ) { $default = "sudo $default"; delete $CPAN::Config->{mbuild_install_build_command} unless $CPAN::Config->{mbuild_install_build_command} =~ /sudo/; } else { $CPAN::Frontend->mywarnonce("Could not find 'sudo' in PATH\n"); } } my_dflt_prompt(mbuild_install_build_command => $default, $matcher); } my_dflt_prompt(mbuild_install_arg => "", $matcher); for my $o (qw( allow_installing_outdated_dists allow_installing_module_downgrades )) { my_prompt_loop($o => 'ask/no', $matcher, 'yes|no|ask/yes|ask/no'); } # #== use_prompt_default # my_yn_prompt(use_prompt_default => 0, $matcher); # #= Alarm period # my_dflt_prompt(inactivity_timeout => 0, $matcher); my_dflt_prompt(version_timeout => 15, $matcher); # #== halt_on_failure # my_yn_prompt(halt_on_failure => 0, $matcher); # #= Proxies # my @proxy_vars = qw/ftp_proxy http_proxy no_proxy/; my @proxy_user_vars = qw/proxy_user proxy_pass/; if (!$matcher or "@proxy_vars @proxy_user_vars" =~ /$matcher/) { $CPAN::Frontend->myprint($prompts{proxy_intro}) unless $auto_config; for (@proxy_vars) { $prompts{$_} = "Your $_?"; my_dflt_prompt($_ => $ENV{$_}||"", $matcher); } if ($CPAN::Config->{ftp_proxy} || $CPAN::Config->{http_proxy}) { $default = $CPAN::Config->{proxy_user} || $CPAN::LWP::UserAgent::USER || ""; $CPAN::Frontend->myprint($prompts{proxy_user}) unless $auto_config; if ($CPAN::Config->{proxy_user} = prompt("Your proxy user id?",$default)) { $CPAN::Frontend->myprint($prompts{proxy_pass}) unless $auto_config; if ($CPAN::META->has_inst("Term::ReadKey")) { Term::ReadKey::ReadMode("noecho"); } else { $CPAN::Frontend->myprint($prompts{password_warn}) unless $auto_config; } $CPAN::Config->{proxy_pass} = prompt_no_strip("Your proxy password?"); if ($CPAN::META->has_inst("Term::ReadKey")) { Term::ReadKey::ReadMode("restore"); } $CPAN::Frontend->myprint("\n\n") unless $auto_config; } } } # #= how plugins work # # XXX MISSING: my_array_prompt to be used with plugins. We did something like this near # git log -p fd68f8f5e33f4cecea4fdb7abc5ee19c12f138f0..test-notest-test-dependency # Need to do similar steps for plugin_list. As long as we do not support it here, people # must use the cpan shell prompt to write something like # o conf plugin_list push CPAN::Plugin::Specfile=dir,/tmp/foo-20141013,... # o conf commit # #= how FTP works # my_yn_prompt(ftp_passive => 1, $matcher); # #= how cwd works # my_prompt_loop(getcwd => 'cwd', $matcher, 'cwd|getcwd|fastcwd|getdcwd|backtickcwd'); # #= the CPAN shell itself (prompt, color) # my_yn_prompt(commandnumber_in_prompt => 1, $matcher); my_yn_prompt(term_ornaments => 1, $matcher); if ("colorize_output colorize_print colorize_warn colorize_debug" =~ $matcher) { my_yn_prompt(colorize_output => 0, $matcher); if ($CPAN::Config->{colorize_output}) { if ($CPAN::META->has_inst("Term::ANSIColor")) { my $T="gYw"; $CPAN::Frontend->myprint( " on_ on_y ". " on_ma on_\n") unless $auto_config; $CPAN::Frontend->myprint( " on_black on_red green ellow ". "on_blue genta on_cyan white\n") unless $auto_config; for my $FG ("", "bold", map {$_,"bold $_"} "black","red","green", "yellow","blue", "magenta", "cyan","white") { $CPAN::Frontend->myprint(sprintf( "%12s ", $FG)) unless $auto_config; for my $BG ("",map {"on_$_"} qw(black red green yellow blue magenta cyan white)) { $CPAN::Frontend->myprint( $FG||$BG ? Term::ANSIColor::colored(" $T ","$FG $BG") : " $T ") unless $auto_config; } $CPAN::Frontend->myprint( "\n" ) unless $auto_config; } $CPAN::Frontend->myprint( "\n" ) unless $auto_config; } for my $tuple ( ["colorize_print", "bold blue on_white"], ["colorize_warn", "bold red on_white"], ["colorize_debug", "black on_cyan"], ) { my_dflt_prompt($tuple->[0] => $tuple->[1], $matcher); if ($CPAN::META->has_inst("Term::ANSIColor")) { eval { Term::ANSIColor::color($CPAN::Config->{$tuple->[0]})}; if ($@) { $CPAN::Config->{$tuple->[0]} = $tuple->[1]; $CPAN::Frontend->mywarn($@."setting to default '$tuple->[1]'\n"); } } } } } # #== term_is_latin # my_yn_prompt(term_is_latin => 1, $matcher); # #== save history in file 'histfile' # if (!$matcher or 'histfile histsize' =~ /$matcher/) { $CPAN::Frontend->myprint($prompts{histfile_intro}) unless $auto_config; defined($default = $CPAN::Config->{histfile}) or $default = File::Spec->catfile($CPAN::Config->{cpan_home},"histfile"); my_dflt_prompt(histfile => $default, $matcher); if ($CPAN::Config->{histfile}) { defined($default = $CPAN::Config->{histsize}) or $default = 100; my_dflt_prompt(histsize => $default, $matcher); } } # #== do an ls on the m or the d command # my_yn_prompt(show_upload_date => 0, $matcher); # #== verbosity at the end of the r command # if (!$matcher or 'show_unparsable_versions' =~ /$matcher/ or 'show_zero_versions' =~ /$matcher/ ) { my_yn_prompt(show_unparsable_versions => 0, $matcher); my_yn_prompt(show_zero_versions => 0, $matcher); } # #= MIRRORED.BY and conf_sites() # # Let's assume they want to use the internet and make them turn it # off if they really don't. my_yn_prompt("connect_to_internet_ok" => 1, $matcher); my_yn_prompt("pushy_https" => 1, $matcher); # Allow matching but don't show during manual config if ($matcher) { if ("urllist_ping_external" =~ $matcher) { my_yn_prompt(urllist_ping_external => 0, $matcher); } if ("urllist_ping_verbose" =~ $matcher) { my_yn_prompt(urllist_ping_verbose => 0, $matcher); } if ("randomize_urllist" =~ $matcher) { my_dflt_prompt(randomize_urllist => 0, $matcher); } if ("ftpstats_size" =~ $matcher) { my_dflt_prompt(ftpstats_size => 99, $matcher); } if ("ftpstats_period" =~ $matcher) { my_dflt_prompt(ftpstats_period => 14, $matcher); } } $CPAN::Config->{urllist} ||= []; if ($auto_config) { if(@{ $CPAN::Config->{urllist} }) { $CPAN::Frontend->myprint( "Your 'urllist' is already configured. Type 'o conf init urllist' to change it.\n" ); } else { # Hint: as of 2021-11: to get http, use http://www.cpan.org/ $CPAN::Config->{urllist} = [ 'https://cpan.org/' ]; $CPAN::Frontend->myprint( "We initialized your 'urllist' to @{$CPAN::Config->{urllist}}. Type 'o conf init urllist' to change it.\n" ); } } elsif (!$matcher || "urllist" =~ $matcher) { _do_pick_mirrors(); } if ($auto_config) { $CPAN::Frontend->myprint( "\nAutoconfiguration complete.\n" ); $auto_config = 0; # reset } # bootstrap local::lib now if requested if ( $CPAN::Config->{install_help} eq 'local::lib' ) { if ( ! @{ $CPAN::Config->{urllist} } ) { $CPAN::Frontend->myprint( "\nALERT: Skipping local::lib bootstrap because 'urllist' is not configured.\n" ); } elsif (! $CPAN::Config->{make} ) { $CPAN::Frontend->mywarn( "\nALERT: Skipping local::lib bootstrap because 'make' is not configured.\n" ); _beg_for_make(); # repetitive, but we don't want users to miss it } else { $CPAN::Frontend->myprint("\nAttempting to bootstrap local::lib...\n"); $CPAN::Frontend->myprint("\nWriting $configpm for bootstrap...\n"); delete $CPAN::Config->{install_help}; # temporary only CPAN::HandleConfig->commit; my($dist, $locallib); $locallib = CPAN::Shell->expand('Module', 'local::lib'); if ( $locallib and $dist = $locallib->distribution ) { # this is a hack to force bootstrapping $dist->{prefs}{pl}{commandline} = "$^X Makefile.PL --bootstrap"; # Set @INC for this process so we find things as they bootstrap require lib; lib->import(_local_lib_inc_path()); eval { $dist->install }; } if ( ! $dist || (my $err = $@) ) { $err ||= 'Could not locate local::lib in the CPAN index'; $CPAN::Frontend->mywarn("Error bootstrapping local::lib: $@\n"); $CPAN::Frontend->myprint("From the CPAN Shell, you might try 'look local::lib' and \n" . "run 'perl Makefile --bootstrap' and see if that is successful. Then\n" . "restart your CPAN client\n" ); } else { _local_lib_config(); } } } # install_help is temporary for configuration and not saved delete $CPAN::Config->{install_help}; $CPAN::Frontend->myprint("\n"); if ($matcher && !$CPAN::Config->{auto_commit}) { $CPAN::Frontend->myprint("Please remember to call 'o conf commit' to ". "make the config permanent!\n"); } else { CPAN::HandleConfig->commit; } if (! $matcher) { $CPAN::Frontend->myprint( "\nYou can re-run configuration any time with 'o conf init' in the CPAN shell\n" ); } } sub _local_lib_config { # Set environment stuff for this process require local::lib; # Tell user about environment vars to set $CPAN::Frontend->myprint($prompts{local_lib_installed}); local $ENV{SHELL} = $CPAN::Config->{shell} || $ENV{SHELL}; my $shellvars = local::lib->environment_vars_string_for(_local_lib_path()); $CPAN::Frontend->myprint($shellvars); # Set %ENV after getting string above my %env = local::lib->build_environment_vars_for(_local_lib_path(), 1); while ( my ($k, $v) = each %env ) { $ENV{$k} = $v; } # Offer to mangle the shell config my $munged_rc; if ( my $rc = _find_shell_config() ) { local $auto_config = 0; # We *must* ask, even under autoconfig local *_real_prompt; # We *must* show prompt my $_conf = prompt( "\nWould you like me to append that to $rc now?", "yes" ); if ($_conf =~ /^y/i) { open my $fh, ">>", $rc; print {$fh} "\n$shellvars"; close $fh; $munged_rc++; } } # Warn at exit time if ($munged_rc) { push @{$CPAN::META->_exit_messages}, << "HERE"; *** Remember to restart your shell before running cpan again *** HERE } else { push @{$CPAN::META->_exit_messages}, << "HERE"; *** Remember to add these environment variables to your shell config and restart your shell before running cpan again *** $shellvars HERE } } { my %shell_rc_map = ( map { $_ => ".${_}rc" } qw/ bash tcsh csh /, map { $_ => ".profile" } qw/dash ash sh/, zsh => ".zshenv", ); sub _find_shell_config { my $shell = File::Basename::basename($CPAN::Config->{shell}); if ( my $rc = $shell_rc_map{$shell} ) { my $path = File::Spec->catfile($ENV{HOME}, $rc); return $path if -w $path; } } } sub _local_lib_inc_path { return File::Spec->catdir(_local_lib_path(), qw/lib perl5/); } sub _local_lib_path { return File::Spec->catdir(_local_lib_home(), 'perl5'); } # Adapted from resolve_home_path() in local::lib -- this is where # local::lib thinks the user's home is { my $local_lib_home; sub _local_lib_home { $local_lib_home ||= File::Spec->rel2abs( do { if ($CPAN::META->has_usable("File::HomeDir") && File::HomeDir->VERSION >= 0.65) { File::HomeDir->my_home; } elsif (defined $ENV{HOME}) { $ENV{HOME}; } else { (getpwuid $<)[7] || "~"; } }); } } sub _do_pick_mirrors { local *_real_prompt; *_real_prompt = \&CPAN::Shell::colorable_makemaker_prompt; $CPAN::Frontend->myprint($prompts{urls_intro}); # Only prompt for auto-pick if Net::Ping is new enough to do timings my $_conf = 'n'; if ( $CPAN::META->has_usable("Net::Ping") && CPAN::Version->vgt(Net::Ping->VERSION, '2.13')) { $_conf = prompt($prompts{auto_pick}, "yes"); } else { prompt("Autoselection disabled due to Net::Ping missing or insufficient. Please press ENTER"); } my @old_list = @{ $CPAN::Config->{urllist} }; if ( $_conf =~ /^y/i ) { conf_sites( auto_pick => 1 ) or bring_your_own(); } else { _print_urllist('Current') if @old_list; my $msg = scalar @old_list ? "\nWould you like to edit the urllist or pick new mirrors from a list?" : "\nWould you like to pick from the CPAN mirror list?" ; my $_conf = prompt($msg, "yes"); if ( $_conf =~ /^y/i ) { conf_sites(); } bring_your_own(); } _print_urllist('New'); } sub _init_external_progs { my($matcher,$args) = @_; my $PATH = $args->{path}; my @external_progs = @{ $args->{progs} }; my $shortcut = $args->{shortcut}; my $showed_make_warning; if (!$matcher or "@external_progs" =~ /$matcher/) { my $old_warn = $^W; local $^W if $^O eq 'MacOS'; local $^W = $old_warn; my $progname; for $progname (@external_progs) { next if $matcher && $progname !~ /$matcher/; if ($^O eq 'MacOS') { $CPAN::Config->{$progname} = 'not_here'; next; } my $progcall = $progname; unless ($matcher) { # we really don't need ncftp if we have ncftpget, but # if they chose this dialog via matcher, they shall have it next if $progname eq "ncftp" && $CPAN::Config->{ncftpget} gt " "; } my $path = $CPAN::Config->{$progname} || $Config::Config{$progname} || ""; if (File::Spec->file_name_is_absolute($path)) { # testing existence is not good enough, some have these exe # extensions # warn "Warning: configured $path does not exist\n" unless -e $path; # $path = ""; } elsif ($path =~ /^\s+$/) { # preserve disabled programs } else { $path = ''; } unless ($path) { # e.g. make -> nmake $progcall = $Config::Config{$progname} if $Config::Config{$progname}; } $path ||= find_exe($progcall,$PATH); unless ($path) { # not -e $path, because find_exe already checked that local $"=";"; $CPAN::Frontend->mywarn("Warning: $progcall not found in PATH[@$PATH]\n") unless $auto_config; _beg_for_make(), $showed_make_warning++ if $progname eq "make"; } $prompts{$progname} = "Where is your $progname program?"; $path = my_dflt_prompt($progname,$path,$matcher,1); # 1 => no strip spaces my $disabling = $path =~ m/^\s*$/; # don't let them disable or misconfigure make without warning if ( $progname eq "make" && ( $disabling || ! _check_found($path) ) ) { if ( $disabling && $showed_make_warning ) { next; } else { _beg_for_make() unless $showed_make_warning++; undef $CPAN::Config->{$progname}; $CPAN::Frontend->mywarn("Press SPACE and ENTER to disable make (NOT RECOMMENDED)\n"); redo; } } elsif ( $disabling ) { next; } elsif ( _check_found( $CPAN::Config->{$progname} ) ) { last if $shortcut && !$matcher; } else { undef $CPAN::Config->{$progname}; $CPAN::Frontend->mywarn("Press SPACE and ENTER to disable $progname\n"); redo; } } } } sub _check_found { my ($prog) = @_; if ( ! -f $prog ) { $CPAN::Frontend->mywarn("Warning: '$prog' does not exist\n") unless $auto_config; return; } elsif ( ! -x $prog ) { $CPAN::Frontend->mywarn("Warning: '$prog' is not executable\n") unless $auto_config; return; } return 1; } sub _beg_for_make { $CPAN::Frontend->mywarn(<<"HERE"); ALERT: 'make' is an essential tool for building perl Modules. Please make sure you have 'make' (or some equivalent) working. HERE if ($^O eq "MSWin32") { $CPAN::Frontend->mywarn(<<"HERE"); Windows users may want to follow this procedure when back in the CPAN shell: look YVES/scripts/alien_nmake.pl perl alien_nmake.pl This will install nmake on your system which can be used as a 'make' substitute. HERE } $CPAN::Frontend->mywarn(<<"HERE"); You can then retry the 'make' configuration step with o conf init make HERE } sub init_cpan_home { my($matcher) = @_; if (!$matcher or 'cpan_home' =~ /$matcher/) { my $cpan_home = $CPAN::Config->{cpan_home} || CPAN::HandleConfig::cpan_home(); if (-d $cpan_home) { $CPAN::Frontend->myprint( "\nI see you already have a directory\n" . "\n$cpan_home\n" . "Shall we use it as the general CPAN build and cache directory?\n\n" ) unless $auto_config; } else { # no cpan-home, must prompt and get one $CPAN::Frontend->myprint($prompts{cpan_home_where}) unless $auto_config; } my $default = $cpan_home; my $loop = 0; my($last_ans,$ans); $CPAN::Frontend->myprint(" \n") unless $auto_config; PROMPT: while ($ans = prompt("CPAN build and cache directory?",$default)) { if (File::Spec->file_name_is_absolute($ans)) { my @cpan_home = split /[\/\\]/, $ans; DIR: for my $dir (@cpan_home) { if ($dir =~ /^~/ and (!$last_ans or $ans ne $last_ans)) { $CPAN::Frontend ->mywarn("Warning: a tilde in the path will be ". "taken as a literal tilde. Please ". "confirm again if you want to keep it\n"); $last_ans = $default = $ans; next PROMPT; } } } else { require Cwd; my $cwd = Cwd::cwd(); my $absans = File::Spec->catdir($cwd,$ans); $CPAN::Frontend->mywarn("The path '$ans' is not an ". "absolute path. Please specify ". "an absolute path\n"); $default = $absans; next PROMPT; } eval { File::Path::mkpath($ans); }; # dies if it can't if ($@) { $CPAN::Frontend->mywarn("Couldn't create directory $ans.\n". "Please retry.\n"); next PROMPT; } if (-d $ans && -w _) { last PROMPT; } else { $CPAN::Frontend->mywarn("Couldn't find directory $ans\n". "or directory is not writable. Please retry.\n"); if (++$loop > 5) { $CPAN::Frontend->mydie("Giving up"); } } } $CPAN::Config->{cpan_home} = $ans; } } sub my_dflt_prompt { my ($item, $dflt, $m, $no_strip) = @_; my $default = $CPAN::Config->{$item} || $dflt; if (!$auto_config && (!$m || $item =~ /$m/)) { if (my $intro = $prompts{$item . "_intro"}) { $CPAN::Frontend->myprint($intro); } $CPAN::Frontend->myprint(" <$item>\n"); $CPAN::Config->{$item} = $no_strip ? prompt_no_strip($prompts{$item}, $default) : prompt( $prompts{$item}, $default); } else { $CPAN::Config->{$item} = $default; } return $CPAN::Config->{$item}; } sub my_yn_prompt { my ($item, $dflt, $m) = @_; my $default; defined($default = $CPAN::Config->{$item}) or $default = $dflt; if (!$auto_config && (!$m || $item =~ /$m/)) { if (my $intro = $prompts{$item . "_intro"}) { $CPAN::Frontend->myprint($intro); } $CPAN::Frontend->myprint(" <$item>\n"); my $ans = prompt($prompts{$item}, $default ? 'yes' : 'no'); $CPAN::Config->{$item} = ($ans =~ /^[y1]/i ? 1 : 0); } else { $CPAN::Config->{$item} = $default; } } sub my_prompt_loop { my ($item, $dflt, $m, $ok) = @_; my $default = $CPAN::Config->{$item} || $dflt; my $ans; if (!$auto_config && (!$m || $item =~ /$m/)) { my $intro = $prompts{$item . "_intro"}; $CPAN::Frontend->myprint($intro) if defined $intro; $CPAN::Frontend->myprint(" <$item>\n"); do { $ans = prompt($prompts{$item}, $default); } until $ans =~ /$ok/; $CPAN::Config->{$item} = $ans; } else { $CPAN::Config->{$item} = $default; } } # Here's the logic about the MIRRORED.BY file. There are a number of scenarios: # (1) We have a cached MIRRORED.BY file # (1a) We're auto-picking # - Refresh it automatically if it's old # (1b) Otherwise, ask if using cached is ok. If old, default to no. # - If cached is not ok, get it from the Internet. If it succeeds we use # the new file. Otherwise, we use the old file. # (2) We don't have a copy at all # (2a) If we are allowed to connect, we try to get a new copy. If it succeeds, # we use it, otherwise, we warn about failure # (2b) If we aren't allowed to connect, sub conf_sites { my %args = @_; # auto pick implies using the internet $CPAN::Config->{connect_to_internet_ok} = 1 if $args{auto_pick}; my $m = 'MIRRORED.BY'; my $mby = File::Spec->catfile($CPAN::Config->{keep_source_where},$m); File::Path::mkpath(File::Basename::dirname($mby)); # Why are we using MIRRORED.BY from the current directory? # Is this for testing? -- dagolden, 2009-11-05 if (-f $mby && -f $m && -M $m < -M $mby) { require File::Copy; File::Copy::copy($m,$mby) or die "Could not update $mby: $!"; } local $^T = time; # if we have a cached copy is not older than 60 days, we either # use it or refresh it or fall back to it if the refresh failed. if ($mby && -f $mby && -s _ > 0 ) { my $very_old = (-M $mby > 60); my $mtime = localtime((stat _)[9]); # if auto_pick, refresh anything old automatically if ( $args{auto_pick} ) { if ( $very_old ) { $CPAN::Frontend->myprint(qq{Trying to refresh your mirror list\n}); eval { CPAN::FTP->localize($m,$mby,3,1) } or $CPAN::Frontend->myprint(qq{Refresh failed. Using the old cached copy instead.\n}); $CPAN::Frontend->myprint("\n"); } } else { my $prompt = qq{Found a cached mirror list as of $mtime If you'd like to just use the cached copy, answer 'yes', below. If you'd like an updated copy of the mirror list, answer 'no' and I'll get a fresh one from the Internet. Shall I use the cached mirror list?}; my $ans = prompt($prompt, $very_old ? "no" : "yes"); if ($ans =~ /^n/i) { $CPAN::Frontend->myprint(qq{Trying to refresh your mirror list\n}); # you asked for it from the Internet $CPAN::Config->{connect_to_internet_ok} = 1; eval { CPAN::FTP->localize($m,$mby,3,1) } or $CPAN::Frontend->myprint(qq{Refresh failed. Using the old cached copy instead.\n}); $CPAN::Frontend->myprint("\n"); } } } # else there is no cached copy and we must fetch or fail else { # If they haven't agree to connect to the internet, ask again if ( ! $CPAN::Config->{connect_to_internet_ok} ) { my $prompt = q{You are missing a copy of the CPAN mirror list. May I connect to the Internet to get it?}; my $ans = prompt($prompt, "yes"); if ($ans =~ /^y/i) { $CPAN::Config->{connect_to_internet_ok} = 1; } } # Now get it from the Internet or complain if ( $CPAN::Config->{connect_to_internet_ok} ) { $CPAN::Frontend->myprint(qq{Trying to fetch a mirror list from the Internet\n}); eval { CPAN::FTP->localize($m,$mby,3,1) } or $CPAN::Frontend->mywarn(<<'HERE'); We failed to get a copy of the mirror list from the Internet. You will need to provide CPAN mirror URLs yourself. HERE $CPAN::Frontend->myprint("\n"); } else { $CPAN::Frontend->mywarn(<<'HERE'); You will need to provide CPAN mirror URLs yourself or set 'o conf connect_to_internet_ok 1' and try again. HERE } } # if we finally have a good local MIRRORED.BY, get on with picking if (-f $mby && -s _ > 0){ $CPAN::Config->{urllist} = $args{auto_pick} ? auto_mirrored_by($mby) : choose_mirrored_by($mby); return 1; } return; } sub find_exe { my($exe,$path) = @_; $path ||= [split /$Config{'path_sep'}/, $ENV{'PATH'}]; my($dir); #warn "in find_exe exe[$exe] path[@$path]"; for $dir (@$path) { my $abs = File::Spec->catfile($dir,$exe); if (($abs = MM->maybe_command($abs))) { return $abs; } } } sub picklist { my($items,$prompt,$default,$require_nonempty,$empty_warning)=@_; CPAN->debug("picklist('$items','$prompt','$default','$require_nonempty',". "'$empty_warning')") if $CPAN::DEBUG; $default ||= ''; my $pos = 0; my @nums; SELECTION: while (1) { # display, at most, 15 items at a time my $limit = $#{ $items } - $pos; $limit = 15 if $limit > 15; # show the next $limit items, get the new position $pos = display_some($items, $limit, $pos, $default); $pos = 0 if $pos >= @$items; my $num = prompt($prompt,$default); @nums = split (' ', $num); { my %seen; @nums = grep { !$seen{$_}++ } @nums; } my $i = scalar @$items; unrangify(\@nums); if (0 == @nums) { # cannot allow nothing because nothing means paging! # return; } elsif (grep (/\D/ || $_ < 1 || $_ > $i, @nums)) { $CPAN::Frontend->mywarn("invalid items entered, try again\n"); if ("@nums" =~ /\D/) { $CPAN::Frontend->mywarn("(we are expecting only numbers between 1 and $i)\n"); } next SELECTION; } if ($require_nonempty && !@nums) { $CPAN::Frontend->mywarn("$empty_warning\n"); } # a blank line continues... unless (@nums){ $CPAN::Frontend->mysleep(0.1); # prevent hot spinning process on the next bug next SELECTION; } last; } for (@nums) { $_-- } @{$items}[@nums]; } sub unrangify ($) { my($nums) = $_[0]; my @nums2 = (); while (@{$nums||[]}) { my $n = shift @$nums; if ($n =~ /^(\d+)-(\d+)$/) { my @range = $1 .. $2; # warn "range[@range]"; push @nums2, @range; } else { push @nums2, $n; } } push @$nums, @nums2; } sub display_some { my ($items, $limit, $pos, $default) = @_; $pos ||= 0; my @displayable = @$items[$pos .. ($pos + $limit)]; for my $item (@displayable) { $CPAN::Frontend->myprint(sprintf "(%d) %s\n", ++$pos, $item); } my $hit_what = $default ? "SPACE ENTER" : "ENTER"; $CPAN::Frontend->myprint(sprintf("%d more items, hit %s to show them\n", (@$items - $pos), $hit_what, )) if $pos < @$items; return $pos; } sub auto_mirrored_by { my $local = shift or return; local $|=1; $CPAN::Frontend->myprint("Looking for CPAN mirrors near you (please be patient)\n"); my $mirrors = CPAN::Mirrors->new($local); my $cnt = 0; my $callback_was_active = 0; my @best = $mirrors->best_mirrors( how_many => 3, callback => sub { $callback_was_active++; $CPAN::Frontend->myprint("."); if ($cnt++>60) { $cnt=0; $CPAN::Frontend->myprint("\n"); } }, $CPAN::Config->{urllist_ping_external} ? (external_ping => 1) : (), $CPAN::Config->{urllist_ping_verbose} ? (verbose => 1) : (), ); my $urllist = [ map { $_->http } grep { $_ && ref $_ && $_->can('http') } @best ]; push @$urllist, grep { /^file:/ } @{$CPAN::Config->{urllist}}; $CPAN::Frontend->myprint(" done!\n\n") if $callback_was_active; return $urllist } sub choose_mirrored_by { my $local = shift or return; my ($default); my $mirrors = CPAN::Mirrors->new($local); my @previous_urls = @{$CPAN::Config->{urllist}}; $CPAN::Frontend->myprint($prompts{urls_picker_intro}); my (@cont, $cont, %cont, @countries, @urls, %seen); my $no_previous_warn = "Sorry! since you don't have any existing picks, you must make a\n" . "geographic selection."; my $offer_cont = [sort $mirrors->continents]; if (@previous_urls) { push @$offer_cont, "(edit previous picks)"; $default = @$offer_cont; } else { # cannot allow nothing because nothing means paging! # push @$offer_cont, "(none of the above)"; } @cont = picklist($offer_cont, "Select your continent (or several nearby continents)", $default, ! @previous_urls, $no_previous_warn); # cannot allow nothing because nothing means paging! # return unless @cont; foreach $cont (@cont) { my @c = sort $mirrors->countries($cont); @cont{@c} = map ($cont, 0..$#c); @c = map ("$_ ($cont)", @c) if @cont > 1; push (@countries, @c); } if (@previous_urls && @countries) { push @countries, "(edit previous picks)"; $default = @countries; } if (@countries) { @countries = picklist (\@countries, "Select your country (or several nearby countries)", $default, ! @previous_urls, $no_previous_warn); %seen = map (($_ => 1), @previous_urls); # hmmm, should take list of defaults from CPAN::Config->{'urllist'}... foreach my $country (@countries) { next if $country =~ /edit previous picks/; (my $bare_country = $country) =~ s/ \(.*\)//; my @u; for my $m ( $mirrors->mirrors($bare_country) ) { push @u, $m->ftp if $m->ftp; push @u, $m->http if $m->http; } @u = grep (! $seen{$_}, @u); @u = map ("$_ ($bare_country)", @u) if @countries > 1; push (@urls, sort @u); } } push (@urls, map ("$_ (previous pick)", @previous_urls)); my $prompt = "Select as many URLs as you like (by number), put them on one line, separated by blanks, hyphenated ranges allowed e.g. '1 4 5' or '7 1-4 8'"; if (@previous_urls) { $default = join (' ', ((scalar @urls) - (scalar @previous_urls) + 1) .. (scalar @urls)); $prompt .= "\n(or just hit ENTER to keep your previous picks)"; } @urls = picklist (\@urls, $prompt, $default); foreach (@urls) { s/ \(.*\)//; } return [ @urls ]; } sub bring_your_own { my $urllist = [ @{$CPAN::Config->{urllist}} ]; my %seen = map (($_ => 1), @$urllist); my($ans,@urls); my $eacnt = 0; # empty answers $CPAN::Frontend->myprint(<<'HERE'); Now you can enter your own CPAN URLs by hand. A local CPAN mirror can be listed using a 'file:' URL like 'file:///path/to/cpan/' HERE do { my $prompt = "Enter another URL or ENTER to quit:"; unless (%seen) { $prompt = qq{CPAN.pm needs at least one URL where it can fetch CPAN files from. Please enter your CPAN site:}; } $ans = prompt ($prompt, ""); if ($ans) { $ans =~ s|/?\z|/|; # has to end with one slash # XXX This manipulation is odd. Shouldn't we check that $ans is # a directory before converting to file:///? And we need /// below, # too, don't we? -- dagolden, 2009-11-05 $ans = "file:$ans" unless $ans =~ /:/; # without a scheme is a file: if ($ans =~ /^\w+:\/./) { push @urls, $ans unless $seen{$ans}++; } else { $CPAN::Frontend-> myprint(sprintf(qq{"%s" doesn\'t look like an URL at first sight. I\'ll ignore it for now. You can add it to your %s later if you\'re sure it\'s right.\n}, $ans, $INC{'CPAN/MyConfig.pm'} || $INC{'CPAN/Config.pm'} || "configuration file", )); } } else { if (++$eacnt >= 5) { $CPAN::Frontend-> mywarn("Giving up.\n"); $CPAN::Frontend->mysleep(5); return; } } } while $ans || !%seen; @$urllist = CPAN::_uniq(@$urllist, @urls); $CPAN::Config->{urllist} = $urllist; } sub _print_urllist { my ($which) = @_; $CPAN::Frontend->myprint("$which urllist\n"); for ( @{$CPAN::Config->{urllist} || []} ) { $CPAN::Frontend->myprint(" $_\n") }; } sub _can_write_to_libdirs { return -w $Config{installprivlib} && -w $Config{installarchlib} && -w $Config{installsitelib} && -w $Config{installsitearch} } sub _using_installbase { return 1 if $ENV{PERL_MM_OPT} && $ENV{PERL_MM_OPT} =~ /install_base/i; return 1 if grep { ($CPAN::Config->{$_}||q{}) =~ /install_base/i } qw(makepl_arg make_install_arg mbuildpl_arg mbuild_install_arg); return; } sub _using_sudo { return 1 if grep { ($CPAN::Config->{$_}||q{}) =~ /sudo/ } qw(make_install_make_command mbuild_install_build_command); return; } sub _strip_spaces { $_[0] =~ s/^\s+//; # no leading spaces $_[0] =~ s/\s+\z//; # no trailing spaces } sub prompt ($;$) { unless (defined &_real_prompt) { *_real_prompt = \&CPAN::Shell::colorable_makemaker_prompt; } my $ans = _real_prompt(@_); _strip_spaces($ans); $CPAN::Frontend->myprint("\n") unless $auto_config; return $ans; } sub prompt_no_strip ($;$) { unless (defined &_real_prompt) { *_real_prompt = \&CPAN::Shell::colorable_makemaker_prompt; } return _real_prompt(@_); } 1; PKA0]CvWvWperl5/CPAN/Module.pmnu6$# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::Module; use strict; @CPAN::Module::ISA = qw(CPAN::InfoObj); use vars qw( $VERSION ); $VERSION = "5.5003"; BEGIN { # alarm() is not implemented in perl 5.6.x and earlier under Windows *ALARM_IMPLEMENTED = sub () { $] >= 5.007 || $^O !~ /MSWin/ }; } # Accessors #-> sub CPAN::Module::userid sub userid { my $self = shift; my $ro = $self->ro; return unless $ro; return $ro->{userid} || $ro->{CPAN_USERID}; } #-> sub CPAN::Module::description sub description { my $self = shift; my $ro = $self->ro or return ""; $ro->{description} } #-> sub CPAN::Module::distribution sub distribution { my($self) = @_; CPAN::Shell->expand("Distribution",$self->cpan_file); } #-> sub CPAN::Module::_is_representative_module sub _is_representative_module { my($self) = @_; return $self->{_is_representative_module} if defined $self->{_is_representative_module}; my $pm = $self->cpan_file or return $self->{_is_representative_module} = 0; $pm =~ s|.+/||; $pm =~ s{\.(?:tar\.(bz2|gz|Z)|t(?:gz|bz)|zip)$}{}i; # see base_id $pm =~ s|-\d+\.\d+.+$||; $pm =~ s|-[\d\.]+$||; $pm =~ s/-/::/g; $self->{_is_representative_module} = $pm eq $self->{ID} ? 1 : 0; # warn "DEBUG: $pm eq $self->{ID} => $self->{_is_representative_module}"; $self->{_is_representative_module}; } #-> sub CPAN::Module::undelay sub undelay { my $self = shift; delete $self->{later}; if ( my $dist = CPAN::Shell->expand("Distribution", $self->cpan_file) ) { $dist->undelay; } } # mark as dirty/clean #-> sub CPAN::Module::color_cmd_tmps ; sub color_cmd_tmps { my($self) = shift; my($depth) = shift || 0; my($color) = shift || 0; my($ancestors) = shift || []; # a module needs to recurse to its cpan_file return if exists $self->{incommandcolor} && $color==1 && $self->{incommandcolor}==$color; return if $color==0 && !$self->{incommandcolor}; if ($color>=1) { if ( $self->uptodate ) { $self->{incommandcolor} = $color; return; } elsif (my $have_version = $self->available_version) { # maybe what we have is good enough if (@$ancestors) { my $who_asked_for_me = $ancestors->[-1]; my $obj = CPAN::Shell->expandany($who_asked_for_me); if (0) { } elsif ($obj->isa("CPAN::Bundle")) { # bundles cannot specify a minimum version return; } elsif ($obj->isa("CPAN::Distribution")) { if (my $prereq_pm = $obj->prereq_pm) { for my $k (keys %$prereq_pm) { if (my $want_version = $prereq_pm->{$k}{$self->id}) { if (CPAN::Version->vcmp($have_version,$want_version) >= 0) { $self->{incommandcolor} = $color; return; } } } } } } } } else { $self->{incommandcolor} = $color; # set me before recursion, # so we can break it } if ($depth>=$CPAN::MAX_RECURSION) { my $e = CPAN::Exception::RecursiveDependency->new($ancestors); if ($e->is_resolvable) { return $self->{incommandcolor}=2; } else { die $e; } } # warn "color_cmd_tmps $depth $color " . $self->id; # sleep 1; if ( my $dist = CPAN::Shell->expand("Distribution", $self->cpan_file) ) { $dist->color_cmd_tmps($depth+1,$color,[@$ancestors, $self->id]); } # unreached code? # if ($color==0) { # delete $self->{badtestcnt}; # } $self->{incommandcolor} = $color; } #-> sub CPAN::Module::as_glimpse ; sub as_glimpse { my($self) = @_; my(@m); my $class = ref($self); $class =~ s/^CPAN:://; my $color_on = ""; my $color_off = ""; if ( $CPAN::Shell::COLOR_REGISTERED && $CPAN::META->has_inst("Term::ANSIColor") && $self->description ) { $color_on = Term::ANSIColor::color("green"); $color_off = Term::ANSIColor::color("reset"); } my $uptodateness = " "; unless ($class eq "Bundle") { my $u = $self->uptodate; $uptodateness = $u ? "=" : "<" if defined $u; }; my $id = do { my $d = $self->distribution; $d ? $d -> pretty_id : $self->cpan_userid; }; push @m, sprintf("%-7s %1s %s%-22s%s (%s)\n", $class, $uptodateness, $color_on, $self->id, $color_off, $id, ); join "", @m; } #-> sub CPAN::Module::dslip_status sub dslip_status { my($self) = @_; my($stat); # development status @{$stat->{D}}{qw,i c a b R M S,} = qw,idea pre-alpha alpha beta released mature standard,; # support level @{$stat->{S}}{qw,m d u n a,} = qw,mailing-list developer comp.lang.perl.* none abandoned,; # language @{$stat->{L}}{qw,p c + o h,} = qw,perl C C++ other hybrid,; # interface @{$stat->{I}}{qw,f r O p h n,} = qw,functions references+ties object-oriented pragma hybrid none,; # public licence @{$stat->{P}}{qw,p g l b a 2 o d r n,} = qw,Standard-Perl GPL LGPL BSD Artistic Artistic_2 open-source distribution_allowed restricted_distribution no_licence,; for my $x (qw(d s l i p)) { $stat->{$x}{' '} = 'unknown'; $stat->{$x}{'?'} = 'unknown'; } my $ro = $self->ro; return +{} unless $ro && $ro->{statd}; return { D => $ro->{statd}, S => $ro->{stats}, L => $ro->{statl}, I => $ro->{stati}, P => $ro->{statp}, DV => $stat->{D}{$ro->{statd}}, SV => $stat->{S}{$ro->{stats}}, LV => $stat->{L}{$ro->{statl}}, IV => $stat->{I}{$ro->{stati}}, PV => $stat->{P}{$ro->{statp}}, }; } #-> sub CPAN::Module::as_string ; sub as_string { my($self) = @_; my(@m); CPAN->debug("$self entering as_string") if $CPAN::DEBUG; my $class = ref($self); $class =~ s/^CPAN:://; local($^W) = 0; push @m, $class, " id = $self->{ID}\n"; my $sprintf = " %-12s %s\n"; push @m, sprintf($sprintf, 'DESCRIPTION', $self->description) if $self->description; my $sprintf2 = " %-12s %s (%s)\n"; my($userid); $userid = $self->userid; if ( $userid ) { my $author; if ($author = CPAN::Shell->expand('Author',$userid)) { my $email = ""; my $m; # old perls if ($m = $author->email) { $email = " <$m>"; } push @m, sprintf( $sprintf2, 'CPAN_USERID', $userid, $author->fullname . $email ); } } push @m, sprintf($sprintf, 'CPAN_VERSION', $self->cpan_version) if $self->cpan_version; if (my $cpan_file = $self->cpan_file) { push @m, sprintf($sprintf, 'CPAN_FILE', $cpan_file); if (my $dist = CPAN::Shell->expand("Distribution",$cpan_file)) { my $upload_date = $dist->upload_date; if ($upload_date) { push @m, sprintf($sprintf, 'UPLOAD_DATE', $upload_date); } } } my $sprintf3 = " %-12s %1s%1s%1s%1s%1s (%s,%s,%s,%s,%s)\n"; my $dslip = $self->dslip_status; push @m, sprintf( $sprintf3, 'DSLIP_STATUS', @{$dslip}{qw(D S L I P DV SV LV IV PV)}, ) if $dslip->{D}; my $local_file = $self->inst_file; unless ($self->{MANPAGE}) { my $manpage; if ($local_file) { $manpage = $self->manpage_headline($local_file); } else { # If we have already untarred it, we should look there my $dist = $CPAN::META->instance('CPAN::Distribution', $self->cpan_file); # warn "dist[$dist]"; # mff=manifest file; mfh=manifest handle my($mff,$mfh); if ( $dist->{build_dir} and (-f ($mff = File::Spec->catfile($dist->{build_dir}, "MANIFEST"))) and $mfh = FileHandle->new($mff) ) { CPAN->debug("mff[$mff]") if $CPAN::DEBUG; my $lfre = $self->id; # local file RE $lfre =~ s/::/./g; $lfre .= "\\.pm\$"; my($lfl); # local file file local $/ = "\n"; my(@mflines) = <$mfh>; for (@mflines) { s/^\s+//; s/\s.*//s; } while (length($lfre)>5 and !$lfl) { ($lfl) = grep /$lfre/, @mflines; CPAN->debug("lfl[$lfl]lfre[$lfre]") if $CPAN::DEBUG; $lfre =~ s/.+?\.//; } $lfl =~ s/\s.*//; # remove comments $lfl =~ s/\s+//g; # chomp would maybe be too system-specific my $lfl_abs = File::Spec->catfile($dist->{build_dir},$lfl); # warn "lfl_abs[$lfl_abs]"; if (-f $lfl_abs) { $manpage = $self->manpage_headline($lfl_abs); } } } $self->{MANPAGE} = $manpage if $manpage; } my($item); for $item (qw/MANPAGE/) { push @m, sprintf($sprintf, $item, $self->{$item}) if exists $self->{$item}; } for $item (qw/CONTAINS/) { push @m, sprintf($sprintf, $item, join(" ",@{$self->{$item}})) if exists $self->{$item} && @{$self->{$item}}; } push @m, sprintf($sprintf, 'INST_FILE', $local_file || "(not installed)"); push @m, sprintf($sprintf, 'INST_VERSION', $self->inst_version) if $local_file; if (%{$CPAN::META->{is_tested}||{}}) { # XXX needs to be methodified somehow my $available_file = $self->available_file; if ($available_file && $available_file ne $local_file) { push @m, sprintf($sprintf, 'AVAILABLE_FILE', $available_file); push @m, sprintf($sprintf, 'AVAILABLE_VERSION', $self->available_version); } } join "", @m, "\n"; } #-> sub CPAN::Module::manpage_headline sub manpage_headline { my($self,$local_file) = @_; my(@local_file) = $local_file; $local_file =~ s/\.pm(?!\n)\Z/.pod/; push @local_file, $local_file; my(@result,$locf); for $locf (@local_file) { next unless -f $locf; my $fh = FileHandle->new($locf) or $Carp::Frontend->mydie("Couldn't open $locf: $!"); my $inpod = 0; local $/ = "\n"; while (<$fh>) { $inpod = m/^=(?!head1\s+NAME\s*$)/ ? 0 : m/^=head1\s+NAME\s*$/ ? 1 : $inpod; next unless $inpod; next if /^=/; next if /^\s+$/; chomp; push @result, $_; } close $fh; last if @result; } for (@result) { s/^\s+//; s/\s+$//; } join " ", @result; } #-> sub CPAN::Module::cpan_file ; # Note: also inherited by CPAN::Bundle sub cpan_file { my $self = shift; # CPAN->debug(sprintf "id[%s]", $self->id) if $CPAN::DEBUG; unless ($self->ro) { CPAN::Index->reload; } my $ro = $self->ro; if ($ro && defined $ro->{CPAN_FILE}) { return $ro->{CPAN_FILE}; } else { my $userid = $self->userid; if ( $userid ) { if ($CPAN::META->exists("CPAN::Author",$userid)) { my $author = $CPAN::META->instance("CPAN::Author", $userid); my $fullname = $author->fullname; my $email = $author->email; unless (defined $fullname && defined $email) { return sprintf("Contact Author %s", $userid, ); } return "Contact Author $fullname <$email>"; } else { return "Contact Author $userid (Email address not available)"; } } else { return "N/A"; } } } #-> sub CPAN::Module::cpan_version ; sub cpan_version { my $self = shift; my $ro = $self->ro; unless ($ro) { # Can happen with modules that are not on CPAN $ro = {}; } $ro->{CPAN_VERSION} = 'undef' unless defined $ro->{CPAN_VERSION}; $ro->{CPAN_VERSION}; } #-> sub CPAN::Module::force ; sub force { my($self) = @_; $self->{force_update} = 1; } #-> sub CPAN::Module::fforce ; sub fforce { my($self) = @_; $self->{force_update} = 2; } #-> sub CPAN::Module::notest ; sub notest { my($self) = @_; # $CPAN::Frontend->mywarn("XDEBUG: set notest for Module"); $self->{notest}++; } #-> sub CPAN::Module::rematein ; sub rematein { my($self,$meth) = @_; $CPAN::Frontend->myprint(sprintf("Running %s for module '%s'\n", $meth, $self->id)); my $cpan_file = $self->cpan_file; if ($cpan_file eq "N/A" || $cpan_file =~ /^Contact Author/) { $CPAN::Frontend->mywarn(sprintf qq{ The module %s isn\'t available on CPAN. Either the module has not yet been uploaded to CPAN, or it is temporary unavailable. Please contact the author to find out more about the status. Try 'i %s'. }, $self->id, $self->id, ); return; } my $pack = $CPAN::META->instance('CPAN::Distribution',$cpan_file); $pack->called_for($self->id); if (exists $self->{force_update}) { if ($self->{force_update} == 2) { $pack->fforce($meth); } else { $pack->force($meth); } } $pack->notest($meth) if exists $self->{notest} && $self->{notest}; $pack->{reqtype} ||= ""; CPAN->debug("dist-reqtype[$pack->{reqtype}]". "self-reqtype[$self->{reqtype}]") if $CPAN::DEBUG; if ($pack->{reqtype}) { if ($pack->{reqtype} eq "b" && $self->{reqtype} =~ /^[rc]$/) { $pack->{reqtype} = $self->{reqtype}; if ( exists $pack->{install} && ( UNIVERSAL::can($pack->{install},"failed") ? $pack->{install}->failed : $pack->{install} =~ /^NO/ ) ) { delete $pack->{install}; $CPAN::Frontend->mywarn ("Promoting $pack->{ID} from 'build_requires' to 'requires'"); } } } else { $pack->{reqtype} = $self->{reqtype}; } my $success = eval { $pack->$meth(); }; my $err = $@; $pack->unforce if $pack->can("unforce") && exists $self->{force_update}; $pack->unnotest if $pack->can("unnotest") && exists $self->{notest}; delete $self->{force_update}; delete $self->{notest}; if ($err) { die $err; } return $success; } #-> sub CPAN::Module::perldoc ; sub perldoc { shift->rematein('perldoc') } #-> sub CPAN::Module::readme ; sub readme { shift->rematein('readme') } #-> sub CPAN::Module::look ; sub look { shift->rematein('look') } #-> sub CPAN::Module::cvs_import ; sub cvs_import { shift->rematein('cvs_import') } #-> sub CPAN::Module::get ; sub get { shift->rematein('get',@_) } #-> sub CPAN::Module::make ; sub make { shift->rematein('make') } #-> sub CPAN::Module::test ; sub test { my $self = shift; # $self->{badtestcnt} ||= 0; $self->rematein('test',@_); } #-> sub CPAN::Module::deprecated_in_core ; sub deprecated_in_core { my ($self) = @_; return unless $CPAN::META->has_inst('Module::CoreList') && Module::CoreList->can('is_deprecated'); return Module::CoreList::is_deprecated($self->{ID}); } #-> sub CPAN::Module::inst_deprecated; # Indicates whether the *installed* version of the module is a deprecated *and* # installed as part of the Perl core library path sub inst_deprecated { my ($self) = @_; my $inst_file = $self->inst_file or return; return $self->deprecated_in_core && $self->_in_priv_or_arch($inst_file); } #-> sub CPAN::Module::uptodate ; sub uptodate { my ($self) = @_; local ($_); my $inst = $self->inst_version or return 0; my $cpan = $self->cpan_version; return 0 if CPAN::Version->vgt($cpan,$inst) || $self->inst_deprecated; CPAN->debug (join ("", "returning uptodate. ", "cpan[$cpan]inst[$inst]", )) if $CPAN::DEBUG; return 1; } # returns true if installed in privlib or archlib sub _in_priv_or_arch { my($self,$inst_file) = @_; foreach my $pair ( [qw(sitearchexp archlibexp)], [qw(sitelibexp privlibexp)] ) { my ($site, $priv) = @Config::Config{@$pair}; if ($^O eq 'VMS') { for my $d ($site, $priv) { $d = VMS::Filespec::unixify($d) }; } s!/*$!!g foreach $site, $priv; next if $site eq $priv; if ($priv eq substr($inst_file,0,length($priv))) { return 1; } } return 0; } #-> sub CPAN::Module::install ; sub install { my($self) = @_; my($doit) = 0; if ($self->uptodate && not exists $self->{force_update} ) { $CPAN::Frontend->myprint(sprintf("%s is up to date (%s).\n", $self->id, $self->inst_version, )); } else { $doit = 1; } my $ro = $self->ro; if ($ro && $ro->{stats} && $ro->{stats} eq "a") { $CPAN::Frontend->mywarn(qq{ \n\n\n ***WARNING*** The module $self->{ID} has no active maintainer (CPAN support level flag 'abandoned').\n\n\n }); $CPAN::Frontend->mysleep(5); } return $doit ? $self->rematein('install') : 1; } #-> sub CPAN::Module::clean ; sub clean { shift->rematein('clean') } #-> sub CPAN::Module::inst_file ; sub inst_file { my($self) = @_; $self->_file_in_path([@INC]); } #-> sub CPAN::Module::available_file ; sub available_file { my($self) = @_; my $sep = $Config::Config{path_sep}; my $perllib = $ENV{PERL5LIB}; $perllib = $ENV{PERLLIB} unless defined $perllib; my @perllib = split(/$sep/,$perllib) if defined $perllib; my @cpan_perl5inc; if ($CPAN::Perl5lib_tempfile) { my $yaml = CPAN->_yaml_loadfile($CPAN::Perl5lib_tempfile); @cpan_perl5inc = @{$yaml->[0]{inc} || []}; } $self->_file_in_path([@cpan_perl5inc,@perllib,@INC]); } #-> sub CPAN::Module::file_in_path ; sub _file_in_path { my($self,$path) = @_; my($dir,@packpath); @packpath = split /::/, $self->{ID}; $packpath[-1] .= ".pm"; if (@packpath == 1 && $packpath[0] eq "readline.pm") { unshift @packpath, "Term", "ReadLine"; # historical reasons } foreach $dir (@$path) { my $pmfile = File::Spec->catfile($dir,@packpath); if (-f $pmfile) { return $pmfile; } } return; } #-> sub CPAN::Module::xs_file ; sub xs_file { my($self) = @_; my($dir,@packpath); @packpath = split /::/, $self->{ID}; push @packpath, $packpath[-1]; $packpath[-1] .= "." . $Config::Config{'dlext'}; foreach $dir (@INC) { my $xsfile = File::Spec->catfile($dir,'auto',@packpath); if (-f $xsfile) { return $xsfile; } } return; } #-> sub CPAN::Module::inst_version ; sub inst_version { my($self) = @_; my $parsefile = $self->inst_file or return; my $have = $self->parse_version($parsefile); $have; } #-> sub CPAN::Module::inst_version ; sub available_version { my($self) = @_; my $parsefile = $self->available_file or return; my $have = $self->parse_version($parsefile); $have; } #-> sub CPAN::Module::parse_version ; sub parse_version { my($self,$parsefile) = @_; if (ALARM_IMPLEMENTED) { my $timeout = (exists($CPAN::Config{'version_timeout'})) ? $CPAN::Config{'version_timeout'} : 15; alarm($timeout); } my $have = eval { local $SIG{ALRM} = sub { die "alarm\n" }; MM->parse_version($parsefile); }; if ($@) { $CPAN::Frontend->mywarn("Error while parsing version number in file '$parsefile'\n"); } alarm(0) if ALARM_IMPLEMENTED; my $leastsanity = eval { defined $have && length $have; }; $have = "undef" unless $leastsanity; $have =~ s/^ //; # since the %vd hack these two lines here are needed $have =~ s/ $//; # trailing whitespace happens all the time $have = CPAN::Version->readable($have); $have =~ s/\s*//g; # stringify to float around floating point issues $have; # no stringify needed, \s* above matches always } #-> sub CPAN::Module::reports sub reports { my($self) = @_; $self->distribution->reports; } 1; PKA0]Booperl5/CPAN/Admin.pmnu6$package CPAN::Admin; use base CPAN; use CPAN; # old base.pm did not load CPAN on previous line use strict; use vars qw(@EXPORT $VERSION); use constant PAUSE_IP => "pause.perl.org"; @EXPORT = qw(shell); $VERSION = "5.501"; push @CPAN::Complete::COMMANDS, qw(register modsearch); $CPAN::Shell::COLOR_REGISTERED = 1; sub shell { CPAN::shell($_[0]||"admin's cpan> ",$_[1]); } sub CPAN::Shell::register { my($self,$mod,@rest) = @_; unless ($mod) { print "register called without argument\n"; return; } if ($CPAN::META->has_inst("URI::Escape")) { require URI::Escape; } else { print "register requires URI::Escape installed, otherwise it cannot work\n"; return; } print "Got request for mod[$mod]\n"; if (@rest) { my $modline = join " ", $mod, @rest; print "Sending to PAUSE [$modline]\n"; my $emodline = URI::Escape::uri_escape($modline, '^\w '); $emodline =~ s/ /+/g; my $url = sprintf("https://%s/pause/authenquery?pause99_add_mod_modid=". "%s;SUBMIT_pause99_add_mod_hint=hint", PAUSE_IP, $emodline, ); print "url[$url]\n\n"; print ">>>>Trying to open a netscape window<<<<\n"; sleep 1; system("netscape","-remote","openURL($url)"); return; } my $m = CPAN::Shell->expand("Module",$mod); unless (ref $m) { print "Could not determine the object for $mod\n"; return; } my $id = $m->id; print "Found module id[$id] in database\n"; if (exists $m->{RO} && $m->{RO}{chapterid}) { print "$id is already registered\n"; return; } my(@namespace) = split /::/, $id; my $rootns = $namespace[0]; # Tk, XML and Apache need special treatment if ($rootns=~/^(Bundle)\b/) { print "Bundles are not yet ready for registering\n"; return; } # make a good suggestion for the chapter my(@simile) = CPAN::Shell->expand("Module","/^$rootns(:|\$)/"); print "Found within this namespace ", join(", ", map { $_->id } @simile), "\n"; my(%seench); for my $ch (map { exists $_->{RO} ? $_->{RO}{chapterid} : ""} @simile) { next unless $ch; $seench{$ch}=undef; } my(@seench) = sort grep {length($_)} keys %seench; my $reco_ch = ""; if (@seench>1) { print "Found rootnamespace[$rootns] in the chapters [", join(", ", @seench), "]\n"; $reco_ch = $seench[0]; print "Picking $reco_ch\n"; } elsif (@seench==1) { print "Found rootnamespace[$rootns] in the chapter[$seench[0]]\n"; $reco_ch = $seench[0]; } else { print "The new rootnamespace[$rootns] needs to be introduced. Oh well.\n"; } # Look closer at the dist my $d = CPAN::Shell->expand("Distribution", $m->cpan_file); printf "Module comes with dist[%s]\n", $d->id; for my $contm ($d->containsmods) { if ($CPAN::META->exists("CPAN::Module",$contm)) { my $contm_obj = CPAN::Shell->expand("Module",$contm) or next; my $is_reg = exists $contm_obj->{RO} && $contm_obj->{RO}{description}; printf(" in same dist: %s%s\n", $contm, $is_reg ? " already in modulelist" : "", ); } } # get it so that m is better and we can inspect for XS CPAN::Shell->get($id); CPAN::Shell->m($id); CPAN::Shell->d($d->id); my $has_xs = 0; { my($mani,@mani); local $/ = "\n"; open $mani, "$d->{build_dir}/MANIFEST" and @mani = <$mani>; my @xs = grep /\.xs\b/, @mani; if (@xs) { print "Found XS files: @xs"; $has_xs=1; } } my $emodid = URI::Escape::uri_escape($id, '\W'); my $ech = $reco_ch; $ech =~ s/ /+/g; my $description = $m->{MANPAGE} || ""; $description =~ s/[A-Z]//; # POD $description =~ s/^\Q$id\E//; # usually this line starts with the modid $description =~ s/^[ \-]+//; # leading spaces and dashes substr($description,44) = "" if length($description)>44; $description = ucfirst($description); my $edescription = URI::Escape::uri_escape($description, '^\w '); $edescription =~ s/ /+/g; my $url = sprintf("https://%s/pause/authenquery?pause99_add_mod_modid=". "%s;pause99_add_mod_chapterid=%s;pause99_add_mod_statd=%s;". "pause99_add_mod_stats=%s;pause99_add_mod_statl=%s;". "pause99_add_mod_stati=%s;pause99_add_mod_description=%s;". "pause99_add_mod_userid=%s;SUBMIT_pause99_add_mod_preview=preview", PAUSE_IP, $emodid, $ech, "R", "d", $has_xs ? "c" : "p", "O", $edescription, $m->{RO}{CPAN_USERID}, ); print "$url\n\n"; print ">>>>Trying to open a netscape window<<<<\n"; system("netscape","-remote","openURL($url)"); } sub CPAN::Shell::modsearch { my($self,@line) = @_; unless (@line) { print "modsearch called without argument\n"; return; } my $request = join " ", @line; print "Got request[$request]\n"; my $erequest = URI::Escape::uri_escape($request, '^\w '); $erequest =~ s/ /+/g; my $url = sprintf("http://www.xray.mpe.mpg.de/cgi-bin/w3glimpse/modules?query=%s". "&errors=0&case=on&maxfiles=100&maxlines=30", $erequest, ); print "$url\n\n"; print ">>>>Trying to open a netscape window<<<<\n"; system("netscape","-remote","openURL('$url')"); } 1; __END__ =head1 NAME CPAN::Admin - A CPAN Shell for CPAN admins =head1 SYNOPSIS perl -MCPAN::Admin -e shell =head1 STATUS Note: this module is currently not maintained. If you need it and fix it for your needs, please submit patches. =head1 DESCRIPTION CPAN::Admin is a subclass of CPAN that adds the commands C and C to the CPAN shell. C calls C on the named module, assembles a couple of informations (description, language), and calls Netscape with the -remote argument so that a form is filled with all the assembled informations and the registration can be performed with a single click. If the command line has more than one argument, register does not run a C, instead it interprets the rest of the line as DSLI status, description, and userid and sends them to netscape such that the form is again mostly filled and can be edited or confirmed with a single click. CPAN::Admin never performs the submission click for you, it is only intended to fill in the form on PAUSE and leave the confirmation to you. C simply passes the arguments to the search engine for the modules@perl.org mailing list at L where all registration requests are stored. It does so in the same way as register, namely with the C command. An experimental feature has also been added, namely to color already registered modules in listings. If you have L installed, the u, r, and m commands will show already registered modules in green. =head1 PREREQUISITES L, a browser available in the path, the browser must understand the -remote switch (as far as I know, this is only available on UNIX); coloring of registered modules is only available if L is installed. =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut PKA0]hz.ηperl5/CPAN/FTP.pmnu6$# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::FTP; use strict; use Errno (); use Fcntl qw(:flock); use File::Basename qw(dirname); use File::Path qw(mkpath); use CPAN::FTP::netrc; use vars qw($connect_to_internet_ok $Ua $Thesite $ThesiteURL $Themethod); @CPAN::FTP::ISA = qw(CPAN::Debug); use vars qw( $VERSION ); $VERSION = "5.5016"; sub _plus_append_open { my($fh, $file) = @_; my $parent_dir = dirname $file; mkpath $parent_dir; my($cnt); until (open $fh, "+>>$file") { next if exists &Errno::EAGAIN && $! == &Errno::EAGAIN; # don't increment on EAGAIN $CPAN::Frontend->mydie("Could not open '$file' after 10000 tries: $!") if ++$cnt > 100000; sleep 0.0001; mkpath $parent_dir; } } #-> sub CPAN::FTP::ftp_statistics # if they want to rewrite, they need to pass in a filehandle sub _ftp_statistics { my($self,$fh) = @_; my $ftpstats_size = $CPAN::Config->{ftpstats_size}; return if defined $ftpstats_size && $ftpstats_size <= 0; my $locktype = $fh ? LOCK_EX : LOCK_SH; # XXX On Windows flock() implements mandatory locking, so we can # XXX only use shared locking to still allow _yaml_loadfile() to # XXX read from the file using a different filehandle. $locktype = LOCK_SH if $^O eq "MSWin32"; $fh ||= FileHandle->new; my $file = File::Spec->catfile($CPAN::Config->{cpan_home},"FTPstats.yml"); _plus_append_open($fh,$file); my $sleep = 1; my $waitstart; while (!CPAN::_flock($fh, $locktype|LOCK_NB)) { $waitstart ||= localtime(); if ($sleep>3) { my $now = localtime(); $CPAN::Frontend->mywarn("$now: waiting for read lock on '$file' (since $waitstart)\n"); } sleep($sleep); # this sleep must not be overridden; # Frontend->mysleep with AUTOMATED_TESTING has # provoked complete lock contention on my NFS if ($sleep <= 6) { $sleep+=0.5; } else { # retry to get a fresh handle. If it is NFS and the handle is stale, we will never get an flock _plus_append_open($fh, $file); } } my $stats = eval { CPAN->_yaml_loadfile($file, {loadblessed => 1}); }; if ($@) { if (ref $@) { if (ref $@ eq "CPAN::Exception::yaml_not_installed") { chomp $@; $CPAN::Frontend->myprintonce("Warning (usually harmless): $@\n"); return; } elsif (ref $@ eq "CPAN::Exception::yaml_process_error") { my $time = time; my $to = "$file.$time"; $CPAN::Frontend->mywarn("Error reading '$file': $@ Trying to stash it away as '$to' to prevent further interruptions. You may want to remove that file later.\n"); # may fail because somebody else has moved it away in the meantime: rename $file, $to or $CPAN::Frontend->mywarn("Could not rename '$file' to '$to': $!\n"); return; } } else { $CPAN::Frontend->mydie($@); } } CPAN::_flock($fh, LOCK_UN); return $stats->[0]; } #-> sub CPAN::FTP::_mytime sub _mytime () { if (CPAN->has_inst("Time::HiRes")) { return Time::HiRes::time(); } else { return time; } } #-> sub CPAN::FTP::_new_stats sub _new_stats { my($self,$file) = @_; my $ret = { file => $file, attempts => [], start => _mytime, }; $ret; } #-> sub CPAN::FTP::_add_to_statistics sub _add_to_statistics { my($self,$stats) = @_; my $yaml_module = CPAN::_yaml_module(); $self->debug("yaml_module[$yaml_module]") if $CPAN::DEBUG; if ($CPAN::META->has_inst($yaml_module)) { $stats->{thesiteurl} = $ThesiteURL; $stats->{end} = CPAN::FTP::_mytime(); my $fh = FileHandle->new; my $time = time; my $sdebug = 0; my @debug; @debug = $time if $sdebug; my $fullstats = $self->_ftp_statistics($fh); close $fh if $fh && defined(fileno($fh)); $fullstats->{history} ||= []; push @debug, scalar @{$fullstats->{history}} if $sdebug; push @debug, time if $sdebug; push @{$fullstats->{history}}, $stats; # YAML.pm 0.62 is unacceptably slow with 999; # YAML::Syck 0.82 has no noticable performance problem with 999; my $ftpstats_size = $CPAN::Config->{ftpstats_size}; $ftpstats_size = 99 unless defined $ftpstats_size; my $ftpstats_period = $CPAN::Config->{ftpstats_period} || 14; while ( @{$fullstats->{history} || []} && ( @{$fullstats->{history}} > $ftpstats_size || $time - $fullstats->{history}[0]{start} > 86400*$ftpstats_period ) ) { shift @{$fullstats->{history}} } push @debug, scalar @{$fullstats->{history}} if $sdebug; push @debug, time if $sdebug; push @debug, scalar localtime($fullstats->{history}[0]{start}) if $sdebug; # need no eval because if this fails, it is serious my $sfile = File::Spec->catfile($CPAN::Config->{cpan_home},"FTPstats.yml"); CPAN->_yaml_dumpfile("$sfile.$$",$fullstats); if ( $sdebug ) { local $CPAN::DEBUG = 512; # FTP push @debug, time; CPAN->debug(sprintf("DEBUG history: before_read[%d]before[%d]at[%d]". "after[%d]at[%d]oldest[%s]dumped backat[%d]", @debug, )); } # Win32 cannot rename a file to an existing filename unlink($sfile) if ($^O eq 'MSWin32' or $^O eq 'os2'); _copy_stat($sfile, "$sfile.$$") if -e $sfile; rename "$sfile.$$", $sfile or $CPAN::Frontend->mywarn("Could not rename '$sfile.$$' to '$sfile': $!\nGiving up\n"); } } # Copy some stat information (owner, group, mode and) from one file to # another. # This is a utility function which might be moved to a utility repository. #-> sub CPAN::FTP::_copy_stat sub _copy_stat { my($src, $dest) = @_; my @stat = stat($src); if (!@stat) { $CPAN::Frontend->mywarn("Can't stat '$src': $!\n"); return; } eval { chmod $stat[2], $dest or $CPAN::Frontend->mywarn("Can't chmod '$dest' to " . sprintf("0%o", $stat[2]) . ": $!\n"); }; warn $@ if $@; eval { chown $stat[4], $stat[5], $dest or do { my $save_err = $!; # otherwise it's lost in the get... calls $CPAN::Frontend->mywarn("Can't chown '$dest' to " . (getpwuid($stat[4]))[0] . "/" . (getgrgid($stat[5]))[0] . ": $save_err\n" ); }; }; warn $@ if $@; } # if file is CHECKSUMS, suggest the place where we got the file to be # checked from, maybe only for young files? #-> sub CPAN::FTP::_recommend_url_for sub _recommend_url_for { my($self, $file, $urllist) = @_; if ($file =~ s|/CHECKSUMS(.gz)?$||) { my $fullstats = $self->_ftp_statistics(); my $history = $fullstats->{history} || []; while (my $last = pop @$history) { last if $last->{end} - time > 3600; # only young results are interesting next unless $last->{file}; # dirname of nothing dies! next unless $file eq dirname($last->{file}); return $last->{thesiteurl}; } } if ($CPAN::Config->{randomize_urllist} && rand(1) < $CPAN::Config->{randomize_urllist} ) { $urllist->[int rand scalar @$urllist]; } else { return (); } } #-> sub CPAN::FTP::_get_urllist sub _get_urllist { my($self, $with_defaults) = @_; $with_defaults ||= 0; CPAN->debug("with_defaults[$with_defaults]") if $CPAN::DEBUG; $CPAN::Config->{urllist} ||= []; unless (ref $CPAN::Config->{urllist} eq 'ARRAY') { $CPAN::Frontend->mywarn("Malformed urllist; ignoring. Configuration file corrupt?\n"); $CPAN::Config->{urllist} = []; } my @urllist = grep { defined $_ and length $_ } @{$CPAN::Config->{urllist}}; push @urllist, @CPAN::Defaultsites if $with_defaults; for my $u (@urllist) { CPAN->debug("u[$u]") if $CPAN::DEBUG; if (UNIVERSAL::can($u,"text")) { $u->{TEXT} .= "/" unless substr($u->{TEXT},-1) eq "/"; } else { $u .= "/" unless substr($u,-1) eq "/"; $u = CPAN::URL->new(TEXT => $u, FROM => "USER"); } } \@urllist; } #-> sub CPAN::FTP::ftp_get ; sub ftp_get { my($class,$host,$dir,$file,$target) = @_; $class->debug( qq[Going to fetch file [$file] from dir [$dir] on host [$host] as local [$target]\n] ) if $CPAN::DEBUG; my $ftp = Net::FTP->new($host); unless ($ftp) { $CPAN::Frontend->mywarn(" Could not connect to host '$host' with Net::FTP\n"); return; } return 0 unless defined $ftp; $ftp->debug(1) if $CPAN::DEBUG{'FTP'} & $CPAN::DEBUG; $class->debug(qq[Going to login("anonymous","$Config::Config{cf_email}")]); unless ( $ftp->login("anonymous",$Config::Config{'cf_email'}) ) { my $msg = $ftp->message; $CPAN::Frontend->mywarn(" Couldn't login on $host: $msg\n"); return; } unless ( $ftp->cwd($dir) ) { my $msg = $ftp->message; $CPAN::Frontend->mywarn(" Couldn't cwd $dir: $msg\n"); return; } $ftp->binary; $class->debug(qq[Going to ->get("$file","$target")\n]) if $CPAN::DEBUG; unless ( $ftp->get($file,$target) ) { my $msg = $ftp->message; $CPAN::Frontend->mywarn(" Couldn't fetch $file from $host: $msg\n"); return; } $ftp->quit; # it's ok if this fails return 1; } # If more accuracy is wanted/needed, Chris Leach sent me this patch... # > *** /install/perl/live/lib/CPAN.pm- Wed Sep 24 13:08:48 1997 # > --- /tmp/cp Wed Sep 24 13:26:40 1997 # > *************** # > *** 1562,1567 **** # > --- 1562,1580 ---- # > return 1 if substr($url,0,4) eq "file"; # > return 1 unless $url =~ m|://([^/]+)|; # > my $host = $1; # > + my $proxy = $CPAN::Config->{'http_proxy'} || $ENV{'http_proxy'}; # > + if ($proxy) { # > + $proxy =~ m|://([^/:]+)|; # > + $proxy = $1; # > + my $noproxy = $CPAN::Config->{'no_proxy'} || $ENV{'no_proxy'}; # > + if ($noproxy) { # > + if ($host !~ /$noproxy$/) { # > + $host = $proxy; # > + } # > + } else { # > + $host = $proxy; # > + } # > + } # > require Net::Ping; # > return 1 unless $Net::Ping::VERSION >= 2; # > my $p; #-> sub CPAN::FTP::localize ; sub localize { my($self,$file,$aslocal,$force,$with_defaults) = @_; $force ||= 0; Carp::croak( "Usage: ->localize(cpan_file,as_local_file[,\$force])" ) unless defined $aslocal; if ($CPAN::DEBUG){ require Carp; my $longmess = Carp::longmess(); $self->debug("file[$file] aslocal[$aslocal] force[$force] carplongmess[$longmess]"); } for ($CPAN::Config->{connect_to_internet_ok}) { $connect_to_internet_ok = $_ if not defined $connect_to_internet_ok and defined $_; } my $ph = $CPAN::Config->{pushy_https}; if (!defined $ph || $ph) { return $self->localize_2021($file,$aslocal,$force,$with_defaults); } else { return $self->localize_1995ff($file,$aslocal,$force,$with_defaults); } } sub have_promising_aslocal { my($self, $aslocal, $force) = @_; if (-f $aslocal && -r _ && !($force & 1)) { my $size; if ($size = -s $aslocal) { $self->debug("aslocal[$aslocal]size[$size]") if $CPAN::DEBUG; return 1; } else { # empty file from a previous unsuccessful attempt to download it unlink $aslocal or $CPAN::Frontend->mydie("Found a zero-length '$aslocal' that I ". "could not remove."); } } return; } #-> sub CPAN::FTP::localize ; sub localize_2021 { my($self,$file,$aslocal,$force,$with_defaults) = @_; return $aslocal if $self->have_promising_aslocal($aslocal, $force); my($aslocal_dir) = dirname($aslocal); my $ret; $self->mymkpath($aslocal_dir); my $aslocal_tempfile = $aslocal . ".tmp" . $$; my $base; if ( ($CPAN::META->has_usable('HTTP::Tiny') && $CPAN::META->has_usable('Net::SSLeay') && $CPAN::META->has_usable('IO::Socket::SSL') ) || $CPAN::Config->{curl} || $CPAN::Config->{wget} ) { for my $prx (qw(https_proxy no_proxy)) { $ENV{$prx} = $CPAN::Config->{$prx} if $CPAN::Config->{$prx}; } $base = "https://cpan.org/"; } else { my @missing_modules = grep { ! $CPAN::META->has_usable($_) } qw(HTTP::Tiny Net::SSLeay IO::Socket::SSL); my $miss = join ", ", map { "'$_'" } @missing_modules; my $modules = @missing_modules == 1 ? "module" : "modules"; $CPAN::Frontend->mywarn("Missing or unusable $modules $miss, and found neither curl nor wget installed.\n"); if ($CPAN::META->has_usable('HTTP::Tiny')) { $CPAN::Frontend->mywarn("Need to fall back to http.\n") } for my $prx (qw(http_proxy no_proxy)) { $ENV{$prx} = $CPAN::Config->{$prx} if $CPAN::Config->{$prx}; } $base = "http://www.cpan.org/"; } $ret = $self->hostdl_2021($base,$file,$aslocal_tempfile); if ($ret) { # c&p from below CPAN->debug("ret[$ret]aslocal[$aslocal]") if $CPAN::DEBUG; if ($ret eq $aslocal_tempfile) { # if we got it exactly as we asked for, only then we # want to rename rename $aslocal_tempfile, $aslocal or $CPAN::Frontend->mydie("Error while trying to rename ". "'$ret' to '$aslocal': $!"); $ret = $aslocal; } } else { unlink $aslocal_tempfile; return; } return $ret; } sub hostdl_2021 { my($self, $base, $file, $aslocal) = @_; # the $aslocal is $aslocal_tempfile in the caller (old convention) my $proxy_vars = $self->_proxy_vars($base); my($proto) = $base =~ /^(https?)/; my $url = "$base$file"; # hostdl_2021 may be called with either http or https urls if ( $CPAN::META->has_usable('HTTP::Tiny') && ( $proto eq "http" || ( $CPAN::META->has_usable('Net::SSLeay') && $CPAN::META->has_usable('IO::Socket::SSL') ) ) ){ # mostly c&p from below require CPAN::HTTP::Client; my $chc = CPAN::HTTP::Client->new( proxy => $CPAN::Config->{http_proxy} || $ENV{http_proxy}, no_proxy => $CPAN::Config->{no_proxy} || $ENV{no_proxy}, ); for my $try ( $url, ( $url !~ /\.gz(?!\n)\Z/ ? "$url.gz" : () ) ) { $CPAN::Frontend->myprint("Fetching with HTTP::Tiny:\n$try\n"); my $res = eval { $chc->mirror($try, $aslocal) }; if ( $res && $res->{success} ) { my $now = time; utime $now, $now, $aslocal; # download time is more # important than upload # time return $aslocal; } elsif ( $res && $res->{status} ne '599') { $CPAN::Frontend->myprint(sprintf( "HTTP::Tiny failed with code[%s] message[%s]\n", $res->{status}, $res->{reason}, ) ); } elsif ( $res && $res->{status} eq '599') { $CPAN::Frontend->myprint(sprintf( "HTTP::Tiny failed with an internal error: %s\n", $res->{content}, ) ); } else { my $err = $@ || 'Unknown error'; $CPAN::Frontend->myprint(sprintf( "Error downloading with HTTP::Tiny: %s\n", $err ) ); } } } elsif ($CPAN::Config->{curl} || $CPAN::Config->{wget}){ # c&p from further down my($src_switch, $stdout_redir); my($devnull) = $CPAN::Config->{devnull} || ""; DLPRG: for my $dlprg (qw(curl wget)) { my $dlprg_configured = $CPAN::Config->{$dlprg}; next unless defined $dlprg_configured && length $dlprg_configured; my $funkyftp = CPAN::HandleConfig->safe_quote($dlprg_configured); if ($dlprg eq "wget") { $src_switch = " -O \"$aslocal\""; $stdout_redir = ""; } elsif ($dlprg eq 'curl') { $src_switch = ' -L -f -s -S --netrc-optional'; $stdout_redir = " > \"$aslocal\""; if ($proxy_vars->{http_proxy}) { $src_switch .= qq{ -U "$proxy_vars->{proxy_user}:$proxy_vars->{proxy_pass}" -x "$proxy_vars->{http_proxy}"}; } } $CPAN::Frontend->myprint( qq[ Trying with $funkyftp$src_switch to get $url ]); my($system) = "$funkyftp$src_switch \"$url\" $devnull$stdout_redir"; $self->debug("system[$system]") if $CPAN::DEBUG; my($wstatus) = system($system); if ($wstatus == 0) { return $aslocal; } else { my $estatus = $wstatus >> 8; my $size = -f $aslocal ? ", left\n$aslocal with size ".-s _ : "\nWarning: expected file [$aslocal] doesn't exist"; $CPAN::Frontend->myprint(qq{ Function system("$system") returned status $estatus (wstat $wstatus)$size }); } } # DLPRG } # curl, wget return; } #-> sub CPAN::FTP::localize ; sub localize_1995ff { my($self,$file,$aslocal,$force,$with_defaults) = @_; if ($^O eq 'MacOS') { # Comment by AK on 2000-09-03: Uniq short filenames would be # available in CHECKSUMS file my($name, $path) = File::Basename::fileparse($aslocal, ''); if (length($name) > 31) { $name =~ s/( \.( readme(\.(gz|Z))? | (tar\.)?(gz|Z) | tgz | zip | pm\.(gz|Z) ) )$//x; my $suf = $1; my $size = 31 - length($suf); while (length($name) > $size) { chop $name; } $name .= $suf; $aslocal = File::Spec->catfile($path, $name); } } return $aslocal if $self->have_promising_aslocal($aslocal, $force); my($maybe_restore) = 0; if (-f $aslocal) { rename $aslocal, "$aslocal.bak$$"; $maybe_restore++; } my($aslocal_dir) = dirname($aslocal); # Inheritance is not easier to manage than a few if/else branches if ($CPAN::META->has_usable('LWP::UserAgent')) { unless ($Ua) { CPAN::LWP::UserAgent->config; eval {$Ua = CPAN::LWP::UserAgent->new;}; # Why is has_usable still not fit enough? if ($@) { $CPAN::Frontend->mywarn("CPAN::LWP::UserAgent->new dies with $@\n") if $CPAN::DEBUG; } else { my($var); $Ua->proxy('ftp', $var) if $var = $CPAN::Config->{ftp_proxy} || $ENV{ftp_proxy}; $Ua->proxy('http', $var) if $var = $CPAN::Config->{http_proxy} || $ENV{http_proxy}; $Ua->no_proxy($var) if $var = $CPAN::Config->{no_proxy} || $ENV{no_proxy}; } } } for my $prx (qw(ftp_proxy http_proxy no_proxy)) { $ENV{$prx} = $CPAN::Config->{$prx} if $CPAN::Config->{$prx}; } # Try the list of urls for each single object. We keep a record # where we did get a file from my(@reordered,$last); my $ccurllist = $self->_get_urllist($with_defaults); $last = $#$ccurllist; if ($force & 2) { # local cpans probably out of date, don't reorder @reordered = (0..$last); } else { @reordered = sort { (substr($ccurllist->[$b],0,4) eq "file") <=> (substr($ccurllist->[$a],0,4) eq "file") or defined($ThesiteURL) and ($ccurllist->[$b] eq $ThesiteURL) <=> ($ccurllist->[$a] eq $ThesiteURL) } 0..$last; } my(@levels); $Themethod ||= ""; $self->debug("Themethod[$Themethod]reordered[@reordered]") if $CPAN::DEBUG; my @all_levels = ( ["dleasy", "file"], ["dleasy"], ["dlhard"], ["dlhardest"], ["dleasy", "http","defaultsites"], ["dlhard", "http","defaultsites"], ["dleasy", "ftp", "defaultsites"], ["dlhard", "ftp", "defaultsites"], ["dlhardest","", "defaultsites"], ); if ($Themethod) { @levels = grep {$_->[0] eq $Themethod} @all_levels; push @levels, grep {$_->[0] ne $Themethod} @all_levels; } else { @levels = @all_levels; } @levels = qw/dleasy/ if $^O eq 'MacOS'; my($levelno); local $ENV{FTP_PASSIVE} = exists $CPAN::Config->{ftp_passive} ? $CPAN::Config->{ftp_passive} : 1; my $ret; my $stats = $self->_new_stats($file); LEVEL: for $levelno (0..$#levels) { my $level_tuple = $levels[$levelno]; my($level,$scheme,$sitetag) = @$level_tuple; $self->mymkpath($aslocal_dir) unless $scheme && "file" eq $scheme; my $defaultsites = $sitetag && $sitetag eq "defaultsites" && !@$ccurllist; my @urllist; if ($defaultsites) { unless (defined $connect_to_internet_ok) { $CPAN::Frontend->myprint(sprintf qq{ I would like to connect to one of the following sites to get '%s': %s }, $file, join("",map { " ".$_->text."\n" } @CPAN::Defaultsites), ); my $answer = CPAN::Shell::colorable_makemaker_prompt("Is it OK to try to connect to the Internet?", "yes"); if ($answer =~ /^y/i) { $connect_to_internet_ok = 1; } else { $connect_to_internet_ok = 0; } } if ($connect_to_internet_ok) { @urllist = @CPAN::Defaultsites; } else { my $sleep = 2; # the tricky thing about dying here is that everybody # believes that calls to exists() or all_objects() are # safe. require CPAN::Exception::blocked_urllist; die CPAN::Exception::blocked_urllist->new; } } else { # ! $defaultsites my @host_seq = $level =~ /dleasy/ ? @reordered : 0..$last; # reordered has file and $Thesiteurl first @urllist = map { $ccurllist->[$_] } @host_seq; } $self->debug("synth. urllist[@urllist]") if $CPAN::DEBUG; my $aslocal_tempfile = $aslocal . ".tmp" . $$; if (my $recommend = $self->_recommend_url_for($file,\@urllist)) { @urllist = grep { $_ ne $recommend } @urllist; unshift @urllist, $recommend; } $self->debug("synth. urllist[@urllist]") if $CPAN::DEBUG; $ret = $self->hostdlxxx($level,$scheme,\@urllist,$file,$aslocal_tempfile,$stats); if ($ret) { CPAN->debug("ret[$ret]aslocal[$aslocal]") if $CPAN::DEBUG; if ($ret eq $aslocal_tempfile) { # if we got it exactly as we asked for, only then we # want to rename rename $aslocal_tempfile, $aslocal or $CPAN::Frontend->mydie("Error while trying to rename ". "'$ret' to '$aslocal': $!"); $ret = $aslocal; } elsif (-f $ret && $scheme eq 'file' ) { # it's a local file, so there's nothing left to do, we # let them read from where it is } $Themethod = $level; my $now = time; # utime $now, $now, $aslocal; # too bad, if we do that, we # might alter a local mirror $self->debug("level[$level]") if $CPAN::DEBUG; last LEVEL; } else { unlink $aslocal_tempfile; last if $CPAN::Signal; # need to cleanup } } if ($ret) { $stats->{filesize} = -s $ret; } $self->debug("before _add_to_statistics") if $CPAN::DEBUG; $self->_add_to_statistics($stats); $self->debug("after _add_to_statistics") if $CPAN::DEBUG; if ($ret) { unlink "$aslocal.bak$$"; return $ret; } unless ($CPAN::Signal) { my(@mess); local $" = " "; if (@{$CPAN::Config->{urllist}}) { push @mess, qq{Please check, if the URLs I found in your configuration file \(}. join(", ", @{$CPAN::Config->{urllist}}). qq{\) are valid.}; } else { push @mess, qq{Your urllist is empty!}; } push @mess, qq{The urllist can be edited.}, qq{E.g. with 'o conf urllist push ftp://myurl/'}; $CPAN::Frontend->mywarn(Text::Wrap::wrap("","","@mess"). "\n\n"); $CPAN::Frontend->mydie("Could not fetch $file\n"); } if ($maybe_restore) { rename "$aslocal.bak$$", $aslocal; $CPAN::Frontend->myprint("Trying to get away with old file:\n" . $self->ls($aslocal) . "\n"); return $aslocal; } return; } sub mymkpath { my($self, $aslocal_dir) = @_; mkpath($aslocal_dir); $CPAN::Frontend->mywarn(qq{Warning: You are not allowed to write into }. qq{directory "$aslocal_dir". I\'ll continue, but if you encounter problems, they may be due to insufficient permissions.\n}) unless -w $aslocal_dir; } sub hostdlxxx { my $self = shift; my $level = shift; my $scheme = shift; my $h = shift; $h = [ grep /^\Q$scheme\E:/, @$h ] if $scheme; my $method = "host$level"; $self->$method($h, @_); } sub _set_attempt { my($self,$stats,$method,$url) = @_; push @{$stats->{attempts}}, { method => $method, start => _mytime, url => $url, }; } # package CPAN::FTP; sub hostdleasy { #called from hostdlxxx my($self,$host_seq,$file,$aslocal,$stats) = @_; my($ro_url); HOSTEASY: for $ro_url (@$host_seq) { $self->_set_attempt($stats,"dleasy",$ro_url); my $url = "$ro_url$file"; $self->debug("localizing perlish[$url]") if $CPAN::DEBUG; if ($url =~ /^file:/) { my $l; if ($CPAN::META->has_inst('URI::URL')) { my $u = URI::URL->new($url); $l = $u->file; } else { # works only on Unix, is poorly constructed, but # hopefully better than nothing. # RFC 1738 says fileurl BNF is # fileurl = "file://" [ host | "localhost" ] "/" fpath # Thanks to "Mark D. Baushke" for # the code ($l = $url) =~ s|^file://[^/]*/|/|; # discard the host part $l =~ s|^file:||; # assume they # meant # file://localhost $l =~ s|^/||s if ! -f $l && $l =~ m|^/\w:|; # e.g. /P: } $self->debug("local file[$l]") if $CPAN::DEBUG; if ( -f $l && -r _) { $ThesiteURL = $ro_url; return $l; } # If request is for a compressed file and we can find the # uncompressed file also, return the path of the uncompressed file # otherwise, decompress it and return the resulting path if ($l =~ /(.+)\.gz$/) { my $ungz = $1; if ( -f $ungz && -r _) { $ThesiteURL = $ro_url; return $ungz; } elsif (-f $l && -r _) { eval { CPAN::Tarzip->new($l)->gunzip($aslocal) }; if ( -f $aslocal && -s _) { $ThesiteURL = $ro_url; return $aslocal; } elsif (! -s $aslocal) { unlink $aslocal; } elsif (-f $l) { $CPAN::Frontend->mywarn("Error decompressing '$l': $@\n") if $@; return; } } } # Otherwise, return the local file path if it exists elsif ( -f $l && -r _) { $ThesiteURL = $ro_url; return $l; } # If we can't find it, but there is a compressed version # of it, then decompress it elsif (-f "$l.gz") { $self->debug("found compressed $l.gz") if $CPAN::DEBUG; eval { CPAN::Tarzip->new("$l.gz")->gunzip($aslocal) }; if ( -f $aslocal) { $ThesiteURL = $ro_url; return $aslocal; } else { $CPAN::Frontend->mywarn("Error decompressing '$l': $@\n") if $@; return; } } $CPAN::Frontend->mywarn("Could not find '$l'\n"); } $self->debug("it was not a file URL") if $CPAN::DEBUG; if ($CPAN::META->has_usable('LWP')) { $CPAN::Frontend->myprint("Fetching with LWP:\n$url\n"); unless ($Ua) { CPAN::LWP::UserAgent->config; eval { $Ua = CPAN::LWP::UserAgent->new; }; if ($@) { $CPAN::Frontend->mywarn("CPAN::LWP::UserAgent->new dies with $@\n"); } } my $res = $Ua->mirror($url, $aslocal); if ($res->is_success) { $ThesiteURL = $ro_url; my $now = time; utime $now, $now, $aslocal; # download time is more # important than upload # time return $aslocal; } elsif ($url !~ /\.gz(?!\n)\Z/) { my $gzurl = "$url.gz"; $CPAN::Frontend->myprint("Fetching with LWP:\n$gzurl\n"); $res = $Ua->mirror($gzurl, "$aslocal.gz"); if ($res->is_success) { if (eval {CPAN::Tarzip->new("$aslocal.gz")->gunzip($aslocal)}) { $ThesiteURL = $ro_url; return $aslocal; } } } else { $CPAN::Frontend->myprint(sprintf( "LWP failed with code[%s] message[%s]\n", $res->code, $res->message, )); # Alan Burlison informed me that in firewall environments # Net::FTP can still succeed where LWP fails. So we do not # skip Net::FTP anymore when LWP is available. } } elsif ($url =~ /^http:/i && $CPAN::META->has_usable('HTTP::Tiny')) { require CPAN::HTTP::Client; my $chc = CPAN::HTTP::Client->new( proxy => $CPAN::Config->{http_proxy} || $ENV{http_proxy}, no_proxy => $CPAN::Config->{no_proxy} || $ENV{no_proxy}, ); for my $try ( $url, ( $url !~ /\.gz(?!\n)\Z/ ? "$url.gz" : () ) ) { $CPAN::Frontend->myprint("Fetching with HTTP::Tiny:\n$try\n"); my $res = eval { $chc->mirror($try, $aslocal) }; if ( $res && $res->{success} ) { $ThesiteURL = $ro_url; my $now = time; utime $now, $now, $aslocal; # download time is more # important than upload # time return $aslocal; } elsif ( $res && $res->{status} ne '599') { $CPAN::Frontend->myprint(sprintf( "HTTP::Tiny failed with code[%s] message[%s]\n", $res->{status}, $res->{reason}, ) ); } elsif ( $res && $res->{status} eq '599') { $CPAN::Frontend->myprint(sprintf( "HTTP::Tiny failed with an internal error: %s\n", $res->{content}, ) ); } else { my $err = $@ || 'Unknown error'; $CPAN::Frontend->myprint(sprintf( "Error downloading with HTTP::Tiny: %s\n", $err ) ); } } } return if $CPAN::Signal; if ($url =~ m|^ftp://(.*?)/(.*)/(.*)|) { # that's the nice and easy way thanks to Graham $self->debug("recognized ftp") if $CPAN::DEBUG; my($host,$dir,$getfile) = ($1,$2,$3); if ($CPAN::META->has_usable('Net::FTP')) { $dir =~ s|/+|/|g; $CPAN::Frontend->myprint("Fetching with Net::FTP:\n$url\n"); $self->debug("getfile[$getfile]dir[$dir]host[$host]" . "aslocal[$aslocal]") if $CPAN::DEBUG; if (CPAN::FTP->ftp_get($host,$dir,$getfile,$aslocal)) { $ThesiteURL = $ro_url; return $aslocal; } if ($aslocal !~ /\.gz(?!\n)\Z/) { my $gz = "$aslocal.gz"; $CPAN::Frontend->myprint("Fetching with Net::FTP\n$url.gz\n"); if (CPAN::FTP->ftp_get($host, $dir, "$getfile.gz", $gz) && eval{CPAN::Tarzip->new($gz)->gunzip($aslocal)} ) { $ThesiteURL = $ro_url; return $aslocal; } } # next HOSTEASY; } else { CPAN->debug("Net::FTP does not count as usable atm") if $CPAN::DEBUG; } } if ( UNIVERSAL::can($ro_url,"text") and $ro_url->{FROM} eq "USER" ) { ##address #17973: default URLs should not try to override ##user-defined URLs just because LWP is not available my $ret = $self->hostdlhard([$ro_url],$file,$aslocal,$stats); return $ret if $ret; } return if $CPAN::Signal; } } # package CPAN::FTP; sub hostdlhard { my($self,$host_seq,$file,$aslocal,$stats) = @_; # Came back if Net::FTP couldn't establish connection (or # failed otherwise) Maybe they are behind a firewall, but they # gave us a socksified (or other) ftp program... my($ro_url); my($devnull) = $CPAN::Config->{devnull} || ""; # < /dev/null "; my($aslocal_dir) = dirname($aslocal); mkpath($aslocal_dir); my $some_dl_success = 0; my $any_attempt = 0; HOSTHARD: for $ro_url (@$host_seq) { $self->_set_attempt($stats,"dlhard",$ro_url); my $url = "$ro_url$file"; my($proto,$host,$dir,$getfile); # Courtesy Mark Conty mark_conty@cargill.com change from # if ($url =~ m|^ftp://(.*?)/(.*)/(.*)|) { # to if ($url =~ m|^([^:]+)://(.*?)/(.*)/(.*)|) { # proto not yet used ($proto,$host,$dir,$getfile) = ($1,$2,$3,$4); } else { next HOSTHARD; # who said, we could ftp anything except ftp? } next HOSTHARD if $proto eq "file"; # file URLs would have had # success above. Likely a bogus URL # making at least one attempt against a host $any_attempt++; $self->debug("localizing funkyftpwise[$url]") if $CPAN::DEBUG; # Try the most capable first and leave ncftp* for last as it only # does FTP. my $proxy_vars = $self->_proxy_vars($ro_url); DLPRG: for my $f (qw(curl wget lynx ncftpget ncftp)) { my $funkyftp = CPAN::HandleConfig->safe_quote($CPAN::Config->{$f}); next DLPRG unless defined $funkyftp; next DLPRG if $funkyftp =~ /^\s*$/; my($src_switch) = ""; my($chdir) = ""; my($stdout_redir) = " > \"$aslocal\""; if ($f eq "lynx") { $src_switch = " -source"; } elsif ($f eq "ncftp") { next DLPRG unless $url =~ m{\Aftp://}; $src_switch = " -c"; } elsif ($f eq "wget") { $src_switch = " -O \"$aslocal\""; $stdout_redir = ""; } elsif ($f eq 'curl') { $src_switch = ' -L -f -s -S --netrc-optional'; if ($proxy_vars->{http_proxy}) { $src_switch .= qq{ -U "$proxy_vars->{proxy_user}:$proxy_vars->{proxy_pass}" -x "$proxy_vars->{http_proxy}"}; } } elsif ($f eq "ncftpget") { next DLPRG unless $url =~ m{\Aftp://}; $chdir = "cd $aslocal_dir && "; $stdout_redir = ""; } $CPAN::Frontend->myprint( qq[ Trying with $funkyftp$src_switch to get $url ]); my($system) = "$chdir$funkyftp$src_switch \"$url\" $devnull$stdout_redir"; $self->debug("system[$system]") if $CPAN::DEBUG; my($wstatus) = system($system); if ($f eq "lynx") { # lynx returns 0 when it fails somewhere if (-s $aslocal) { my $content = do { local *FH; open FH, $aslocal or die; local $/; }; if ($content =~ /^<.*([45]|Error [45])/si) { $CPAN::Frontend->mywarn(qq{ No success, the file that lynx has downloaded looks like an error message: $content }); $CPAN::Frontend->mysleep(1); next DLPRG; } $some_dl_success++; } else { $CPAN::Frontend->myprint(qq{ No success, the file that lynx has downloaded is an empty file. }); next DLPRG; } } if ($wstatus == 0) { if (-s $aslocal) { # Looks good $some_dl_success++; } $ThesiteURL = $ro_url; return $aslocal; } else { my $estatus = $wstatus >> 8; my $size = -f $aslocal ? ", left\n$aslocal with size ".-s _ : "\nWarning: expected file [$aslocal] doesn't exist"; $CPAN::Frontend->myprint(qq{ Function system("$system") returned status $estatus (wstat $wstatus)$size }); } return if $CPAN::Signal; } # download/transfer programs (DLPRG) } # host return unless $any_attempt; if ($some_dl_success) { $CPAN::Frontend->mywarn("Warning: doesn't seem we had substantial success downloading '$aslocal'. Don't know how to proceed.\n"); } else { $CPAN::Frontend->mywarn("Warning: no success downloading '$aslocal'. Giving up on it.\n"); } return; } #-> CPAN::FTP::_proxy_vars sub _proxy_vars { my($self,$url) = @_; my $ret = +{}; my $http_proxy = $CPAN::Config->{'http_proxy'} || $ENV{'http_proxy'}; if ($http_proxy) { my($host) = $url =~ m|://([^/:]+)|; my $want_proxy = 1; my $noproxy = $CPAN::Config->{'no_proxy'} || $ENV{'no_proxy'} || ""; my @noproxy = split /\s*,\s*/, $noproxy; if ($host) { DOMAIN: for my $domain (@noproxy) { if ($host =~ /\Q$domain\E$/) { # cf. LWP::UserAgent $want_proxy = 0; last DOMAIN; } } } else { $CPAN::Frontend->mywarn(" Could not determine host from http_proxy '$http_proxy'\n"); } if ($want_proxy) { my($user, $pass) = CPAN::HTTP::Credentials->get_proxy_credentials(); $ret = { proxy_user => $user, proxy_pass => $pass, http_proxy => $http_proxy }; } } return $ret; } # package CPAN::FTP; sub hostdlhardest { my($self,$host_seq,$file,$aslocal,$stats) = @_; return unless @$host_seq; my($ro_url); my($aslocal_dir) = dirname($aslocal); mkpath($aslocal_dir); my $ftpbin = $CPAN::Config->{ftp}; unless ($ftpbin && length $ftpbin && MM->maybe_command($ftpbin)) { $CPAN::Frontend->myprint("No external ftp command available\n\n"); return; } $CPAN::Frontend->mywarn(qq{ As a last resort we now switch to the external ftp command '$ftpbin' to get '$aslocal'. Doing so often leads to problems that are hard to diagnose. If you're the victim of such problems, please consider unsetting the ftp config variable with o conf ftp "" o conf commit }); $CPAN::Frontend->mysleep(2); HOSTHARDEST: for $ro_url (@$host_seq) { $self->_set_attempt($stats,"dlhardest",$ro_url); my $url = "$ro_url$file"; $self->debug("localizing ftpwise[$url]") if $CPAN::DEBUG; unless ($url =~ m|^ftp://(.*?)/(.*)/(.*)|) { next; } my($host,$dir,$getfile) = ($1,$2,$3); my $timestamp = 0; my($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime, $ctime,$blksize,$blocks) = stat($aslocal); $timestamp = $mtime ||= 0; my($netrc) = CPAN::FTP::netrc->new; my($netrcfile) = $netrc->netrc; my($verbose) = $CPAN::DEBUG{'FTP'} & $CPAN::DEBUG ? " -v" : ""; my $targetfile = File::Basename::basename($aslocal); my(@dialog); push( @dialog, "lcd $aslocal_dir", "cd /", map("cd $_", split /\//, $dir), # RFC 1738 "bin", "passive", "get $getfile $targetfile", "quit" ); if (! $netrcfile) { CPAN->debug("No ~/.netrc file found") if $CPAN::DEBUG; } elsif ($netrc->hasdefault || $netrc->contains($host)) { CPAN->debug(sprintf("hasdef[%d]cont($host)[%d]", $netrc->hasdefault, $netrc->contains($host))) if $CPAN::DEBUG; if ($netrc->protected) { my $dialog = join "", map { " $_\n" } @dialog; my $netrc_explain; if ($netrc->contains($host)) { $netrc_explain = "Relying that your .netrc entry for '$host' ". "manages the login"; } else { $netrc_explain = "Relying that your default .netrc entry ". "manages the login"; } $CPAN::Frontend->myprint(qq{ Trying with external ftp to get '$url' $netrc_explain Sending the dialog $dialog } ); $self->talk_ftp("$ftpbin$verbose $host", @dialog); ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat($aslocal); $mtime ||= 0; if ($mtime > $timestamp) { $CPAN::Frontend->myprint("GOT $aslocal\n"); $ThesiteURL = $ro_url; return $aslocal; } else { $CPAN::Frontend->myprint("Hmm... Still failed!\n"); } return if $CPAN::Signal; } else { $CPAN::Frontend->mywarn(qq{Your $netrcfile is not }. qq{correctly protected.\n}); } } else { $CPAN::Frontend->mywarn("Your ~/.netrc neither contains $host nor does it have a default entry\n"); } # OK, they don't have a valid ~/.netrc. Use 'ftp -n' # then and login manually to host, using e-mail as # password. $CPAN::Frontend->myprint(qq{Issuing "$ftpbin$verbose -n"\n}); unshift( @dialog, "open $host", "user anonymous $Config::Config{'cf_email'}" ); my $dialog = join "", map { " $_\n" } @dialog; $CPAN::Frontend->myprint(qq{ Trying with external ftp to get $url Sending the dialog $dialog } ); $self->talk_ftp("$ftpbin$verbose -n", @dialog); ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat($aslocal); $mtime ||= 0; if ($mtime > $timestamp) { $CPAN::Frontend->myprint("GOT $aslocal\n"); $ThesiteURL = $ro_url; return $aslocal; } else { $CPAN::Frontend->myprint("Bad luck... Still failed!\n"); } return if $CPAN::Signal; $CPAN::Frontend->mywarn("Can't access URL $url.\n\n"); $CPAN::Frontend->mysleep(2); } # host } # package CPAN::FTP; sub talk_ftp { my($self,$command,@dialog) = @_; my $fh = FileHandle->new; $fh->open("|$command") or die "Couldn't open ftp: $!"; foreach (@dialog) { $fh->print("$_\n") } $fh->close; # Wait for process to complete my $wstatus = $?; my $estatus = $wstatus >> 8; $CPAN::Frontend->myprint(qq{ Subprocess "|$command" returned status $estatus (wstat $wstatus) }) if $wstatus; } # find2perl needs modularization, too, all the following is stolen # from there # CPAN::FTP::ls sub ls { my($self,$name) = @_; my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$sizemm, $atime,$mtime,$ctime,$blksize,$blocks) = lstat($name); my($perms,%user,%group); my $pname = $name; if ($blocks) { $blocks = int(($blocks + 1) / 2); } else { $blocks = int(($sizemm + 1023) / 1024); } if (-f _) { $perms = '-'; } elsif (-d _) { $perms = 'd'; } elsif (-c _) { $perms = 'c'; $sizemm = &sizemm; } elsif (-b _) { $perms = 'b'; $sizemm = &sizemm; } elsif (-p _) { $perms = 'p'; } elsif (-S _) { $perms = 's'; } else { $perms = 'l'; $pname .= ' -> ' . readlink($_); } my(@rwx) = ('---','--x','-w-','-wx','r--','r-x','rw-','rwx'); my(@moname) = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); my $tmpmode = $mode; my $tmp = $rwx[$tmpmode & 7]; $tmpmode >>= 3; $tmp = $rwx[$tmpmode & 7] . $tmp; $tmpmode >>= 3; $tmp = $rwx[$tmpmode & 7] . $tmp; substr($tmp,2,1) =~ tr/-x/Ss/ if -u _; substr($tmp,5,1) =~ tr/-x/Ss/ if -g _; substr($tmp,8,1) =~ tr/-x/Tt/ if -k _; $perms .= $tmp; my $user = $user{$uid} || $uid; # too lazy to implement lookup my $group = $group{$gid} || $gid; my($sec,$min,$hour,$mday,$mon,$year) = localtime($mtime); my($timeyear); my($moname) = $moname[$mon]; if (-M _ > 365.25 / 2) { $timeyear = $year + 1900; } else { $timeyear = sprintf("%02d:%02d", $hour, $min); } sprintf "%5lu %4ld %-10s %2d %-8s %-8s %8s %s %2d %5s %s\n", $ino, $blocks, $perms, $nlink, $user, $group, $sizemm, $moname, $mday, $timeyear, $pname; } 1; PK�����A0]UV��V����perl5/CPAN/Index.pmnu�6$��������package CPAN::Index; use strict; use vars qw($LAST_TIME $DATE_OF_02 $DATE_OF_03 $HAVE_REANIMATED $VERSION); $VERSION = "2.29"; @CPAN::Index::ISA = qw(CPAN::Debug); $LAST_TIME ||= 0; $DATE_OF_03 ||= 0; # use constant PROTOCOL => "2.0"; # commented out to avoid warning on upgrade from 1.57 sub PROTOCOL { 2.0 } #-> sub CPAN::Index::force_reload ; sub force_reload { my($class) = @_; $CPAN::Index::LAST_TIME = 0; $class->reload(1); } my @indexbundle = ( { reader => "rd_authindex", dir => "authors", remotefile => '01mailrc.txt.gz', shortlocalfile => '01mailrc.gz', }, { reader => "rd_modpacks", dir => "modules", remotefile => '02packages.details.txt.gz', shortlocalfile => '02packag.gz', }, { reader => "rd_modlist", dir => "modules", remotefile => '03modlist.data.gz', shortlocalfile => '03mlist.gz', }, ); #-> sub CPAN::Index::reload ; sub reload { my($self,$force) = @_; my $time = time; # XXX check if a newer one is available. (We currently read it # from time to time) for ($CPAN::Config->{index_expire}) { $_ = 0.001 unless $_ && $_ > 0.001; } unless (1 || $CPAN::Have_warned->{readmetadatacache}++) { # debug here when CPAN doesn't seem to read the Metadata require Carp; Carp::cluck("META-PROTOCOL[$CPAN::META->{PROTOCOL}]"); } unless ($CPAN::META->{PROTOCOL}) { $self->read_metadata_cache; $CPAN::META->{PROTOCOL} ||= "1.0"; } if ( $CPAN::META->{PROTOCOL} < PROTOCOL ) { # warn "Setting last_time to 0"; $LAST_TIME = 0; # No warning necessary } if ($LAST_TIME + $CPAN::Config->{index_expire}*86400 > $time and ! $force) { # called too often # CPAN->debug("LAST_TIME[$LAST_TIME]index_expire[$CPAN::Config->{index_expire}]time[$time]"); } elsif (0) { # IFF we are developing, it helps to wipe out the memory # between reloads, otherwise it is not what a user expects. undef $CPAN::META; # Neue Gruendlichkeit since v1.52(r1.274) $CPAN::META = CPAN->new; } else { my($debug,$t2); local $LAST_TIME = $time; local $CPAN::META->{PROTOCOL} = PROTOCOL; my $needshort = $^O eq "dos"; INX: for my $indexbundle (@indexbundle) { my $reader = $indexbundle->{reader}; my $localfile = $needshort ? $indexbundle->{shortlocalfile} : $indexbundle->{remotefile}; my $localpath = File::Spec->catfile($indexbundle->{dir}, $localfile); my $remote = join "/", $indexbundle->{dir}, $indexbundle->{remotefile}; my $localized = $self->reload_x($remote, $localpath, $force); $self->$reader($localized); # may die but we let the shell catch it if ($CPAN::DEBUG){ $t2 = time; $debug = "timing reading 01[".($t2 - $time)."]"; $time = $t2; } return if $CPAN::Signal; # this is sometimes lengthy } $self->write_metadata_cache; if ($CPAN::DEBUG){ $t2 = time; $debug .= "03[".($t2 - $time)."]"; $time = $t2; } CPAN->debug($debug) if $CPAN::DEBUG; } if ($CPAN::Config->{build_dir_reuse}) { $self->reanimate_build_dir; } if (CPAN::_sqlite_running()) { $CPAN::SQLite->reload(time => $time, force => $force) if not $LAST_TIME; } $LAST_TIME = $time; $CPAN::META->{PROTOCOL} = PROTOCOL; } #-> sub CPAN::Index::reanimate_build_dir ; sub reanimate_build_dir { my($self) = @_; unless ($CPAN::META->has_inst($CPAN::Config->{yaml_module}||"YAML")) { return; } return if $HAVE_REANIMATED++; my $d = $CPAN::Config->{build_dir}; my $dh = DirHandle->new; opendir $dh, $d or return; # does not exist my $dirent; my $i = 0; my $painted = 0; my $restored = 0; my $start = CPAN::FTP::_mytime(); my @candidates = map { $_->[0] } sort { $b->[1] <=> $a->[1] } map { [ $_, -M File::Spec->catfile($d,$_) ] } grep {/(.+)\.yml$/ && -d File::Spec->catfile($d,$1)} readdir $dh; if ( @candidates ) { $CPAN::Frontend->myprint (sprintf("Reading %d yaml file%s from %s/\n", scalar @candidates, @candidates==1 ? "" : "s", $CPAN::Config->{build_dir} )); DISTRO: for $i (0..$#candidates) { my $dirent = $candidates[$i]; my $y = eval {CPAN->_yaml_loadfile(File::Spec->catfile($d,$dirent), {loadblessed => 1})}; if ($@) { warn "Error while parsing file '$dirent'; error: '$@'"; next DISTRO; } my $c = $y->[0]; if ($c && $c->{perl} && $c->{distribution} && CPAN->_perl_fingerprint($c->{perl})) { my $key = $c->{distribution}{ID}; for my $k (keys %{$c->{distribution}}) { if ($c->{distribution}{$k} && ref $c->{distribution}{$k} && UNIVERSAL::isa($c->{distribution}{$k},"CPAN::Distrostatus")) { $c->{distribution}{$k}{COMMANDID} = $i - @candidates; } } #we tried to restore only if element already #exists; but then we do not work with metadata #turned off. my $do = $CPAN::META->{readwrite}{'CPAN::Distribution'}{$key} = $c->{distribution}; for my $skipper (qw( badtestcnt configure_requires_later configure_requires_later_for force_update later later_for notest should_report sponsored_mods prefs negative_prefs_cache )) { delete $do->{$skipper}; } if ($do->can("tested_ok_but_not_installed")) { if ($do->tested_ok_but_not_installed) { $CPAN::META->is_tested($do->{build_dir},$do->{make_test}{TIME}); } else { next DISTRO; } } $restored++; } $i++; while (($painted/76) < ($i/@candidates)) { $CPAN::Frontend->myprint("."); $painted++; } } } else { $CPAN::Frontend->myprint("Build_dir empty, nothing to restore\n"); } my $took = CPAN::FTP::_mytime() - $start; $CPAN::Frontend->myprint(sprintf( "DONE\nRestored the state of %s (in %.4f secs)\n", $restored || "none", $took, )); } #-> sub CPAN::Index::reload_x ; sub reload_x { my($cl,$wanted,$localname,$force) = @_; $force |= 2; # means we're dealing with an index here CPAN::HandleConfig->load; # we should guarantee loading wherever # we rely on Config XXX $localname ||= $wanted; my $abs_wanted = File::Spec->catfile($CPAN::Config->{'keep_source_where'}, $localname); if ( -f $abs_wanted && -M $abs_wanted < $CPAN::Config->{'index_expire'} && !($force & 1) ) { my $s = $CPAN::Config->{'index_expire'} == 1 ? "" : "s"; $cl->debug(qq{$abs_wanted younger than $CPAN::Config->{'index_expire'} }. qq{day$s. I\'ll use that.}); return $abs_wanted; } else { $force |= 1; # means we're quite serious about it. } return CPAN::FTP->localize($wanted,$abs_wanted,$force); } #-> sub CPAN::Index::rd_authindex ; sub rd_authindex { my($cl, $index_target) = @_; return unless defined $index_target; return if CPAN::_sqlite_running(); my @lines; $CPAN::Frontend->myprint("Reading '$index_target'\n"); local(*FH); tie *FH, 'CPAN::Tarzip', $index_target; local($/) = "\n"; local($_); push @lines, split /\012/ while <FH>; my $i = 0; my $painted = 0; foreach (@lines) { my($userid,$fullname,$email) = m/alias\s+(\S+)\s+\"([^\"\<]*)\s+\<(.*)\>\"/; $fullname ||= $email; if ($userid && $fullname && $email) { my $userobj = $CPAN::META->instance('CPAN::Author',$userid); $userobj->set('FULLNAME' => $fullname, 'EMAIL' => $email); } else { CPAN->debug(sprintf "line[%s]", $_) if $CPAN::DEBUG; } $i++; while (($painted/76) < ($i/@lines)) { $CPAN::Frontend->myprint("."); $painted++; } return if $CPAN::Signal; } $CPAN::Frontend->myprint("DONE\n"); } sub userid { my($self,$dist) = @_; $dist = $self->{'id'} unless defined $dist; my($ret) = $dist =~ m|(?:\w/\w\w/)?([^/]+)/|; $ret; } #-> sub CPAN::Index::rd_modpacks ; sub rd_modpacks { my($self, $index_target) = @_; return unless defined $index_target; return if CPAN::_sqlite_running(); $CPAN::Frontend->myprint("Reading '$index_target'\n"); my $fh = CPAN::Tarzip->TIEHANDLE($index_target); local $_; CPAN->debug(sprintf "start[%d]", time) if $CPAN::DEBUG; my $slurp = ""; my $chunk; while (my $bytes = $fh->READ(\$chunk,8192)) { $slurp.=$chunk; } my @lines = split /\012/, $slurp; CPAN->debug(sprintf "end[%d]", time) if $CPAN::DEBUG; undef $fh; # read header my($line_count,$last_updated); while (@lines) { my $shift = shift(@lines); last if $shift =~ /^\s*$/; $shift =~ /^Line-Count:\s+(\d+)/ and $line_count = $1; $shift =~ /^Last-Updated:\s+(.+)/ and $last_updated = $1; } CPAN->debug("line_count[$line_count]last_updated[$last_updated]") if $CPAN::DEBUG; my $errors = 0; if (not defined $line_count) { $CPAN::Frontend->mywarn(qq{Warning: Your $index_target does not contain a Line-Count header. Please check the validity of the index file by comparing it to more than one CPAN mirror. I'll continue but problems seem likely to happen.\a }); $errors++; $CPAN::Frontend->mysleep(5); } elsif ($line_count != scalar @lines) { $CPAN::Frontend->mywarn(sprintf qq{Warning: Your %s contains a Line-Count header of %d but I see %d lines there. Please check the validity of the index file by comparing it to more than one CPAN mirror. I'll continue but problems seem likely to happen.\a\n}, $index_target, $line_count, scalar(@lines)); } if (not defined $last_updated) { $CPAN::Frontend->mywarn(qq{Warning: Your $index_target does not contain a Last-Updated header. Please check the validity of the index file by comparing it to more than one CPAN mirror. I'll continue but problems seem likely to happen.\a }); $errors++; $CPAN::Frontend->mysleep(5); } else { $CPAN::Frontend ->myprint(sprintf qq{ Database was generated on %s\n}, $last_updated); $DATE_OF_02 = $last_updated; my $age = time; if ($CPAN::META->has_inst('HTTP::Date')) { require HTTP::Date; $age -= HTTP::Date::str2time($last_updated); } else { $CPAN::Frontend->mywarn(" HTTP::Date not available\n"); require Time::Local; my(@d) = $last_updated =~ / (\d+) (\w+) (\d+) (\d+):(\d+):(\d+) /; $d[1] = index("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec", $d[1])/4; $age -= $d[1]>=0 ? Time::Local::timegm(@d[5,4,3,0,1,2]) : 0; } $age /= 3600*24; if ($age > 30) { $CPAN::Frontend ->mywarn(sprintf qq{Warning: This index file is %d days old. Please check the host you chose as your CPAN mirror for staleness. I'll continue but problems seem likely to happen.\a\n}, $age); } elsif ($age < -1) { $CPAN::Frontend ->mywarn(sprintf qq{Warning: Your system date is %d days behind this index file! System time: %s Timestamp index file: %s Please fix your system time, problems with the make command expected.\n}, -$age, scalar gmtime, $DATE_OF_02, ); } } # A necessity since we have metadata_cache: delete what isn't # there anymore my $secondtime = $CPAN::META->exists("CPAN::Module","CPAN"); CPAN->debug("secondtime[$secondtime]") if $CPAN::DEBUG; my(%exists); my $i = 0; my $painted = 0; LINE: foreach (@lines) { # before 1.56 we split into 3 and discarded the rest. From # 1.57 we assign remaining text to $comment thus allowing to # influence isa_perl my($mod,$version,$dist,$comment) = split " ", $_, 4; unless ($mod && defined $version && $dist) { require Dumpvalue; my $dv = Dumpvalue->new(tick => '"'); $CPAN::Frontend->mywarn(sprintf "Could not split line[%s]\n", $dv->stringify($_)); if ($errors++ >= 5){ $CPAN::Frontend->mydie("Giving up parsing your $index_target, too many errors"); } next LINE; } my($bundle,$id,$userid); if ($mod eq 'CPAN' && ! ( CPAN::Queue->exists('Bundle::CPAN') || CPAN::Queue->exists('CPAN') ) ) { local($^W)= 0; if ($version > $CPAN::VERSION) { $CPAN::Frontend->mywarn(qq{ New CPAN.pm version (v$version) available. [Currently running version is v$CPAN::VERSION] You might want to try install CPAN reload cpan to both upgrade CPAN.pm and run the new version without leaving the current session. }); #}); $CPAN::Frontend->mysleep(2); $CPAN::Frontend->myprint(qq{\n}); } last if $CPAN::Signal; } elsif ($mod =~ /^Bundle::(.*)/) { $bundle = $1; } if ($bundle) { $id = $CPAN::META->instance('CPAN::Bundle',$mod); # Let's make it a module too, because bundles have so much # in common with modules. # Changed in 1.57_63: seems like memory bloat now without # any value, so commented out # $CPAN::META->instance('CPAN::Module',$mod); } else { # instantiate a module object $id = $CPAN::META->instance('CPAN::Module',$mod); } # Although CPAN prohibits same name with different version the # indexer may have changed the version for the same distro # since the last time ("Force Reindexing" feature) if ($id->cpan_file ne $dist || $id->cpan_version ne $version ) { $userid = $id->userid || $self->userid($dist); $id->set( 'CPAN_USERID' => $userid, 'CPAN_VERSION' => $version, 'CPAN_FILE' => $dist, ); } # instantiate a distribution object if ($CPAN::META->exists('CPAN::Distribution',$dist)) { # we do not need CONTAINSMODS unless we do something with # this dist, so we better produce it on demand. ## my $obj = $CPAN::META->instance( ## 'CPAN::Distribution' => $dist ## ); ## $obj->{CONTAINSMODS}{$mod} = undef; # experimental } else { $CPAN::META->instance( 'CPAN::Distribution' => $dist )->set( 'CPAN_USERID' => $userid, 'CPAN_COMMENT' => $comment, ); } if ($secondtime) { for my $name ($mod,$dist) { # $self->debug("exists name[$name]") if $CPAN::DEBUG; $exists{$name} = undef; } } $i++; while (($painted/76) < ($i/@lines)) { $CPAN::Frontend->myprint("."); $painted++; } return if $CPAN::Signal; } $CPAN::Frontend->myprint("DONE\n"); if ($secondtime) { for my $class (qw(CPAN::Module CPAN::Bundle CPAN::Distribution)) { for my $o ($CPAN::META->all_objects($class)) { next if exists $exists{$o->{ID}}; $CPAN::META->delete($class,$o->{ID}); # CPAN->debug("deleting ID[$o->{ID}] in class[$class]") # if $CPAN::DEBUG; } } } } #-> sub CPAN::Index::rd_modlist ; sub rd_modlist { my($cl,$index_target) = @_; return unless defined $index_target; return if CPAN::_sqlite_running(); $CPAN::Frontend->myprint("Reading '$index_target'\n"); my $fh = CPAN::Tarzip->TIEHANDLE($index_target); local $_; my $slurp = ""; my $chunk; while (my $bytes = $fh->READ(\$chunk,8192)) { $slurp.=$chunk; } my @eval2 = split /\012/, $slurp; while (@eval2) { my $shift = shift(@eval2); if ($shift =~ /^Date:\s+(.*)/) { if ($DATE_OF_03 eq $1) { $CPAN::Frontend->myprint("Unchanged.\n"); return; } ($DATE_OF_03) = $1; } last if $shift =~ /^\s*$/; } push @eval2, q{CPAN::Modulelist->data;}; local($^W) = 0; my($compmt) = Safe->new("CPAN::Safe1"); my($eval2) = join("\n", @eval2); CPAN->debug(sprintf "length of eval2[%d]", length $eval2) if $CPAN::DEBUG; my $ret = $compmt->reval($eval2); Carp::confess($@) if $@; return if $CPAN::Signal; my $i = 0; my $until = keys(%$ret); my $painted = 0; CPAN->debug(sprintf "until[%d]", $until) if $CPAN::DEBUG; for (sort keys %$ret) { my $obj = $CPAN::META->instance("CPAN::Module",$_); delete $ret->{$_}{modid}; # not needed here, maybe elsewhere $obj->set(%{$ret->{$_}}); $i++; while (($painted/76) < ($i/$until)) { $CPAN::Frontend->myprint("."); $painted++; } return if $CPAN::Signal; } $CPAN::Frontend->myprint("DONE\n"); } #-> sub CPAN::Index::write_metadata_cache ; sub write_metadata_cache { my($self) = @_; return unless $CPAN::Config->{'cache_metadata'}; return if CPAN::_sqlite_running(); return unless $CPAN::META->has_usable("Storable"); my $cache; foreach my $k (qw(CPAN::Bundle CPAN::Author CPAN::Module CPAN::Distribution)) { $cache->{$k} = $CPAN::META->{readonly}{$k}; # unsafe meta access, ok } my $metadata_file = File::Spec->catfile($CPAN::Config->{cpan_home},"Metadata"); $cache->{last_time} = $LAST_TIME; $cache->{DATE_OF_02} = $DATE_OF_02; $cache->{PROTOCOL} = PROTOCOL; $CPAN::Frontend->myprint("Writing $metadata_file\n"); eval { Storable::nstore($cache, $metadata_file) }; $CPAN::Frontend->mywarn($@) if $@; # ?? missing "\n" after $@ in mywarn ?? } #-> sub CPAN::Index::read_metadata_cache ; sub read_metadata_cache { my($self) = @_; return unless $CPAN::Config->{'cache_metadata'}; return if CPAN::_sqlite_running(); return unless $CPAN::META->has_usable("Storable"); my $metadata_file = File::Spec->catfile($CPAN::Config->{cpan_home},"Metadata"); return unless -r $metadata_file and -f $metadata_file; $CPAN::Frontend->myprint("Reading '$metadata_file'\n"); my $cache; eval { $cache = Storable::retrieve($metadata_file) }; $CPAN::Frontend->mywarn($@) if $@; # ?? missing "\n" after $@ in mywarn ?? if (!$cache || !UNIVERSAL::isa($cache, 'HASH')) { $LAST_TIME = 0; return; } if (exists $cache->{PROTOCOL}) { if (PROTOCOL > $cache->{PROTOCOL}) { $CPAN::Frontend->mywarn(sprintf("Ignoring Metadata cache written ". "with protocol v%s, requiring v%s\n", $cache->{PROTOCOL}, PROTOCOL) ); return; } } else { $CPAN::Frontend->mywarn("Ignoring Metadata cache written ". "with protocol v1.0\n"); return; } my $clcnt = 0; my $idcnt = 0; while(my($class,$v) = each %$cache) { next unless $class =~ /^CPAN::/; $CPAN::META->{readonly}{$class} = $v; # unsafe meta access, ok while (my($id,$ro) = each %$v) { $CPAN::META->{readwrite}{$class}{$id} ||= $class->new(ID=>$id, RO=>$ro); $idcnt++; } $clcnt++; } unless ($clcnt) { # sanity check $CPAN::Frontend->myprint("Warning: Found no data in $metadata_file\n"); return; } if ($idcnt < 1000) { $CPAN::Frontend->myprint("Warning: Found only $idcnt objects ". "in $metadata_file\n"); return; } $CPAN::META->{PROTOCOL} ||= $cache->{PROTOCOL}; # reading does not up or downgrade, but it # does initialize to some protocol $LAST_TIME = $cache->{last_time}; $DATE_OF_02 = $cache->{DATE_OF_02}; $CPAN::Frontend->myprint(" Database was generated on $DATE_OF_02\n") if defined $DATE_OF_02; # An old cache may not contain DATE_OF_02 return; } 1; PK�����A0]B3������perl5/CPAN/Queue.pmnu�6$��������# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- use strict; package CPAN::Queue::Item; # CPAN::Queue::Item::new ; sub new { my($class,@attr) = @_; my $self = bless { @attr }, $class; return $self; } sub as_string { my($self) = @_; $self->{qmod}; } # r => requires, b => build_requires, c => commandline sub reqtype { my($self) = @_; $self->{reqtype}; } sub optional { my($self) = @_; $self->{optional}; } package CPAN::Queue; # One use of the queue is to determine if we should or shouldn't # announce the availability of a new CPAN module # Now we try to use it for dependency tracking. For that to happen # we need to draw a dependency tree and do the leaves first. This can # easily be reached by running CPAN.pm recursively, but we don't want # to waste memory and run into deep recursion. So what we can do is # this: # CPAN::Queue is the package where the queue is maintained. Dependencies # often have high priority and must be brought to the head of the queue, # possibly by jumping the queue if they are already there. My first code # attempt tried to be extremely correct. Whenever a module needed # immediate treatment, I either unshifted it to the front of the queue, # or, if it was already in the queue, I spliced and let it bypass the # others. This became a too correct model that made it impossible to put # an item more than once into the queue. Why would you need that? Well, # you need temporary duplicates as the manager of the queue is a loop # that # # (1) looks at the first item in the queue without shifting it off # # (2) cares for the item # # (3) removes the item from the queue, *even if its agenda failed and # even if the item isn't the first in the queue anymore* (that way # protecting against never ending queues) # # So if an item has prerequisites, the installation fails now, but we # want to retry later. That's easy if we have it twice in the queue. # # I also expect insane dependency situations where an item gets more # than two lives in the queue. Simplest example is triggered by 'install # Foo Foo Foo'. People make this kind of mistakes and I don't want to # get in the way. I wanted the queue manager to be a dumb servant, not # one that knows everything. # # Who would I tell in this model that the user wants to be asked before # processing? I can't attach that information to the module object, # because not modules are installed but distributions. So I'd have to # tell the distribution object that it should ask the user before # processing. Where would the question be triggered then? Most probably # in CPAN::Distribution::rematein. use vars qw{ @All $VERSION }; $VERSION = "5.5003"; # CPAN::Queue::queue_item ; sub queue_item { my($class,@attr) = @_; my $item = "$class\::Item"->new(@attr); $class->qpush($item); return 1; } # CPAN::Queue::qpush ; sub qpush { my($class,$obj) = @_; push @All, $obj; CPAN->debug(sprintf("in new All[%s]", join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All), )) if $CPAN::DEBUG; } # CPAN::Queue::first ; sub first { my $obj = $All[0]; $obj; } # CPAN::Queue::delete_first ; sub delete_first { my($class,$what) = @_; my $i; for my $i (0..$#All) { if ( $All[$i]->{qmod} eq $what ) { splice @All, $i, 1; last; } } CPAN->debug(sprintf("after delete_first mod[%s] All[%s]", $what, join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All) )) if $CPAN::DEBUG; } # CPAN::Queue::jumpqueue ; sub jumpqueue { my $class = shift; my @what = @_; CPAN->debug(sprintf("before jumpqueue All[%s] what[%s]", join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All), join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @what), )) if $CPAN::DEBUG; unless (defined $what[0]{reqtype}) { # apparently it was not the Shell that sent us this enquiry, # treat it as commandline $what[0]{reqtype} = "c"; } my $inherit_reqtype = $what[0]{reqtype} =~ /^(c|r)$/ ? "r" : "b"; WHAT: for my $what_tuple (@what) { my($qmod,$reqtype,$optional) = @$what_tuple{qw(qmod reqtype optional)}; if ($reqtype eq "r" && $inherit_reqtype eq "b" ) { $reqtype = "b"; } my $jumped = 0; for (my $i=0; $i<$#All;$i++) { #prevent deep recursion if ($All[$i]{qmod} eq $qmod) { $jumped++; } } # high jumped values are normal for popular modules when # dealing with large bundles: XML::Simple, # namespace::autoclean, UNIVERSAL::require CPAN->debug("qmod[$qmod]jumped[$jumped]") if $CPAN::DEBUG; my $obj = "$class\::Item"->new( qmod => $qmod, reqtype => $reqtype, optional => !! $optional, ); unshift @All, $obj; } CPAN->debug(sprintf("after jumpqueue All[%s]", join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All) )) if $CPAN::DEBUG; } # CPAN::Queue::exists ; sub exists { my($self,$what) = @_; my @all = map { $_->{qmod} } @All; my $exists = grep { $_->{qmod} eq $what } @All; # warn "in exists what[$what] all[@all] exists[$exists]"; $exists; } # CPAN::Queue::delete ; sub delete { my($self,$mod) = @_; @All = grep { $_->{qmod} ne $mod } @All; CPAN->debug(sprintf("after delete mod[%s] All[%s]", $mod, join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All) )) if $CPAN::DEBUG; } # CPAN::Queue::nullify_queue ; sub nullify_queue { @All = (); } # CPAN::Queue::size ; sub size { return scalar @All; } sub reqtype_of { my($self,$mod) = @_; my $best = ""; for my $item (grep { $_->{qmod} eq $mod } @All) { my $c = $item->{reqtype}; if ($c eq "c") { $best = $c; last; } elsif ($c eq "r") { $best = $c; } elsif ($c eq "b") { if ($best eq "") { $best = $c; } } else { die "Panic: in reqtype_of: reqtype[$c] seen, should never happen"; } } return $best; } sub iterator { my $i = 0; return sub { until ($All[$i] || $i > $#All) { $i++; } return if $i > $#All; return $All[$i++] }; } 1; __END__ =head1 NAME CPAN::Queue - internal queue support for CPAN.pm =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut PK�����A0]}l+��+����perl5/CPAN/API/HOWTO.podnu�6$��������=head1 NAME CPAN::API::HOWTO - a recipe book for programming with CPAN.pm =head1 RECIPES All of these recipes assume that you have put "use CPAN" at the top of your program. =head2 What distribution contains a particular module? my $distribution = CPAN::Shell->expand( "Module", "Data::UUID" )->distribution()->pretty_id(); This returns a string of the form "AUTHORID/TARBALL". If you want the full path and filename to this distribution on a CPAN mirror, then it is C<.../authors/id/A/AU/AUTHORID/TARBALL>. =head2 What modules does a particular distribution contain? CPAN::Index->reload(); my @modules = CPAN::Shell->expand( "Distribution", "JHI/Graph-0.83.tar.gz" )->containsmods(); You may also refer to a distribution in the form A/AU/AUTHORID/TARBALL. =head1 SEE ALSO the main CPAN.pm documentation =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L<http://www.perl.com/perl/misc/Artistic.html> =head1 AUTHOR David Cantrell =cut PK�����A0]L����perl5/CPAN/Distribution.pmnu�6$��������# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::Distribution; use strict; use Cwd qw(chdir); use CPAN::Distroprefs; use CPAN::InfoObj; use File::Path (); use POSIX ":sys_wait_h"; @CPAN::Distribution::ISA = qw(CPAN::InfoObj); use vars qw($VERSION); $VERSION = "2.34"; my $run_allow_installing_within_test = 1; # boolean; either in test or in install, there is no third option # no prepare, because prepare is not a command on the shell command line # TODO: clear instance cache on reload my %instance; for my $method (qw(get make test install)) { no strict 'refs'; for my $prefix (qw(pre post)) { my $hookname = sprintf "%s_%s", $prefix, $method; *$hookname = sub { my($self) = @_; for my $plugin (@{$CPAN::Config->{plugin_list}}) { my($plugin_proper,$args) = split /=/, $plugin, 2; $args = "" unless defined $args; if ($CPAN::META->has_inst($plugin_proper)){ my @args = split /,/, $args; $instance{$plugin} ||= $plugin_proper->new(@args); if ($instance{$plugin}->can($hookname)) { $instance{$plugin}->$hookname($self); } } else { $CPAN::Frontend->mydie("Plugin '$plugin_proper' not found for hook '$hookname'"); } } }; } } # Accessors sub cpan_comment { my $self = shift; my $ro = $self->ro or return; $ro->{CPAN_COMMENT} } #-> CPAN::Distribution::undelay sub undelay { my $self = shift; for my $delayer ( "configure_requires_later", "configure_requires_later_for", "later", "later_for", ) { delete $self->{$delayer}; } } #-> CPAN::Distribution::is_dot_dist sub is_dot_dist { my($self) = @_; return substr($self->id,-1,1) eq "."; } # add the A/AN/ stuff #-> CPAN::Distribution::normalize sub normalize { my($self,$s) = @_; $s = $self->id unless defined $s; if (substr($s,-1,1) eq ".") { # using a global because we are sometimes called as static method if (!$CPAN::META->{LOCK} && !$CPAN::Have_warned->{"$s is unlocked"}++ ) { $CPAN::Frontend->mywarn("You are visiting the local directory '$s' without lock, take care that concurrent processes do not do likewise.\n"); $CPAN::Frontend->mysleep(1); } if ($s eq ".") { $s = "$CPAN::iCwd/."; } elsif (File::Spec->file_name_is_absolute($s)) { } elsif (File::Spec->can("rel2abs")) { $s = File::Spec->rel2abs($s); } else { $CPAN::Frontend->mydie("Your File::Spec is too old, please upgrade File::Spec"); } CPAN->debug("s[$s]") if $CPAN::DEBUG; unless ($CPAN::META->exists("CPAN::Distribution", $s)) { for ($CPAN::META->instance("CPAN::Distribution", $s)) { $_->{build_dir} = $s; $_->{archived} = "local_directory"; $_->{unwrapped} = CPAN::Distrostatus->new("YES -- local_directory"); } } } elsif ( $s =~ tr|/|| == 1 or $s !~ m|[A-Z]/[A-Z-0-9]{2}/[A-Z-0-9]{2,}/| ) { return $s if $s =~ m:^N/A|^Contact Author: ; $s =~ s|^(.)(.)([^/]*/)(.+)$|$1/$1$2/$1$2$3$4|; CPAN->debug("s[$s]") if $CPAN::DEBUG; } $s; } #-> sub CPAN::Distribution::author ; sub author { my($self) = @_; my($authorid); if (substr($self->id,-1,1) eq ".") { $authorid = "LOCAL"; } else { ($authorid) = $self->pretty_id =~ /^([\w\-]+)/; } CPAN::Shell->expand("Author",$authorid); } # tries to get the yaml from CPAN instead of the distro itself: # EXPERIMENTAL, UNDOCUMENTED AND UNTESTED, for Tels sub fast_yaml { my($self) = @_; my $meta = $self->pretty_id; $meta =~ s/\.(tar.gz|tgz|zip|tar.bz2)/.meta/; my(@ls) = CPAN::Shell->globls($meta); my $norm = $self->normalize($meta); my($local_file); my($local_wanted) = File::Spec->catfile( $CPAN::Config->{keep_source_where}, "authors", "id", split(/\//,$norm) ); $self->debug("Doing localize") if $CPAN::DEBUG; unless ($local_file = CPAN::FTP->localize("authors/id/$norm", $local_wanted)) { $CPAN::Frontend->mydie("Giving up on downloading yaml file '$local_wanted'\n"); } my $yaml = CPAN->_yaml_loadfile($local_file)->[0]; } #-> sub CPAN::Distribution::cpan_userid sub cpan_userid { my $self = shift; if ($self->{ID} =~ m{[A-Z]/[A-Z\-]{2}/([A-Z\-]+)/}) { return $1; } return $self->SUPER::cpan_userid; } #-> sub CPAN::Distribution::pretty_id sub pretty_id { my $self = shift; my $id = $self->id; return $id unless $id =~ m|^./../|; substr($id,5); } #-> sub CPAN::Distribution::base_id sub base_id { my $self = shift; my $id = $self->pretty_id(); my $base_id = File::Basename::basename($id); $base_id =~ s{\.(?:tar\.(bz2|gz|Z)|t(?:gz|bz)|zip)$}{}i; return $base_id; } #-> sub CPAN::Distribution::tested_ok_but_not_installed sub tested_ok_but_not_installed { my $self = shift; return ( $self->{make_test} && $self->{build_dir} && (UNIVERSAL::can($self->{make_test},"failed") ? ! $self->{make_test}->failed : $self->{make_test} =~ /^YES/ ) && ( !$self->{install} || $self->{install}->failed ) ); } # mark as dirty/clean for the sake of recursion detection. $color=1 # means "in use", $color=0 means "not in use anymore". $color=2 means # we have determined prereqs now and thus insist on passing this # through (at least) once again. #-> sub CPAN::Distribution::color_cmd_tmps ; sub color_cmd_tmps { my($self) = shift; my($depth) = shift || 0; my($color) = shift || 0; my($ancestors) = shift || []; # a distribution needs to recurse into its prereq_pms $self->debug("color_cmd_tmps[$depth,$color,@$ancestors]") if $CPAN::DEBUG; return if exists $self->{incommandcolor} && $color==1 && $self->{incommandcolor}==$color; $CPAN::MAX_RECURSION||=0; # silence 'once' warnings if ($depth>=$CPAN::MAX_RECURSION) { my $e = CPAN::Exception::RecursiveDependency->new($ancestors); if ($e->is_resolvable) { return $self->{incommandcolor}=2; } else { die $e; } } # warn "color_cmd_tmps $depth $color " . $self->id; # sleep 1; my $prereq_pm = $self->prereq_pm; if (defined $prereq_pm) { # XXX also optional_req & optional_breq? -- xdg, 2012-04-01 # A: no, optional deps may recurse -- ak, 2014-05-07 PREREQ: for my $pre (sort( keys %{$prereq_pm->{requires}||{}}, keys %{$prereq_pm->{build_requires}||{}}, )) { next PREREQ if $pre eq "perl"; my $premo; unless ($premo = CPAN::Shell->expand("Module",$pre)) { $CPAN::Frontend->mywarn("prerequisite module[$pre] not known\n"); $CPAN::Frontend->mysleep(0.2); next PREREQ; } $premo->color_cmd_tmps($depth+1,$color,[@$ancestors, $self->id]); } } if ($color==0) { delete $self->{sponsored_mods}; # as we are at the end of a command, we'll give up this # reminder of a broken test. Other commands may test this guy # again. Maybe 'badtestcnt' should be renamed to # 'make_test_failed_within_command'? delete $self->{badtestcnt}; } $self->{incommandcolor} = $color; } #-> sub CPAN::Distribution::as_string ; sub as_string { my $self = shift; $self->containsmods; $self->upload_date; $self->SUPER::as_string(@_); } #-> sub CPAN::Distribution::containsmods ; sub containsmods { my $self = shift; return sort keys %{$self->{CONTAINSMODS}} if exists $self->{CONTAINSMODS}; my $dist_id = $self->{ID}; for my $mod ($CPAN::META->all_objects("CPAN::Module")) { my $mod_file = $mod->cpan_file or next; my $mod_id = $mod->{ID} or next; # warn "mod_file[$mod_file] dist_id[$dist_id] mod_id[$mod_id]"; # sleep 1; if ($CPAN::Signal) { delete $self->{CONTAINSMODS}; return; } $self->{CONTAINSMODS}{$mod_id} = undef if $mod_file eq $dist_id; } sort keys %{$self->{CONTAINSMODS}||={}}; } #-> sub CPAN::Distribution::upload_date ; sub upload_date { my $self = shift; return $self->{UPLOAD_DATE} if exists $self->{UPLOAD_DATE}; my(@local_wanted) = split(/\//,$self->id); my $filename = pop @local_wanted; push @local_wanted, "CHECKSUMS"; my $author = CPAN::Shell->expand("Author",$self->cpan_userid); return unless $author; my @dl = $author->dir_listing(\@local_wanted,0,$CPAN::Config->{show_upload_date}); return unless @dl; my($dirent) = grep { $_->[2] eq $filename } @dl; # warn sprintf "dirent[%s]id[%s]", $dirent, $self->id; return unless $dirent->[1]; return $self->{UPLOAD_DATE} = $dirent->[1]; } #-> sub CPAN::Distribution::uptodate ; sub uptodate { my($self) = @_; my $c; foreach $c ($self->containsmods) { my $obj = CPAN::Shell->expandany($c); unless ($obj->uptodate) { my $id = $self->pretty_id; $self->debug("$id not uptodate due to $c") if $CPAN::DEBUG; return 0; } } return 1; } #-> sub CPAN::Distribution::called_for ; sub called_for { my($self,$id) = @_; $self->{CALLED_FOR} = $id if defined $id; return $self->{CALLED_FOR}; } #-> sub CPAN::Distribution::shortcut_get ; # return values: undef means don't shortcut; 0 means shortcut as fail; # and 1 means shortcut as success sub shortcut_get { my ($self) = @_; if (exists $self->{cleanup_after_install_done}) { if ($self->{force_update}) { delete $self->{cleanup_after_install_done}; } else { my $id = $self->{CALLED_FOR} || $self->pretty_id; return $self->success( "Has already been *installed and cleaned up in the staging area* within this session, will not work on it again; if you really want to start over, try something like `force get $id`" ); } } if (my $why = $self->check_disabled) { $self->{unwrapped} = CPAN::Distrostatus->new("NO $why"); # XXX why is this goodbye() instead of just print/warn? # Alternatively, should other print/warns here be goodbye()? # -- xdg, 2012-04-05 return $self->goodbye("[disabled] -- NA $why"); } $self->debug("checking already unwrapped[$self->{ID}]") if $CPAN::DEBUG; if (exists $self->{build_dir} && -d $self->{build_dir}) { # this deserves print, not warn: return $self->success("Has already been unwrapped into directory ". "$self->{build_dir}" ); } # XXX I'm not sure this should be here because it's not really # a test for whether get should continue or return; this is # a side effect -- xdg, 2012-04-05 $self->debug("checking missing build_dir[$self->{ID}]") if $CPAN::DEBUG; if (exists $self->{build_dir} && ! -d $self->{build_dir}){ # we have lost it. $self->fforce(""); # no method to reset all phases but not set force (dodge) return undef; # no shortcut } # although we talk about 'force' we shall not test on # force directly. New model of force tries to refrain from # direct checking of force. $self->debug("checking unwrapping error[$self->{ID}]") if $CPAN::DEBUG; if ( exists $self->{unwrapped} and ( UNIVERSAL::can($self->{unwrapped},"failed") ? $self->{unwrapped}->failed : $self->{unwrapped} =~ /^NO/ ) ) { return $self->goodbye("Unwrapping had some problem, won't try again without force"); } return undef; # no shortcut } #-> sub CPAN::Distribution::get ; sub get { my($self) = @_; $self->pre_get(); $self->debug("checking goto id[$self->{ID}]") if $CPAN::DEBUG; if (my $goto = $self->prefs->{goto}) { $self->post_get(); return $self->goto($goto); } if ( defined( my $sc = $self->shortcut_get) ) { $self->post_get(); return $sc; } local $ENV{PERL5LIB} = defined($ENV{PERL5LIB}) ? $ENV{PERL5LIB} : ($ENV{PERLLIB} || ""); local $ENV{PERL5OPT} = defined $ENV{PERL5OPT} ? $ENV{PERL5OPT} : ""; # local $ENV{PERL_USE_UNSAFE_INC} = exists $ENV{PERL_USE_UNSAFE_INC} ? $ENV{PERL_USE_UNSAFE_INC} : 1; # get $CPAN::META->set_perl5lib; local $ENV{MAKEFLAGS}; # protect us from outer make calls my $sub_wd = CPAN::anycwd(); # for cleaning up as good as possible my($local_file); # XXX I don't think this check needs to be here, as it # is already checked in shortcut_get() -- xdg, 2012-04-05 unless ($self->{build_dir} && -d $self->{build_dir}) { $self->get_file_onto_local_disk; if ($CPAN::Signal){ $self->post_get(); return; } $self->check_integrity; if ($CPAN::Signal){ $self->post_get(); return; } (my $packagedir,$local_file) = $self->run_preps_on_packagedir; # XXX why is this check here? -- xdg, 2012-04-08 if (exists $self->{writemakefile} && ref $self->{writemakefile} && $self->{writemakefile}->can("failed") && $self->{writemakefile}->failed) { # $self->post_get(); return; } $packagedir ||= $self->{build_dir}; $self->{build_dir} = $packagedir; } # XXX should this move up to after run_preps_on_packagedir? # Otherwise, failing writemakefile can return without # a $CPAN::Signal check -- xdg, 2012-04-05 if ($CPAN::Signal) { $self->safe_chdir($sub_wd); $self->post_get(); return; } unless ($self->patch){ $self->post_get(); return; } $self->store_persistent_state; $self->post_get(); return 1; # success } #-> CPAN::Distribution::get_file_onto_local_disk sub get_file_onto_local_disk { my($self) = @_; return if $self->is_dot_dist; my($local_file); my($local_wanted) = File::Spec->catfile( $CPAN::Config->{keep_source_where}, "authors", "id", split(/\//,$self->id) ); $self->debug("Doing localize") if $CPAN::DEBUG; unless ($local_file = CPAN::FTP->localize("authors/id/$self->{ID}", $local_wanted)) { my $note = ""; if ($CPAN::Index::DATE_OF_02) { $note = "Note: Current database in memory was generated ". "on $CPAN::Index::DATE_OF_02\n"; } $CPAN::Frontend->mydie("Giving up on '$local_wanted'\n$note"); } $self->debug("local_wanted[$local_wanted]local_file[$local_file]") if $CPAN::DEBUG; $self->{localfile} = $local_file; } #-> CPAN::Distribution::check_integrity sub check_integrity { my($self) = @_; return if $self->is_dot_dist; if ($CPAN::META->has_inst("Digest::SHA")) { $self->debug("Digest::SHA is installed, verifying"); $self->verifyCHECKSUM; } else { $self->debug("Digest::SHA is NOT installed"); } } #-> CPAN::Distribution::run_preps_on_packagedir sub run_preps_on_packagedir { my($self) = @_; return if $self->is_dot_dist; $CPAN::META->{cachemgr} ||= CPAN::CacheMgr->new(); # unsafe meta access, ok my $builddir = $CPAN::META->{cachemgr}->dir; # unsafe meta access, ok $self->safe_chdir($builddir); $self->debug("Removing tmp-$$") if $CPAN::DEBUG; File::Path::rmtree("tmp-$$"); unless (mkdir "tmp-$$", 0755) { $CPAN::Frontend->unrecoverable_error(<<EOF); Couldn't mkdir '$builddir/tmp-$$': $! Cannot continue: Please find the reason why I cannot make the directory $builddir/tmp-$$ and fix the problem, then retry. EOF } if ($CPAN::Signal) { return; } $self->safe_chdir("tmp-$$"); # # Unpack the goods # my $local_file = $self->{localfile}; my $ct = eval{CPAN::Tarzip->new($local_file)}; unless ($ct) { $self->{unwrapped} = CPAN::Distrostatus->new("NO"); delete $self->{build_dir}; return; } if ($local_file =~ /(\.tar\.(bz2|gz|Z)|\.tgz)(?!\n)\Z/i) { $self->{was_uncompressed}++ unless eval{$ct->gtest()}; $self->untar_me($ct); } elsif ( $local_file =~ /\.zip(?!\n)\Z/i ) { $self->unzip_me($ct); } else { $self->{was_uncompressed}++ unless $ct->gtest(); $local_file = $self->handle_singlefile($local_file); } # we are still in the tmp directory! # Let's check if the package has its own directory. my $dh = DirHandle->new(File::Spec->curdir) or Carp::croak("Couldn't opendir .: $!"); my @readdir = grep $_ !~ /^\.\.?(?!\n)\Z/s, $dh->read; ### MAC?? if (grep { $_ eq "pax_global_header" } @readdir) { $CPAN::Frontend->mywarn("Your (un)tar seems to have extracted a file named 'pax_global_header' from the tarball '$local_file'. This is almost certainly an error. Please upgrade your tar. I'll ignore this file for now. See also http://rt.cpan.org/Ticket/Display.html?id=38932\n"); $CPAN::Frontend->mysleep(5); @readdir = grep { $_ ne "pax_global_header" } @readdir; } $dh->close; my $tdir_base; my $from_dir; my @dirents; if (@readdir == 1 && -d $readdir[0]) { $tdir_base = $readdir[0]; $from_dir = File::Spec->catdir(File::Spec->curdir,$readdir[0]); my($mode) = (stat $from_dir)[2]; chmod $mode | 00755, $from_dir; # JONATHAN/Math-Calculus-TaylorSeries-0.1.tar.gz has 0644 my $dh2; unless ($dh2 = DirHandle->new($from_dir)) { my $why = sprintf ( "Couldn't opendir '%s', mode '%o': %s", $from_dir, $mode, $!, ); $CPAN::Frontend->mywarn("$why\n"); $self->{writemakefile} = CPAN::Distrostatus->new("NO -- $why"); return; } @dirents = grep $_ !~ /^\.\.?(?!\n)\Z/s, $dh2->read; ### MAC?? } else { my $userid = $self->cpan_userid; CPAN->debug("userid[$userid]"); if (!$userid or $userid eq "N/A") { $userid = "anon"; } $tdir_base = $userid; $from_dir = File::Spec->curdir; @dirents = @readdir; } my $packagedir; my $eexist = ($CPAN::META->has_usable("Errno") && defined &Errno::EEXIST) ? &Errno::EEXIST : undef; for(my $suffix = 0; ; $suffix++) { $packagedir = File::Spec->catdir($builddir, "$tdir_base-$suffix"); my $parent = $builddir; mkdir($packagedir, 0777) and last; if((defined($eexist) && $! != $eexist) || $suffix == 999) { $CPAN::Frontend->mydie("Cannot create directory $packagedir: $!\n"); } } my $f; for $f (@dirents) { # is already without "." and ".." my $from = File::Spec->catfile($from_dir,$f); my($mode) = (stat $from)[2]; chmod $mode | 00755, $from if -d $from; # OTTO/Pod-Trial-LinkImg-0.005.tgz my $to = File::Spec->catfile($packagedir,$f); unless (File::Copy::move($from,$to)) { my $err = $!; $from = File::Spec->rel2abs($from); $CPAN::Frontend->mydie( "Couldn't move $from to $to: $err; #82295? ". "CPAN::VERSION=$CPAN::VERSION; ". "File::Copy::VERSION=$File::Copy::VERSION; ". "$from " . (-e $from ? "exists; " : "does not exist; "). "$to " . (-e $to ? "exists; " : "does not exist; "). "cwd=" . CPAN::anycwd() . ";" ); } } $self->{build_dir} = $packagedir; $self->safe_chdir($builddir); File::Path::rmtree("tmp-$$"); $self->safe_chdir($packagedir); $self->_signature_business(); $self->safe_chdir($builddir); return($packagedir,$local_file); } #-> sub CPAN::Distribution::pick_meta_file ; sub pick_meta_file { my($self, $filter) = @_; $filter = '.' unless defined $filter; my $build_dir; unless ($build_dir = $self->{build_dir}) { # maybe permission on build_dir was missing $CPAN::Frontend->mywarn("Warning: cannot determine META.yml without a build_dir.\n"); return; } my $has_cm = $CPAN::META->has_usable("CPAN::Meta"); my $has_pcm = $CPAN::META->has_usable("Parse::CPAN::Meta"); my @choices; push @choices, 'MYMETA.json' if $has_cm; push @choices, 'MYMETA.yml' if $has_cm || $has_pcm; push @choices, 'META.json' if $has_cm; push @choices, 'META.yml' if $has_cm || $has_pcm; for my $file ( grep { /$filter/ } @choices ) { my $path = File::Spec->catfile( $build_dir, $file ); return $path if -f $path } return; } #-> sub CPAN::Distribution::parse_meta_yml ; sub parse_meta_yml { my($self, $yaml) = @_; $self->debug(sprintf("parse_meta_yml[%s]",$yaml||'undef')) if $CPAN::DEBUG; my $build_dir = $self->{build_dir} or die "PANIC: cannot parse yaml without a build_dir"; $yaml ||= File::Spec->catfile($build_dir,"META.yml"); $self->debug("meta[$yaml]") if $CPAN::DEBUG; return unless -f $yaml; my $early_yaml; eval { $CPAN::META->has_inst("Parse::CPAN::Meta") or die; die "Parse::CPAN::Meta yaml too old" unless $Parse::CPAN::Meta::VERSION >= "1.40"; # P::C::M returns last document in scalar context $early_yaml = Parse::CPAN::Meta::LoadFile($yaml); }; unless ($early_yaml) { eval { $early_yaml = CPAN->_yaml_loadfile($yaml)->[0]; }; } $self->debug(sprintf("yaml[%s]", $early_yaml || 'UNDEF')) if $CPAN::DEBUG; $self->debug($early_yaml) if $CPAN::DEBUG && $early_yaml; if (!ref $early_yaml or ref $early_yaml ne "HASH"){ # fix rt.cpan.org #95271 $CPAN::Frontend->mywarn("The content of '$yaml' is not a HASH reference. Cannot use it.\n"); return {}; } return $early_yaml || undef; } #-> sub CPAN::Distribution::satisfy_requires ; # return values: 1 means requirements are satisfied; # and 0 means not satisfied (and maybe queued) sub satisfy_requires { my ($self) = @_; $self->debug("Entering satisfy_requires") if $CPAN::DEBUG; if (my @prereq = $self->unsat_prereq("later")) { if ($CPAN::DEBUG){ require Data::Dumper; my $prereq = Data::Dumper->new(\@prereq)->Terse(1)->Indent(0)->Dump; $self->debug("unsatisfied[$prereq]"); } if ($prereq[0][0] eq "perl") { my $need = "requires perl '$prereq[0][1]'"; my $id = $self->pretty_id; $CPAN::Frontend->mywarn("$id $need; you have only $]; giving up\n"); $self->{make} = CPAN::Distrostatus->new("NO $need"); $self->store_persistent_state; die "[prereq] -- NOT OK\n"; } else { my $follow = eval { $self->follow_prereqs("later",@prereq); }; if (0) { } elsif ($follow) { return; # we need deps } elsif ($@ && ref $@ && $@->isa("CPAN::Exception::RecursiveDependency")) { $CPAN::Frontend->mywarn($@); die "[depend] -- NOT OK\n"; } } } return 1; } #-> sub CPAN::Distribution::satisfy_configure_requires ; # return values: 1 means configure_require is satisfied; # and 0 means not satisfied (and maybe queued) sub satisfy_configure_requires { my($self) = @_; $self->debug("Entering satisfy_configure_requires") if $CPAN::DEBUG; my $enable_configure_requires = 1; if (!$enable_configure_requires) { return 1; # if we return 1 here, everything is as before we introduced # configure_requires that means, things with # configure_requires simply fail, all others succeed } my @prereq = $self->unsat_prereq("configure_requires_later"); $self->debug(sprintf "configure_requires[%s]", join(",",map {join "/",@$_} @prereq)) if $CPAN::DEBUG; return 1 unless @prereq; $self->debug(\@prereq) if $CPAN::DEBUG; if ($self->{configure_requires_later}) { for my $k (sort keys %{$self->{configure_requires_later_for}||{}}) { if ($self->{configure_requires_later_for}{$k}>1) { my $type = ""; for my $p (@prereq) { if ($p->[0] eq $k) { $type = $p->[1]; } } $type = " $type" if $type; $CPAN::Frontend->mywarn("Warning: unmanageable(?) prerequisite $k$type"); sleep 1; } } } if ($prereq[0][0] eq "perl") { my $need = "requires perl '$prereq[0][1]'"; my $id = $self->pretty_id; $CPAN::Frontend->mywarn("$id $need; you have only $]; giving up\n"); $self->{make} = CPAN::Distrostatus->new("NO $need"); $self->store_persistent_state; return $self->goodbye("[prereq] -- NOT OK"); } else { my $follow = eval { $self->follow_prereqs("configure_requires_later", @prereq); }; if (0) { } elsif ($follow) { return; # we need deps } elsif ($@ && ref $@ && $@->isa("CPAN::Exception::RecursiveDependency")) { $CPAN::Frontend->mywarn($@); return $self->goodbye("[depend] -- NOT OK"); } else { return $self->goodbye("[configure_requires] -- NOT OK"); } } die "never reached"; } #-> sub CPAN::Distribution::choose_MM_or_MB ; sub choose_MM_or_MB { my($self) = @_; $self->satisfy_configure_requires() or return; my $local_file = $self->{localfile}; my($mpl) = File::Spec->catfile($self->{build_dir},"Makefile.PL"); my($mpl_exists) = -f $mpl; unless ($mpl_exists) { # NFS has been reported to have racing problems after the # renaming of a directory in some environments. # This trick helps. $CPAN::Frontend->mysleep(1); my $mpldh = DirHandle->new($self->{build_dir}) or Carp::croak("Couldn't opendir $self->{build_dir}: $!"); $mpl_exists = grep /^Makefile\.PL$/, $mpldh->read; $mpldh->close; } my $prefer_installer = "eumm"; # eumm|mb if (-f File::Spec->catfile($self->{build_dir},"Build.PL")) { if ($mpl_exists) { # they *can* choose if ($CPAN::META->has_inst("Module::Build")) { $prefer_installer = CPAN::HandleConfig->prefs_lookup( $self, q{prefer_installer} ); # M::B <= 0.35 left a DATA handle open that # causes problems upgrading M::B on Windows close *Module::Build::Version::DATA if fileno *Module::Build::Version::DATA; } } else { $prefer_installer = "mb"; } } if (lc($prefer_installer) eq "rand") { $prefer_installer = rand()<.5 ? "eumm" : "mb"; } if (lc($prefer_installer) eq "mb") { $self->{modulebuild} = 1; } elsif ($self->{archived} eq "patch") { # not an edge case, nothing to install for sure my $why = "A patch file cannot be installed"; $CPAN::Frontend->mywarn("Refusing to handle this file: $why\n"); $self->{writemakefile} = CPAN::Distrostatus->new("NO $why"); } elsif (! $mpl_exists) { $self->_edge_cases($mpl,$local_file); } if ($self->{build_dir} && $CPAN::Config->{build_dir_reuse} ) { $self->store_persistent_state; } return $self; } # see also reanimate_build_dir #-> CPAN::Distribution::store_persistent_state sub store_persistent_state { my($self) = @_; my $dir = $self->{build_dir}; unless (defined $dir && length $dir) { my $id = $self->id; $CPAN::Frontend->mywarnonce("build_dir of $id is not known, ". "will not store persistent state\n"); return; } # self-build-dir my $sbd = Cwd::realpath( File::Spec->catdir($dir, File::Spec->updir ()) ); # config-build-dir my $cbd = Cwd::realpath( # the catdir is a workaround for bug https://rt.cpan.org/Ticket/Display.html?id=101283 File::Spec->catdir($CPAN::Config->{build_dir}, File::Spec->curdir()) ); unless ($sbd eq $cbd) { $CPAN::Frontend->mywarnonce("Directory '$dir' not below $CPAN::Config->{build_dir}, ". "will not store persistent state\n"); return; } my $file = sprintf "%s.yml", $dir; my $yaml_module = CPAN::_yaml_module(); if ($CPAN::META->has_inst($yaml_module)) { CPAN->_yaml_dumpfile( $file, { time => time, perl => CPAN::_perl_fingerprint(), distribution => $self, } ); } else { $CPAN::Frontend->myprintonce("'$yaml_module' not installed, ". "will not store persistent state\n"); } } #-> CPAN::Distribution::try_download sub try_download { my($self,$patch) = @_; my $norm = $self->normalize($patch); my($local_wanted) = File::Spec->catfile( $CPAN::Config->{keep_source_where}, "authors", "id", split(/\//,$norm), ); $self->debug("Doing localize") if $CPAN::DEBUG; return CPAN::FTP->localize("authors/id/$norm", $local_wanted); } { my $stdpatchargs = ""; #-> CPAN::Distribution::patch sub patch { my($self) = @_; $self->debug("checking patches id[$self->{ID}]") if $CPAN::DEBUG; my $patches = $self->prefs->{patches}; $patches ||= ""; $self->debug("patches[$patches]") if $CPAN::DEBUG; if ($patches) { return unless @$patches; $self->safe_chdir($self->{build_dir}); CPAN->debug("patches[$patches]") if $CPAN::DEBUG; my $patchbin = $CPAN::Config->{patch}; unless ($patchbin && length $patchbin) { $CPAN::Frontend->mydie("No external patch command configured\n\n". "Please run 'o conf init /patch/'\n\n"); } unless (MM->maybe_command($patchbin)) { $CPAN::Frontend->mydie("No external patch command available\n\n". "Please run 'o conf init /patch/'\n\n"); } $patchbin = CPAN::HandleConfig->safe_quote($patchbin); local $ENV{PATCH_GET} = 0; # formerly known as -g0 unless ($stdpatchargs) { my $system = "$patchbin --version |"; local *FH; open FH, $system or die "Could not fork '$system': $!"; local $/ = "\n"; my $pversion; PARSEVERSION: while (<FH>) { if (/^patch\s+([\d\.]+)/) { $pversion = $1; last PARSEVERSION; } } if ($pversion) { $stdpatchargs = "-N --fuzz=3"; } else { $stdpatchargs = "-N"; } } my $countedpatches = @$patches == 1 ? "1 patch" : (scalar @$patches . " patches"); $CPAN::Frontend->myprint("Applying $countedpatches:\n"); my $patches_dir = $CPAN::Config->{patches_dir}; for my $patch (@$patches) { if ($patches_dir && !File::Spec->file_name_is_absolute($patch)) { my $f = File::Spec->catfile($patches_dir, $patch); $patch = $f if -f $f; } unless (-f $patch) { CPAN->debug("not on disk: patch[$patch]") if $CPAN::DEBUG; if (my $trydl = $self->try_download($patch)) { $patch = $trydl; } else { my $fail = "Could not find patch '$patch'"; $CPAN::Frontend->mywarn("$fail; cannot continue\n"); $self->{unwrapped} = CPAN::Distrostatus->new("NO -- $fail"); delete $self->{build_dir}; return; } } $CPAN::Frontend->myprint(" $patch\n"); my $readfh = CPAN::Tarzip->TIEHANDLE($patch); my $pcommand; my($ppp,$pfiles) = $self->_patch_p_parameter($readfh); if ($ppp eq "applypatch") { $pcommand = "$CPAN::Config->{applypatch} -verbose"; } else { my $thispatchargs = join " ", $stdpatchargs, $ppp; $pcommand = "$patchbin $thispatchargs"; require Config; # usually loaded from CPAN.pm if ($Config::Config{osname} eq "solaris") { # native solaris patch cannot patch readonly files for my $file (@{$pfiles||[]}) { my @stat = stat $file or next; chmod $stat[2] | 0600, $file; # may fail } } } $readfh = CPAN::Tarzip->TIEHANDLE($patch); # open again my $writefh = FileHandle->new; $CPAN::Frontend->myprint(" $pcommand\n"); unless (open $writefh, "|$pcommand") { my $fail = "Could not fork '$pcommand'"; $CPAN::Frontend->mywarn("$fail; cannot continue\n"); $self->{unwrapped} = CPAN::Distrostatus->new("NO -- $fail"); delete $self->{build_dir}; return; } binmode($writefh); while (my $x = $readfh->READLINE) { print $writefh $x; } unless (close $writefh) { my $fail = "Could not apply patch '$patch'"; $CPAN::Frontend->mywarn("$fail; cannot continue\n"); $self->{unwrapped} = CPAN::Distrostatus->new("NO -- $fail"); delete $self->{build_dir}; return; } } $self->{patched}++; } return 1; } } # may return # - "applypatch" # - ("-p0"|"-p1", $files) sub _patch_p_parameter { my($self,$fh) = @_; my $cnt_files = 0; my $cnt_p0files = 0; my @files; local($_); while ($_ = $fh->READLINE) { if ( $CPAN::Config->{applypatch} && /\#\#\#\# ApplyPatch data follows \#\#\#\#/ ) { return "applypatch" } next unless /^[\*\+]{3}\s(\S+)/; my $file = $1; push @files, $file; $cnt_files++; $cnt_p0files++ if -f $file; CPAN->debug("file[$file]cnt_files[$cnt_files]cnt_p0files[$cnt_p0files]") if $CPAN::DEBUG; } return "-p1" unless $cnt_files; my $opt_p = $cnt_files==$cnt_p0files ? "-p0" : "-p1"; return ($opt_p, \@files); } #-> sub CPAN::Distribution::_edge_cases # with "configure" or "Makefile" or single file scripts sub _edge_cases { my($self,$mpl,$local_file) = @_; $self->debug(sprintf("makefilepl[%s]anycwd[%s]", $mpl, CPAN::anycwd(), )) if $CPAN::DEBUG; my $build_dir = $self->{build_dir}; my($configure) = File::Spec->catfile($build_dir,"Configure"); if (-f $configure) { # do we have anything to do? $self->{configure} = $configure; } elsif (-f File::Spec->catfile($build_dir,"Makefile")) { $CPAN::Frontend->mywarn(qq{ Package comes with a Makefile and without a Makefile.PL. We\'ll try to build it with that Makefile then. }); $self->{writemakefile} = CPAN::Distrostatus->new("YES"); $CPAN::Frontend->mysleep(2); } else { my $cf = $self->called_for || "unknown"; if ($cf =~ m|/|) { $cf =~ s|.*/||; $cf =~ s|\W.*||; } $cf =~ s|[/\\:]||g; # risk of filesystem damage $cf = "unknown" unless length($cf); if (my $crud = $self->_contains_crud($build_dir)) { my $why = qq{Package contains $crud; not recognized as a perl package, giving up}; $CPAN::Frontend->mywarn("$why\n"); $self->{writemakefile} = CPAN::Distrostatus->new(qq{NO -- $why}); return; } $CPAN::Frontend->mywarn(qq{Package seems to come without Makefile.PL. (The test -f "$mpl" returned false.) Writing one on our own (setting NAME to $cf)\a\n}); $self->{had_no_makefile_pl}++; $CPAN::Frontend->mysleep(3); # Writing our own Makefile.PL my $exefile_stanza = ""; if ($self->{archived} eq "maybe_pl") { $exefile_stanza = $self->_exefile_stanza($build_dir,$local_file); } my $fh = FileHandle->new; $fh->open(">$mpl") or Carp::croak("Could not open >$mpl: $!"); $fh->print( qq{# This Makefile.PL has been autogenerated by the module CPAN.pm # because there was no Makefile.PL supplied. # Autogenerated on: }.scalar localtime().qq{ use ExtUtils::MakeMaker; WriteMakefile( NAME => q[$cf],$exefile_stanza ); }); $fh->close; } } #-> CPAN;:Distribution::_contains_crud sub _contains_crud { my($self,$dir) = @_; my(@dirs, $dh, @files); opendir $dh, $dir or return; my $dirent; for $dirent (readdir $dh) { next if $dirent =~ /^\.\.?$/; my $path = File::Spec->catdir($dir,$dirent); if (-d $path) { push @dirs, $dirent; } elsif (-f $path) { push @files, $dirent; } } if (@dirs && @files) { return "both files[@files] and directories[@dirs]"; } elsif (@files > 2) { return "several files[@files] but no Makefile.PL or Build.PL"; } return; } #-> CPAN;:Distribution::_exefile_stanza sub _exefile_stanza { my($self,$build_dir,$local_file) = @_; my $fh = FileHandle->new; my $script_file = File::Spec->catfile($build_dir,$local_file); $fh->open($script_file) or Carp::croak("Could not open script '$script_file': $!"); local $/ = "\n"; # parse name and prereq my($state) = "poddir"; my($name, $prereq) = ("", ""); while (<$fh>) { if ($state eq "poddir" && /^=head\d\s+(\S+)/) { if ($1 eq 'NAME') { $state = "name"; } elsif ($1 eq 'PREREQUISITES') { $state = "prereq"; } } elsif ($state =~ m{^(name|prereq)$}) { if (/^=/) { $state = "poddir"; } elsif (/^\s*$/) { # nop } elsif ($state eq "name") { if ($name eq "") { ($name) = /^(\S+)/; $state = "poddir"; } } elsif ($state eq "prereq") { $prereq .= $_; } } elsif (/^=cut\b/) { last; } } $fh->close; for ($name) { s{.*<}{}; # strip X<...> s{>.*}{}; } chomp $prereq; $prereq = join " ", split /\s+/, $prereq; my($PREREQ_PM) = join("\n", map { s{.*<}{}; # strip X<...> s{>.*}{}; if (/[\s\'\"]/) { # prose? } else { s/[^\w:]$//; # period? " "x28 . "'$_' => 0,"; } } split /\s*,\s*/, $prereq); if ($name) { my $to_file = File::Spec->catfile($build_dir, $name); rename $script_file, $to_file or die "Can't rename $script_file to $to_file: $!"; } return " EXE_FILES => ['$name'], PREREQ_PM => { $PREREQ_PM }, "; } #-> CPAN::Distribution::_signature_business sub _signature_business { my($self) = @_; my $check_sigs = CPAN::HandleConfig->prefs_lookup($self, q{check_sigs}); if ($check_sigs) { if ($CPAN::META->has_inst("Module::Signature")) { if (-f "SIGNATURE") { $self->debug("Module::Signature is installed, verifying") if $CPAN::DEBUG; my $rv = Module::Signature::verify(); if ($rv != Module::Signature::SIGNATURE_OK() and $rv != Module::Signature::SIGNATURE_MISSING()) { $CPAN::Frontend->mywarn( qq{\nSignature invalid for }. qq{distribution file. }. qq{Please investigate.\n\n} ); my $wrap = sprintf(qq{I'd recommend removing %s. Some error occurred }. qq{while checking its signature, so it could }. qq{be invalid. Maybe you have configured }. qq{your 'urllist' with a bad URL. Please check this }. qq{array with 'o conf urllist' and retry. Or }. qq{examine the distribution in a subshell. Try look %s and run cpansign -v }, $self->{localfile}, $self->pretty_id, ); $self->{signature_verify} = CPAN::Distrostatus->new("NO"); $CPAN::Frontend->mywarn(Text::Wrap::wrap("","",$wrap)); $CPAN::Frontend->mysleep(5) if $CPAN::Frontend->can("mysleep"); } else { $self->{signature_verify} = CPAN::Distrostatus->new("YES"); $self->debug("Module::Signature has verified") if $CPAN::DEBUG; } } else { $CPAN::Frontend->mywarn(qq{Package came without SIGNATURE\n\n}); } } else { $self->debug("Module::Signature is NOT installed") if $CPAN::DEBUG; } } } #-> CPAN::Distribution::untar_me ; sub untar_me { my($self,$ct) = @_; $self->{archived} = "tar"; my $result = eval { $ct->untar() }; if ($result) { $self->{unwrapped} = CPAN::Distrostatus->new("YES"); } else { # unfortunately we have no $@ here, Tarzip is using mydie which dies with "\n" $self->{unwrapped} = CPAN::Distrostatus->new("NO -- untar failed"); } } # CPAN::Distribution::unzip_me ; sub unzip_me { my($self,$ct) = @_; $self->{archived} = "zip"; if (eval { $ct->unzip() }) { $self->{unwrapped} = CPAN::Distrostatus->new("YES"); } else { $self->{unwrapped} = CPAN::Distrostatus->new("NO -- unzip failed during unzip"); } return; } sub handle_singlefile { my($self,$local_file) = @_; if ( $local_file =~ /\.pm(\.(gz|Z))?(?!\n)\Z/ ) { $self->{archived} = "pm"; } elsif ( $local_file =~ /\.patch(\.(gz|bz2))?(?!\n)\Z/ ) { $self->{archived} = "patch"; } else { $self->{archived} = "maybe_pl"; } my $to = File::Basename::basename($local_file); if ($to =~ s/\.(gz|Z)(?!\n)\Z//) { if (eval{CPAN::Tarzip->new($local_file)->gunzip($to)}) { $self->{unwrapped} = CPAN::Distrostatus->new("YES"); } else { $self->{unwrapped} = CPAN::Distrostatus->new("NO -- uncompressing failed"); } } else { if (File::Copy::cp($local_file,".")) { $self->{unwrapped} = CPAN::Distrostatus->new("YES"); } else { $self->{unwrapped} = CPAN::Distrostatus->new("NO -- copying failed"); } } return $to; } #-> sub CPAN::Distribution::new ; sub new { my($class,%att) = @_; # $CPAN::META->{cachemgr} ||= CPAN::CacheMgr->new(); my $this = { %att }; return bless $this, $class; } #-> sub CPAN::Distribution::look ; sub look { my($self) = @_; if ($^O eq 'MacOS') { $self->Mac::BuildTools::look; return; } if ( $CPAN::Config->{'shell'} ) { $CPAN::Frontend->myprint(qq{ Trying to open a subshell in the build directory... }); } else { $CPAN::Frontend->myprint(qq{ Your configuration does not define a value for subshells. Please define it with "o conf shell <your shell>" }); return; } my $dist = $self->id; my $dir; unless ($dir = $self->dir) { $self->get; } unless ($dir ||= $self->dir) { $CPAN::Frontend->mywarn(qq{ Could not determine which directory to use for looking at $dist. }); return; } my $pwd = CPAN::anycwd(); $self->safe_chdir($dir); $CPAN::Frontend->myprint(qq{Working directory is $dir\n}); { local $ENV{CPAN_SHELL_LEVEL} = $ENV{CPAN_SHELL_LEVEL}||0; $ENV{CPAN_SHELL_LEVEL} += 1; my $shell = CPAN::HandleConfig->safe_quote($CPAN::Config->{'shell'}); local $ENV{PERL5LIB} = defined($ENV{PERL5LIB}) ? $ENV{PERL5LIB} : ($ENV{PERLLIB} || ""); local $ENV{PERL5OPT} = defined $ENV{PERL5OPT} ? $ENV{PERL5OPT} : ""; # local $ENV{PERL_USE_UNSAFE_INC} = exists $ENV{PERL_USE_UNSAFE_INC} ? $ENV{PERL_USE_UNSAFE_INC} : 1; # look $CPAN::META->set_perl5lib; local $ENV{MAKEFLAGS}; # protect us from outer make calls unless (system($shell) == 0) { my $code = $? >> 8; $CPAN::Frontend->mywarn("Subprocess shell exit code $code\n"); } } $self->safe_chdir($pwd); } # CPAN::Distribution::cvs_import ; sub cvs_import { my($self) = @_; $self->get; my $dir = $self->dir; my $package = $self->called_for; my $module = $CPAN::META->instance('CPAN::Module', $package); my $version = $module->cpan_version; my $userid = $self->cpan_userid; my $cvs_dir = (split /\//, $dir)[-1]; $cvs_dir =~ s/-\d+[^-]+(?!\n)\Z//; my $cvs_root = $CPAN::Config->{cvsroot} || $ENV{CVSROOT}; my $cvs_site_perl = $CPAN::Config->{cvs_site_perl} || $ENV{CVS_SITE_PERL}; if ($cvs_site_perl) { $cvs_dir = "$cvs_site_perl/$cvs_dir"; } my $cvs_log = qq{"imported $package $version sources"}; $version =~ s/\./_/g; # XXX cvs: undocumented and unclear how it was meant to work my @cmd = ('cvs', '-d', $cvs_root, 'import', '-m', $cvs_log, "$cvs_dir", $userid, "v$version"); my $pwd = CPAN::anycwd(); chdir($dir) or $CPAN::Frontend->mydie(qq{Could not chdir to "$dir": $!}); $CPAN::Frontend->myprint(qq{Working directory is $dir\n}); $CPAN::Frontend->myprint(qq{@cmd\n}); system(@cmd) == 0 or # XXX cvs $CPAN::Frontend->mydie("cvs import failed"); chdir($pwd) or $CPAN::Frontend->mydie(qq{Could not chdir to "$pwd": $!}); } #-> sub CPAN::Distribution::readme ; sub readme { my($self) = @_; my($dist) = $self->id; my($sans,$suffix) = $dist =~ /(.+)\.(tgz|tar[\._-]gz|tar\.Z|zip)$/; $self->debug("sans[$sans] suffix[$suffix]\n") if $CPAN::DEBUG; my($local_file); my($local_wanted) = File::Spec->catfile( $CPAN::Config->{keep_source_where}, "authors", "id", split(/\//,"$sans.readme"), ); my $readme = "authors/id/$sans.readme"; $self->debug("Doing localize for '$readme'") if $CPAN::DEBUG; $local_file = CPAN::FTP->localize($readme, $local_wanted) or $CPAN::Frontend->mydie(qq{No $sans.readme found}); if ($^O eq 'MacOS') { Mac::BuildTools::launch_file($local_file); return; } my $fh_pager = FileHandle->new; local($SIG{PIPE}) = "IGNORE"; my $pager = $CPAN::Config->{'pager'} || "cat"; $fh_pager->open("|$pager") or die "Could not open pager $pager\: $!"; my $fh_readme = FileHandle->new; $fh_readme->open($local_file) or $CPAN::Frontend->mydie(qq{Could not open "$local_file": $!}); $CPAN::Frontend->myprint(qq{ Displaying file $local_file with pager "$pager" }); $fh_pager->print(<$fh_readme>); $fh_pager->close; } #-> sub CPAN::Distribution::verifyCHECKSUM ; sub verifyCHECKSUM { my($self) = @_; EXCUSE: { my @e; $self->{CHECKSUM_STATUS} ||= ""; $self->{CHECKSUM_STATUS} eq "OK" and push @e, "Checksum was ok"; $CPAN::Frontend->myprint(join "", map {" $_\n"} @e) and return if @e; } my($lc_want,$lc_file,@local,$basename); @local = split(/\//,$self->id); pop @local; push @local, "CHECKSUMS"; $lc_want = File::Spec->catfile($CPAN::Config->{keep_source_where}, "authors", "id", @local); local($") = "/"; if (my $size = -s $lc_want) { $self->debug("lc_want[$lc_want]size[$size]") if $CPAN::DEBUG; my @stat = stat $lc_want; my $epoch_starting_support_of_cpan_path = 1637471530; if ($stat[9] >= $epoch_starting_support_of_cpan_path) { if ($self->CHECKSUM_check_file($lc_want, 1)) { return $self->{CHECKSUM_STATUS} = "OK"; } } else { unlink $lc_want; } } $lc_file = CPAN::FTP->localize("authors/id/@local", $lc_want,1); unless ($lc_file) { $CPAN::Frontend->myprint("Trying $lc_want.gz\n"); $local[-1] .= ".gz"; $lc_file = CPAN::FTP->localize("authors/id/@local", "$lc_want.gz",1); if ($lc_file) { $lc_file =~ s/\.gz(?!\n)\Z//; eval{CPAN::Tarzip->new("$lc_file.gz")->gunzip($lc_file)}; } else { return; } } if ($self->CHECKSUM_check_file($lc_file)) { return $self->{CHECKSUM_STATUS} = "OK"; } } #-> sub CPAN::Distribution::SIG_check_file ; sub SIG_check_file { my($self,$chk_file) = @_; my $rv = eval { Module::Signature::_verify($chk_file) }; if ($rv eq Module::Signature::CANNOT_VERIFY()) { $CPAN::Frontend->myprint(qq{\nSignature for }. qq{file $chk_file could not be verified for an unknown reason. }. $self->as_string. qq{Module::Signature verification returned value $rv\n\n} ); my $wrap = qq{The manual says for this case: Cannot verify the OpenPGP signature, maybe due to the lack of a network connection to the key server, or if neither gnupg nor Crypt::OpenPGP exists on the system. You probably want to analyse the situation and if you cannot fix it you will have to decide whether you want to stop this session or you want to turn off signature verification. The latter would be done with the command 'o conf init check_sigs'}; $CPAN::Frontend->mydie(Text::Wrap::wrap("","",$wrap)); } if ($rv == Module::Signature::SIGNATURE_OK()) { $CPAN::Frontend->myprint("Signature for $chk_file ok\n"); return $self->{SIG_STATUS} = "OK"; } else { $CPAN::Frontend->mywarn(qq{\nSignature invalid for }. qq{file $chk_file. }. qq{Please investigate.\n\n}. $self->as_string. qq{Module::Signature verification returned value $rv\n\n} ); my $wrap = qq{I\'d recommend removing $chk_file. Its signature is invalid. Maybe you have configured your 'urllist' with a bad URL. Please check this array with 'o conf urllist', and retry.}; $CPAN::Frontend->mydie(Text::Wrap::wrap("","",$wrap)); } } #-> sub CPAN::Distribution::CHECKSUM_check_file ; # sloppy is 1 when we have an old checksums file that maybe is good # enough sub CHECKSUM_check_file { my($self,$chk_file,$sloppy) = @_; my($cksum,$file,$basename); $sloppy ||= 0; $self->debug("chk_file[$chk_file]sloppy[$sloppy]") if $CPAN::DEBUG; my $check_sigs = CPAN::HandleConfig->prefs_lookup($self, q{check_sigs}); if ($check_sigs) { if ($CPAN::META->has_inst("Module::Signature")) { $self->debug("Module::Signature is installed, verifying") if $CPAN::DEBUG; $self->SIG_check_file($chk_file); } else { $self->debug("Module::Signature is NOT installed") if $CPAN::DEBUG; } } $file = $self->{localfile}; $basename = File::Basename::basename($file); my($signed_data); my $fh = FileHandle->new; if ($check_sigs) { my $tempdir; if ($CPAN::META->has_usable("File::Temp")) { $tempdir = File::Temp::tempdir("CHECKSUMS-XXXX", CLEANUP => 1, DIR => "/tmp" ); } else { $tempdir = File::Spec->catdir(File::Spec->tmpdir, "CHECKSUMS-$$"); File::Path::mkpath($tempdir); } my $tempfile = File::Spec->catfile($tempdir, "CHECKSUMS.$$"); unlink $tempfile; # ignore missing file my $devnull = File::Spec->devnull; my $gpg = $CPAN::Config->{gpg} or $CPAN::Frontend->mydie("Your configuration suggests that you do not have 'gpg' installed. This is needed to verify checksums with the config variable 'check_sigs' on. Please configure it with 'o conf init gpg'"); my $system = qq{"$gpg" --verify --batch --no-tty --output "$tempfile" "$chk_file" 2> "$devnull"}; 0 == system $system or $CPAN::Frontend->mydie("gpg run was failing, cannot continue: $system"); open $fh, $tempfile or $CPAN::Frontend->mydie("Could not open $tempfile: $!"); local $/; $signed_data = <$fh>; close $fh; File::Path::rmtree($tempdir); } else { my $fh = FileHandle->new; if (open $fh, $chk_file) { local($/); $signed_data = <$fh>; } else { $CPAN::Frontend->mydie("Could not open $chk_file for reading"); } close $fh; } $signed_data =~ s/\015?\012/\n/g; my($compmt) = Safe->new(); $cksum = $compmt->reval($signed_data); if ($@) { rename $chk_file, "$chk_file.bad"; Carp::confess($@) if $@; } if (! ref $cksum or ref $cksum ne "HASH") { $CPAN::Frontend->mywarn(qq{ Warning: checksum file '$chk_file' broken. When trying to read that file I expected to get a hash reference for further processing, but got garbage instead. }); my $answer = CPAN::Shell::colorable_makemaker_prompt("Proceed nonetheless?", "no"); $answer =~ /^\s*y/i or $CPAN::Frontend->mydie("Aborted.\n"); $self->{CHECKSUM_STATUS} = "NIL -- CHECKSUMS file broken"; return; } elsif (exists $cksum->{$basename} && ! exists $cksum->{$basename}{cpan_path}) { $CPAN::Frontend->mywarn(qq{ Warning: checksum file '$chk_file' not conforming. The cksum does not contain the key 'cpan_path' for '$basename'. }); my $answer = CPAN::Shell::colorable_makemaker_prompt("Proceed nonetheless?", "no"); $answer =~ /^\s*y/i or $CPAN::Frontend->mydie("Aborted.\n"); $self->{CHECKSUM_STATUS} = "NIL -- CHECKSUMS file without cpan_path"; return; } elsif (exists $cksum->{$basename} && substr($self->{ID},0,length($cksum->{$basename}{cpan_path})) ne $cksum->{$basename}{cpan_path}) { $CPAN::Frontend->mywarn(qq{ Warning: checksum file not matching path '$self->{ID}'. The cksum contain the key 'cpan_path=$cksum->{$basename}{cpan_path}' which does not match the ID of the distribution '$self->{ID}'. Something's suspicious might be going on here. Please investigate. }); my $answer = CPAN::Shell::colorable_makemaker_prompt("Proceed nonetheless?", "no"); $answer =~ /^\s*y/i or $CPAN::Frontend->mydie("Aborted.\n"); $self->{CHECKSUM_STATUS} = "NIL -- CHECKSUMS non-matching cpan_path vs. ID"; return; } elsif (exists $cksum->{$basename}{sha256}) { $self->debug("Found checksum for $basename:" . "$cksum->{$basename}{sha256}\n") if $CPAN::DEBUG; open($fh, $file); binmode $fh; my $eq = $self->eq_CHECKSUM($fh,$cksum->{$basename}{sha256}); $fh->close; $fh = CPAN::Tarzip->TIEHANDLE($file); unless ($eq) { my $dg = Digest::SHA->new(256); my($data,$ref); $ref = \$data; while ($fh->READ($ref, 4096) > 0) { $dg->add($data); } my $hexdigest = $dg->hexdigest; $eq += $hexdigest eq $cksum->{$basename}{'sha256-ungz'}; } if ($eq) { $CPAN::Frontend->myprint("Checksum for $file ok\n"); return $self->{CHECKSUM_STATUS} = "OK"; } else { $CPAN::Frontend->myprint(qq{\nChecksum mismatch for }. qq{distribution file. }. qq{Please investigate.\n\n}. $self->as_string, $CPAN::META->instance( 'CPAN::Author', $self->cpan_userid )->as_string); my $wrap = qq{I\'d recommend removing $file. Its checksum is incorrect. Maybe you have configured your 'urllist' with a bad URL. Please check this array with 'o conf urllist', and retry.}; $CPAN::Frontend->mydie(Text::Wrap::wrap("","",$wrap)); # former versions just returned here but this seems a # serious threat that deserves a die # $CPAN::Frontend->myprint("\n\n"); # sleep 3; # return; } # close $fh if fileno($fh); } else { return if $sloppy; unless ($self->{CHECKSUM_STATUS}) { $CPAN::Frontend->mywarn(qq{ Warning: No checksum for $basename in $chk_file. The cause for this may be that the file is very new and the checksum has not yet been calculated, but it may also be that something is going awry right now. }); my $answer = CPAN::Shell::colorable_makemaker_prompt("Proceed?", "yes"); $answer =~ /^\s*y/i or $CPAN::Frontend->mydie("Aborted.\n"); } $self->{CHECKSUM_STATUS} = "NIL -- distro not in CHECKSUMS file"; return; } } #-> sub CPAN::Distribution::eq_CHECKSUM ; sub eq_CHECKSUM { my($self,$fh,$expect) = @_; if ($CPAN::META->has_inst("Digest::SHA")) { my $dg = Digest::SHA->new(256); my($data); while (read($fh, $data, 4096)) { $dg->add($data); } my $hexdigest = $dg->hexdigest; # warn "fh[$fh] hex[$hexdigest] aexp[$expectMD5]"; return $hexdigest eq $expect; } return 1; } #-> sub CPAN::Distribution::force ; # Both CPAN::Modules and CPAN::Distributions know if "force" is in # effect by autoinspection, not by inspecting a global variable. One # of the reason why this was chosen to work that way was the treatment # of dependencies. They should not automatically inherit the force # status. But this has the downside that ^C and die() will return to # the prompt but will not be able to reset the force_update # attributes. We try to correct for it currently in the read_metadata # routine, and immediately before we check for a Signal. I hope this # works out in one of v1.57_53ff # "Force get forgets previous error conditions" #-> sub CPAN::Distribution::fforce ; sub fforce { my($self, $method) = @_; $self->force($method,1); } #-> sub CPAN::Distribution::force ; sub force { my($self, $method,$fforce) = @_; my %phase_map = ( get => [ "unwrapped", "build_dir", "archived", "localfile", "CHECKSUM_STATUS", "signature_verify", "prefs", "prefs_file", "prefs_file_doc", "cleanup_after_install_done", ], make => [ "writemakefile", "make", "modulebuild", "prereq_pm", "cleanup_after_install_done", ], test => [ "badtestcnt", "make_test", "cleanup_after_install_done", ], install => [ "install", "cleanup_after_install_done", ], unknown => [ "reqtype", "yaml_content", "cleanup_after_install_done", ], ); my $methodmatch = 0; my $ldebug = 0; PHASE: for my $phase (qw(unknown get make test install)) { # order matters $methodmatch = 1 if $fforce || ($method && $phase eq $method); next unless $methodmatch; ATTRIBUTE: for my $att (@{$phase_map{$phase}}) { if ($phase eq "get") { if (substr($self->id,-1,1) eq "." && $att =~ /(unwrapped|build_dir|archived)/ ) { # cannot be undone for local distros next ATTRIBUTE; } if ($att eq "build_dir" && $self->{build_dir} && $CPAN::META->{is_tested} ) { delete $CPAN::META->{is_tested}{$self->{build_dir}}; } } elsif ($phase eq "test") { if ($att eq "make_test" && $self->{make_test} && $self->{make_test}{COMMANDID} && $self->{make_test}{COMMANDID} == $CPAN::CurrentCommandId ) { # endless loop too likely next ATTRIBUTE; } } delete $self->{$att}; if ($ldebug || $CPAN::DEBUG) { # local $CPAN::DEBUG = 16; # Distribution CPAN->debug(sprintf "id[%s]phase[%s]att[%s]", $self->id, $phase, $att); } } } if ($method && $method =~ /make|test|install/) { $self->{force_update} = 1; # name should probably have been force_install } } #-> sub CPAN::Distribution::notest ; sub notest { my($self, $method) = @_; # $CPAN::Frontend->mywarn("XDEBUG: set notest for $self $method"); $self->{"notest"}++; # name should probably have been force_install } #-> sub CPAN::Distribution::unnotest ; sub unnotest { my($self) = @_; # warn "XDEBUG: deleting notest"; delete $self->{notest}; } #-> sub CPAN::Distribution::unforce ; sub unforce { my($self) = @_; delete $self->{force_update}; } #-> sub CPAN::Distribution::isa_perl ; sub isa_perl { my($self) = @_; my $file = File::Basename::basename($self->id); if ($file =~ m{ ^ perl ( -(5\.\d+\.\d+) | (5)[._-](00[0-5](?:_[0-4][0-9])?) ) \.tar[._-](?:gz|bz2) (?!\n)\Z }xs) { my $perl_version; if ($2) { $perl_version = $2; } else { $perl_version = "$3.$4"; } return $perl_version; } elsif ($self->cpan_comment && $self->cpan_comment =~ /isa_perl\(.+?\)/) { return $1; } } #-> sub CPAN::Distribution::perl ; sub perl { my ($self) = @_; if (! $self) { use Carp qw(carp); carp __PACKAGE__ . "::perl was called without parameters."; } return CPAN::HandleConfig->safe_quote($CPAN::Perl); } #-> sub CPAN::Distribution::shortcut_prepare ; # return values: undef means don't shortcut; 0 means shortcut as fail; # and 1 means shortcut as success sub shortcut_prepare { my ($self) = @_; $self->debug("checking archive type[$self->{ID}]") if $CPAN::DEBUG; if (!$self->{archived} || $self->{archived} eq "NO") { return $self->goodbye("Is neither a tar nor a zip archive."); } $self->debug("checking unwrapping[$self->{ID}]") if $CPAN::DEBUG; if (!$self->{unwrapped} || ( UNIVERSAL::can($self->{unwrapped},"failed") ? $self->{unwrapped}->failed : $self->{unwrapped} =~ /^NO/ )) { return $self->goodbye("Had problems unarchiving. Please build manually"); } $self->debug("checking signature[$self->{ID}]") if $CPAN::DEBUG; if ( ! $self->{force_update} && exists $self->{signature_verify} && ( UNIVERSAL::can($self->{signature_verify},"failed") ? $self->{signature_verify}->failed : $self->{signature_verify} =~ /^NO/ ) ) { return $self->goodbye("Did not pass the signature test."); } $self->debug("checking writemakefile[$self->{ID}]") if $CPAN::DEBUG; if ($self->{writemakefile}) { if ( UNIVERSAL::can($self->{writemakefile},"failed") ? $self->{writemakefile}->failed : $self->{writemakefile} =~ /^NO/ ) { # XXX maybe a retry would be in order? my $err = UNIVERSAL::can($self->{writemakefile},"text") ? $self->{writemakefile}->text : $self->{writemakefile}; $err =~ s/^NO\s*(--\s+)?//; $err ||= "Had some problem writing Makefile"; $err .= ", not re-running"; return $self->goodbye($err); } else { return $self->success("Has already been prepared"); } } $self->debug("checking configure_requires_later[$self->{ID}]") if $CPAN::DEBUG; if( my $later = $self->{configure_requires_later} ) { # see also undelay return $self->goodbye($later); } return undef; # no shortcut } sub prepare { my ($self) = @_; $self->get or return; if ( defined( my $sc = $self->shortcut_prepare) ) { return $sc; } local $ENV{PERL5LIB} = defined($ENV{PERL5LIB}) ? $ENV{PERL5LIB} : ($ENV{PERLLIB} || ""); local $ENV{PERL5OPT} = defined $ENV{PERL5OPT} ? $ENV{PERL5OPT} : ""; local $ENV{PERL_USE_UNSAFE_INC} = exists $ENV{PERL_USE_UNSAFE_INC} && defined $ENV{PERL_USE_UNSAFE_INC} ? $ENV{PERL_USE_UNSAFE_INC} : 1; # prepare $CPAN::META->set_perl5lib; local $ENV{MAKEFLAGS}; # protect us from outer make calls if ($CPAN::Signal) { delete $self->{force_update}; return; } my $builddir = $self->dir or $CPAN::Frontend->mydie("PANIC: Cannot determine build directory\n"); unless (chdir $builddir) { $CPAN::Frontend->mywarn("Couldn't chdir to '$builddir': $!"); return; } if ($CPAN::Signal) { delete $self->{force_update}; return; } $self->debug("Changed directory to $builddir") if $CPAN::DEBUG; local $ENV{PERL_AUTOINSTALL} = $ENV{PERL_AUTOINSTALL} || ''; local $ENV{PERL_EXTUTILS_AUTOINSTALL} = $ENV{PERL_EXTUTILS_AUTOINSTALL} || ''; $self->choose_MM_or_MB or return; my $configurator = $self->{configure} ? "Configure" : $self->{modulebuild} ? "Build.PL" : "Makefile.PL"; $CPAN::Frontend->myprint("Configuring ".$self->id." with $configurator\n"); if ($CPAN::Config->{prerequisites_policy} eq "follow") { $ENV{PERL_AUTOINSTALL} ||= "--defaultdeps"; $ENV{PERL_EXTUTILS_AUTOINSTALL} ||= "--defaultdeps"; } my $system; my $pl_commandline; if ($self->prefs->{pl}) { $pl_commandline = $self->prefs->{pl}{commandline}; } local $ENV{PERL} = defined $ENV{PERL}? $ENV{PERL} : $^X; local $ENV{PERL5_CPAN_IS_EXECUTING} = $ENV{PERL5_CPAN_IS_EXECUTING} || ''; local $ENV{PERL_MM_USE_DEFAULT} = 1 if $CPAN::Config->{use_prompt_default}; local $ENV{NONINTERACTIVE_TESTING} = 1 if $CPAN::Config->{use_prompt_default}; if ($pl_commandline) { $system = $pl_commandline; $ENV{PERL} = $^X; } elsif ($self->{'configure'}) { $system = $self->{'configure'}; } elsif ($self->{modulebuild}) { my($perl) = $self->perl or die "Couldn\'t find executable perl\n"; my $mbuildpl_arg = $self->_make_phase_arg("pl"); $system = sprintf("%s Build.PL%s", $perl, $mbuildpl_arg ? " $mbuildpl_arg" : "", ); } else { my($perl) = $self->perl or die "Couldn\'t find executable perl\n"; my $switch = ""; # This needs a handler that can be turned on or off: # $switch = "-MExtUtils::MakeMaker ". # "-Mops=:default,:filesys_read,:filesys_open,require,chdir" # if $] > 5.00310; my $makepl_arg = $self->_make_phase_arg("pl"); $ENV{PERL5_CPAN_IS_EXECUTING} = File::Spec->catfile($self->{build_dir}, "Makefile.PL"); $system = sprintf("%s%s Makefile.PL%s", $perl, $switch ? " $switch" : "", $makepl_arg ? " $makepl_arg" : "", ); } my $pl_env; if ($self->prefs->{pl}) { $pl_env = $self->prefs->{pl}{env}; } local @ENV{keys %$pl_env} = values %$pl_env if $pl_env; if (exists $self->{writemakefile}) { } else { local($SIG{ALRM}) = sub { die "inactivity_timeout reached\n" }; my($ret,$pid,$output); $@ = ""; my $go_via_alarm; if ($CPAN::Config->{inactivity_timeout}) { require Config; if ($Config::Config{d_alarm} && $Config::Config{d_alarm} eq "define" ) { $go_via_alarm++ } else { $CPAN::Frontend->mywarn("Warning: you have configured the config ". "variable 'inactivity_timeout' to ". "'$CPAN::Config->{inactivity_timeout}'. But ". "on this machine the system call 'alarm' ". "isn't available. This means that we cannot ". "provide the feature of intercepting long ". "waiting code and will turn this feature off.\n" ); $CPAN::Config->{inactivity_timeout} = 0; } } if ($go_via_alarm) { if ( $self->_should_report('pl') ) { ($output, $ret) = CPAN::Reporter::record_command( $system, $CPAN::Config->{inactivity_timeout}, ); CPAN::Reporter::grade_PL( $self, $system, $output, $ret ); } else { eval { alarm $CPAN::Config->{inactivity_timeout}; local $SIG{CHLD}; # = sub { wait }; if (defined($pid = fork)) { if ($pid) { #parent # wait; waitpid $pid, 0; } else { #child # note, this exec isn't necessary if # inactivity_timeout is 0. On the Mac I'd # suggest, we set it always to 0. exec $system; } } else { $CPAN::Frontend->myprint("Cannot fork: $!"); return; } }; alarm 0; if ($@) { kill 9, $pid; waitpid $pid, 0; my $err = "$@"; $CPAN::Frontend->myprint($err); $self->{writemakefile} = CPAN::Distrostatus->new("NO $err"); $@ = ""; $self->store_persistent_state; return $self->goodbye("$system -- TIMED OUT"); } } } else { if (my $expect_model = $self->_prefs_with_expect("pl")) { # XXX probably want to check _should_report here and warn # about not being able to use CPAN::Reporter with expect $ret = $self->_run_via_expect($system,'writemakefile',$expect_model); if (! defined $ret && $self->{writemakefile} && $self->{writemakefile}->failed) { # timeout return; } } elsif ( $self->_should_report('pl') ) { ($output, $ret) = eval { CPAN::Reporter::record_command($system) }; if (! defined $output or $@) { my $err = $@ || "Unknown error"; $CPAN::Frontend->mywarn("Error while running PL phase: $err\n"); $self->{writemakefile} = CPAN::Distrostatus ->new("NO '$system' returned status $ret and no output"); return $self->goodbye("$system -- NOT OK"); } CPAN::Reporter::grade_PL( $self, $system, $output, $ret ); } else { $ret = system($system); } if ($ret != 0) { $self->{writemakefile} = CPAN::Distrostatus ->new("NO '$system' returned status $ret"); $CPAN::Frontend->mywarn("Warning: No success on command[$system]\n"); $self->store_persistent_state; return $self->goodbye("$system -- NOT OK"); } } if (-f "Makefile" || -f "Build" || ($^O eq 'VMS' && (-f 'descrip.mms' || -f 'Build.com'))) { $self->{writemakefile} = CPAN::Distrostatus->new("YES"); delete $self->{make_clean}; # if cleaned before, enable next $self->store_persistent_state; return $self->success("$system -- OK"); } else { my $makefile = $self->{modulebuild} ? "Build" : "Makefile"; my $why = "No '$makefile' created"; $CPAN::Frontend->mywarn($why); $self->{writemakefile} = CPAN::Distrostatus ->new(qq{NO -- $why\n}); $self->store_persistent_state; return $self->goodbye("$system -- NOT OK"); } } $self->store_persistent_state; return 1; # success } #-> sub CPAN::Distribution::shortcut_make ; # return values: undef means don't shortcut; 0 means shortcut as fail; # and 1 means shortcut as success sub shortcut_make { my ($self) = @_; $self->debug("checking make/build results[$self->{ID}]") if $CPAN::DEBUG; if (defined $self->{make}) { if (UNIVERSAL::can($self->{make},"failed") ? $self->{make}->failed : $self->{make} =~ /^NO/ ) { if ($self->{force_update}) { # Trying an already failed 'make' (unless somebody else blocks) return undef; # no shortcut } else { # introduced for turning recursion detection into a distrostatus my $error = length $self->{make}>3 ? substr($self->{make},3) : "Unknown error"; $self->store_persistent_state; return $self->goodbye("Could not make: $error\n"); } } else { return $self->success("Has already been made") } } return undef; # no shortcut } #-> sub CPAN::Distribution::make ; sub make { my($self) = @_; $self->pre_make(); if (exists $self->{cleanup_after_install_done}) { $self->post_make(); return $self->get; } $self->debug("checking goto id[$self->{ID}]") if $CPAN::DEBUG; if (my $goto = $self->prefs->{goto}) { $self->post_make(); return $self->goto($goto); } # Emergency brake if they said install Pippi and get newest perl # XXX Would this make more sense in shortcut_prepare, since # that doesn't make sense on a perl dist either? Broader # question: what is the purpose of suggesting force install # on a perl distribution? That seems unlikely to result in # such a dependency being satisfied, even if the perl is # successfully installed. This situation is tantamount to # a prereq on a version of perl greater than the current one # so I think we should just abort. -- xdg, 2012-04-06 if ($self->isa_perl) { if ( $self->called_for ne $self->id && ! $self->{force_update} ) { # if we die here, we break bundles $CPAN::Frontend ->mywarn(sprintf( qq{The most recent version "%s" of the module "%s" is part of the perl-%s distribution. To install that, you need to run force install %s --or-- install %s }, $CPAN::META->instance( 'CPAN::Module', $self->called_for )->cpan_version, $self->called_for, $self->isa_perl, $self->called_for, $self->pretty_id, )); $self->{make} = CPAN::Distrostatus->new("NO isa perl"); $CPAN::Frontend->mysleep(1); $self->post_make(); return; } } unless ($self->prepare){ $self->post_make(); return; } if ( defined( my $sc = $self->shortcut_make) ) { $self->post_make(); return $sc; } if ($CPAN::Signal) { delete $self->{force_update}; $self->post_make(); return; } my $builddir = $self->dir or $CPAN::Frontend->mydie("PANIC: Cannot determine build directory\n"); unless (chdir $builddir) { $CPAN::Frontend->mywarn("Couldn't chdir to '$builddir': $!"); $self->post_make(); return; } my $make = $self->{modulebuild} ? "Build" : "make"; $CPAN::Frontend->myprint(sprintf "Running %s for %s\n", $make, $self->id); local $ENV{PERL5LIB} = defined($ENV{PERL5LIB}) ? $ENV{PERL5LIB} : ($ENV{PERLLIB} || ""); local $ENV{PERL5OPT} = defined $ENV{PERL5OPT} ? $ENV{PERL5OPT} : ""; local $ENV{PERL_USE_UNSAFE_INC} = exists $ENV{PERL_USE_UNSAFE_INC} && defined $ENV{PERL_USE_UNSAFE_INC} ? $ENV{PERL_USE_UNSAFE_INC} : 1; # make $CPAN::META->set_perl5lib; local $ENV{MAKEFLAGS}; # protect us from outer make calls if ($CPAN::Signal) { delete $self->{force_update}; $self->post_make(); return; } if ($^O eq 'MacOS') { Mac::BuildTools::make($self); $self->post_make(); return; } my %env; while (my($k,$v) = each %ENV) { next if defined $v; $env{$k} = ''; } local @ENV{keys %env} = values %env; my $satisfied = eval { $self->satisfy_requires }; if ($@) { return $self->goodbye($@); } unless ($satisfied){ $self->post_make(); return; } if ($CPAN::Signal) { delete $self->{force_update}; $self->post_make(); return; } # need to chdir again, because $self->satisfy_requires might change the directory unless (chdir $builddir) { $CPAN::Frontend->mywarn("Couldn't chdir to '$builddir': $!"); $self->post_make(); return; } my $system; my $make_commandline; if ($self->prefs->{make}) { $make_commandline = $self->prefs->{make}{commandline}; } local $ENV{PERL} = defined $ENV{PERL}? $ENV{PERL} : $^X; local $ENV{PERL_MM_USE_DEFAULT} = 1 if $CPAN::Config->{use_prompt_default}; local $ENV{NONINTERACTIVE_TESTING} = 1 if $CPAN::Config->{use_prompt_default}; if ($make_commandline) { $system = $make_commandline; $ENV{PERL} = CPAN::find_perl(); } else { if ($self->{modulebuild}) { unless (-f "Build" || ($^O eq 'VMS' && -f 'Build.com')) { my $cwd = CPAN::anycwd(); $CPAN::Frontend->mywarn("Alert: no Build file available for 'make $self->{id}'". " in cwd[$cwd]. Danger, Will Robinson!\n"); $CPAN::Frontend->mysleep(5); } $system = join " ", $self->_build_command(), $CPAN::Config->{mbuild_arg}; } else { $system = join " ", $self->_make_command(), $CPAN::Config->{make_arg}; } $system =~ s/\s+$//; my $make_arg = $self->_make_phase_arg("make"); $system = sprintf("%s%s", $system, $make_arg ? " $make_arg" : "", ); } my $make_env; if ($self->prefs->{make}) { $make_env = $self->prefs->{make}{env}; } local @ENV{keys %$make_env} = values %$make_env if $make_env; my $expect_model = $self->_prefs_with_expect("make"); my $want_expect = 0; if ( $expect_model && @{$expect_model->{talk}} ) { my $can_expect = $CPAN::META->has_inst("Expect"); if ($can_expect) { $want_expect = 1; } else { $CPAN::Frontend->mywarn("Expect not installed, falling back to ". "system()\n"); } } my ($system_ok, $system_err); if ($want_expect) { # XXX probably want to check _should_report here and # warn about not being able to use CPAN::Reporter with expect $system_ok = $self->_run_via_expect($system,'make',$expect_model) == 0; } elsif ( $self->_should_report('make') ) { my ($output, $ret) = CPAN::Reporter::record_command($system); CPAN::Reporter::grade_make( $self, $system, $output, $ret ); $system_ok = ! $ret; } else { my $rc = system($system); $system_ok = $rc == 0; $system_err = $! if $rc == -1; } $self->introduce_myself; if ( $system_ok ) { $CPAN::Frontend->myprint(" $system -- OK\n"); $self->{make} = CPAN::Distrostatus->new("YES"); } else { $self->{writemakefile} ||= CPAN::Distrostatus->new("YES"); $self->{make} = CPAN::Distrostatus->new("NO"); $CPAN::Frontend->mywarn(" $system -- NOT OK\n"); $CPAN::Frontend->mywarn(" $system_err\n") if defined $system_err; } $self->store_persistent_state; $self->post_make(); return !! $system_ok; } # CPAN::Distribution::goodbye ; sub goodbye { my($self,$goodbye) = @_; my $id = $self->pretty_id; $CPAN::Frontend->mywarn(" $id\n $goodbye\n"); return 0; # must be explicit false, not undef } sub success { my($self,$why) = @_; my $id = $self->pretty_id; $CPAN::Frontend->myprint(" $id\n $why\n"); return 1; } # CPAN::Distribution::_run_via_expect ; sub _run_via_expect { my($self,$system,$phase,$expect_model) = @_; CPAN->debug("system[$system]expect_model[$expect_model]") if $CPAN::DEBUG; if ($CPAN::META->has_inst("Expect")) { my $expo = Expect->new; # expo Expect object; $expo->spawn($system); $expect_model->{mode} ||= "deterministic"; if ($expect_model->{mode} eq "deterministic") { return $self->_run_via_expect_deterministic($expo,$phase,$expect_model); } elsif ($expect_model->{mode} eq "anyorder") { return $self->_run_via_expect_anyorder($expo,$phase,$expect_model); } else { die "Panic: Illegal expect mode: $expect_model->{mode}"; } } else { $CPAN::Frontend->mywarn("Expect not installed, falling back to system()\n"); return system($system); } } sub _run_via_expect_anyorder { my($self,$expo,$phase,$expect_model) = @_; my $timeout = $expect_model->{timeout} || 5; my $reuse = $expect_model->{reuse}; my @expectacopy = @{$expect_model->{talk}}; # we trash it! my $but = ""; my $timeout_start = time; EXPECT: while () { my($eof,$ran_into_timeout); # XXX not up to the full power of expect. one could certainly # wrap all of the talk pairs into a single expect call and on # success tweak it and step ahead to the next question. The # current implementation unnecessarily limits itself to a # single match. my @match = $expo->expect(1, [ eof => sub { $eof++; } ], [ timeout => sub { $ran_into_timeout++; } ], -re => eval"qr{.}", ); if ($match[2]) { $but .= $match[2]; } $but .= $expo->clear_accum; if ($eof) { $expo->soft_close; return $expo->exitstatus(); } elsif ($ran_into_timeout) { # warn "DEBUG: they are asking a question, but[$but]"; for (my $i = 0; $i <= $#expectacopy; $i+=2) { my($next,$send) = @expectacopy[$i,$i+1]; my $regex = eval "qr{$next}"; # warn "DEBUG: will compare with regex[$regex]."; if ($but =~ /$regex/) { # warn "DEBUG: will send send[$send]"; $expo->send($send); # never allow reusing an QA pair unless they told us splice @expectacopy, $i, 2 unless $reuse; $but =~ s/(?s:^.*?)$regex//; $timeout_start = time; next EXPECT; } } my $have_waited = time - $timeout_start; if ($have_waited < $timeout) { # warn "DEBUG: have_waited[$have_waited]timeout[$timeout]"; next EXPECT; } my $why = "could not answer a question during the dialog"; $CPAN::Frontend->mywarn("Failing: $why\n"); $self->{$phase} = CPAN::Distrostatus->new("NO $why"); return 0; } } } sub _run_via_expect_deterministic { my($self,$expo,$phase,$expect_model) = @_; my $ran_into_timeout; my $ran_into_eof; my $timeout = $expect_model->{timeout} || 15; # currently unsettable my $expecta = $expect_model->{talk}; EXPECT: for (my $i = 0; $i <= $#$expecta; $i+=2) { my($re,$send) = @$expecta[$i,$i+1]; CPAN->debug("timeout[$timeout]re[$re]") if $CPAN::DEBUG; my $regex = eval "qr{$re}"; $expo->expect($timeout, [ eof => sub { my $but = $expo->clear_accum; $CPAN::Frontend->mywarn("EOF (maybe harmless) expected[$regex]\nbut[$but]\n\n"); $ran_into_eof++; } ], [ timeout => sub { my $but = $expo->clear_accum; $CPAN::Frontend->mywarn("TIMEOUT expected[$regex]\nbut[$but]\n\n"); $ran_into_timeout++; } ], -re => $regex); if ($ran_into_timeout) { # note that the caller expects 0 for success $self->{$phase} = CPAN::Distrostatus->new("NO timeout during expect dialog"); return 0; } elsif ($ran_into_eof) { last EXPECT; } $expo->send($send); } $expo->soft_close; return $expo->exitstatus(); } #-> CPAN::Distribution::_validate_distropref sub _validate_distropref { my($self,@args) = @_; if ( $CPAN::META->has_inst("CPAN::Kwalify") && $CPAN::META->has_inst("Kwalify") ) { eval {CPAN::Kwalify::_validate("distroprefs",@args);}; if ($@) { $CPAN::Frontend->mywarn($@); } } else { CPAN->debug("not validating '@args'") if $CPAN::DEBUG; } } #-> CPAN::Distribution::_find_prefs sub _find_prefs { my($self) = @_; my $distroid = $self->pretty_id; #CPAN->debug("distroid[$distroid]") if $CPAN::DEBUG; my $prefs_dir = $CPAN::Config->{prefs_dir}; return if $prefs_dir =~ /^\s*$/; eval { File::Path::mkpath($prefs_dir); }; if ($@) { $CPAN::Frontend->mydie("Cannot create directory $prefs_dir"); } # shortcut if there are no distroprefs files { my $dh = DirHandle->new($prefs_dir) or $CPAN::Frontend->mydie("Couldn't open '$prefs_dir': $!"); my @files = map { /\.(yml|dd|st)\z/i } $dh->read; return unless @files; } my $yaml_module = CPAN::_yaml_module(); my $ext_map = {}; my @extensions; if ($CPAN::META->has_inst($yaml_module)) { $ext_map->{yml} = 'CPAN'; } else { my @fallbacks; if ($CPAN::META->has_inst("Data::Dumper")) { push @fallbacks, $ext_map->{dd} = 'Data::Dumper'; } if ($CPAN::META->has_inst("Storable")) { push @fallbacks, $ext_map->{st} = 'Storable'; } if (@fallbacks) { local $" = " and "; unless ($self->{have_complained_about_missing_yaml}++) { $CPAN::Frontend->mywarnonce("'$yaml_module' not installed, falling back ". "to @fallbacks to read prefs '$prefs_dir'\n"); } } else { unless ($self->{have_complained_about_missing_yaml}++) { $CPAN::Frontend->mywarnonce("'$yaml_module' not installed, cannot ". "read prefs '$prefs_dir'\n"); } } } my $finder = CPAN::Distroprefs->find($prefs_dir, $ext_map); DIRENT: while (my $result = $finder->next) { if ($result->is_warning) { $CPAN::Frontend->mywarn($result->as_string); $CPAN::Frontend->mysleep(1); next DIRENT; } elsif ($result->is_fatal) { $CPAN::Frontend->mydie($result->as_string); } my @prefs = @{ $result->prefs }; ELEMENT: for my $y (0..$#prefs) { my $pref = $prefs[$y]; $self->_validate_distropref($pref->data, $result->abs, $y); # I don't know why we silently skip when there's no match, but # complain if there's an empty match hashref, and there's no # comment explaining why -- hdp, 2008-03-18 unless ($pref->has_any_match) { next ELEMENT; } unless ($pref->has_valid_subkeys) { $CPAN::Frontend->mydie(sprintf "Nonconforming .%s file '%s': " . "missing match/* subattribute. " . "Please remove, cannot continue.", $result->ext, $result->abs, ); } my $arg = { env => \%ENV, distribution => $distroid, perl => \&CPAN::find_perl, perlconfig => \%Config::Config, module => sub { [ $self->containsmods ] }, }; if ($pref->matches($arg)) { return { prefs => $pref->data, prefs_file => $result->abs, prefs_file_doc => $y, }; } } } return; } # CPAN::Distribution::prefs sub prefs { my($self) = @_; if (exists $self->{negative_prefs_cache} && $self->{negative_prefs_cache} != $CPAN::CurrentCommandId ) { delete $self->{negative_prefs_cache}; delete $self->{prefs}; } if (exists $self->{prefs}) { return $self->{prefs}; # XXX comment out during debugging } if ($CPAN::Config->{prefs_dir}) { CPAN->debug("prefs_dir[$CPAN::Config->{prefs_dir}]") if $CPAN::DEBUG; my $prefs = $self->_find_prefs(); $prefs ||= ""; # avoid warning next line CPAN->debug("prefs[$prefs]") if $CPAN::DEBUG; if ($prefs) { for my $x (qw(prefs prefs_file prefs_file_doc)) { $self->{$x} = $prefs->{$x}; } my $bs = sprintf( "%s[%s]", File::Basename::basename($self->{prefs_file}), $self->{prefs_file_doc}, ); my $filler1 = "_" x 22; my $filler2 = int(66 - length($bs))/2; $filler2 = 0 if $filler2 < 0; $filler2 = " " x $filler2; $CPAN::Frontend->myprint(" $filler1 D i s t r o P r e f s $filler1 $filler2 $bs $filler2 "); $CPAN::Frontend->mysleep(1); return $self->{prefs}; } } $self->{negative_prefs_cache} = $CPAN::CurrentCommandId; return $self->{prefs} = +{}; } # CPAN::Distribution::_make_phase_arg sub _make_phase_arg { my($self, $phase) = @_; my $_make_phase_arg; my $prefs = $self->prefs; if ( $prefs && exists $prefs->{$phase} && exists $prefs->{$phase}{args} && $prefs->{$phase}{args} ) { $_make_phase_arg = join(" ", map {CPAN::HandleConfig ->safe_quote($_)} @{$prefs->{$phase}{args}}, ); } # cpan[2]> o conf make[TAB] # make make_install_make_command # make_arg makepl_arg # make_install_arg # cpan[2]> o conf mbuild[TAB] # mbuild_arg mbuild_install_build_command # mbuild_install_arg mbuildpl_arg my $mantra; # must switch make/mbuild here if ($self->{modulebuild}) { $mantra = "mbuild"; } else { $mantra = "make"; } my %map = ( pl => "pl_arg", make => "_arg", test => "_test_arg", # does not really exist but maybe # will some day and now protects # us from unini warnings install => "_install_arg", ); my $phase_underscore_meshup = $map{$phase}; my $what = sprintf "%s%s", $mantra, $phase_underscore_meshup; $_make_phase_arg ||= $CPAN::Config->{$what}; return $_make_phase_arg; } # CPAN::Distribution::_make_command sub _make_command { my ($self) = @_; if ($self) { return CPAN::HandleConfig ->safe_quote( CPAN::HandleConfig->prefs_lookup($self, q{make}) || $Config::Config{make} || 'make' ); } else { # Old style call, without object. Deprecated Carp::confess("CPAN::_make_command() used as function. Don't Do That."); return safe_quote(undef, CPAN::HandleConfig->prefs_lookup($self,q{make}) || $CPAN::Config->{make} || $Config::Config{make} || 'make'); } } sub _make_install_make_command { my ($self) = @_; my $mimc = CPAN::HandleConfig->prefs_lookup($self, q{make_install_make_command}); return $self->_make_command() unless $mimc; # Quote the "make install" make command on Windows, where it is commonly # found in, e.g., C:\Program Files\... and therefore needs quoting. We can't # do this in general because the command maybe "sudo make..." (i.e. a # program with arguments), but that is unlikely to be the case on Windows. $mimc = CPAN::HandleConfig->safe_quote($mimc) if $^O eq 'MSWin32'; return $mimc; } #-> sub CPAN::Distribution::is_locally_optional sub is_locally_optional { my($self, $prereq_pm, $prereq) = @_; $prereq_pm ||= $self->{prereq_pm}; my($nmo,$opt); for my $rt (qw(requires build_requires)) { if (exists $prereq_pm->{$rt}{$prereq}) { # rt 121914 $nmo ||= $CPAN::META->instance("CPAN::Module",$prereq); my $av = $nmo->available_version; return 0 if !$av || CPAN::Version->vlt($av,$prereq_pm->{$rt}{$prereq}); } if (exists $prereq_pm->{"opt_$rt"}{$prereq}) { $opt = 1; } } return $opt||0; } #-> sub CPAN::Distribution::follow_prereqs ; sub follow_prereqs { my($self) = shift; my($slot) = shift; my(@prereq_tuples) = grep {$_->[0] ne "perl"} @_; return unless @prereq_tuples; my(@good_prereq_tuples); for my $p (@prereq_tuples) { # e.g. $p = ['Devel::PartialDump', 'r', 1] # promote if possible if ($p->[1] =~ /^(r|c)$/) { push @good_prereq_tuples, $p; } elsif ($p->[1] =~ /^(b)$/) { my $reqtype = CPAN::Queue->reqtype_of($p->[0]); if ($reqtype =~ /^(r|c)$/) { push @good_prereq_tuples, [$p->[0], $reqtype, $p->[2]]; } else { push @good_prereq_tuples, $p; } } else { die "Panic: in follow_prereqs: reqtype[$p->[1]] seen, should never happen"; } } my $pretty_id = $self->pretty_id; my %map = ( b => "build_requires", r => "requires", c => "commandline", ); my($filler1,$filler2,$filler3,$filler4); my $unsat = "Unsatisfied dependencies detected during"; my $w = length($unsat) > length($pretty_id) ? length($unsat) : length($pretty_id); { my $r = int(($w - length($unsat))/2); my $l = $w - length($unsat) - $r; $filler1 = "-"x4 . " "x$l; $filler2 = " "x$r . "-"x4 . "\n"; } { my $r = int(($w - length($pretty_id))/2); my $l = $w - length($pretty_id) - $r; $filler3 = "-"x4 . " "x$l; $filler4 = " "x$r . "-"x4 . "\n"; } $CPAN::Frontend-> myprint("$filler1 $unsat $filler2". "$filler3 $pretty_id $filler4". join("", map {sprintf " %s \[%s%s]\n", $_->[0], $map{$_->[1]}, $self->is_locally_optional(undef,$_->[0]) ? ",optional" : ""} @good_prereq_tuples), ); my $follow = 0; if ($CPAN::Config->{prerequisites_policy} eq "follow") { $follow = 1; } elsif ($CPAN::Config->{prerequisites_policy} eq "ask") { my $answer = CPAN::Shell::colorable_makemaker_prompt( "Shall I follow them and prepend them to the queue of modules we are processing right now?", "yes"); $follow = $answer =~ /^\s*y/i; } else { my @prereq = map { $_->[0] } @good_prereq_tuples; local($") = ", "; $CPAN::Frontend-> myprint(" Ignoring dependencies on modules @prereq\n"); } if ($follow) { my $id = $self->id; my(@to_queue_mand,@to_queue_opt); for my $gp (@good_prereq_tuples) { my($prereq,$reqtype,$optional) = @$gp; my $qthing = +{qmod=>$prereq,reqtype=>$reqtype,optional=>$optional}; if ($optional && $self->is_locally_optional(undef,$prereq) ){ # Since we do not depend on this one, we do not need # this in a mandatory arrangement: push @to_queue_opt, $qthing; } else { my $any = CPAN::Shell->expandany($prereq); $self->{$slot . "_for"}{$any->id}++; if ($any) { unless ($optional) { # No recursion check in an optional area of the tree $any->color_cmd_tmps(0,2); } } else { $CPAN::Frontend->mywarn("Warning (maybe a bug): Cannot expand prereq '$prereq'\n"); $CPAN::Frontend->mysleep(2); } # order everything that is not locally_optional just # like mandatory items: this keeps leaves before # branches unshift @to_queue_mand, $qthing; } } if (@to_queue_mand) { unshift @to_queue_mand, {qmod => $id, reqtype => $self->{reqtype}, optional=> !$self->{mandatory}}; CPAN::Queue->jumpqueue(@to_queue_opt,@to_queue_mand); $self->{$slot} = "Delayed until after prerequisites"; return 1; # signal we need dependencies } elsif (@to_queue_opt) { CPAN::Queue->jumpqueue(@to_queue_opt); } } return; } sub _feature_depends { my($self) = @_; my $meta_yml = $self->parse_meta_yml(); my $optf = $meta_yml->{optional_features} or return; if (!ref $optf or ref $optf ne "HASH"){ $CPAN::Frontend->mywarn("The content of optional_features is not a HASH reference. Cannot use it.\n"); $optf = {}; } my $wantf = $self->prefs->{features} or return; if (!ref $wantf or ref $wantf ne "ARRAY"){ $CPAN::Frontend->mywarn("The content of 'features' is not an ARRAY reference. Cannot use it.\n"); $wantf = []; } my $dep = +{}; for my $wf (@$wantf) { if (my $f = $optf->{$wf}) { $CPAN::Frontend->myprint("Found the demanded feature '$wf' that ". "is accompanied by this description:\n". $f->{description}. "\n\n" ); # configure_requires currently not in the spec, unlikely to be useful anyway for my $reqtype (qw(configure_requires build_requires requires)) { my $reqhash = $f->{$reqtype} or next; while (my($k,$v) = each %$reqhash) { $dep->{$reqtype}{$k} = $v; } } } else { $CPAN::Frontend->mywarn("The demanded feature '$wf' was not ". "found in the META.yml file". "\n\n" ); } } $dep; } sub prereqs_for_slot { my($self,$slot) = @_; my($prereq_pm); unless ($CPAN::META->has_usable("CPAN::Meta::Requirements")) { my $whynot = "not available"; if (defined $CPAN::Meta::Requirements::VERSION) { $whynot = "version $CPAN::Meta::Requirements::VERSION not sufficient"; } $CPAN::Frontend->mywarn("CPAN::Meta::Requirements $whynot\n"); my $before = ""; if ($self->{CALLED_FOR}){ if ($self->{CALLED_FOR} =~ /^( CPAN::Meta::Requirements |CPAN::DistnameInfo |version |parent |ExtUtils::MakeMaker |Test::Harness )$/x) { $CPAN::Frontend->mywarn("Please install CPAN::Meta::Requirements ". "as soon as possible; it is needed for a reliable operation of ". "the cpan shell; setting requirements to nil for '$1' for now ". "to prevent deadlock during bootstrapping\n"); return; } $before = " before $self->{CALLED_FOR}"; } $CPAN::Frontend->mydie("Please install CPAN::Meta::Requirements manually$before"); } my $merged = CPAN::Meta::Requirements->new; my $prefs_depends = $self->prefs->{depends}||{}; my $feature_depends = $self->_feature_depends(); if ($slot eq "configure_requires_later") { for my $hash ( $self->configure_requires, $prefs_depends->{configure_requires}, $feature_depends->{configure_requires}, ) { $merged->add_requirements( CPAN::Meta::Requirements->from_string_hash($hash) ); } if (-f "Build.PL" && ! -f File::Spec->catfile($self->{build_dir},"Makefile.PL") && ! @{[ $merged->required_modules ]} && ! $CPAN::META->has_inst("Module::Build") ) { $CPAN::Frontend->mywarn( " Warning: CPAN.pm discovered Module::Build as undeclared prerequisite.\n". " Adding it now as such.\n" ); $CPAN::Frontend->mysleep(5); $merged->add_minimum( "Module::Build" => 0 ); delete $self->{writemakefile}; } $prereq_pm = {}; # configure_requires defined as "b" } elsif ($slot eq "later") { my $prereq_pm_0 = $self->prereq_pm || {}; for my $reqtype (qw(requires build_requires opt_requires opt_build_requires)) { $prereq_pm->{$reqtype} = {%{$prereq_pm_0->{$reqtype}||{}}}; # copy to not pollute it for my $dep ($prefs_depends,$feature_depends) { for my $k (keys %{$dep->{$reqtype}||{}}) { $prereq_pm->{$reqtype}{$k} = $dep->{$reqtype}{$k}; } } } # XXX what about optional_req|breq? -- xdg, 2012-04-01 for my $hash ( $prereq_pm->{requires}, $prereq_pm->{build_requires}, $prereq_pm->{opt_requires}, $prereq_pm->{opt_build_requires}, ) { $merged->add_requirements( CPAN::Meta::Requirements->from_string_hash($hash) ); } } else { die "Panic: illegal slot '$slot'"; } return ($merged->as_string_hash, $prereq_pm); } #-> sub CPAN::Distribution::unsat_prereq ; # return ([Foo,"r"],[Bar,"b"]) for normal modules # return ([perl=>5.008]) if we need a newer perl than we are running under # (sorry for the inconsistency, it was an accident) sub unsat_prereq { my($self,$slot) = @_; my($merged_hash,$prereq_pm) = $self->prereqs_for_slot($slot); my(@need); unless ($CPAN::META->has_usable("CPAN::Meta::Requirements")) { $CPAN::Frontend->mywarn("CPAN::Meta::Requirements not available, please install as soon as possible, trying to continue with severly limited capabilities\n"); return; } my $merged = CPAN::Meta::Requirements->from_string_hash($merged_hash); my @merged = sort $merged->required_modules; CPAN->debug("all merged_prereqs[@merged]") if $CPAN::DEBUG; NEED: for my $need_module ( @merged ) { my $need_version = $merged->requirements_for_module($need_module); my($available_version,$inst_file,$available_file,$nmo); if ($need_module eq "perl") { $available_version = $]; $available_file = CPAN::find_perl(); } else { if (CPAN::_sqlite_running()) { CPAN::Index->reload; $CPAN::SQLite->search("CPAN::Module",$need_module); } $nmo = $CPAN::META->instance("CPAN::Module",$need_module); $inst_file = $nmo->inst_file || ''; $available_file = $nmo->available_file || ''; $available_version = $nmo->available_version; if ($nmo->uptodate) { my $accepts = eval { $merged->accepts_module($need_module, $available_version); }; unless ($accepts) { my $rq = $merged->requirements_for_module( $need_module ); $CPAN::Frontend->mywarn( "Warning: Version '$available_version' of ". "'$need_module' is up to date but does not ". "fulfill requirements ($rq). I will continue, ". "but chances to succeed are low.\n"); } next NEED; } # if they have not specified a version, we accept any # installed one; in that case inst_file is always # sufficient and available_file is sufficient on # both build_requires and configure_requires my $sufficient = $inst_file || ( exists $prereq_pm->{requires}{$need_module} ? 0 : $available_file ); if ( $sufficient and ( # a few quick short circuits not defined $need_version or $need_version eq '0' # "==" would trigger warning when not numeric or $need_version eq "undef" )) { unless ($nmo->inst_deprecated) { next NEED; } } } # We only want to install prereqs if either they're not installed # or if the installed version is too old. We cannot omit this # check, because if 'force' is in effect, nobody else will check. # But we don't want to accept a deprecated module installed as part # of the Perl core, so we continue if the available file is the installed # one and is deprecated if ( $available_file ) { my $fulfills_all_version_rqs = $self->_fulfills_all_version_rqs ( $need_module, $available_file, $available_version, $need_version, ); if ( $inst_file && $available_file eq $inst_file && $nmo->inst_deprecated ) { # continue installing as a prereq. we really want that # because the deprecated module may spit out warnings # and third party did not know until today. Only one # exception is OK, because CPANPLUS is special after # all: if ( $fulfills_all_version_rqs and $nmo->id =~ /^CPANPLUS(?:::Dist::Build)$/ ) { # here we have an available version that is good # enough although deprecated (preventing circular # loop CPANPLUS => CPANPLUS::Dist::Build RT#83042) next NEED; } } elsif ( $self->{reqtype} # e.g. maybe we came via goto? && $self->{reqtype} =~ /^(r|c)$/ && ( exists $prereq_pm->{requires}{$need_module} || exists $prereq_pm->{opt_requires}{$need_module} ) && $nmo && !$inst_file ) { # continue installing as a prereq; this may be a # distro we already used when it was a build_requires # so we did not install it. But suddenly somebody # wants it as a requires my $need_distro = $nmo->distribution; if ($need_distro->{install} && $need_distro->{install}->failed && $need_distro->{install}->text =~ /is only/) { my $id = $need_distro->pretty_id; $CPAN::Frontend->myprint("Promoting $id from build_requires to requires due $need_module\n"); delete $need_distro->{install}; # promote to another installation attempt $need_distro->{reqtype} = "r"; $need_distro->install; next NEED; } } else { next NEED if $fulfills_all_version_rqs; } } if ($need_module eq "perl") { return ["perl", $need_version]; } $self->{sponsored_mods}{$need_module} ||= 0; CPAN->debug("need_module[$need_module]s/s/n[$self->{sponsored_mods}{$need_module}]") if $CPAN::DEBUG; if (my $sponsoring = $self->{sponsored_mods}{$need_module}++) { # We have already sponsored it and for some reason it's still # not available. So we do ... what?? # if we push it again, we have a potential infinite loop # The following "next" was a very problematic construct. # It helped a lot but broke some day and had to be # replaced. # We must be able to deal with modules that come again and # again as a prereq and have themselves prereqs and the # queue becomes long but finally we would find the correct # order. The RecursiveDependency check should trigger a # die when it's becoming too weird. Unfortunately removing # this next breaks many other things. # The bug that brought this up is described in Todo under # "5.8.9 cannot install Compress::Zlib" # next; # this is the next that had to go away # The following "next NEED" are fine and the error message # explains well what is going on. For example when the DBI # fails and consequently DBD::SQLite fails and now we are # processing CPAN::SQLite. Then we must have a "next" for # DBD::SQLite. How can we get it and how can we identify # all other cases we must identify? my $do = $nmo->distribution; next NEED unless $do; # not on CPAN if (CPAN::Version->vcmp($need_version, $nmo->ro->{CPAN_VERSION}) > 0){ $CPAN::Frontend->mywarn("Warning: Prerequisite ". "'$need_module => $need_version' ". "for '$self->{ID}' seems ". "not available according to the indices\n" ); next NEED; } NOSAYER: for my $nosayer ( "unwrapped", "writemakefile", "signature_verify", "make", "make_test", "install", "make_clean", ) { if ($do->{$nosayer}) { my $selfid = $self->pretty_id; my $did = $do->pretty_id; if (UNIVERSAL::can($do->{$nosayer},"failed") ? $do->{$nosayer}->failed : $do->{$nosayer} =~ /^NO/) { if ($nosayer eq "make_test" && $do->{make_test}{COMMANDID} != $CPAN::CurrentCommandId ) { next NOSAYER; } ### XXX don't complain about missing optional deps -- xdg, 2012-04-01 if ($self->is_locally_optional($prereq_pm, $need_module)) { # don't complain about failing optional prereqs } else { $CPAN::Frontend->mywarn("Warning: Prerequisite ". "'$need_module => $need_version' ". "for '$selfid' failed when ". "processing '$did' with ". "'$nosayer => $do->{$nosayer}'. Continuing, ". "but chances to succeed are limited.\n" ); $CPAN::Frontend->mysleep($sponsoring/10); } next NEED; } else { # the other guy succeeded if ($nosayer =~ /^(install|make_test)$/) { # we had this with # DMAKI/DateTime-Calendar-Chinese-0.05.tar.gz # in 2007-03 for 'make install' # and 2008-04: #30464 (for 'make test') # $CPAN::Frontend->mywarn("Warning: Prerequisite ". # "'$need_module => $need_version' ". # "for '$selfid' already built ". # "but the result looks suspicious. ". # "Skipping another build attempt, ". # "to prevent looping endlessly.\n" # ); next NEED; } } } } } my $needed_as; if (0) { } elsif (exists $prereq_pm->{requires}{$need_module} || exists $prereq_pm->{opt_requires}{$need_module} ) { $needed_as = "r"; } elsif ($slot eq "configure_requires_later") { # in ae872487d5 we said: C< we have not yet run the # {Build,Makefile}.PL, we must presume "r" >; but the # meta.yml standard says C< These dependencies are not # required after the distribution is installed. >; so now # we change it back to "b" and care for the proper # promotion later. $needed_as = "b"; } else { $needed_as = "b"; } # here need to flag as optional for recommends/suggests # -- xdg, 2012-04-01 $self->debug(sprintf "%s manadory?[%s]", $self->pretty_id, $self->{mandatory}) if $CPAN::DEBUG; my $optional = !$self->{mandatory} || $self->is_locally_optional($prereq_pm, $need_module); push @need, [$need_module,$needed_as,$optional]; } my @unfolded = map { "[".join(",",@$_)."]" } @need; CPAN->debug("returning from unsat_prereq[@unfolded]") if $CPAN::DEBUG; @need; } sub _fulfills_all_version_rqs { my($self,$need_module,$available_file,$available_version,$need_version) = @_; my(@all_requirements) = split /\s*,\s*/, $need_version; local($^W) = 0; my $ok = 0; RQ: for my $rq (@all_requirements) { if ($rq =~ s|>=\s*||) { } elsif ($rq =~ s|>\s*||) { # 2005-12: one user if (CPAN::Version->vgt($available_version,$rq)) { $ok++; } next RQ; } elsif ($rq =~ s|!=\s*||) { # 2005-12: no user if (CPAN::Version->vcmp($available_version,$rq)) { $ok++; next RQ; } else { $ok=0; last RQ; } } elsif ($rq =~ m|<=?\s*|) { # 2005-12: no user $CPAN::Frontend->mywarn("Downgrading not supported (rq[$rq])\n"); $ok++; next RQ; } elsif ($rq =~ s|==\s*||) { # 2009-07: ELLIOTJS/Perl-Critic-1.099_002.tar.gz if (CPAN::Version->vcmp($available_version,$rq)) { $ok=0; last RQ; } else { $ok++; next RQ; } } if (! CPAN::Version->vgt($rq, $available_version)) { $ok++; } CPAN->debug(sprintf("need_module[%s]available_file[%s]". "available_version[%s]rq[%s]ok[%d]", $need_module, $available_file, $available_version, CPAN::Version->readable($rq), $ok, )) if $CPAN::DEBUG; } my $ret = $ok == @all_requirements; CPAN->debug(sprintf("need_module[%s]ok[%s]all_requirements[%d]",$need_module, $ok, scalar @all_requirements)) if $CPAN::DEBUG; return $ret; } #-> sub CPAN::Distribution::read_meta # read any sort of meta files, return CPAN::Meta object if no errors sub read_meta { my($self) = @_; my $meta_file = $self->pick_meta_file or return; return unless $CPAN::META->has_usable("CPAN::Meta"); my $meta = eval { CPAN::Meta->load_file($meta_file)} or return; # Very old EU::MM could have wrong META if ($meta_file eq 'META.yml' && $meta->generated_by =~ /ExtUtils::MakeMaker version ([\d\._]+)/ ) { my $eummv = do { local $^W = 0; $1+0; }; return if $eummv < 6.2501; } return $meta; } #-> sub CPAN::Distribution::read_yaml ; # XXX This should be DEPRECATED -- dagolden, 2011-02-05 sub read_yaml { my($self) = @_; my $meta_file = $self->pick_meta_file('\.yml$'); $self->debug("meta_file[$meta_file]") if $CPAN::DEBUG; return unless $meta_file; my $yaml; eval { $yaml = $self->parse_meta_yml($meta_file) }; if ($@ or ! $yaml) { return undef; # if we die, then we cannot read YAML's own META.yml } # not "authoritative" if (defined $yaml && (! ref $yaml || ref $yaml ne "HASH")) { $CPAN::Frontend->mywarn("META.yml does not seem to be conforming, cannot use it.\n"); $yaml = undef; } $self->debug(sprintf "yaml[%s]", $yaml || "UNDEF") if $CPAN::DEBUG; $self->debug($yaml) if $CPAN::DEBUG && $yaml; # MYMETA.yml is static and authoritative by definition if ( $meta_file =~ /MYMETA\.yml/ ) { return $yaml; } # META.yml is authoritative only if dynamic_config is defined and false if ( defined $yaml->{dynamic_config} && ! $yaml->{dynamic_config} ) { return $yaml; } # otherwise, we can't use what we found return undef; } #-> sub CPAN::Distribution::configure_requires ; sub configure_requires { my($self) = @_; return unless my $meta_file = $self->pick_meta_file('^META'); if (my $meta_obj = $self->read_meta) { my $prereqs = $meta_obj->effective_prereqs; my $cr = $prereqs->requirements_for(qw/configure requires/); return $cr ? $cr->as_string_hash : undef; } else { my $yaml = eval { $self->parse_meta_yml($meta_file) }; return $yaml->{configure_requires}; } } #-> sub CPAN::Distribution::prereq_pm ; sub prereq_pm { my($self) = @_; return unless $self->{writemakefile} # no need to have succeeded # but we must have run it || $self->{modulebuild}; unless ($self->{build_dir}) { return; } # no Makefile/Build means configuration aborted, so don't look for prereqs my $makefile = File::Spec->catfile($self->{build_dir}, $^O eq 'VMS' ? 'descrip.mms' : 'Makefile'); my $buildfile = File::Spec->catfile($self->{build_dir}, $^O eq 'VMS' ? 'Build.com' : 'Build'); return unless -f $makefile || -f $buildfile; CPAN->debug(sprintf "writemakefile[%s]modulebuild[%s]", $self->{writemakefile}||"", $self->{modulebuild}||"", ) if $CPAN::DEBUG; my($req,$breq, $opt_req, $opt_breq); my $meta_obj = $self->read_meta; # META/MYMETA is only authoritative if dynamic_config is false if ($meta_obj && ! $meta_obj->dynamic_config) { my $prereqs = $meta_obj->effective_prereqs; my $requires = $prereqs->requirements_for(qw/runtime requires/); my $build_requires = $prereqs->requirements_for(qw/build requires/); my $test_requires = $prereqs->requirements_for(qw/test requires/); # XXX we don't yet distinguish build vs test, so merge them for now $build_requires->add_requirements($test_requires); $req = $requires->as_string_hash; $breq = $build_requires->as_string_hash; # XXX assemble optional_req && optional_breq from recommends/suggests # depending on corresponding policies -- xdg, 2012-04-01 CPAN->use_inst("CPAN::Meta::Requirements"); my $opt_runtime = CPAN::Meta::Requirements->new; my $opt_build = CPAN::Meta::Requirements->new; if ( $CPAN::Config->{recommends_policy} ) { $opt_runtime->add_requirements( $prereqs->requirements_for(qw/runtime recommends/)); $opt_build->add_requirements( $prereqs->requirements_for(qw/build recommends/)); $opt_build->add_requirements( $prereqs->requirements_for(qw/test recommends/)); } if ( $CPAN::Config->{suggests_policy} ) { $opt_runtime->add_requirements( $prereqs->requirements_for(qw/runtime suggests/)); $opt_build->add_requirements( $prereqs->requirements_for(qw/build suggests/)); $opt_build->add_requirements( $prereqs->requirements_for(qw/test suggests/)); } $opt_req = $opt_runtime->as_string_hash; $opt_breq = $opt_build->as_string_hash; } elsif (my $yaml = $self->read_yaml) { # often dynamic_config prevents a result here $req = $yaml->{requires} || {}; $breq = $yaml->{build_requires} || {}; if ( $CPAN::Config->{recommends_policy} ) { $opt_req = $yaml->{recommends} || {}; } undef $req unless ref $req eq "HASH" && %$req; if ($req) { if ($yaml->{generated_by} && $yaml->{generated_by} =~ /ExtUtils::MakeMaker version ([\d\._]+)/) { my $eummv = do { local $^W = 0; $1+0; }; if ($eummv < 6.2501) { # thanks to Slaven for digging that out: MM before # that could be wrong because it could reflect a # previous release undef $req; } } my $areq; my $do_replace; foreach my $k (sort keys %{$req||{}}) { my $v = $req->{$k}; next unless defined $v; if ($v =~ /\d/) { $areq->{$k} = $v; } elsif ($k =~ /[A-Za-z]/ && $v =~ /[A-Za-z]/ && $CPAN::META->exists("CPAN::Module",$v) ) { $CPAN::Frontend->mywarn("Suspicious key-value pair in META.yml's ". "requires hash: $k => $v; I'll take both ". "key and value as a module name\n"); $CPAN::Frontend->mysleep(1); $areq->{$k} = 0; $areq->{$v} = 0; $do_replace++; } } $req = $areq if $do_replace; } } else { $CPAN::Frontend->mywarnonce("Could not read metadata file. Falling back to other ". "methods to determine prerequisites\n"); } unless ($req || $breq) { my $build_dir; unless ( $build_dir = $self->{build_dir} ) { return; } my $makefile = File::Spec->catfile($build_dir,"Makefile"); my $fh; if (-f $makefile and $fh = FileHandle->new("<$makefile\0")) { CPAN->debug("Getting prereq from Makefile") if $CPAN::DEBUG; local($/) = "\n"; while (<$fh>) { last if /MakeMaker post_initialize section/; my($p) = m{^[\#] \s+PREREQ_PM\s+=>\s+(.+) }x; next unless $p; # warn "Found prereq expr[$p]"; # Regexp modified by A.Speer to remember actual version of file # PREREQ_PM hash key wants, then add to while ( $p =~ m/(?:\s)([\w\:]+)=>(q\[.*?\]|undef),?/g ) { my($m,$n) = ($1,$2); # When a prereq is mentioned twice: let the bigger # win; usual culprit is that they declared # build_requires separately from requires; see # rt.cpan.org #47774 my($prevn); if ( defined $req->{$m} ) { $prevn = $req->{$m}; } if ($n =~ /^q\[(.*?)\]$/) { $n = $1; } if (!$prevn || CPAN::Version->vlt($prevn, $n)){ $req->{$m} = $n; } } last; } } } unless ($req || $breq) { my $build_dir = $self->{build_dir} or die "Panic: no build_dir?"; my $buildfile = File::Spec->catfile($build_dir,"Build"); if (-f $buildfile) { CPAN->debug("Found '$buildfile'") if $CPAN::DEBUG; my $build_prereqs = File::Spec->catfile($build_dir,"_build","prereqs"); if (-f $build_prereqs) { CPAN->debug("Getting prerequisites from '$build_prereqs'") if $CPAN::DEBUG; my $content = do { local *FH; open FH, $build_prereqs or $CPAN::Frontend->mydie("Could not open ". "'$build_prereqs': $!"); local $/; <FH>; }; my $bphash = eval $content; if ($@) { } else { $req = $bphash->{requires} || +{}; $breq = $bphash->{build_requires} || +{}; } } } } # XXX needs to be adapted for optional_req & optional_breq -- xdg, 2012-04-01 if ($req || $breq || $opt_req || $opt_breq ) { return $self->{prereq_pm} = { requires => $req, build_requires => $breq, opt_requires => $opt_req, opt_build_requires => $opt_breq, }; } } #-> sub CPAN::Distribution::shortcut_test ; # return values: undef means don't shortcut; 0 means shortcut as fail; # and 1 means shortcut as success sub shortcut_test { my ($self) = @_; $self->debug("checking badtestcnt[$self->{ID}]") if $CPAN::DEBUG; $self->{badtestcnt} ||= 0; if ($self->{badtestcnt} > 0) { require Data::Dumper; CPAN->debug(sprintf "NOREPEAT[%s]", Data::Dumper::Dumper($self)) if $CPAN::DEBUG; return $self->goodbye("Won't repeat unsuccessful test during this command"); } for my $slot ( qw/later configure_requires_later/ ) { $self->debug("checking $slot slot[$self->{ID}]") if $CPAN::DEBUG; return $self->success($self->{$slot}) if $self->{$slot}; } $self->debug("checking if tests passed[$self->{ID}]") if $CPAN::DEBUG; if ( $self->{make_test} ) { if ( UNIVERSAL::can($self->{make_test},"failed") ? $self->{make_test}->failed : $self->{make_test} =~ /^NO/ ) { if ( UNIVERSAL::can($self->{make_test},"commandid") && $self->{make_test}->commandid == $CPAN::CurrentCommandId ) { return $self->goodbye("Has already been tested within this command"); } } else { # if global "is_tested" has been cleared, we need to mark this to # be added to PERL5LIB if not already installed if ($self->tested_ok_but_not_installed) { $CPAN::META->is_tested($self->{build_dir},$self->{make_test}{TIME}); } return $self->success("Has already been tested successfully"); } } if ($self->{notest}) { $self->{make_test} = CPAN::Distrostatus->new("YES"); return $self->success("Skipping test because of notest pragma"); } return undef; # no shortcut } #-> sub CPAN::Distribution::_exe_files ; sub _exe_files { my($self) = @_; return unless $self->{writemakefile} # no need to have succeeded # but we must have run it || $self->{modulebuild}; unless ($self->{build_dir}) { return; } CPAN->debug(sprintf "writemakefile[%s]modulebuild[%s]", $self->{writemakefile}||"", $self->{modulebuild}||"", ) if $CPAN::DEBUG; my $build_dir; unless ( $build_dir = $self->{build_dir} ) { return; } my $makefile = File::Spec->catfile($build_dir,"Makefile"); my $fh; my @exe_files; if (-f $makefile and $fh = FileHandle->new("<$makefile\0")) { CPAN->debug("Getting exefiles from Makefile") if $CPAN::DEBUG; local($/) = "\n"; while (<$fh>) { last if /MakeMaker post_initialize section/; my($p) = m{^[\#] \s+EXE_FILES\s+=>\s+\[(.+)\] }x; next unless $p; # warn "Found exefiles expr[$p]"; my @p = split /,\s*/, $p; for my $p2 (@p) { if ($p2 =~ /^q\[(.+)\]/) { push @exe_files, $1; } } } } return \@exe_files if @exe_files; my $buildparams = File::Spec->catfile($build_dir,"_build","build_params"); if (-f $buildparams) { CPAN->debug("Found '$buildparams'") if $CPAN::DEBUG; my $x = do $buildparams; for my $sf ($x->[2]{script_files}) { if (my $reftype = ref $sf) { if ($reftype eq "ARRAY") { push @exe_files, @$sf; } elsif ($reftype eq "HASH") { push @exe_files, keys %$sf; } else { $CPAN::Frontend->mywarn("Invalid reftype $reftype for Build.PL 'script_files'\n"); } } elsif (defined $sf) { push @exe_files, $sf; } } } return \@exe_files; } #-> sub CPAN::Distribution::test ; sub test { my($self) = @_; $self->pre_test(); if (exists $self->{cleanup_after_install_done}) { $self->post_test(); return $self->make; } $self->debug("checking goto id[$self->{ID}]") if $CPAN::DEBUG; if (my $goto = $self->prefs->{goto}) { $self->post_test(); return $self->goto($goto); } unless ($self->make){ $self->post_test(); return; } if ( defined( my $sc = $self->shortcut_test ) ) { $self->post_test(); return $sc; } if ($CPAN::Signal) { delete $self->{force_update}; $self->post_test(); return; } # warn "XDEBUG: checking for notest: $self->{notest} $self"; my $make = $self->{modulebuild} ? "Build" : "make"; local $ENV{PERL5LIB} = defined($ENV{PERL5LIB}) ? $ENV{PERL5LIB} : ($ENV{PERLLIB} || ""); local $ENV{PERL5OPT} = defined $ENV{PERL5OPT} ? $ENV{PERL5OPT} : ""; local $ENV{PERL_USE_UNSAFE_INC} = exists $ENV{PERL_USE_UNSAFE_INC} && defined $ENV{PERL_USE_UNSAFE_INC} ? $ENV{PERL_USE_UNSAFE_INC} : 1; # test $CPAN::META->set_perl5lib; local $ENV{MAKEFLAGS}; # protect us from outer make calls local $ENV{PERL_MM_USE_DEFAULT} = 1 if $CPAN::Config->{use_prompt_default}; local $ENV{NONINTERACTIVE_TESTING} = 1 if $CPAN::Config->{use_prompt_default}; if ($run_allow_installing_within_test) { my($allow_installing, $why) = $self->_allow_installing; if (! $allow_installing) { $CPAN::Frontend->mywarn("Testing/Installation stopped: $why\n"); $self->introduce_myself; $self->{make_test} = CPAN::Distrostatus->new("NO -- testing/installation stopped due $why"); $CPAN::Frontend->mywarn(" [testing] -- NOT OK\n"); delete $self->{force_update}; $self->post_test(); return; } } $CPAN::Frontend->myprint(sprintf "Running %s test for %s\n", $make, $self->pretty_id); my $builddir = $self->dir or $CPAN::Frontend->mydie("PANIC: Cannot determine build directory\n"); unless (chdir $builddir) { $CPAN::Frontend->mywarn("Couldn't chdir to '$builddir': $!"); $self->post_test(); return; } $self->debug("Changed directory to $self->{build_dir}") if $CPAN::DEBUG; if ($^O eq 'MacOS') { Mac::BuildTools::make_test($self); $self->post_test(); return; } if ($self->{modulebuild}) { my $thm = CPAN::Shell->expand("Module","Test::Harness"); my $v = $thm->inst_version; if (CPAN::Version->vlt($v,2.62)) { # XXX Eric Wilhelm reported this as a bug: klapperl: # Test::Harness 3.0 self-tests, so that should be 'unless # installing Test::Harness' unless ($self->id eq $thm->distribution->id) { $CPAN::Frontend->mywarn(qq{The version of your Test::Harness is only '$v', you need at least '2.62'. Please upgrade your Test::Harness.\n}); $self->{make_test} = CPAN::Distrostatus->new("NO Test::Harness too old"); $self->post_test(); return; } } } if ( ! $self->{force_update} ) { # bypass actual tests if "trust_test_report_history" and have a report my $have_tested_fcn; if ( $CPAN::Config->{trust_test_report_history} && $CPAN::META->has_inst("CPAN::Reporter::History") && ( $have_tested_fcn = CPAN::Reporter::History->can("have_tested" ))) { if ( my @reports = $have_tested_fcn->( dist => $self->base_id ) ) { # Do nothing if grade was DISCARD if ( $reports[-1]->{grade} =~ /^(?:PASS|UNKNOWN)$/ ) { $self->{make_test} = CPAN::Distrostatus->new("YES"); # if global "is_tested" has been cleared, we need to mark this to # be added to PERL5LIB if not already installed if ($self->tested_ok_but_not_installed) { $CPAN::META->is_tested($self->{build_dir},$self->{make_test}{TIME}); } $CPAN::Frontend->myprint("Found prior test report -- OK\n"); $self->post_test(); return; } elsif ( $reports[-1]->{grade} =~ /^(?:FAIL|NA)$/ ) { $self->{make_test} = CPAN::Distrostatus->new("NO"); $self->{badtestcnt}++; $CPAN::Frontend->mywarn("Found prior test report -- NOT OK\n"); $self->post_test(); return; } } } } my $system; my $prefs_test = $self->prefs->{test}; if (my $commandline = exists $prefs_test->{commandline} ? $prefs_test->{commandline} : "") { $system = $commandline; $ENV{PERL} = CPAN::find_perl(); } elsif ($self->{modulebuild}) { $system = sprintf "%s test", $self->_build_command(); unless (-e "Build" || ($^O eq 'VMS' && -e "Build.com")) { my $id = $self->pretty_id; $CPAN::Frontend->mywarn("Alert: no 'Build' file found while trying to test '$id'"); } } else { $system = join " ", $self->_make_command(), "test"; } my $make_test_arg = $self->_make_phase_arg("test"); $system = sprintf("%s%s", $system, $make_test_arg ? " $make_test_arg" : "", ); my($tests_ok); my $test_env; if ($self->prefs->{test}) { $test_env = $self->prefs->{test}{env}; } local @ENV{keys %$test_env} = values %$test_env if $test_env; my $expect_model = $self->_prefs_with_expect("test"); my $want_expect = 0; if ( $expect_model && @{$expect_model->{talk}} ) { my $can_expect = $CPAN::META->has_inst("Expect"); if ($can_expect) { $want_expect = 1; } else { $CPAN::Frontend->mywarn("Expect not installed, falling back to ". "testing without\n"); } } FORK: { my $pid = fork; if (! defined $pid) { # contention warn "Contention '$!', sleeping 2"; sleep 2; redo FORK; } elsif ($pid) { # parent if ($^O eq "MSWin32") { wait; } else { SUPERVISE: while (waitpid($pid, WNOHANG) <= 0) { if ($CPAN::Signal) { kill 9, -$pid; } sleep 1; } } $tests_ok = !$?; } else { # child POSIX::setsid() unless $^O eq "MSWin32"; my $c_ok; $|=1; if ($want_expect) { if ($self->_should_report('test')) { $CPAN::Frontend->mywarn("Reporting via CPAN::Reporter is currently ". "not supported when distroprefs specify ". "an interactive test\n"); } $c_ok = $self->_run_via_expect($system,'test',$expect_model) == 0; } elsif ( $self->_should_report('test') ) { $c_ok = CPAN::Reporter::test($self, $system); } else { $c_ok = system($system) == 0; } exit !$c_ok; } } # FORK $self->introduce_myself; my $but = $self->_make_test_illuminate_prereqs(); if ( $tests_ok ) { if ($but) { $CPAN::Frontend->mywarn("Tests succeeded but $but\n"); $self->{make_test} = CPAN::Distrostatus->new("NO $but"); $self->store_persistent_state; $self->post_test(); return $self->goodbye("[dependencies] -- NA"); } $CPAN::Frontend->myprint(" $system -- OK\n"); $self->{make_test} = CPAN::Distrostatus->new("YES"); $CPAN::META->is_tested($self->{build_dir},$self->{make_test}{TIME}); # probably impossible to need the next line because badtestcnt # has a lifespan of one command delete $self->{badtestcnt}; } else { if ($but) { $but .= "; additionally test harness failed"; $CPAN::Frontend->mywarn("$but\n"); $self->{make_test} = CPAN::Distrostatus->new("NO $but"); } elsif ( $self->{force_update} ) { $self->{make_test} = CPAN::Distrostatus->new( "NO but failure ignored because 'force' in effect" ); } elsif ($CPAN::Signal) { $self->{make_test} = CPAN::Distrostatus->new("NO -- Interrupted"); } else { $self->{make_test} = CPAN::Distrostatus->new("NO"); } $self->{badtestcnt}++; $CPAN::Frontend->mywarn(" $system -- NOT OK\n"); CPAN::Shell->optprint ("hint", sprintf ("//hint// to see the cpan-testers results for installing this module, try: reports %s\n", $self->pretty_id)); } $self->store_persistent_state; $self->post_test(); return $self->{force_update} ? 1 : !! $tests_ok; } sub _make_test_illuminate_prereqs { my($self) = @_; my @prereq; # local $CPAN::DEBUG = 16; # Distribution for my $m (sort keys %{$self->{sponsored_mods}}) { next unless $self->{sponsored_mods}{$m} > 0; my $m_obj = CPAN::Shell->expand("Module",$m) or next; # XXX we need available_version which reflects # $ENV{PERL5LIB} so that already tested but not yet # installed modules are counted. my $available_version = $m_obj->available_version; my $available_file = $m_obj->available_file; if ($available_version && !CPAN::Version->vlt($available_version,$self->{prereq_pm}{$m}) ) { CPAN->debug("m[$m] good enough available_version[$available_version]") if $CPAN::DEBUG; } elsif ($available_file && ( !$self->{prereq_pm}{$m} || $self->{prereq_pm}{$m} == 0 ) ) { # lex Class::Accessor::Chained::Fast which has no $VERSION CPAN->debug("m[$m] have available_file[$available_file]") if $CPAN::DEBUG; } else { push @prereq, $m unless $self->is_locally_optional(undef, $m); } } my $but; if (@prereq) { my $cnt = @prereq; my $which = join ",", @prereq; $but = $cnt == 1 ? "one dependency not OK ($which)" : "$cnt dependencies missing ($which)"; } $but; } sub _prefs_with_expect { my($self,$where) = @_; return unless my $prefs = $self->prefs; return unless my $where_prefs = $prefs->{$where}; if ($where_prefs->{expect}) { return { mode => "deterministic", timeout => 15, talk => $where_prefs->{expect}, }; } elsif ($where_prefs->{"eexpect"}) { return $where_prefs->{"eexpect"}; } return; } #-> sub CPAN::Distribution::clean ; sub clean { my($self) = @_; my $make = $self->{modulebuild} ? "Build" : "make"; $CPAN::Frontend->myprint(sprintf "Running %s clean for %s\n", $make, $self->pretty_id); unless (exists $self->{archived}) { $CPAN::Frontend->mywarn("Distribution seems to have never been unzipped". "/untarred, nothing done\n"); return 1; } unless (exists $self->{build_dir}) { $CPAN::Frontend->mywarn("Distribution has no own directory, nothing to do.\n"); return 1; } if (exists $self->{writemakefile} and $self->{writemakefile}->failed ) { $CPAN::Frontend->mywarn("No Makefile, don't know how to 'make clean'\n"); return 1; } EXCUSE: { my @e; exists $self->{make_clean} and $self->{make_clean} eq "YES" and push @e, "make clean already called once"; $CPAN::Frontend->myprint(join "", map {" $_\n"} @e) and return if @e; } chdir "$self->{build_dir}" or Carp::confess("Couldn't chdir to $self->{build_dir}: $!"); $self->debug("Changed directory to $self->{build_dir}") if $CPAN::DEBUG; if ($^O eq 'MacOS') { Mac::BuildTools::make_clean($self); return; } my $system; if ($self->{modulebuild}) { unless (-f "Build") { my $cwd = CPAN::anycwd(); $CPAN::Frontend->mywarn("Alert: no Build file available for 'clean $self->{id}". " in cwd[$cwd]. Danger, Will Robinson!"); $CPAN::Frontend->mysleep(5); } $system = sprintf "%s clean", $self->_build_command(); } else { $system = join " ", $self->_make_command(), "clean"; } my $system_ok = system($system) == 0; $self->introduce_myself; if ( $system_ok ) { $CPAN::Frontend->myprint(" $system -- OK\n"); # $self->force; # Jost Krieger pointed out that this "force" was wrong because # it has the effect that the next "install" on this distribution # will untar everything again. Instead we should bring the # object's state back to where it is after untarring. for my $k (qw( force_update install writemakefile make make_test )) { delete $self->{$k}; } $self->{make_clean} = CPAN::Distrostatus->new("YES"); } else { # Hmmm, what to do if make clean failed? $self->{make_clean} = CPAN::Distrostatus->new("NO"); $CPAN::Frontend->mywarn(qq{ $system -- NOT OK\n}); # 2006-02-27: seems silly to me to force a make now # $self->force("make"); # so that this directory won't be used again } $self->store_persistent_state; } #-> sub CPAN::Distribution::check_disabled ; sub check_disabled { my ($self) = @_; $self->debug("checking disabled id[$self->{ID}]") if $CPAN::DEBUG; if ($self->prefs->{disabled} && ! $self->{force_update}) { return sprintf( "Disabled via prefs file '%s' doc %d", $self->{prefs_file}, $self->{prefs_file_doc}, ); } return; } #-> sub CPAN::Distribution::goto ; sub goto { my($self,$goto) = @_; $goto = $self->normalize($goto); my $why = sprintf( "Goto '$goto' via prefs file '%s' doc %d", $self->{prefs_file}, $self->{prefs_file_doc}, ); $self->{unwrapped} = CPAN::Distrostatus->new("NO $why"); # 2007-07-16 akoenig : Better than NA would be if we could inherit # the status of the $goto distro but given the exceptional nature # of 'goto' I feel reluctant to implement it my $goodbye_message = "[goto] -- NA $why"; $self->goodbye($goodbye_message); # inject into the queue CPAN::Queue->delete($self->id); CPAN::Queue->jumpqueue({qmod => $goto, reqtype => $self->{reqtype}}); # and run where we left off my($method) = (caller(1))[3]; my $goto_do = CPAN->instance("CPAN::Distribution",$goto); $goto_do->called_for($self->called_for) unless $goto_do->called_for; $goto_do->{mandatory} ||= $self->{mandatory}; $goto_do->{reqtype} ||= $self->{reqtype}; $goto_do->{coming_from} = $self->pretty_id; $goto_do->$method(); CPAN::Queue->delete_first($goto); # XXX delete_first returns undef; is that what this should return # up the call stack, eg. return $sefl->goto($goto) -- xdg, 2012-04-04 } #-> sub CPAN::Distribution::shortcut_install ; # return values: undef means don't shortcut; 0 means shortcut as fail; # and 1 means shortcut as success sub shortcut_install { my ($self) = @_; $self->debug("checking previous install results[$self->{ID}]") if $CPAN::DEBUG; if (exists $self->{install}) { my $text = UNIVERSAL::can($self->{install},"text") ? $self->{install}->text : $self->{install}; if ($text =~ /^YES/) { $CPAN::META->is_installed($self->{build_dir}); return $self->success("Already done"); } elsif ($text =~ /is only/) { # e.g. 'is only build_requires': may be overruled later return $self->goodbye($text); } else { # comment in Todo on 2006-02-11; maybe retry? return $self->goodbye("Already tried without success"); } } for my $slot ( qw/later configure_requires_later/ ) { return $self->success($self->{$slot}) if $self->{$slot}; } return undef; } #-> sub CPAN::Distribution::is_being_sponsored ; # returns true if we find a distro object in the queue that has # sponsored this one sub is_being_sponsored { my($self) = @_; my $iterator = CPAN::Queue->iterator; QITEM: while (my $q = $iterator->()) { my $s = $q->as_string; my $obj = CPAN::Shell->expandany($s) or next QITEM; my $type = ref $obj; if ( $type eq 'CPAN::Distribution' ){ for my $module (sort keys %{$obj->{sponsored_mods} || {}}) { return 1 if grep { $_ eq $module } $self->containsmods; } } } return 0; } #-> sub CPAN::Distribution::install ; sub install { my($self) = @_; $self->pre_install(); if (exists $self->{cleanup_after_install_done}) { return $self->test; } $self->debug("checking goto id[$self->{ID}]") if $CPAN::DEBUG; if (my $goto = $self->prefs->{goto}) { $self->goto($goto); $self->post_install(); return; } unless ($self->test) { $self->post_install(); return; } if ( defined( my $sc = $self->shortcut_install ) ) { $self->post_install(); return $sc; } if ($CPAN::Signal) { delete $self->{force_update}; $self->post_install(); return; } my $builddir = $self->dir or $CPAN::Frontend->mydie("PANIC: Cannot determine build directory\n"); unless (chdir $builddir) { $CPAN::Frontend->mywarn("Couldn't chdir to '$builddir': $!"); $self->post_install(); return; } $self->debug("Changed directory to $self->{build_dir}") if $CPAN::DEBUG; my $make = $self->{modulebuild} ? "Build" : "make"; $CPAN::Frontend->myprint(sprintf "Running %s install for %s\n", $make, $self->pretty_id); if ($^O eq 'MacOS') { Mac::BuildTools::make_install($self); $self->post_install(); return; } my $system; if (my $commandline = $self->prefs->{install}{commandline}) { $system = $commandline; $ENV{PERL} = CPAN::find_perl(); } elsif ($self->{modulebuild}) { my($mbuild_install_build_command) = exists $CPAN::HandleConfig::keys{mbuild_install_build_command} && $CPAN::Config->{mbuild_install_build_command} ? $CPAN::Config->{mbuild_install_build_command} : $self->_build_command(); my $install_directive = $^O eq 'VMS' ? '"install"' : 'install'; $system = sprintf("%s %s %s", $mbuild_install_build_command, $install_directive, $CPAN::Config->{mbuild_install_arg}, ); } else { my($make_install_make_command) = $self->_make_install_make_command(); $system = sprintf("%s install %s", $make_install_make_command, $CPAN::Config->{make_install_arg}, ); } my($stderr) = $^O eq "MSWin32" || $^O eq 'VMS' ? "" : " 2>&1 "; my $brip = CPAN::HandleConfig->prefs_lookup($self, q{build_requires_install_policy}); $brip ||="ask/yes"; my $id = $self->id; my $reqtype = $self->{reqtype} ||= "c"; # in doubt it was a command my $want_install = "yes"; if ($reqtype eq "b") { if ($brip eq "no") { $want_install = "no"; } elsif ($brip =~ m|^ask/(.+)|) { my $default = $1; $default = "yes" unless $default =~ /^(y|n)/i; $want_install = CPAN::Shell::colorable_makemaker_prompt ("$id is just needed temporarily during building or testing. ". "Do you want to install it permanently?", $default); } } unless ($want_install =~ /^y/i) { my $is_only = "is only 'build_requires'"; $self->{install} = CPAN::Distrostatus->new("NO -- $is_only"); delete $self->{force_update}; $self->goodbye("Not installing because $is_only"); $self->post_install(); return; } local $ENV{PERL5LIB} = defined($ENV{PERL5LIB}) ? $ENV{PERL5LIB} : ($ENV{PERLLIB} || ""); local $ENV{PERL5OPT} = defined $ENV{PERL5OPT} ? $ENV{PERL5OPT} : ""; local $ENV{PERL_USE_UNSAFE_INC} = exists $ENV{PERL_USE_UNSAFE_INC} && defined $ENV{PERL_USE_UNSAFE_INC} ? $ENV{PERL_USE_UNSAFE_INC} : 1; # install $CPAN::META->set_perl5lib; local $ENV{PERL_MM_USE_DEFAULT} = 1 if $CPAN::Config->{use_prompt_default}; local $ENV{NONINTERACTIVE_TESTING} = 1 if $CPAN::Config->{use_prompt_default}; my $install_env; if ($self->prefs->{install}) { $install_env = $self->prefs->{install}{env}; } local @ENV{keys %$install_env} = values %$install_env if $install_env; if (! $run_allow_installing_within_test) { my($allow_installing, $why) = $self->_allow_installing; if (! $allow_installing) { $CPAN::Frontend->mywarn("Installation stopped: $why\n"); $self->introduce_myself; $self->{install} = CPAN::Distrostatus->new("NO -- installation stopped due $why"); $CPAN::Frontend->mywarn(" $system -- NOT OK\n"); delete $self->{force_update}; $self->post_install(); return; } } my($pipe) = FileHandle->new("$system $stderr |"); unless ($pipe) { $CPAN::Frontend->mywarn("Can't execute $system: $!"); $self->introduce_myself; $self->{install} = CPAN::Distrostatus->new("NO"); $CPAN::Frontend->mywarn(" $system -- NOT OK\n"); delete $self->{force_update}; $self->post_install(); return; } my($makeout) = ""; while (<$pipe>) { print $_; # intentionally NOT use Frontend->myprint because it # looks irritating when we markup in color what we # just pass through from an external program $makeout .= $_; } $pipe->close; my $close_ok = $? == 0; $self->introduce_myself; if ( $close_ok ) { $CPAN::Frontend->myprint(" $system -- OK\n"); $CPAN::META->is_installed($self->{build_dir}); $self->{install} = CPAN::Distrostatus->new("YES"); if ($CPAN::Config->{'cleanup_after_install'} && ! $self->is_dot_dist && ! $self->is_being_sponsored) { my $parent = File::Spec->catdir( $self->{build_dir}, File::Spec->updir ); chdir $parent or $CPAN::Frontend->mydie("Couldn't chdir to $parent: $!\n"); File::Path::rmtree($self->{build_dir}); my $yml = "$self->{build_dir}.yml"; if (-e $yml) { unlink $yml or $CPAN::Frontend->mydie("Couldn't unlink $yml: $!\n"); } $self->{cleanup_after_install_done}=1; } } else { $self->{install} = CPAN::Distrostatus->new("NO"); $CPAN::Frontend->mywarn(" $system -- NOT OK\n"); my $mimc = CPAN::HandleConfig->prefs_lookup($self, q{make_install_make_command}); if ( $makeout =~ /permission/s && $> > 0 && ( ! $mimc || $mimc eq (CPAN::HandleConfig->prefs_lookup($self, q{make})) ) ) { $CPAN::Frontend->myprint( qq{----\n}. qq{ You may have to su }. qq{to root to install the package\n}. qq{ (Or you may want to run something like\n}. qq{ o conf make_install_make_command 'sudo make'\n}. qq{ to raise your permissions.} ); } } delete $self->{force_update}; unless ($CPAN::Config->{'cleanup_after_install'}) { $self->store_persistent_state; } $self->post_install(); return !! $close_ok; } sub blib_pm_walk { my @queue = grep { -e $_ } File::Spec->catdir("blib","lib"), File::Spec->catdir("blib","arch"); return sub { LOOP: { if (@queue) { my $file = shift @queue; if (-d $file) { my $dh; opendir $dh, $file or next; my @newfiles = map { my @ret; my $maybedir = File::Spec->catdir($file, $_); if (-d $maybedir) { unless (File::Spec->catdir("blib","arch","auto") eq $maybedir) { # prune the blib/arch/auto directory, no pm files there @ret = $maybedir; } } elsif (/\.pm$/) { my $mustbefile = File::Spec->catfile($file, $_); if (-f $mustbefile) { @ret = $mustbefile; } } @ret; } grep { $_ ne "." && $_ ne ".." } readdir $dh; push @queue, @newfiles; redo LOOP; } else { return $file; } } else { return; } } }; } sub _allow_installing { my($self) = @_; my $id = my $pretty_id = $self->pretty_id; if ($self->{CALLED_FOR}) { $id .= " (called for $self->{CALLED_FOR})"; } my $allow_down = CPAN::HandleConfig->prefs_lookup($self,q{allow_installing_module_downgrades}); $allow_down ||= "ask/yes"; my $allow_outdd = CPAN::HandleConfig->prefs_lookup($self,q{allow_installing_outdated_dists}); $allow_outdd ||= "ask/yes"; return 1 if $allow_down eq "yes" && $allow_outdd eq "yes"; if (($allow_outdd ne "yes") && ! $CPAN::META->has_inst('CPAN::DistnameInfo')) { return 1 if grep { $_ eq 'CPAN::DistnameInfo'} $self->containsmods; if ($allow_outdd ne "yes") { $CPAN::Frontend->mywarn("The current configuration of allow_installing_outdated_dists is '$allow_outdd', but for this option we would need 'CPAN::DistnameInfo' installed. Please install 'CPAN::DistnameInfo' as soon as possible. As long as we are not equipped with 'CPAN::DistnameInfo' this option does not take effect\n"); $allow_outdd = "yes"; } } return 1 if $allow_down eq "yes" && $allow_outdd eq "yes"; my($dist_version, $dist_dist); if ($allow_outdd ne "yes"){ my $dni = CPAN::DistnameInfo->new($pretty_id); $dist_version = $dni->version; $dist_dist = $dni->dist; } my $iterator = blib_pm_walk(); my(@down,@outdd); while (my $file = $iterator->()) { my $version = CPAN::Module->parse_version($file); my($volume, $directories, $pmfile) = File::Spec->splitpath( $file ); my @dirs = File::Spec->splitdir( $directories ); my(@blib_plus1) = splice @dirs, 0, 2; my($pmpath) = File::Spec->catfile(grep { length($_) } @dirs, $pmfile); unless ($allow_down eq "yes") { if (my $inst_file = $self->_file_in_path($pmpath, \@INC)) { my $inst_version = CPAN::Module->parse_version($inst_file); my $cmp = CPAN::Version->vcmp($version, $inst_version); if ($cmp) { if ($cmp < 0) { push @down, { pmpath => $pmpath, version => $version, inst_version => $inst_version }; } } if (@down) { my $why = "allow_installing_module_downgrades: $id contains downgrading module(s) (e.g. '$down[0]{pmpath}' would downgrade installed '$down[0]{inst_version}' to '$down[0]{version}')"; if (my($default) = $allow_down =~ m|^ask/(.+)|) { $default = "yes" unless $default =~ /^(y|n)/i; my $answer = CPAN::Shell::colorable_makemaker_prompt ("$why. Do you want to allow installing it?", $default, "colorize_warn"); $allow_down = $answer =~ /^\s*y/i ? "yes" : "no"; } if ($allow_down eq "no") { return (0, $why); } } } } unless ($allow_outdd eq "yes") { my @pmpath = (@dirs, $pmfile); $pmpath[-1] =~ s/\.pm$//; my $mo = CPAN::Shell->expand("Module",join "::", grep { length($_) } @pmpath); if ($mo) { my $cpan_version = $mo->cpan_version; my $is_lower = CPAN::Version->vlt($version, $cpan_version); my $other_dist; if (my $mo_dist = $mo->distribution) { $other_dist = $mo_dist->pretty_id; my $dni = CPAN::DistnameInfo->new($other_dist); if ($dni->dist eq $dist_dist){ if (CPAN::Version->vgt($dni->version, $dist_version)) { push @outdd, { pmpath => $pmpath, cpan_path => $dni->pathname, dist_version => $dni->version, dist_dist => $dni->dist, }; } } } } if (@outdd && $allow_outdd ne "yes") { my $why = "allow_installing_outdated_dists: $id contains module(s) that are indexed on the CPAN with a different distro: (e.g. '$outdd[0]{pmpath}' is indexed with '$outdd[0]{cpan_path}')"; if ($outdd[0]{dist_dist} eq $dist_dist) { $why .= ", and this has a higher distribution-version, i.e. version '$outdd[0]{dist_version}' is higher than '$dist_version')"; } if (my($default) = $allow_outdd =~ m|^ask/(.+)|) { $default = "yes" unless $default =~ /^(y|n)/i; my $answer = CPAN::Shell::colorable_makemaker_prompt ("$why. Do you want to allow installing it?", $default, "colorize_warn"); $allow_outdd = $answer =~ /^\s*y/i ? "yes" : "no"; } if ($allow_outdd eq "no") { return (0, $why); } } } } return 1; } sub _file_in_path { # similar to CPAN::Module::_file_in_path my($self,$pmpath,$incpath) = @_; my($dir,@packpath); foreach $dir (@$incpath) { my $pmfile = File::Spec->catfile($dir,$pmpath); if (-f $pmfile) { return $pmfile; } } return; } sub introduce_myself { my($self) = @_; $CPAN::Frontend->myprint(sprintf(" %s\n",$self->pretty_id)); } #-> sub CPAN::Distribution::dir ; sub dir { shift->{build_dir}; } #-> sub CPAN::Distribution::perldoc ; sub perldoc { my($self) = @_; my($dist) = $self->id; my $package = $self->called_for; if ($CPAN::META->has_inst("Pod::Perldocs")) { my($perl) = $self->perl or $CPAN::Frontend->mydie("Couldn't find executable perl\n"); my @args = ($perl, q{-MPod::Perldocs}, q{-e}, q{Pod::Perldocs->run()}, $package); my($wstatus); unless ( ($wstatus = system(@args)) == 0 ) { my $estatus = $wstatus >> 8; $CPAN::Frontend->myprint(qq{ Function system("@args") returned status $estatus (wstat $wstatus) }); } } else { $self->_display_url( $CPAN::Defaultdocs . $package ); } } #-> sub CPAN::Distribution::_check_binary ; sub _check_binary { my ($dist,$shell,$binary) = @_; my ($pid,$out); $CPAN::Frontend->myprint(qq{ + _check_binary($binary)\n}) if $CPAN::DEBUG; if ($CPAN::META->has_inst("File::Which")) { return File::Which::which($binary); } else { local *README; $pid = open README, "which $binary|" or $CPAN::Frontend->mywarn(qq{Could not fork 'which $binary': $!\n}); return unless $pid; while (<README>) { $out .= $_; } close README or $CPAN::Frontend->mywarn("Could not run 'which $binary': $!\n") and return; } $CPAN::Frontend->myprint(qq{ + $out \n}) if $CPAN::DEBUG && $out; return $out; } #-> sub CPAN::Distribution::_display_url ; sub _display_url { my($self,$url) = @_; my($res,$saved_file,$pid,$out); $CPAN::Frontend->myprint(qq{ + _display_url($url)\n}) if $CPAN::DEBUG; # should we define it in the config instead? my $html_converter = "html2text.pl"; my $web_browser = $CPAN::Config->{'lynx'} || undef; my $web_browser_out = $web_browser ? CPAN::Distribution->_check_binary($self,$web_browser) : undef; if ($web_browser_out) { # web browser found, run the action my $browser = CPAN::HandleConfig->safe_quote($CPAN::Config->{'lynx'}); $CPAN::Frontend->myprint(qq{system[$browser $url]}) if $CPAN::DEBUG; $CPAN::Frontend->myprint(qq{ Displaying URL $url with browser $browser }); $CPAN::Frontend->mysleep(1); system("$browser $url"); if ($saved_file) { 1 while unlink($saved_file) } } else { # web browser not found, let's try text only my $html_converter_out = CPAN::Distribution->_check_binary($self,$html_converter); $html_converter_out = CPAN::HandleConfig->safe_quote($html_converter_out); if ($html_converter_out ) { # html2text found, run it $saved_file = CPAN::Distribution->_getsave_url( $self, $url ); $CPAN::Frontend->mydie(qq{ERROR: problems while getting $url\n}) unless defined($saved_file); local *README; $pid = open README, "$html_converter $saved_file |" or $CPAN::Frontend->mydie(qq{ Could not fork '$html_converter $saved_file': $!}); my($fh,$filename); if ($CPAN::META->has_usable("File::Temp")) { $fh = File::Temp->new( dir => File::Spec->tmpdir, template => 'cpan_htmlconvert_XXXX', suffix => '.txt', unlink => 0, ); $filename = $fh->filename; } else { $filename = "cpan_htmlconvert_$$.txt"; $fh = FileHandle->new(); open $fh, ">$filename" or die; } while (<README>) { $fh->print($_); } close README or $CPAN::Frontend->mydie(qq{Could not run '$html_converter $saved_file': $!}); my $tmpin = $fh->filename; $CPAN::Frontend->myprint(sprintf(qq{ Run '%s %s' and saved output to %s\n}, $html_converter, $saved_file, $tmpin, )) if $CPAN::DEBUG; close $fh; local *FH; open FH, $tmpin or $CPAN::Frontend->mydie(qq{Could not open "$tmpin": $!}); my $fh_pager = FileHandle->new; local($SIG{PIPE}) = "IGNORE"; my $pager = $CPAN::Config->{'pager'} || "cat"; $fh_pager->open("|$pager") or $CPAN::Frontend->mydie(qq{ Could not open pager '$pager': $!}); $CPAN::Frontend->myprint(qq{ Displaying URL $url with pager "$pager" }); $CPAN::Frontend->mysleep(1); $fh_pager->print(<FH>); $fh_pager->close; } else { # coldn't find the web browser or html converter $CPAN::Frontend->myprint(qq{ You need to install lynx or $html_converter to use this feature.}); } } } #-> sub CPAN::Distribution::_getsave_url ; sub _getsave_url { my($dist, $shell, $url) = @_; $CPAN::Frontend->myprint(qq{ + _getsave_url($url)\n}) if $CPAN::DEBUG; my($fh,$filename); if ($CPAN::META->has_usable("File::Temp")) { $fh = File::Temp->new( dir => File::Spec->tmpdir, template => "cpan_getsave_url_XXXX", suffix => ".html", unlink => 0, ); $filename = $fh->filename; } else { $fh = FileHandle->new; $filename = "cpan_getsave_url_$$.html"; } my $tmpin = $filename; if ($CPAN::META->has_usable('LWP')) { $CPAN::Frontend->myprint("Fetching with LWP: $url "); my $Ua; CPAN::LWP::UserAgent->config; eval { $Ua = CPAN::LWP::UserAgent->new; }; if ($@) { $CPAN::Frontend->mywarn("ERROR: CPAN::LWP::UserAgent->new dies with $@\n"); return; } else { my($var); $Ua->proxy('http', $var) if $var = $CPAN::Config->{http_proxy} || $ENV{http_proxy}; $Ua->no_proxy($var) if $var = $CPAN::Config->{no_proxy} || $ENV{no_proxy}; } my $req = HTTP::Request->new(GET => $url); $req->header('Accept' => 'text/html'); my $res = $Ua->request($req); if ($res->is_success) { $CPAN::Frontend->myprint(" + request successful.\n") if $CPAN::DEBUG; print $fh $res->content; close $fh; $CPAN::Frontend->myprint(qq{ + saved content to $tmpin \n}) if $CPAN::DEBUG; return $tmpin; } else { $CPAN::Frontend->myprint(sprintf( "LWP failed with code[%s], message[%s]\n", $res->code, $res->message, )); return; } } else { $CPAN::Frontend->mywarn(" LWP not available\n"); return; } } #-> sub CPAN::Distribution::_build_command sub _build_command { my($self) = @_; if ($^O eq "MSWin32") { # special code needed at least up to # Module::Build 0.2611 and 0.2706; a fix # in M:B has been promised 2006-01-30 my($perl) = $self->perl or $CPAN::Frontend->mydie("Couldn't find executable perl\n"); return "$perl ./Build"; } elsif ($^O eq 'VMS') { return "$^X Build.com"; } return "./Build"; } #-> sub CPAN::Distribution::_should_report sub _should_report { my($self, $phase) = @_; die "_should_report() requires a 'phase' argument" if ! defined $phase; return unless $CPAN::META->has_usable("CPAN::Reporter"); # configured my $test_report = CPAN::HandleConfig->prefs_lookup($self, q{test_report}); return unless $test_report; # don't repeat if we cached a result return $self->{should_report} if exists $self->{should_report}; # don't report if we generated a Makefile.PL if ( $self->{had_no_makefile_pl} ) { $CPAN::Frontend->mywarn( "Will not send CPAN Testers report with generated Makefile.PL.\n" ); return $self->{should_report} = 0; } # available if ( ! $CPAN::META->has_inst("CPAN::Reporter")) { $CPAN::Frontend->mywarnonce( "CPAN::Reporter not installed. No reports will be sent.\n" ); return $self->{should_report} = 0; } # capable my $crv = CPAN::Reporter->VERSION; if ( CPAN::Version->vlt( $crv, 0.99 ) ) { # don't cache $self->{should_report} -- need to check each phase if ( $phase eq 'test' ) { return 1; } else { $CPAN::Frontend->mywarn( "Reporting on the '$phase' phase requires CPAN::Reporter 0.99, but \n" . "you only have version $crv\. Only 'test' phase reports will be sent.\n" ); return; } } # appropriate if ($self->is_dot_dist) { $CPAN::Frontend->mywarn("Reporting via CPAN::Reporter is disabled ". "for local directories\n"); return $self->{should_report} = 0; } if ($self->prefs->{patches} && @{$self->prefs->{patches}} && $self->{patched} ) { $CPAN::Frontend->mywarn("Reporting via CPAN::Reporter is disabled ". "when the source has been patched\n"); return $self->{should_report} = 0; } # proceed and cache success return $self->{should_report} = 1; } #-> sub CPAN::Distribution::reports sub reports { my($self) = @_; my $pathname = $self->id; $CPAN::Frontend->myprint("Distribution: $pathname\n"); unless ($CPAN::META->has_inst("CPAN::DistnameInfo")) { $CPAN::Frontend->mydie("CPAN::DistnameInfo not installed; cannot continue"); } unless ($CPAN::META->has_usable("LWP")) { $CPAN::Frontend->mydie("LWP not installed; cannot continue"); } unless ($CPAN::META->has_usable("File::Temp")) { $CPAN::Frontend->mydie("File::Temp not installed; cannot continue"); } my $format; if ($CPAN::META->has_inst("YAML::XS") || $CPAN::META->has_inst("YAML::Syck")){ $format = 'yaml'; } elsif (!$format && $CPAN::META->has_inst("JSON::PP") ) { $format = 'json'; } else { $CPAN::Frontend->mydie("JSON::PP not installed, cannot continue"); } my $d = CPAN::DistnameInfo->new($pathname); my $dist = $d->dist; # "CPAN-DistnameInfo" my $version = $d->version; # "0.02" my $maturity = $d->maturity; # "released" my $filename = $d->filename; # "CPAN-DistnameInfo-0.02.tar.gz" my $cpanid = $d->cpanid; # "GBARR" my $distvname = $d->distvname; # "CPAN-DistnameInfo-0.02" my $url = sprintf "http://www.cpantesters.org/show/%s.%s", $dist, $format; CPAN::LWP::UserAgent->config; my $Ua; eval { $Ua = CPAN::LWP::UserAgent->new; }; if ($@) { $CPAN::Frontend->mydie("CPAN::LWP::UserAgent->new dies with $@\n"); } $CPAN::Frontend->myprint("Fetching '$url'..."); my $resp = $Ua->get($url); unless ($resp->is_success) { $CPAN::Frontend->mydie(sprintf "Could not download '%s': %s\n", $url, $resp->code); } $CPAN::Frontend->myprint("DONE\n\n"); my $unserialized; if ( $format eq 'yaml' ) { my $yaml = $resp->content; # what a long way round! my $fh = File::Temp->new( dir => File::Spec->tmpdir, template => 'cpan_reports_XXXX', suffix => '.yaml', unlink => 0, ); my $tfilename = $fh->filename; print $fh $yaml; close $fh or $CPAN::Frontend->mydie("Could not close '$tfilename': $!"); $unserialized = CPAN->_yaml_loadfile($tfilename)->[0]; unlink $tfilename or $CPAN::Frontend->mydie("Could not unlink '$tfilename': $!"); } else { require JSON::PP; $unserialized = JSON::PP->new->utf8->decode($resp->content); } my %other_versions; my $this_version_seen; for my $rep (@$unserialized) { my $rversion = $rep->{version}; if ($rversion eq $version) { unless ($this_version_seen++) { $CPAN::Frontend->myprint ("$rep->{version}:\n"); } my $arch = $rep->{archname} || $rep->{platform} || '????'; my $grade = $rep->{action} || $rep->{status} || '????'; my $ostext = $rep->{ostext} || ucfirst($rep->{osname}) || '????'; $CPAN::Frontend->myprint (sprintf("%1s%1s%-4s %s on %s %s (%s)\n", $arch eq $Config::Config{archname}?"*":"", $grade eq "PASS"?"+":$grade eq"FAIL"?"-":"", $grade, $rep->{perl}, $ostext, $rep->{osvers}, $arch, )); } else { $other_versions{$rep->{version}}++; } } unless ($this_version_seen) { $CPAN::Frontend->myprint("No reports found for version '$version' Reports for other versions:\n"); for my $v (sort keys %other_versions) { $CPAN::Frontend->myprint(" $v\: $other_versions{$v}\n"); } } $url = substr($url,0,-4) . 'html'; $CPAN::Frontend->myprint("See $url for details\n"); } 1; PK�����A0]z6��6����perl5/CPAN/Debug.pmnu�6$��������# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- package CPAN::Debug; use strict; use vars qw($VERSION); $VERSION = "5.5001"; # module is internal to CPAN.pm %CPAN::DEBUG = qw[ CPAN 1 Index 2 InfoObj 4 Author 8 Distribution 16 Bundle 32 Module 64 CacheMgr 128 Complete 256 FTP 512 Shell 1024 Eval 2048 HandleConfig 4096 Tarzip 8192 Version 16384 Queue 32768 FirstTime 65536 ]; $CPAN::DEBUG ||= 0; #-> sub CPAN::Debug::debug ; sub debug { my($self,$arg) = @_; my @caller; my $i = 0; while () { my(@c) = (caller($i))[0 .. ($i ? 3 : 2)]; last unless defined $c[0]; push @caller, \@c; for (0,3) { last if $_ > $#c; $c[$_] =~ s/.*:://; } for (1) { $c[$_] =~ s|.*/||; } last if ++$i>=3; } pop @caller; if ($CPAN::DEBUG{$caller[0][0]} & $CPAN::DEBUG) { if ($arg and ref $arg) { eval { require Data::Dumper }; if ($@) { $CPAN::Frontend->myprint("Debug(\n" . $arg->as_string . ")\n"); } else { $CPAN::Frontend->myprint("Debug(\n" . Data::Dumper::Dumper($arg) . ")\n"); } } else { my $outer = ""; local $" = ","; if (@caller>1) { $outer = ",[@{$caller[1]}]"; } $CPAN::Frontend->myprint("Debug(@{$caller[0]}$outer): $arg\n"); } } } 1; __END__ =head1 NAME CPAN::Debug - internal debugging for CPAN.pm =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut PK�����A0]e������perl5/LWP/Protocol/https.pmnu�6$��������package LWP::Protocol::https; use strict; use warnings; our $VERSION = '6.14'; use parent qw(LWP::Protocol::http); require Net::HTTPS; sub socket_type { return "https"; } sub _extra_sock_opts { my $self = shift; my %ssl_opts = %{$self->{ua}{ssl_opts} || {}}; if (delete $ssl_opts{verify_hostname}) { $ssl_opts{SSL_verify_mode} ||= 1; $ssl_opts{SSL_verifycn_scheme} = 'www'; } else { if ( $Net::HTTPS::SSL_SOCKET_CLASS eq 'Net::SSL' ) { $ssl_opts{SSL_verifycn_scheme} = ''; } else { $ssl_opts{SSL_verifycn_scheme} = 'none'; } } if ($ssl_opts{SSL_verify_mode}) { unless (exists $ssl_opts{SSL_ca_file} || exists $ssl_opts{SSL_ca_path}) { if ($Net::HTTPS::SSL_SOCKET_CLASS eq 'IO::Socket::SSL' && defined &IO::Socket::SSL::default_ca && IO::Socket::SSL::default_ca() ) { # IO::Socket::SSL has a usable default CA } elsif ( my $cafile = eval { require Mozilla::CA; Mozilla::CA::SSL_ca_file() }) { # use Mozilla::CA $ssl_opts{SSL_ca_file} = $cafile; } else { die <<'EOT'; Can't verify SSL peers without knowing which Certificate Authorities to trust. This problem can be fixed by either setting the PERL_LWP_SSL_CA_FILE environment variable to the file where your trusted CA are, or by installing the Mozilla::CA module for set of commonly trusted CAs. To completely disable the verification that you talk to the correct SSL peer you can set SSL_verify_mode to 0 within ssl_opts. But, if you do this you can't be sure that you communicate with the expected peer. EOT } } } $self->{ssl_opts} = \%ssl_opts; return (%ssl_opts, MultiHomed => 1, $self->SUPER::_extra_sock_opts); } # This is a subclass of LWP::Protocol::http. # That parent class calls ->_check_sock() during the # request method. This allows us to hook in and run checks # sub _check_sock # { # my($self, $req, $sock) = @_; # } sub _get_sock_info { my $self = shift; $self->SUPER::_get_sock_info(@_); my($res, $sock) = @_; if ($sock->can('get_sslversion') and my $sslversion = $sock->get_sslversion) { $res->header("Client-SSL-Version" => $sslversion); } $res->header("Client-SSL-Cipher" => $sock->get_cipher); my $cert = $sock->get_peer_certificate; if ($cert) { $res->header("Client-SSL-Cert-Subject" => $cert->subject_name); $res->header("Client-SSL-Cert-Issuer" => $cert->issuer_name); } if (!$self->{ssl_opts}{SSL_verify_mode}) { $res->push_header("Client-SSL-Warning" => "Peer certificate not verified"); } elsif (!$self->{ssl_opts}{SSL_verifycn_scheme}) { $res->push_header("Client-SSL-Warning" => "Peer hostname match with certificate not verified"); } $res->header("Client-SSL-Socket-Class" => $Net::HTTPS::SSL_SOCKET_CLASS); } # upgrade plain socket to SSL, used for CONNECT tunnel when proxying https # will only work if the underlying socket class of Net::HTTPS is # IO::Socket::SSL, but code will only be called in this case if ( $Net::HTTPS::SSL_SOCKET_CLASS->can('start_SSL')) { *_upgrade_sock = sub { my ($self,$sock,$url) = @_; # SNI should be passed there only if it is not an IP address. # Details: https://github.com/libwww-perl/libwww-perl/issues/449#issuecomment-1896175509 my $host = $url->host() =~ m/:|^[\d.]+$/s ? undef : $url->host(); $sock = LWP::Protocol::https::Socket->start_SSL( $sock, SSL_verifycn_name => $url->host, SSL_hostname => $host, $self->_extra_sock_opts, ); $@ = LWP::Protocol::https::Socket->errstr if ! $sock; return $sock; } } #----------------------------------------------------------- package LWP::Protocol::https::Socket; use parent -norequire, qw(Net::HTTPS LWP::Protocol::http::SocketMethods); 1; __END__ =head1 NAME LWP::Protocol::https - Provide https support for LWP::UserAgent =head1 SYNOPSIS use LWP::UserAgent; $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 1 }); $res = $ua->get("https://www.example.com"); # specify a CA path $ua = LWP::UserAgent->new( ssl_opts => { SSL_ca_path => '/etc/ssl/certs', verify_hostname => 1, } ); =head1 DESCRIPTION The LWP::Protocol::https module provides support for using https schemed URLs with LWP. This module is a plug-in to the LWP protocol handling, so you don't use it directly. Once the module is installed LWP is able to access sites using HTTP over SSL/TLS. If hostname verification is requested by LWP::UserAgent's C<ssl_opts>, and neither C<SSL_ca_file> nor C<SSL_ca_path> is set, then C<SSL_ca_file> is implied to be the one provided by L<Mozilla::CA>. If the Mozilla::CA module isn't available SSL requests will fail. Either install this module, set up an alternative C<SSL_ca_file> or disable hostname verification. This module used to be bundled with the libwww-perl, but it was unbundled in v6.02 in order to be able to declare its dependencies properly for the CPAN tool-chain. Applications that need https support can just declare their dependency on LWP::Protocol::https and will no longer need to know what underlying modules to install. =head1 SEE ALSO L<IO::Socket::SSL>, L<Crypt::SSLeay>, L<Mozilla::CA> =head1 COPYRIGHT & LICENSE Copyright (c) 1997-2011 Gisle Aas. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut PK�����A0]|1��1����perl5/cPanelUserConfig.pmnu�[��������# cpanel - cPanelUserConfig.pm Copyright(c) 2021 cPanel, L.L.C. # All Rights Reserved. # copyright@cpanel.net http://cpanel.net # This code is subject to the cPanel license. Unauthorized copying is prohibited BEGIN { if ( $> != 0 ) { my $b__dir = ( getpwuid($>) )[7] . '/perl'; unshift @INC, $b__dir . '5/lib/perl5', $b__dir . '5/lib/perl5/x86_64-linux-thread-multi', map { $b__dir . $_ } grep { $_ ne '.' } @INC; } } 1; PK�����A0]-*��*����perl5/Canary/Stability.pmnu�6$��������=head1 NAME Canary::Stability - canary to check perl compatibility for schmorp's modules =head1 SYNOPSIS # in Makefile.PL use Canary::Stability DISTNAME => 2001, MINIMUM_PERL_VERSION; =head1 DESCRIPTION This module is used by Schmorp's modules during configuration stage to test the installed perl for compatibility with his modules. It's not, at this stage, meant as a tool for other module authors, although in principle nothing prevents them from subscribing to the same ideas. See the F<Makefile.PL> in L<Coro> or L<AnyEvent> for usage examples. =cut package Canary::Stability; BEGIN { $VERSION = 2013; } sub sgr { # we just assume ANSI almost everywhere # red 31, yellow 33, green 32 local $| = 1; $ENV{PERL_CANARY_STABILITY_COLOUR} ne 0 and ((-t STDOUT and length $ENV{TERM}) or $ENV{PERL_CANARY_STABILITY_COLOUR}) and print "\e[$_[0]m"; } sub import { my (undef, $distname, $minvers, $minperl) = @_; $ENV{PERL_CANARY_STABILITY_DISABLE} and return; $minperl ||= 5.008002; print <<EOF; *** *** Canary::Stability COMPATIBILITY AND SUPPORT CHECK *** ================================================= *** *** Hi! *** *** I do my best to provide predictable and reliable software. *** *** However, in recent releases, P5P (who maintain perl) have been *** introducing regressions that are sometimes subtle and at other times *** catastrophic, often for personal preferences with little or no concern *** for existing code, most notably CPAN. *** *** For this reason, it has become very hard for me to maintain the level *** of reliability and support I have committed myself to in the past, at *** least with some perl versions: I simply can't keep up working around new *** bugs or gratituous incompatibilities, and in turn you might suffer from *** unanticipated problems. *** *** Therefore I have introduced a support and compatibility check, the results *** of which follow below, together with a FAQ and some recommendations. *** *** This check is just to let you know that there might be a risk, so you can *** make judgement calls on how to proceed - it will not keep the module from *** installing or working. *** EOF if ($minvers > $VERSION) { sgr 33; print <<EOF; *** The stability canary says: (nothing, it died of old age). *** *** Your Canary::Stability module (used by $distname) is too old. *** This is not a fatal problem - while you might want to upgrade to version *** $minvers (currently installed version: $VERSION) to get better support *** status testing, you might also not want to care at all, and all will *** be well as long $distname works well enough for you, as the stability *** canary is only used when installing the distribution. *** EOF } elsif ($] < $minperl) { sgr 33; print <<EOF; *** The stability canary says: chirp (it seems concerned about something). *** *** Your perl version ($]) is older than the $distname distribution *** likes ($minperl). This is not a fatal problem - the module might work *** well with your version of perl, but it does mean the author likely *** won't do anything to make it work if it breaks. *** EOF if ($ENV{AUTOMATED_TESTING}) { print <<EOF; *** Since this is an AUTOMATED_TESTING environment, the stability canary *** decided to fail cleanly here, rather than to generate a false test *** result. *** EOF exit 0; } } elsif (defined $Internals::StabilityBranchVersion) { # note to people studying this modules sources: # the above test is not considered a clean or stable way to # test for the stability branch. sgr 32; print <<EOF; *** The stability canary says: chirp! chirp! (it seems to be quite excited) *** *** It seems you are running schmorp's stability branch of perl. *** All should be well, and if it isn't, you should report this as a bug *** to the $distname author. *** EOF } elsif ($] < 5.021) { #sgr 32; print <<EOF; *** The stability canary says: chirp! chirp! (it seems to be quite happy) *** *** Your version of perl ($]) is quite supported by $distname, nothing *** else to be said, hope it comes in handy. *** EOF } else { sgr 31; print <<EOF; *** The stability canary says: (nothing, it was driven away by harsh weather) *** *** It seems you are running perl version $], likely the "official" or *** "standard" version. While there is nothing wrong with doing that, *** standard perl versions 5.022 and up are not supported by $distname. *** While this might be fatal, it might also be all right - if you run into *** problems, you might want to downgrade your perl or switch to the *** stability branch. *** *** If everything works fine, you can ignore this message. *** EOF sgr 0; print <<EOF; *** *** Stability canary mini-FAQ: *** *** Do I need to do anything? *** With luck, no. While some distributions are known to fail *** already, most should probably work. This message is here *** to alert you that your perl is not supported by $distname, *** and if things go wrong, you either need to downgrade, or *** sidegrade to the stability variant of your perl version, *** or simply live with the consequences. *** *** What is this canary thing? *** It's purpose is to check support status of $distname with *** respect to your perl version. *** *** What is this "stability branch"? *** It's a branch or fork of the official perl, by schmorp, to *** improve stability and compatibility with existing modules. *** *** How can I skip this prompt on automated installs? *** Set PERL_CANARY_STABILITY_NOPROMPT=1 in your environment. *** More info is in the Canary::Stability manpage. *** *** Long version of this FAQ: http://stableperl.schmorp.de/faq.html *** Stability Branch homepage: http://stableperl.schmorp.de/ *** EOF unless ($ENV{PERL_CANARY_STABILITY_NOPROMPT}) { require ExtUtils::MakeMaker; ExtUtils::MakeMaker::prompt ("Continue anyways? ", "y") =~ /^y/i or die "FATAL: User aborted configuration of $distname.\n"; } } sgr 0; } =head1 ENVIRONMENT VARIABLES =over 4 =item C<PERL_CANARY_STABILITY_NOPROMPT=1> Do not prompt the user on alert messages. =item C<PERL_CANARY_STABILITY_COLOUR=0> Disable use of colour. =item C<PERL_CANARY_STABILITY_COLOUR=1> Force use of colour. =item C<PERL_CANARY_STABILITY_DISABLE=1> Disable this modules functionality completely. =item C<AUTOMATED_TESTING=1> When this variable is set to a true value and the perl minimum version requirement is not met, the module will exit, which should skip testing under automated testing environments. This is done to avoid false failure or success reports when the chances of success are already quite low and the failures are not supported by the author. =back =head1 AUTHOR Marc Lehmann <schmorp@schmorp.de> http://software.schmorp.de/pkg/Canary-Stability.html =cut 1 PK�����A0]Gp��p����perl5/Switch.pmnu�6$��������package Switch; use 5.005; use strict; use vars qw($VERSION); use Carp; use if $] >= 5.011, 'deprecate'; $VERSION = '2.17'; # LOAD FILTERING MODULE... use Filter::Util::Call; sub __(); # CATCH ATTEMPTS TO CALL case OUTSIDE THE SCOPE OF ANY switch $::_S_W_I_T_C_H = sub { croak "case/when statement not in switch/given block" }; my $offset; my $fallthrough; my ($Perl5, $Perl6) = (0,0); sub import { $fallthrough = grep /\bfallthrough\b/, @_; $offset = (caller)[2]+1; filter_add({}) unless @_>1 && $_[1] eq 'noimport'; my $pkg = caller; no strict 'refs'; for ( qw( on_defined on_exists ) ) { *{"${pkg}::$_"} = \&$_; } *{"${pkg}::__"} = \&__ if grep /__/, @_; $Perl6 = 1 if grep(/Perl\s*6/i, @_); $Perl5 = 1 if grep(/Perl\s*5/i, @_) || !grep(/Perl\s*6/i, @_); 1; } sub unimport { filter_del() } sub filter { my($self) = @_ ; local $Switch::file = (caller)[1]; my $status = 1; $status = filter_read(1_000_000); return $status if $status<0; $_ = filter_blocks($_,$offset); $_ = "# line $offset\n" . $_ if $offset; undef $offset; return $status; } use Text::Balanced ':ALL'; sub line { my ($pretext,$offset) = @_; ($pretext=~tr/\n/\n/)+($offset||0); } sub is_block { local $SIG{__WARN__}=sub{die$@}; local $^W=1; my $ishash = defined eval 'my $hr='.$_[0]; undef $@; return !$ishash; } my $pod_or_DATA = qr/ ^=[A-Za-z] .*? ^=cut (?![A-Za-z]) .*? $ | ^__(DATA|END)__\n.* /smx; my $casecounter = 1; sub filter_blocks { my ($source, $line) = @_; return $source unless $Perl5 && $source =~ /case|switch/ || $Perl6 && $source =~ /when|given|default/; pos $source = 0; my $text = ""; component: while (pos $source < length $source) { if ($source =~ m/(\G\s*use\s+Switch\b)/gc) { $text .= q{use Switch 'noimport'}; next component; } my @pos = Text::Balanced::_match_quotelike(\$source,qr/\s*/,1,0); if (defined $pos[0]) { my $pre = substr($source,$pos[0],$pos[1]); # matched prefix my $iEol; if( substr($source,$pos[4],$pos[5]) eq '/' && # 1st delimiter substr($source,$pos[2],$pos[3]) eq '' && # no op like 'm' index( substr($source,$pos[16],$pos[17]), 'x' ) == -1 && # no //x ($iEol = index( $source, "\n", $pos[4] )) > 0 && $iEol < $pos[8] ){ # embedded newlines # If this is a pattern, it isn't compatible with Switch. Backup past 1st '/'. pos( $source ) = $pos[6]; $text .= $pre . substr($source,$pos[2],$pos[6]-$pos[2]); } else { $text .= $pre . substr($source,$pos[2],$pos[18]-$pos[2]); } next component; } if ($source =~ m/(\G\s*$pod_or_DATA)/gc) { $text .= $1; next component; } @pos = Text::Balanced::_match_variable(\$source,qr/\s*/); if (defined $pos[0]) { $text .= " " if $pos[0] < $pos[2]; $text .= substr($source,$pos[0],$pos[4]-$pos[0]); next component; } if ($Perl5 && $source =~ m/\G(\n*)(\s*)(switch)\b(?=\s*[(])/gc || $Perl6 && $source =~ m/\G(\n*)(\s*)(given)\b(?=\s*[(])/gc || $Perl6 && $source =~ m/\G(\n*)(\s*)(given)\b(.*)(?=\{)/gc) { my $keyword = $3; my $arg = $4; $text .= $1.$2.'S_W_I_T_C_H: while (1) '; unless ($arg) { @pos = Text::Balanced::_match_codeblock(\$source,qr/\s*/,qr/\(/,qr/\)/,qr/[[{(<]/,qr/[]})>]/,undef) or do { die "Bad $keyword statement (problem in the parentheses?) near $Switch::file line ", line(substr($source,0,pos $source),$line), "\n"; }; $arg = filter_blocks(substr($source,$pos[0],$pos[4]-$pos[0]),line(substr($source,0,$pos[0]),$line)); } $arg =~ s {^\s*[(]\s*%} { ( \\\%} || $arg =~ s {^\s*[(]\s*m\b} { ( qr} || $arg =~ s {^\s*[(]\s*/} { ( qr/} || $arg =~ s {^\s*[(]\s*qw} { ( \\qw}; @pos = Text::Balanced::_match_codeblock(\$source,qr/\s*/,qr/\{/,qr/\}/,qr/\{/,qr/\}/,undef) or do { die "Bad $keyword statement (problem in the code block?) near $Switch::file line ", line(substr($source,0, pos $source), $line), "\n"; }; my $code = filter_blocks(substr($source,$pos[0],$pos[4]-$pos[0]),line(substr($source,0,$pos[0]),$line)); $code =~ s/{/{ local \$::_S_W_I_T_C_H; Switch::switch($arg);/; $text .= $code . 'continue {last}'; next component; } elsif ($Perl5 && $source =~ m/\G(\s*)(case\b)(?!\s*=>)/gc || $Perl6 && $source =~ m/\G(\s*)(when\b)(?!\s*=>)/gc || $Perl6 && $source =~ m/\G(\s*)(default\b)(?=\s*\{)/gc) { my $keyword = $2; $text .= $1 . ($keyword eq "default" ? "if (1)" : "if (Switch::case"); if ($keyword eq "default") { # Nothing to do } elsif (@pos = Text::Balanced::_match_codeblock(\$source,qr/\s*/,qr/\{/,qr/\}/,qr/\{/,qr/\}/,undef)) { my $code = substr($source,$pos[0],$pos[4]-$pos[0]); $text .= " " if $pos[0] < $pos[2]; $text .= "sub " if is_block $code; $text .= filter_blocks($code,line(substr($source,0,$pos[0]),$line)) . ")"; } elsif (@pos = Text::Balanced::_match_codeblock(\$source,qr/\s*/,qr/[[(]/,qr/[])]/,qr/[[({]/,qr/[])}]/,undef)) { my $code = filter_blocks(substr($source,$pos[0],$pos[4]-$pos[0]),line(substr($source,0,$pos[0]),$line)); $code =~ s {^\s*[(]\s*%} { ( \\\%} || $code =~ s {^\s*[(]\s*m\b} { ( qr} || $code =~ s {^\s*[(]\s*/} { ( qr/} || $code =~ s {^\s*[(]\s*qw} { ( \\qw}; $text .= " " if $pos[0] < $pos[2]; $text .= "$code)"; } elsif ($Perl6 && do{@pos = Text::Balanced::_match_variable(\$source,qr/\s*/)}) { my $code = filter_blocks(substr($source,$pos[0],$pos[4]-$pos[0]),line(substr($source,0,$pos[0]),$line)); $code =~ s {^\s*%} { \%} || $code =~ s {^\s*@} { \@}; $text .= " " if $pos[0] < $pos[2]; $text .= "$code)"; } elsif ( @pos = Text::Balanced::_match_quotelike(\$source,qr/\s*/,1,0)) { my $code = substr($source,$pos[2],$pos[18]-$pos[2]); $code = filter_blocks($code,line(substr($source,0,$pos[2]),$line)); $code =~ s {^\s*m} { qr} || $code =~ s {^\s*/} { qr/} || $code =~ s {^\s*qw} { \\qw}; $text .= " " if $pos[0] < $pos[2]; $text .= "$code)"; } elsif ($Perl5 && $source =~ m/\G\s*(([^\$\@{])[^\$\@{]*)(?=\s*{)/gc || $Perl6 && $source =~ m/\G\s*([^;{]*)()/gc) { my $code = filter_blocks($1,line(substr($source,0,pos $source),$line)); $text .= ' \\' if $2 eq '%'; $text .= " $code)"; } else { die "Bad $keyword statement (invalid $keyword value?) near $Switch::file line ", line(substr($source,0,pos $source), $line), "\n"; } die "Missing opening brace or semi-colon after 'when' value near $Switch::file line ", line(substr($source,0,pos $source), $line), "\n" unless !$Perl6 || $source =~ m/\G(\s*)(?=;|\{)/gc; do{@pos = Text::Balanced::_match_codeblock(\$source,qr/\s*/,qr/\{/,qr/\}/,qr/\{/,qr/\}/,undef)} or do { if ($source =~ m/\G\s*(?=([};]|\Z))/gc) { $casecounter++; next component; } die "Bad $keyword statement (problem in the code block?) near $Switch::file line ", line(substr($source,0,pos $source),$line), "\n"; }; my $code = filter_blocks(substr($source,$pos[0],$pos[4]-$pos[0]),line(substr($source,0,$pos[0]),$line)); $code =~ s/}(?=\s*\Z)/;last S_W_I_T_C_H }/ unless $fallthrough; $text .= "{ while (1) $code continue { goto C_A_S_E_$casecounter } last S_W_I_T_C_H; C_A_S_E_$casecounter: }"; $casecounter++; next component; } $source =~ m/\G(\s*(-[sm]\s+|\w+|#.*\n|\W))/gc; $text .= $1; } $text; } sub in { my ($x,$y) = @_; my @numy; for my $nextx ( @$x ) { my $numx = ref($nextx) || defined $nextx && (~$nextx&$nextx) eq 0; for my $j ( 0..$#$y ) { my $nexty = $y->[$j]; push @numy, ref($nexty) || defined $nexty && (~$nexty&$nexty) eq 0 if @numy <= $j; return 1 if $numx && $numy[$j] && $nextx==$nexty || $nextx eq $nexty; } } return ""; } sub on_exists { my $ref = @_==1 && ref($_[0]) eq 'HASH' ? $_[0] : { @_ }; [ keys %$ref ] } sub on_defined { my $ref = @_==1 && ref($_[0]) eq 'HASH' ? $_[0] : { @_ }; [ grep { defined $ref->{$_} } keys %$ref ] } sub switch(;$) { my ($s_val) = @_ ? $_[0] : $_; my $s_ref = ref $s_val; if ($s_ref eq 'CODE') { $::_S_W_I_T_C_H = sub { my $c_val = $_[0]; return $s_val == $c_val if ref $c_val eq 'CODE'; return $s_val->(@$c_val) if ref $c_val eq 'ARRAY'; return $s_val->($c_val); }; } elsif ($s_ref eq "" && defined $s_val && (~$s_val&$s_val) eq 0) # NUMERIC SCALAR { $::_S_W_I_T_C_H = sub { my $c_val = $_[0]; my $c_ref = ref $c_val; return $s_val == $c_val if $c_ref eq "" && defined $c_val && (~$c_val&$c_val) eq 0; return $s_val eq $c_val if $c_ref eq ""; return in([$s_val],$c_val) if $c_ref eq 'ARRAY'; return $c_val->($s_val) if $c_ref eq 'CODE'; return $c_val->call($s_val) if $c_ref eq 'Switch'; return scalar $s_val=~/$c_val/ if $c_ref eq 'Regexp'; return scalar $c_val->{$s_val} if $c_ref eq 'HASH'; return; }; } elsif ($s_ref eq "") # STRING SCALAR { $::_S_W_I_T_C_H = sub { my $c_val = $_[0]; my $c_ref = ref $c_val; return $s_val eq $c_val if $c_ref eq ""; return in([$s_val],$c_val) if $c_ref eq 'ARRAY'; return $c_val->($s_val) if $c_ref eq 'CODE'; return $c_val->call($s_val) if $c_ref eq 'Switch'; return scalar $s_val=~/$c_val/ if $c_ref eq 'Regexp'; return scalar $c_val->{$s_val} if $c_ref eq 'HASH'; return; }; } elsif ($s_ref eq 'ARRAY') { $::_S_W_I_T_C_H = sub { my $c_val = $_[0]; my $c_ref = ref $c_val; return in($s_val,[$c_val]) if $c_ref eq ""; return in($s_val,$c_val) if $c_ref eq 'ARRAY'; return $c_val->(@$s_val) if $c_ref eq 'CODE'; return $c_val->call(@$s_val) if $c_ref eq 'Switch'; return scalar grep {$_=~/$c_val/} @$s_val if $c_ref eq 'Regexp'; return scalar grep {$c_val->{$_}} @$s_val if $c_ref eq 'HASH'; return; }; } elsif ($s_ref eq 'Regexp') { $::_S_W_I_T_C_H = sub { my $c_val = $_[0]; my $c_ref = ref $c_val; return $c_val=~/s_val/ if $c_ref eq ""; return scalar grep {$_=~/s_val/} @$c_val if $c_ref eq 'ARRAY'; return $c_val->($s_val) if $c_ref eq 'CODE'; return $c_val->call($s_val) if $c_ref eq 'Switch'; return $s_val eq $c_val if $c_ref eq 'Regexp'; return grep {$_=~/$s_val/ && $c_val->{$_}} keys %$c_val if $c_ref eq 'HASH'; return; }; } elsif ($s_ref eq 'HASH') { $::_S_W_I_T_C_H = sub { my $c_val = $_[0]; my $c_ref = ref $c_val; return $s_val->{$c_val} if $c_ref eq ""; return scalar grep {$s_val->{$_}} @$c_val if $c_ref eq 'ARRAY'; return $c_val->($s_val) if $c_ref eq 'CODE'; return $c_val->call($s_val) if $c_ref eq 'Switch'; return grep {$_=~/$c_val/ && $s_val->{"$_"}} keys %$s_val if $c_ref eq 'Regexp'; return $s_val==$c_val if $c_ref eq 'HASH'; return; }; } elsif ($s_ref eq 'Switch') { $::_S_W_I_T_C_H = sub { my $c_val = $_[0]; return $s_val == $c_val if ref $c_val eq 'Switch'; return $s_val->call(@$c_val) if ref $c_val eq 'ARRAY'; return $s_val->call($c_val); }; } else { croak "Cannot switch on $s_ref"; } return 1; } sub case($) { local $SIG{__WARN__} = \&carp; $::_S_W_I_T_C_H->(@_); } # IMPLEMENT __ my $placeholder = bless { arity=>1, impl=>sub{$_[1+$_[0]]} }; sub __() { $placeholder } sub __arg($) { my $index = $_[0]+1; bless { arity=>0, impl=>sub{$_[$index]} }; } sub hosub(&@) { # WRITE THIS } sub call { my ($self,@args) = @_; return $self->{impl}->(0,@args); } sub meta_bop(&) { my ($op) = @_; sub { my ($left, $right, $reversed) = @_; ($right,$left) = @_ if $reversed; my $rop = ref $right eq 'Switch' ? $right : bless { arity=>0, impl=>sub{$right} }; my $lop = ref $left eq 'Switch' ? $left : bless { arity=>0, impl=>sub{$left} }; my $arity = $lop->{arity} + $rop->{arity}; return bless { arity => $arity, impl => sub { my $start = shift; return $op->($lop->{impl}->($start,@_), $rop->{impl}->($start+$lop->{arity},@_)); } }; }; } sub meta_uop(&) { my ($op) = @_; sub { my ($left) = @_; my $lop = ref $left eq 'Switch' ? $left : bless { arity=>0, impl=>sub{$left} }; my $arity = $lop->{arity}; return bless { arity => $arity, impl => sub { $op->($lop->{impl}->(@_)) } }; }; } use overload "+" => meta_bop {$_[0] + $_[1]}, "-" => meta_bop {$_[0] - $_[1]}, "*" => meta_bop {$_[0] * $_[1]}, "/" => meta_bop {$_[0] / $_[1]}, "%" => meta_bop {$_[0] % $_[1]}, "**" => meta_bop {$_[0] ** $_[1]}, "<<" => meta_bop {$_[0] << $_[1]}, ">>" => meta_bop {$_[0] >> $_[1]}, "x" => meta_bop {$_[0] x $_[1]}, "." => meta_bop {$_[0] . $_[1]}, "<" => meta_bop {$_[0] < $_[1]}, "<=" => meta_bop {$_[0] <= $_[1]}, ">" => meta_bop {$_[0] > $_[1]}, ">=" => meta_bop {$_[0] >= $_[1]}, "==" => meta_bop {$_[0] == $_[1]}, "!=" => meta_bop {$_[0] != $_[1]}, "<=>" => meta_bop {$_[0] <=> $_[1]}, "lt" => meta_bop {$_[0] lt $_[1]}, "le" => meta_bop {$_[0] le $_[1]}, "gt" => meta_bop {$_[0] gt $_[1]}, "ge" => meta_bop {$_[0] ge $_[1]}, "eq" => meta_bop {$_[0] eq $_[1]}, "ne" => meta_bop {$_[0] ne $_[1]}, "cmp" => meta_bop {$_[0] cmp $_[1]}, "\&" => meta_bop {$_[0] & $_[1]}, "^" => meta_bop {$_[0] ^ $_[1]}, "|" => meta_bop {$_[0] | $_[1]}, "atan2" => meta_bop {atan2 $_[0], $_[1]}, "neg" => meta_uop {-$_[0]}, "!" => meta_uop {!$_[0]}, "~" => meta_uop {~$_[0]}, "cos" => meta_uop {cos $_[0]}, "sin" => meta_uop {sin $_[0]}, "exp" => meta_uop {exp $_[0]}, "abs" => meta_uop {abs $_[0]}, "log" => meta_uop {log $_[0]}, "sqrt" => meta_uop {sqrt $_[0]}, "bool" => sub { croak "Can't use && or || in expression containing __" }, # "&()" => sub { $_[0]->{impl} }, # "||" => meta_bop {$_[0] || $_[1]}, # "&&" => meta_bop {$_[0] && $_[1]}, # fallback => 1, ; 1; __END__ =head1 NAME Switch - A switch statement for Perl, do not use if you can use given/when =head1 SYNOPSIS use Switch; switch ($val) { case 1 { print "number 1" } case "a" { print "string a" } case [1..10,42] { print "number in list" } case (\@array) { print "number in list" } case /\w+/ { print "pattern" } case qr/\w+/ { print "pattern" } case (\%hash) { print "entry in hash" } case (\&sub) { print "arg to subroutine" } else { print "previous case not true" } } =head1 BACKGROUND [Skip ahead to L<"DESCRIPTION"> if you don't care about the whys and wherefores of this control structure] In seeking to devise a "Swiss Army" case mechanism suitable for Perl, it is useful to generalize this notion of distributed conditional testing as far as possible. Specifically, the concept of "matching" between the switch value and the various case values need not be restricted to numeric (or string or referential) equality, as it is in other languages. Indeed, as Table 1 illustrates, Perl offers at least eighteen different ways in which two values could generate a match. Table 1: Matching a switch value ($s) with a case value ($c) Switch Case Type of Match Implied Matching Code Value Value ====== ===== ===================== ============= number same numeric or referential match if $s == $c; or ref equality object method result of method call match if $s->$c(); ref name match if defined $s->$c(); or ref other other string equality match if $s eq $c; non-ref non-ref scalar scalar string regexp pattern match match if $s =~ /$c/; array scalar array entry existence match if 0<=$c && $c<@$s; ref array entry definition match if defined $s->[$c]; array entry truth match if $s->[$c]; array array array intersection match if intersects(@$s, @$c); ref ref (apply this table to all pairs of elements $s->[$i] and $c->[$j]) array regexp array grep match if grep /$c/, @$s; ref hash scalar hash entry existence match if exists $s->{$c}; ref hash entry definition match if defined $s->{$c}; hash entry truth match if $s->{$c}; hash regexp hash grep match if grep /$c/, keys %$s; ref sub scalar return value defn match if defined $s->($c); ref return value truth match if $s->($c); sub array return value defn match if defined $s->(@$c); ref ref return value truth match if $s->(@$c); In reality, Table 1 covers 31 alternatives, because only the equality and intersection tests are commutative; in all other cases, the roles of the C<$s> and C<$c> variables could be reversed to produce a different test. For example, instead of testing a single hash for the existence of a series of keys (C<match if exists $s-E<gt>{$c}>), one could test for the existence of a single key in a series of hashes (C<match if exists $c-E<gt>{$s}>). =head1 DESCRIPTION The Switch.pm module implements a generalized case mechanism that covers most (but not all) of the numerous possible combinations of switch and case values described above. The module augments the standard Perl syntax with two new control statements: C<switch> and C<case>. The C<switch> statement takes a single scalar argument of any type, specified in parentheses. C<switch> stores this value as the current switch value in a (localized) control variable. The value is followed by a block which may contain one or more Perl statements (including the C<case> statement described below). The block is unconditionally executed once the switch value has been cached. A C<case> statement takes a single scalar argument (in mandatory parentheses if it's a variable; otherwise the parens are optional) and selects the appropriate type of matching between that argument and the current switch value. The type of matching used is determined by the respective types of the switch value and the C<case> argument, as specified in Table 1. If the match is successful, the mandatory block associated with the C<case> statement is executed. In most other respects, the C<case> statement is semantically identical to an C<if> statement. For example, it can be followed by an C<else> clause, and can be used as a postfix statement qualifier. However, when a C<case> block has been executed control is automatically transferred to the statement after the immediately enclosing C<switch> block, rather than to the next statement within the block. In other words, the success of any C<case> statement prevents other cases in the same scope from executing. But see L<"Allowing fall-through"> below. Together these two new statements provide a fully generalized case mechanism: use Switch; # AND LATER... %special = ( woohoo => 1, d'oh => 1 ); while (<>) { chomp; switch ($_) { case (%special) { print "homer\n"; } # if $special{$_} case /[a-z]/i { print "alpha\n"; } # if $_ =~ /a-z/i case [1..9] { print "small num\n"; } # if $_ in [1..9] case { $_[0] >= 10 } { print "big num\n"; } # if $_ >= 10 print "must be punctuation\n" case /\W/; # if $_ ~= /\W/ } } Note that C<switch>es can be nested within C<case> (or any other) blocks, and a series of C<case> statements can try different types of matches -- hash membership, pattern match, array intersection, simple equality, etc. -- against the same switch value. The use of intersection tests against an array reference is particularly useful for aggregating integral cases: sub classify_digit { switch ($_[0]) { case 0 { return 'zero' } case [2,4,6,8] { return 'even' } case [1,3,5,7,9] { return 'odd' } case /[A-F]/i { return 'hex' } } } =head2 Allowing fall-through Fall-though (trying another case after one has already succeeded) is usually a Bad Idea in a switch statement. However, this is Perl, not a police state, so there I<is> a way to do it, if you must. If a C<case> block executes an untargeted C<next>, control is immediately transferred to the statement I<after> the C<case> statement (i.e. usually another case), rather than out of the surrounding C<switch> block. For example: switch ($val) { case 1 { handle_num_1(); next } # and try next case... case "1" { handle_str_1(); next } # and try next case... case [0..9] { handle_num_any(); } # and we're done case /\d/ { handle_dig_any(); next } # and try next case... case /.*/ { handle_str_any(); next } # and try next case... } If $val held the number C<1>, the above C<switch> block would call the first three C<handle_...> subroutines, jumping to the next case test each time it encountered a C<next>. After the third C<case> block was executed, control would jump to the end of the enclosing C<switch> block. On the other hand, if $val held C<10>, then only the last two C<handle_...> subroutines would be called. Note that this mechanism allows the notion of I<conditional fall-through>. For example: switch ($val) { case [0..9] { handle_num_any(); next if $val < 7; } case /\d/ { handle_dig_any(); } } If an untargeted C<last> statement is executed in a case block, this immediately transfers control out of the enclosing C<switch> block (in other words, there is an implicit C<last> at the end of each normal C<case> block). Thus the previous example could also have been written: switch ($val) { case [0..9] { handle_num_any(); last if $val >= 7; next; } case /\d/ { handle_dig_any(); } } =head2 Automating fall-through In situations where case fall-through should be the norm, rather than an exception, an endless succession of terminal C<next>s is tedious and ugly. Hence, it is possible to reverse the default behaviour by specifying the string "fallthrough" when importing the module. For example, the following code is equivalent to the first example in L<"Allowing fall-through">: use Switch 'fallthrough'; switch ($val) { case 1 { handle_num_1(); } case "1" { handle_str_1(); } case [0..9] { handle_num_any(); last } case /\d/ { handle_dig_any(); } case /.*/ { handle_str_any(); } } Note the explicit use of a C<last> to preserve the non-fall-through behaviour of the third case. =head2 Alternative syntax Perl 6 will provide a built-in switch statement with essentially the same semantics as those offered by Switch.pm, but with a different pair of keywords. In Perl 6 C<switch> will be spelled C<given>, and C<case> will be pronounced C<when>. In addition, the C<when> statement will not require switch or case values to be parenthesized. This future syntax is also (largely) available via the Switch.pm module, by importing it with the argument C<"Perl6">. For example: use Switch 'Perl6'; given ($val) { when 1 { handle_num_1(); } when ($str1) { handle_str_1(); } when [0..9] { handle_num_any(); last } when /\d/ { handle_dig_any(); } when /.*/ { handle_str_any(); } default { handle anything else; } } Note that scalars still need to be parenthesized, since they would be ambiguous in Perl 5. Note too that you can mix and match both syntaxes by importing the module with: use Switch 'Perl5', 'Perl6'; =head2 Higher-order Operations One situation in which C<switch> and C<case> do not provide a good substitute for a cascaded C<if>, is where a switch value needs to be tested against a series of conditions. For example: sub beverage { switch (shift) { case { $_[0] < 10 } { return 'milk' } case { $_[0] < 20 } { return 'coke' } case { $_[0] < 30 } { return 'beer' } case { $_[0] < 40 } { return 'wine' } case { $_[0] < 50 } { return 'malt' } case { $_[0] < 60 } { return 'Moet' } else { return 'milk' } } } (This is equivalent to writing C<case (sub { $_[0] < 10 })>, etc.; C<$_[0]> is the argument to the anonymous subroutine.) The need to specify each condition as a subroutine block is tiresome. To overcome this, when importing Switch.pm, a special "placeholder" subroutine named C<__> [sic] may also be imported. This subroutine converts (almost) any expression in which it appears to a reference to a higher-order function. That is, the expression: use Switch '__'; __ < 2 is equivalent to: sub { $_[0] < 2 } With C<__>, the previous ugly case statements can be rewritten: case __ < 10 { return 'milk' } case __ < 20 { return 'coke' } case __ < 30 { return 'beer' } case __ < 40 { return 'wine' } case __ < 50 { return 'malt' } case __ < 60 { return 'Moet' } else { return 'milk' } The C<__> subroutine makes extensive use of operator overloading to perform its magic. All operations involving __ are overloaded to produce an anonymous subroutine that implements a lazy version of the original operation. The only problem is that operator overloading does not allow the boolean operators C<&&> and C<||> to be overloaded. So a case statement like this: case 0 <= __ && __ < 10 { return 'digit' } doesn't act as expected, because when it is executed, it constructs two higher order subroutines and then treats the two resulting references as arguments to C<&&>: sub { 0 <= $_[0] } && sub { $_[0] < 10 } This boolean expression is inevitably true, since both references are non-false. Fortunately, the overloaded C<'bool'> operator catches this situation and flags it as an error. =head1 DEPENDENCIES The module is implemented using Filter::Util::Call and Text::Balanced and requires both these modules to be installed. =head1 AUTHOR Damian Conway (damian@conway.org). This module is now maintained by Alexandr Ciornii (alexchorny@gmail.com). Previously was maintained by Rafael Garcia-Suarez and perl5 porters. =head1 BUGS There are undoubtedly serious bugs lurking somewhere in code this funky :-) Bug reports and other feedback are most welcome. May create syntax errors in other parts of code. On perl 5.10.x may cause syntax error if "case" is present inside heredoc. In general, use given/when instead. It were introduced in perl 5.10.0. Perl 5.10.0 was released in 2007. =head1 LIMITATIONS Due to the heuristic nature of Switch.pm's source parsing, the presence of regexes with embedded newlines that are specified with raw C</.../> delimiters and don't have a modifier C<//x> are indistinguishable from code chunks beginning with the division operator C</>. As a workaround you must use C<m/.../> or C<m?...?> for such patterns. Also, the presence of regexes specified with raw C<?...?> delimiters may cause mysterious errors. The workaround is to use C<m?...?> instead. Due to the way source filters work in Perl, you can't use Switch inside an string C<eval>. May not work if sub prototypes are used (RT#33988). Regex captures in when are not available to code. If your source file is longer then 1 million characters and you have a switch statement that crosses the 1 million (or 2 million, etc.) character boundary you will get mysterious errors. The workaround is to use smaller source files. =head1 COPYRIGHT Copyright (c) 1997-2008, Damian Conway. All Rights Reserved. This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself. PK�����A0]( @��@����perl5/POD2/DE/local/lib.podnu�6$��������=encoding utf8 =head1 NAME local::lib~[de] - Erschaffen und benutzen von Perl Modulen in einem lokalen lib/ Verzeichnis mit PERL5LIB =head1 SYNOPSIS Im Code - use local::lib; # Benutzt das Verzeichnis ~/perl5 zum anlegen des lokalen lib/ Verzeichnisses use local::lib '~/foo'; # das selbe, aber mit ~/foo # Oder... use FindBin; use local::lib "$FindBin::Bin/../support"; # Applikationsspezifische Sammlung von Modulen Von der Shell - # Installiert LWP und alle notwendigen Abhängigkeiten in das '~/perl5' Verzeichnis perl -MCPAN -Mlocal::lib -e 'CPAN::install(LWP)' # Gibt die Shell Kommandos aus um die Umgebung vorzubereiten $ perl -Mlocal::lib export PERL_MB_OPT='--install_base /home/username/perl5' export PERL_MM_OPT='INSTALL_BASE=/home/username/perl5' export PERL5LIB='/home/username/perl5/lib/perl5/i386-linux:/home/username/perl5/lib/perl5' export PATH="/home/username/perl5/bin:$PATH" =head2 Die Bootstrapping Methode Ein typischer Weg um local::lib zu benutzen ist die sogenannte "Bootstrapping" Methode. Diese Methode wird benutzt wenn noch kein local::lib auf dem System installiert ist. In diesem Fall kannst du einfach local::lib direkt in deinem Home-Verzeichnis installieren. Selbst wenn du administrative Rechte hast, ist es wichtig das die Umgebungsvariablen von Schritt 4 in deinem Shell Startup Skript gesetzt werden. Ohne diesen Schritt werden die Module von CPAN weiterhin im System installiert und auch Perl Skripte die du startest würden das von local::lib erstellte lib/ Verzeichnis nicht nutzen. Standardmäßig installiert sich local::lib in ~/perl5. Windows Benutzern müssen ausserdem dies hier lesen: L</Unterschiede bei Benutzung dieses Module mit Win32>. 1. Lade das Tar-Archiv von CPAN runter (Suche nach "Download" auf der CPAN Seite von local::lib) und entpacke es in einem beliebigem Verzeichnis. Um das obige Problem zu vermeiden, sollte man dies als normaler User tun und nicht als root oder Administrator. 2. Starte in dem entstandenen Verzeichnis folgenden Befehl: perl Makefile.PL --bootstrap Wenn das System dir vorschlägt gewisse Dinge eigenständig zu konfigurieren ist es in fast allen Fällen vollkommen in Ordnung einfach "yes" zu antworten. Falls du local::lib nicht in das Standard Verzeichnis installieren willst, musst du dieses Verzeichnis als Parameter angeben: perl Makefile.PL --bootstrap=~/foo 3. Danach folgenden Befehl starten: (local::lib erwartet make auf dem System) make test && make install 4. Nun müssen wir die benötigten Umgebungsvariablen, damit Perl unser neu generiertes lib/ Verzeichnis benutzt. Wenn du bash oder eine andere Bourne Shell benutzt, kannst du es über diesen Weg zu deinem Shell Startup Skript hinzufügen: echo 'eval $(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib)' >>~/.bashrc Wenn du C Shell benutzt, du kannst das gleiche hiermit erreichen: /bin/csh echo $SHELL /bin/csh perl -I$HOME/perl5/lib/perl5 -Mlocal::lib >> ~/.cshrc Wenn du beim bootstrappen ein anderes Verzeichnis benutzt als das Standardverzeichnis, dann musst du dieses Verzeichnis als Parameter beim Laden des Modules local::lib angeben: echo 'eval $(perl -I$HOME/foo/lib/perl5 -Mlocal::lib=$HOME/foo)' >>~/.bashrc Nachdem diese Änderungen in deinem Shell Startup Skript gemacht wurden, ist es nun wichtig das diese Umgebungsvariablen auch gesetzt sind in deiner aktuellen Umgebung. In Bourne Shells macht man dies z.B. mit C<. ~/.bashrc>, und in C Shell würde man es mit: C<source ~/.cshrc> mit. Wenn du eine sehr langsames System hast, oder du unter drakonischen Regulierungen des Plattenplatz leben musst, kann man die automatische Generierung der manpages vom POD bei der Installation des Moduls deaktivieren beim bootstrapping mit dem C<--no-manpages> Parameter: perl Makefile.PL --bootstrap --no-manpages Um zu vermeiden das man mehrere bootstraps macht um z.B. für verschiedene Applikationen eigene local::lib Installationen zu nutzen, kann man eine dieser Umgebungen benutzen um einfach in beliebigen anderen Verzeichnis Module zu installieren und somit weitere eigenständige lib/ Umgebungen zu bekommen: cd ~/mydir1 perl -Mlocal::lib=./ eval $(perl -Mlocal::lib=./) ### Um die Umgebungsvariablen für die ### aktuelle Shell zusetzen printenv ### Hier kannst du sehen das ~/mydir1 ### in der PERL5LIB Umgebungsvariable ### steht perl -MCPAN -e install ... ### welche Module auch immer ... cd ../mydir2 ... WIEDERHOLEN ... Für mehrere Umgebungen in dieser Form brauch man eine Modifikation in der Benutzung von C<< use FindBin >> in dem "Im Code" Beispiel oben. Wenn du sowas machst, und du hast damit z.B. Perl Module nach C<< ~/mydir1/lib >> installiert und du hast ein Script in C<< ~/mydir1/scripts/myscript.pl >>, du musst dort angeben das die Module die es braucht im Verzeichnis C<< ~/mydir1/lib >> liegen. In C<< ~/mydir1/scripts/myscript.pl >> steht dann: use strict; use warnings; use local::lib "$FindBin::Bin/.."; ### zeigt auf ~/mydir1 und local::lib ### findet dort lib use lib "$FindBin::Bin/../lib"; ### zeigt auf ~/mydir1/lib Setze das vor jeden BEGIN { ... } Block der die Module braucht die du installiert hast. =head2 Unterschiede bei Benutzung dieses Module mit Win32 Um die nötigen Umgebungsvariablen für diese Variablen in der derzeitigen Sitzung mit C<CMD.EXE> zu setzen, kann man folgendes kommando nutzen: C:\>perl -Mlocal::lib set PERL_MB_OPT=--install_base C:\DOCUME~1\ADMINI~1\perl5 set PERL_MM_OPT=INSTALL_BASE=C:\DOCUME~1\ADMINI~1\perl5 set PERL5LIB=C:\DOCUME~1\ADMINI~1\perl5\lib\perl5;C:\DOCUME~1\ADMINI~1\perl5\lib\perl5\MSWin32-x86-multi-thread set PATH=C:\DOCUME~1\ADMINI~1\perl5\bin;%PATH% ### Um die Umgebungsvariablen für diese Shell alleine zu setzen C:\>perl -Mlocal::lib > %TEMP%\tmp.bat && %TEMP%\tmp.bat && del %TEMP%\temp.bat ### anstelle von $(perl -Mlocal::lib=./) in bash. Wenn du willst das die Umgebungsvariablen dauerhaft gesetzt sind, musst du diese in Systemsteuerung / System dauerhaft selber eintragen oder L<App::local::lib::Win32Helper> benutzen. Die "~" wird übersetzt zu dem Benutzer Profil Verzeichnis (das Verzeichnis was beim User als "Dokumente und Einstellungen" bekannt ist unter Windows XP und vorher oder das "Benutzer" Verzeichnis bei Windows Vista und später), solange $ENV{HOME} nicht gesetzt ist. Das Verzeichnis wird hierbei zu dem korrekten Kurznamen umgewandelt, und muss daher definitiv existieren, und wird um die nötigen Unterverzeichnise erweitert. =head1 GRUNDPRINZIP Die Version von den Perl Paketen die man benötigt für spezifische Aufgaben sind sehr häufig nicht die richtigen oder korrekten Versionen auf dem System vorinstalliert. Ein Updaten von diesen Modulen ist in vielen Fällen einfach nicht möglich weil die nötigen Rechte fehlen. Ausserdem ist es generell nicht gut eigenständig die Versionen der Module auf dem System auszutauschen, weil natürlich der Rest des Systems genau die Version erwartet die von der Systemverwaltung auch installiert wurde. local::lib löst dieses Problem, es erlaubt dir dein komplett eigenes Verzeichnis für deine CPAN Module zu haben und bist so nicht genötigt die Module vom System zu nutzen oder andersrum andere User nicht mit individuellen Modulwünschen zu Überarbeitung ihres Codes zu zwingen, weil bestimmte Module zentral für alle auf neuere Version upgedatet werden. Die Installation findet hierbei dann z.B. im Home Verzeichnis statt. Es werden nur Umgebungsvariablen gesetzt die das installierte Perl dazu bewegen die im Homeverzeichnis installierten Module zu benutzen, zusätzlich und vorgezogen zu denen auf dem System. Daher muss man sich wenn man ein Paket System benutzt, wie z.b. Debian, garnicht mehr Sorgen machen, irgendwas auf dem System zu verletzten nur durch die Installation von Perl Modulen. =head1 BESCHREIBUNG Dieses Modul bietet eine schnelle und legitime Art und Weise ein sogenanntes bootstrapping zu machen um in einem User Homeverzeichnis eine Sammlung von Modulen zu installieren. Es erstellt auch die nötigen Umgebungsvariablen die benötigt werden um diese Module zu nutzen, passend zu der Shell die der User in der Umgebungsvariable C<SHELL> angegeben hat, um dann direkt passend in die entsprechenden Konfigurationsdateien der Shell einfügt zu werden. Weitergehend ist local::lib in der Lage Module zu nutzen die nicht im standardmäßigen C<@INC> Pfad von Perl enthalten sind. Das macht es einfacher für bestimmte Applikationen ein bestimmtes Set von Modulen zu installieren ohne die anderen Module auf dem System in irgendeiner Art anzufassen. Damit es z.B. auch sicherer Module zu installieren die vom Maintainer noch nicht als Release verfügbar sind. Beim Import setzt local::lib die folgenden Umgebungsvariablen zu den nötigen Werten: =over 4 =item PERL_MB_OPT =item PERL_MM_OPT =item PERL5LIB =item PATH Am PATH wird natürlich angehangen, und nicht ersetzt. =back Diese Werte sind dann verfügbar für jeden Code der danach importiert wurde. =head1 ERSTELLEN EINES EIGENSTÄNDIGE SAMMLUNG VON MODULEN Mit L<lib::core::only> besteht eine Möglichkeit dieses zutun, aber beachte das hier eine Menge von Fallstricken und Problemen existieren, und man sollte immer darauf achten das man auf einem Perl aufbaut was sowenig wie möglich verändert wurde (d.h. site und vendor Verzeichnis so leer wie möglich). =head1 METHODEN =head2 ensure_dir_structure_for =over 4 =item Parameter: $path =item Rückgabewert: Keiner =back Versucht den angegebenen Pfad anzulegen, mit allen nötigen drüberliegenden Verzeichnissen. Im Fehlerfall wird eine Exception geworfen. =head2 print_environment_vars_for =over 4 =item Parameter: $pfad =item Rückgabewert: Keiner =back Gibt die Umgebungsvariablen aus, die benötigt werden um den angegebenen Pfad als Basis Verzeichnis zu nutzen. =head2 build_environment_vars_for =over 4 =item Parameter: $pfad, $interpolate =item Rückgabewert: \%umgebungs_variablen =back Gibt ein Hash zurück mit den Variablen die nötig sind in den Umgebungsvariablen um eine Installation in dem gegebenen Pfad zu benutzen. =head2 setup_env_hash_for =over 4 =item Parameter: $pfad =item Rückgabewert: Keiner =back Setzt die C<%ENV> Einträge basierend auf dem Aufruf von L</build_environment_vars_for>. =head2 install_base_perl_path =over 4 =item Parameter: $pfad =item Rückgabewert: $module_installations_pfad =back Gibt den Pfad zurück der benutzt wird um Perl Module zu installieren bei dem gegebenen Pfad als Basis. Prinzipiell wird nur C<lib> und C<perl5> als Pfadelemente angehangen. =head2 install_base_arch_path =over 4 =item Parameter: $pfad =item Rückgabewert: $architektur_module_installations_pfad =back Gibt den Pfad zurück der benutzt wird um die Architektur-abhängigen Perl Module zu installieren basirend auf dem angegebenen Pfad als Basis. Basierend auf dem was L</install_base_perl_path> zurückgibt, and appends the value of C<$Config{archname}>.asis. =head2 install_base_bin_path =over 4 =item Parameter: $pfad =item Rückgabewert: $ausfuehrbare_programme_installations_pfad =back Gibt den Pfad zurück, wo ausführbare Programme installiert werden, basierend auf der Basis des angegebenen Pfad. Basierend auf L</install_base_perl_path> Rückgabewert, hängt diese Methode noch C<bin> an. =head2 resolve_empty_path =over 4 =item Parameter: $pfad =item Rückgabewert: $basis_pfad =back Erstellt und gibt zurück den Pfad der benutzt wird als Basis zur Installation der Module. Standardmäßig dies ist C<~/perl5>. =head2 resolve_home_path( $path ) =over 4 =item Parameter: $pfad =item Rückgabewert: $home =back Versucht das Home Verzeichnis vom aktullen User zu finden. Es wird eine Exception geworfen, wenn kein Home Verzeichnis ermittelt werden konnte. =head2 resolve_relative_path =over 4 =item Parameter: $pfad =item Rückgabewert: $absoluter_pfad =back Macht aus dem angegebenen Pfad einen absoluten Pfad. =head2 resolve_path =over 4 =item Parameter: $pfad =item Rückgabewert: $absoluter_pfad =back Hierbei wird der Pfad durch die folgende Methoden gegeben, wobei der Rückgabewert der ersten an die nächste weitergeben wird, um die Umgebung zu konfigurieren für die lokale Bibliotheks Installation: L</resolve_empty_path>, L</resolve_home_path>, L</resolve_relative_path>. Der daraus resultierende Pfad wird zu L</resolve_empty_path> übergeben, dessen Resultat dann weitergegeben wird an L</resolve_home_path>, wessen Resultat dann weitergegeben wird an L</resolve_relative_path>. Dieses Resultat wird dann final an L</resolve_path> übergeben, welches dann den Rückgabewert stellt. =head1 EINE WARNUNG VOR UNINST=1 Wenn man local::lib in Kombination mit "make install UNINST=1" benutzt, muss man vorsichtig sein über die Tatsache das der Prozess über die Neuinstallation eine nicht ausreichende Sicherheit hat bezüglich wo er nun installieren muss. Hierdurch mann es passieren das beim deinstallieren eines Modul u.U. das globale Modul deinstalliert wird (wenn die Rechte vorhanden sind) aber die neue Version nur in der lokalen Version installiert ist. Es ist hier also sehr wichtig das man "make install UNINST=1" und local::lib nur gleichzeitig benutzt wenn man sehr sicher darüber ist welche Konsequenzen einem entgegenkommen. =head1 EINSCHRÄNKUNGEN Die Werkzeuge von perl, die benutzt werden um die Pakete zu installieren (die sogenannte toolchain), sind leider nicht in der Lage sauber mit Verzeichnissen umzugehen die Leerzeichen enthalten und können daher local::lib nicht direkt in ein solches Verzeichnis installieren. Was du machen kannst ist B<nach> der Installation von local::lib und der Module die du in deiner local::lib haben willst, das gesamte Verzeichnis dahin zu bewegen. local::lib kann mit dem Verzeichnis mit Leerzeichen umgehen. Bitte aufpassen das natürlich eine weitere Installation oder ein Erneuern von Paketen mit dem CPAN Programm nicht mehr möglich ist. Die Shell Erkennung ist sehr primitiv. Derzeit ist es so das alles was "csh" im Namen hat auch als C Shell eingeordnet wird, und alles andere wird als Bourne Shell betrachet, ausser auf Win32 Systemen. Wenn die C<SHELL> Variable nicht gesetzt ist, eine Bourne Shell wird angenommen. Bootstrap ist leider ein Hack, und wird auf jedenfall CPAN.pm benutzen für ExtUtils::MakeMaker, auch wenn CPANPLUS installiert ist. Es setzt definitiv PERL5LIB, PERL_MM_OPT und PERL_MB_OPT neu und vernichtet jeden Wert der vorher gesetzt war. Es sollte vielleicht eine automatische Korrektur der CPAN Config machen, wenn das nicht schon gemacht wurde. "Patches Welcome" - Patches sind immer willkommen beim Autor oder den anderen Mitwirkenden. Auf Win32 Systemen werden die Umgebungsvariablen nicht direkt in die Registrierung geschrieben damit sie auch nach dem Neustarten erhalten bleiben. =head1 FEHLERANALYSE Wenn du local::lib konfiguriert hast CPAN Module in deinem Home Verzeichnis zu installieren, und du danach versuchst mit C<cpan -i Foo::Bar> ein Modul zu installieren, und dabei einen Fehler bekommst, wie: C<Warning: You do not have permissions to install into /usr/lib64/perl5/site_perl/5.8.8/x86_64-linux at /usr/lib64/perl5/5.8.8/Foo/Bar.pm> und in der installationsausgabe steht irgendwo ein Fehler der sagt C<'INSTALL_BASE' is not a known MakeMaker parameter name>, dann hast du aus irgendeinem Grund dein neue Version von ExtUtils::MakeMaker verloren. Um dies zu korrigieren, einfach nochmal die bootstrapping Methode laufen lassen, wie oben beschrieben. Dann starte C<rm -r ~/.cpan/build/Foo-Bar*> Abschliessend dann nochmal mit C<cpan -i Foo::Bar> installieren und die Probleme sollten verschwunden sein. =head1 UMGEBUNGSVARIABLEN =over 4 =item SHELL =item COMSPEC local::lib schaut in die C<SHELL> Umgebungsvariable um die korrekten Kommandos zu der Shell Konfiguration hinzuzufügen. Auf Win32 Systemen, C<COMSPEC> wird auch analysiert. =back =head1 SUPPORT IRC: Wir sind im Channel #local-lib auf dem Server irc.perl.org. =head1 AUTOR DER ÜBERSETZUNG Torsten Raudssus <torsten@raudssus.de> http://www.raudssus.de/ =head1 URHEBERRECHT Copyright (c) 2007 - 2010 von den local::lib L<local::lib/"AUTHOR"> und L<local::lib/"CONTRIBUTORS"> aufgelistet in L<local::lib|local::lib>. =head1 LIZENZ Diese Sammlung ist freie Software und kann unter der selben Lizenz verbreitet werden wie Perl selber. =cut 1; PK�����A0]CP>��>����perl5/POD2/PT_BR/local/lib.podnu�6$��������=encoding utf8 =head1 NAME local::lib~[pt_br] - crie e use um diretório lib/ local para módulos perl com PERL5LIB =head1 SINOPSE No código - use local::lib; # configura um lib local em ~/perl5 use local::lib '~/foo'; # idem, mas ~/foo # Ou... use FindBin; use local::lib "$FindBin::Bin/../suporte"; # bibliotecas de suporte locais à aplicação Pela linha de comando (shell) - # Instala o LWP e suas dependências não encontradas no diretório '~/perl5' perl -MCPAN -Mlocal::lib -e 'CPAN::install(LWP)' # Apenas exibe alguns comandos úteis para a shell $ perl -Mlocal::lib export PERL_MB_OPT='--install_base /home/username/perl5' export PERL_MM_OPT='INSTALL_BASE=/home/username/perl5' export PERL5LIB='/home/username/perl5/lib/perl5/i386-linux:/home/username/perl5/lib/perl5' export PATH="/home/username/perl5/bin:$PATH" =head2 A técnica de 'bootstrapping' Uma forma comum de instalar o local::lib é usando o que é conhecido como técnica de "bootstrapping". É uma boa abordagem caso seu administrador de sistemas não tenha instalado o local::lib. Nesse caso, você precisará instalar o local::lib em seu diretório de usuário. Caso você tenha privilégios de administrador, ainda assim deverá configurar suas variáveis de ambiente, como discutido no passo 4, abaixo. Sem elas, você ainda instalará módulos no CPAN do sistema e seus scripts Perl não utilizarão o caminho para o lib/ que você definiu com o local::lib. Por padrão, o local::lib instala os módulos do CPAN e a si próprio em ~/perl5. Usuários do Windows devem ler L</Diferenças ao usar esse módulo em Win32>. 1. Baixe e descompacte o local::lib do CPAN (procure por "Download" na página do CPAN sobre o local::lib). Faça isso como um usuário comum, não como root ou administrador. Descompacte o arquivo em seu diretório de usuário ou em qualquer outro local conveniente. 2. Execute isso: perl Makefile.PL --bootstrap Caso o sistema pergunte se deve configurar tudo que puder automaticamente, você provavelmente deve responder que sim (yes). Para instalar o local::lib em um diretório que não o padrão, você precisará especificá-lo ao chamar o bootstrap, da seguinte forma: perl Makefile.PL --bootstrap=~/foo 3. Execute isso: (local::lib assume que você possui o comando 'make' instalado em seu sistema) make test && make install 4. Agora precisamos configurar as variáveis de ambiente apropriadas para que o Perl use nosso recém-criado diretório lib/. Caso esteja usando bash ou outra shell Bourne, você pode fazer isso adicionando a seguinte linha em seu script de inicialização da shell: echo 'eval $(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib)' >>~/.bashrc Caso esteja usando a shell C, pode fazer da seguinte forma: /bin/csh echo $SHELL /bin/csh perl -I$HOME/perl5/lib/perl5 -Mlocal::lib >> ~/.cshrc Caso tenha passado para o bootstrap um diretório que não o padrão, você precisará indicá-lo na chamada ao local::lib, dessa forma: echo 'eval $(perl -I$HOME/foo/lib/perl5 -Mlocal::lib=$HOME/foo)' >>~/.bashrc Após atualizar seu arquivo de configuração da shell, certifique-se de processá-lo novamente para obter as modificações em sua shell atual. Shells Bourne usam C<. ~/.bashrc> para isso, enquanto shells C usam C<source ~/.cshrc>. Se estiver em uma máquina lenta ou operando com grandes limitações de espaço em disco, você pode desativar a geração automática de manpages a partir do POD ao instalar módulos. Para isso, basta passar o argumento C<--no-manpages> durante o bootstrap: perl Makefile.PL --bootstrap --no-manpages Para evitar ter que fazer vários bootstraps para vários ambientes de módulos Perl na mesma conta de usuário - por exemplo se você usa o local::lib para desenvolver diferentes aplicativos independentes - você pode utilizar uma única instalação bootstrap do local::lib para instalar módulos em diretórios diferentes da seguinte forma: cd ~/meudir1 perl -Mlocal::lib=./ eval $(perl -Mlocal::lib=./) ### Para configurar o ambiente apenas nessa shell printenv ### Veja que o ~/meudir1 está na PERL5LIB perl -MCPAN -e install ... ### Os módulos que quiser cd ../meudir2 ... REPITA ... Para múltiplos ambientes destinados a múltiplos aplicativos, você pode precisar incluir uma versão modificada das instruções de C<< use FindBin >> no exemplo "No código" acima. Caso tenha feito algo como o que foi descrito acima, terá um conjunto de módulos Perl em C<< ~/meudir1/lib >>. Caso tenha um script em C<< ~/meudir1/scripts/meuscript.pl >>, você precisará indicar a ele onde encontrar os módulos que instalou para ele em C<< ~/meudir1/lib >>. Em C<< ~/meudir1/scripts/meuscript.pl >>: use strict; use warnings; use local::lib "$FindBin::Bin/.."; ### aponta para ~/meudir1 e o local::lib acha o lib/ use lib "$FindBin::Bin/../lib"; ### aponta para ~/meudir1/lib Coloque isso antes de qualquer bloco BEGIN { ... } que precise dos módulos instalados. =head2 Diferenças ao usar esse módulo em Win32 Para configurar as variáveis de ambiente apropriadas para sua sessão atual do C<CMD.exe>, você pode fazer assim: C:\>perl -Mlocal::lib set PERL_MB_OPT=--install_base C:\DOCUME~1\ADMINI~1\perl5 set PERL_MM_OPT=INSTALL_BASE=C:\DOCUME~1\ADMINI~1\perl5 set PERL5LIB=C:\DOCUME~1\ADMINI~1\perl5\lib\perl5;C:\DOCUME~1\ADMINI~1\perl5\lib\perl5\MSWin32-x86-multi-thread set PATH=C:\DOCUME~1\ADMINI~1\perl5\bin;%PATH% ### Para configurar o ambiente apenas dessa shell C:\>perl -Mlocal::lib > %TEMP%\tmp.bat && %TEMP%\tmp.bat && del %TEMP%\temp.bat ### em vez de $(perl -Mlocal::lib=./) Caso queira que as configurações do ambiente persistam, você precisará adicioná-las em Painel de Controle -> Sistema, ou usar o L<App::local::lib::Win32Helper>. O "~" é transformado no diretório do perfil do usuário (o diretório com o nome do usuário dentro de "Documents and Settings" (Windows XP ou anterior) ou "Usuários" (Windows Vista e mais recentes)) a menos que $ENV{HOME} exista. Após isso, o nome do diretório é encurtado e os subdiretórios são criados (o que significa que o diretório deve existir). =head1 MOTIVAÇÃO A versão de um pacote Perl na sua máquina nem sempre é a que você precisa. Obviamente, a melhor coisa a fazer seria atualizá-la para a versão desejada. No entanto, você pode estar em uma situação que o impede de fazer isso. Talvez você não tenha privilégios de administrador do sistema; ou talvez esteja usando um sistema de gerenciamento de pacotes como o do Debian, e ainda não exista um pacote disponível na versão desejada. local::lib resolve esse problema possibilitando a criação de seu próprio diretório de pacotes Perl obtidos do CPAN (em sistemas multi-usuário, isso normalmente fica dentro do diretório de seu usuário). A instalação do Perl no sistema permanece inalterada; você simplesmente chama o Perl com opções especiais para que ele use os pacotes em seu diretório local em vez dos pacotes do sistema. O local::lib organiza as coisas para que versões dos pacotes Perl instalados localmente tenham precedência sobre as do sistema. Caso esteja usando um sistema de gerenciamento de pacote (como em sistemas Debian), não precisará se preocupar com conflitos entre o Debian e o CPAN. Sua versão local dos pacotes será instalada em um diretório completamente diferente das versões instaladas pelo gerenciador de pacotes do sistema. =head1 DESCRIÇÃO Este módulo oferece uma forma rápida e conveniente para criar um repositório de módulos locais ao usuário, dentro do diretório do mesmo. Ele também monta e exibe para o usuário uma lista de variáveis de ambiente utilizando a sintaxe da shell atual do usuário (conforme especificado pela variável de ambiente C<SHELL>), pronta para ser adicionada diretamente no arquivo de configuração da shell. Generalizando, o local::lib permite a criação e uso de um diretório contendo módulos Perl fora do C<@INC> do Perl. Isso facilita a produção de aplicações com uma versão específica de determinado módulo, ou coleção de módulos. Também é útil quando o mantenedor de um módulo não aplicou determinado patch que você precisa para seu aplicativo. Durante o C<import>, o local::lib define valores apropriados para as seguintes variáveis de ambiente: =over 4 =item PERL_MB_OPT =item PERL_MM_OPT =item PERL5LIB =item PATH valores serão anexados ao PATH, em vez de substituí-lo. =back Esses valores são então disponibilizados para referência por qualquer outro código após o C<import>. =head1 CRIANDO UM CONJUNTO AUTO-CONTIDO DE MÓDULOS Veja L<lib::core::only|lib::core::only> para uma maneira de fazer isso - mas note que há uma série de ressalvas na abordagem, e a melhor forma é sempre fazer o 'build' contra uma versão limpa do perl (i.e. com 'site' e 'vendor' o mais vazios possível). =head1 MÉTODOS =head2 ensure_dir_structure_for =over 4 =item Argumentos: $caminho_do_diretorio =item Valor de Retorno: Nenhum =back Tenta criar o caminho fornecido, e todos os diretórios superiores necessários. Gera uma exceção em caso de falha. =head2 print_environment_vars_for =over 4 =item Argumentos: $caminho_do_diretorio =item Valor de Retorno: Nenhum =back Exibe na saída padrão as variáveis listadas acima, devidamente ajustadas para utilizar o caminho fornecido como diretório base. =head2 build_environment_vars_for =over 4 =item Argumentos: $caminho_do_diretorio, $interpolar =item Valor de Retorno: %variaveis_de_ambiente =back Retorna hash contendo as variáveis de ambiente listadas acima, devidamente ajustadas para utilizar o caminho fornecido como diretório base. =head2 setup_env_hash_for =over 4 =item Argumentos: $caminho_do_diretorio =item Valor de Retorno: Nenhum =back Constrói as chaves no C<%ENV> para o caminho fornecido, chamando C<build_environment_vars_for>. =head2 install_base_perl_path =over 4 =item Argumentos: $caminho_do_diretorio =item Valor de Retorno: $caminho_base_de_instalacao =back Retorna um caminho de diretório indicando onde instalar os módulos Perl para essa instalação local de bibliotecas. Adiciona os diretórios C<lib> e C<perl5> ao final do caminho fornecido. =head2 install_base_arch_path =over 4 =item Argumentos: $caminho_do_diretorio =item Valor de Retorno: $caminho_base_de_instalacao_arch =back Retorna um caminho de diretório indicando onde instalar os módulos Perl de arquiteturas específicas para essa instalação local de bibliotecas. Baseia-se no valor de retorno do método L</install_base_perl_path>, adicionando o valor de C<$Config{archname}>. =head2 install_base_bin_path =over 4 =item Argumentos: $caminho_do_diretorio =item Valor de Retorno: $caminho_base_de_instalacao_bin =back Retorna um caminho de diretório indicando onde instalar programas executáveis para essa instalação local de bibliotecas. Baseia-se no valor de retorno do método L</install_base_perl_path>, adicionando o diretório C<bin>. =head2 resolve_empty_path =over 4 =item Argumentos: $caminho_do_diretorio =item Valor de Retorno: $caminho_base_de_instalacao =back Cria e retorna o caminho de diretório raiz em que a instalação local de módulos deve ser feita. O padrão é C<~/perl5>. =head2 resolve_home_path =over 4 =item Argumentos: $caminho_do_diretorio =item Valor de Retorno: $caminho_para_home =back Procura pelo diretório padrão (home) do usuário. Gera uma exceção caso não encontre resultado definitivo. =head2 resolve_relative_path =over 4 =item Argumentos: $caminho_do_diretorio =item Valor de Retorno: $caminho_absoluto =back Transforma o caminho fornecido em um caminho absoluto. =head2 resolve_path =over 4 =item Argumentos: $caminho_do_diretorio =item Valor de Retorno: $caminho_absoluto =back Invoca os seguintes métodos em sequência, passando o resultado do método anterior para o seguinte, na tentativa de descobrir onde configurar o ambiente para a instalação local de bibliotecas: L</resolve_empty_path>, L</resolve_home_path>, L</resolve_relative_path>. Passa o caminho de diretório fornecido para L</resolve_empty_path> que retorna um resultado que é passado para L</resolve_home_path>, que então tem seu resultado passado para L</resolve_relative_path>. O resultado dessa chamada final é então retornado pelo L</resolve_path>. =head1 UM AVISO SOBRE UNINST=1 Tenha cuidado ao usar o local::lib em conjunto com "make install UNINST=1". A idéia dessa opção é desinstalar a versão anterior de um módulo antes de instalar a mais recente. No entanto ela não possui uma verificação de segurança de que a versão antiga e a nova referem-se ao mesmo diretório. Usada em combinação com o local::lib, você pode potencialmente apagar uma versão globalmente acessível de um módulo e instalar a versão mais nova no diretório local. Apenas utilize "make install UNINST=1" junto com o local::lib se você entende essas possíveis consequências. =head1 LIMITAÇÕES As ferramentas auxiliares do perl não conseguem lidar com nomes de diretórios contendo espaços, então não é possível fazer seu bootstrap do local::lib em um diretório com espaços. O que você pode fazer é mover seu local::lib para um diretório com espaços B<após> ter instalado todos os módulos dentro dele. Mas esteja ciente que você não poderá atualizar ou instalar outros módulos do CPAN nesse diretório local após a mudança. A detecção da shell é relativamente básica. Neste momento, qualquer coisa com csh no nome será tratada como a C shell ou compatível, e todo o resto será tratado como Bourne, exceto em sistemas Win32. Caso a variável de ambiente C<SHELL> não esteja disponível, assumiremos tratar-se de uma shell compatível com a Bourne. A técnica de bootstrap é um hack e usará o CPAN.pm para o ExtUtils::MakeMaker mesmo que você tenha o CPANPLUS instalado. Destrói qualquer valor pré-existente nas variáveis de ambiente PERL5LIB, PERL_MM_OPT e PERL_MB_OPT. Provavelmente deveria auto-configurar o CPAN caso isso ainda não tenha sido feito. Correções (patches) são muito bem-vindos para quaisquer dos itens acima. Em sistemas Win32, não há uma forma de escrever no registro as variáveis de ambiente criadas, para que elas persistam a uma reinicialização. =head1 SOLUÇÃO DE PROBLEMAS Se você configurou o local::lib para instalar módulos do CPAN em algum lugar do seu 'home', e mais tarde tentou instalar um módulo fazendo C<cpan -i Foo::Bar>, mas ele falhou com um erro como: C<Warning: You do not have permissions to install into /usr/lib64/perl5/site_perl/5.8.8/x86_64-linux at /usr/lib64/perl5/5.8.8/Foo/Bar.pm> e em algum lugar no seu log de instalação houver um erro dizendo C<'INSTALL_BASE' is not a known MakeMaker parameter name>, então você de alguma forma perdeu seu ExtUtils::MakeMaker atualizado. Para remediar a situação, execute novamente o procedimento de bootstrap descrito acima. Então, execute C<rm -r ~/.cpan/build/Foo-Bar*> Finalmente, execute novamente o C<cpan -i Foo::Bar> e ele deve instalar sem problemas. =head1 AMBIENTE =over 4 =item SHELL =item COMSPEC O local::lib procura pela variável de ambiente C<SHELL> do usuário ao processar e exibir os comandos a serem adicionados no arquivo de configuração da shell. Em sistemas Win32, C<COMSPEC> também será examinado. =back =head1 SUPORTE IRC: Acesse #local-lib em irc.perl.org. =head1 AUTOR DA TRADUÇÃO Breno G. de Oliveira, C<< <garu at cpan.org> >>, após ter perdido uma aposta para o L<Getty|http://search.cpan.org/~getty/> durante a Copa de 2010. =head1 COPYRIGHT Copyright (c) 2007 - 2010 L<local::lib/"AUTHOR"> e L<local::lib/"CONTRIBUTORS"> do local::lib como listados em L<local::lib>. =head1 LICENÇA Esta biblioteca é software livre e pode ser distribuída sob os mesmo termos do perl. PK�����A0]X6qR��R����perl5/Try/Tiny.pmnu�6$��������package Try::Tiny; # git description: v0.30-11-g1b81d0a use 5.006; # ABSTRACT: Minimal try/catch with proper preservation of $@ our $VERSION = '0.31'; use strict; use warnings; use Exporter 5.57 'import'; our @EXPORT = our @EXPORT_OK = qw(try catch finally); use Carp; $Carp::Internal{+__PACKAGE__}++; BEGIN { my $su = $INC{'Sub/Util.pm'} && defined &Sub::Util::set_subname; my $sn = $INC{'Sub/Name.pm'} && eval { Sub::Name->VERSION(0.08) }; unless ($su || $sn) { $su = eval { require Sub::Util; } && defined &Sub::Util::set_subname; unless ($su) { $sn = eval { require Sub::Name; Sub::Name->VERSION(0.08) }; } } *_subname = $su ? \&Sub::Util::set_subname : $sn ? \&Sub::Name::subname : sub { $_[1] }; *_HAS_SUBNAME = ($su || $sn) ? sub(){1} : sub(){0}; } my %_finally_guards; # Need to prototype as @ not $$ because of the way Perl evaluates the prototype. # Keeping it at $$ means you only ever get 1 sub because we need to eval in a list # context & not a scalar one sub try (&;@) { my ( $try, @code_refs ) = @_; # we need to save this here, the eval block will be in scalar context due # to $failed my $wantarray = wantarray; # work around perl bug by explicitly initializing these, due to the likelyhood # this will be used in global destruction (perl rt#119311) my ( $catch, @finally ) = (); # find labeled blocks in the argument list. # catch and finally tag the blocks by blessing a scalar reference to them. foreach my $code_ref (@code_refs) { if ( ref($code_ref) eq 'Try::Tiny::Catch' ) { croak 'A try() may not be followed by multiple catch() blocks' if $catch; $catch = ${$code_ref}; } elsif ( ref($code_ref) eq 'Try::Tiny::Finally' ) { push @finally, ${$code_ref}; } else { croak( 'try() encountered an unexpected argument (' . ( defined $code_ref ? $code_ref : 'undef' ) . ') - perhaps a missing semi-colon before or' ); } } # FIXME consider using local $SIG{__DIE__} to accumulate all errors. It's # not perfect, but we could provide a list of additional errors for # $catch->(); # name the blocks if we have Sub::Name installed _subname(caller().'::try {...} ' => $try) if _HAS_SUBNAME; # set up scope guards to invoke the finally blocks at the end. # this should really be a function scope lexical variable instead of # file scope + local but that causes issues with perls < 5.20 due to # perl rt#119311 local $_finally_guards{guards} = [ map Try::Tiny::ScopeGuard->_new($_), @finally ]; # save the value of $@ so we can set $@ back to it in the beginning of the eval # and restore $@ after the eval finishes my $prev_error = $@; my ( @ret, $error ); # failed will be true if the eval dies, because 1 will not be returned # from the eval body my $failed = not eval { $@ = $prev_error; # evaluate the try block in the correct context if ( $wantarray ) { @ret = $try->(); } elsif ( defined $wantarray ) { $ret[0] = $try->(); } else { $try->(); }; return 1; # properly set $failed to false }; # preserve the current error and reset the original value of $@ $error = $@; $@ = $prev_error; # at this point $failed contains a true value if the eval died, even if some # destructor overwrote $@ as the eval was unwinding. if ( $failed ) { # pass $error to the finally blocks push @$_, $error for @{$_finally_guards{guards}}; # if we got an error, invoke the catch block. if ( $catch ) { # This works like given($error), but is backwards compatible and # sets $_ in the dynamic scope for the body of C<$catch> for ($error) { return $catch->($error); } # in case when() was used without an explicit return, the C<for> # loop will be aborted and there's no useful return value } return; } else { # no failure, $@ is back to what it was, everything is fine return $wantarray ? @ret : $ret[0]; } } sub catch (&;@) { my ( $block, @rest ) = @_; croak 'Useless bare catch()' unless wantarray; _subname(caller().'::catch {...} ' => $block) if _HAS_SUBNAME; return ( bless(\$block, 'Try::Tiny::Catch'), @rest, ); } sub finally (&;@) { my ( $block, @rest ) = @_; croak 'Useless bare finally()' unless wantarray; _subname(caller().'::finally {...} ' => $block) if _HAS_SUBNAME; return ( bless(\$block, 'Try::Tiny::Finally'), @rest, ); } { package # hide from PAUSE Try::Tiny::ScopeGuard; use constant UNSTABLE_DOLLARAT => ("$]" < '5.013002') ? 1 : 0; sub _new { shift; bless [ @_ ]; } sub DESTROY { my ($code, @args) = @{ $_[0] }; local $@ if UNSTABLE_DOLLARAT; eval { $code->(@args); 1; } or do { warn "Execution of finally() block $code resulted in an exception, which " . '*CAN NOT BE PROPAGATED* due to fundamental limitations of Perl. ' . 'Your program will continue as if this event never took place. ' . "Original exception text follows:\n\n" . (defined $@ ? $@ : '$@ left undefined...') . "\n" ; } } } __PACKAGE__ __END__ =pod =encoding UTF-8 =head1 NAME Try::Tiny - Minimal try/catch with proper preservation of $@ =head1 VERSION version 0.31 =head1 SYNOPSIS You can use Try::Tiny's C<try> and C<catch> to expect and handle exceptional conditions, avoiding quirks in Perl and common mistakes: # handle errors with a catch handler try { die "foo"; } catch { warn "caught error: $_"; # not $@ }; You can also use it like a standalone C<eval> to catch and ignore any error conditions. Obviously, this is an extreme measure not to be undertaken lightly: # just silence errors try { die "foo"; }; =head1 DESCRIPTION This module provides bare bones C<try>/C<catch>/C<finally> statements that are designed to minimize common mistakes with eval blocks, and NOTHING else. This is unlike L<TryCatch> which provides a nice syntax and avoids adding another call stack layer, and supports calling C<return> from the C<try> block to return from the parent subroutine. These extra features come at a cost of a few dependencies, namely L<Devel::Declare> and L<Scope::Upper> which are occasionally problematic, and the additional catch filtering uses L<Moose> type constraints which may not be desirable either. The main focus of this module is to provide simple and reliable error handling for those having a hard time installing L<TryCatch>, but who still want to write correct C<eval> blocks without 5 lines of boilerplate each time. It's designed to work as correctly as possible in light of the various pathological edge cases (see L</BACKGROUND>) and to be compatible with any style of error values (simple strings, references, objects, overloaded objects, etc). If the C<try> block dies, it returns the value of the last statement executed in the C<catch> block, if there is one. Otherwise, it returns C<undef> in scalar context or the empty list in list context. The following examples all assign C<"bar"> to C<$x>: my $x = try { die "foo" } catch { "bar" }; my $x = try { die "foo" } || "bar"; my $x = (try { die "foo" }) // "bar"; my $x = eval { die "foo" } || "bar"; You can add C<finally> blocks, yielding the following: my $x; try { die 'foo' } finally { $x = 'bar' }; try { die 'foo' } catch { warn "Got a die: $_" } finally { $x = 'bar' }; C<finally> blocks are always executed making them suitable for cleanup code which cannot be handled using local. You can add as many C<finally> blocks to a given C<try> block as you like. Note that adding a C<finally> block without a preceding C<catch> block suppresses any errors. This behaviour is consistent with using a standalone C<eval>, but it is not consistent with C<try>/C<finally> patterns found in other programming languages, such as Java, Python, Javascript or C#. If you learned the C<try>/C<finally> pattern from one of these languages, watch out for this. =head1 EXPORTS All functions are exported by default using L<Exporter>. If you need to rename the C<try>, C<catch> or C<finally> keyword consider using L<Sub::Import> to get L<Sub::Exporter>'s flexibility. =over 4 =item try (&;@) Takes one mandatory C<try> subroutine, an optional C<catch> subroutine and C<finally> subroutine. The mandatory subroutine is evaluated in the context of an C<eval> block. If no error occurred the value from the first block is returned, preserving list/scalar context. If there was an error and the second subroutine was given it will be invoked with the error in C<$_> (localized) and as that block's first and only argument. C<$@> does B<not> contain the error. Inside the C<catch> block it has the same value it had before the C<try> block was executed. Note that the error may be false, but if that happens the C<catch> block will still be invoked. Once all execution is finished then the C<finally> block, if given, will execute. =item catch (&;@) Intended to be used in the second argument position of C<try>. Returns a reference to the subroutine it was given but blessed as C<Try::Tiny::Catch> which allows try to decode correctly what to do with this code reference. catch { ... } Inside the C<catch> block the caught error is stored in C<$_>, while previous value of C<$@> is still available for use. This value may or may not be meaningful depending on what happened before the C<try>, but it might be a good idea to preserve it in an error stack. For code that captures C<$@> when throwing new errors (i.e. L<Class::Throwable>), you'll need to do: local $@ = $_; =item finally (&;@) try { ... } catch { ... } finally { ... }; Or try { ... } finally { ... }; Or even try { ... } finally { ... } catch { ... }; Intended to be the second or third element of C<try>. C<finally> blocks are always executed in the event of a successful C<try> or if C<catch> is run. This allows you to locate cleanup code which cannot be done via C<local()> e.g. closing a file handle. When invoked, the C<finally> block is passed the error that was caught. If no error was caught, it is passed nothing. (Note that the C<finally> block does not localize C<$_> with the error, since unlike in a C<catch> block, there is no way to know if C<$_ == undef> implies that there were no errors.) In other words, the following code does just what you would expect: try { die_sometimes(); } catch { # ...code run in case of error } finally { if (@_) { print "The try block died with: @_\n"; } else { print "The try block ran without error.\n"; } }; B<You must always do your own error handling in the C<finally> block>. C<Try::Tiny> will not do anything about handling possible errors coming from code located in these blocks. Furthermore B<exceptions in C<finally> blocks are not trappable and are unable to influence the execution of your program>. This is due to limitation of C<DESTROY>-based scope guards, which C<finally> is implemented on top of. This may change in a future version of Try::Tiny. In the same way C<catch()> blesses the code reference this subroutine does the same except it bless them as C<Try::Tiny::Finally>. =back =head1 BACKGROUND There are a number of issues with C<eval>. =head2 Clobbering $@ When you run an C<eval> block and it succeeds, C<$@> will be cleared, potentially clobbering an error that is currently being caught. This causes action at a distance, clearing previous errors your caller may have not yet handled. C<$@> must be properly localized before invoking C<eval> in order to avoid this issue. More specifically, L<before Perl version 5.14.0|perl5140delta/"Exception Handling"> C<$@> was clobbered at the beginning of the C<eval>, which also made it impossible to capture the previous error before you die (for instance when making exception objects with error stacks). For this reason C<try> will actually set C<$@> to its previous value (the one available before entering the C<try> block) in the beginning of the C<eval> block. =head2 Localizing $@ silently masks errors Inside an C<eval> block, C<die> behaves sort of like: sub die { $@ = $_[0]; return_undef_from_eval(); } This means that if you were polite and localized C<$@> you can't die in that scope, or your error will be discarded (printing "Something's wrong" instead). The workaround is very ugly: my $error = do { local $@; eval { ... }; $@; }; ... die $error; =head2 $@ might not be a true value This code is wrong: if ( $@ ) { ... } because due to the previous caveats it may have been unset. C<$@> could also be an overloaded error object that evaluates to false, but that's asking for trouble anyway. The classic failure mode (fixed in L<Perl 5.14.0|perl5140delta/"Exception Handling">) is: sub Object::DESTROY { eval { ... } } eval { my $obj = Object->new; die "foo"; }; if ( $@ ) { } In this case since C<Object::DESTROY> is not localizing C<$@> but still uses C<eval>, it will set C<$@> to C<"">. The destructor is called when the stack is unwound, after C<die> sets C<$@> to C<"foo at Foo.pm line 42\n">, so by the time C<if ( $@ )> is evaluated it has been cleared by C<eval> in the destructor. The workaround for this is even uglier than the previous ones. Even though we can't save the value of C<$@> from code that doesn't localize, we can at least be sure the C<eval> was aborted due to an error: my $failed = not eval { ... return 1; }; This is because an C<eval> that caught a C<die> will always return a false value. =head1 ALTERNATE SYNTAX Using Perl 5.10 you can use L<perlsyn/"Switch statements"> (but please don't, because that syntax has since been deprecated because there was too much unexpected magical behaviour). =for stopwords topicalizer The C<catch> block is invoked in a topicalizer context (like a C<given> block), but note that you can't return a useful value from C<catch> using the C<when> blocks without an explicit C<return>. This is somewhat similar to Perl 6's C<CATCH> blocks. You can use it to concisely match errors: try { require Foo; } catch { when (/^Can't locate .*?\.pm in \@INC/) { } # ignore default { die $_ } }; =head1 CAVEATS =over 4 =item * C<@_> is not available within the C<try> block, so you need to copy your argument list. In case you want to work with argument values directly via C<@_> aliasing (i.e. allow C<$_[1] = "foo">), you need to pass C<@_> by reference: sub foo { my ( $self, @args ) = @_; try { $self->bar(@args) } } or sub bar_in_place { my $self = shift; my $args = \@_; try { $_ = $self->bar($_) for @$args } } =item * C<return> returns from the C<try> block, not from the parent sub (note that this is also how C<eval> works, but not how L<TryCatch> works): sub parent_sub { try { die; } catch { return; }; say "this text WILL be displayed, even though an exception is thrown"; } Instead, you should capture the return value: sub parent_sub { my $success = try { die; 1; }; return unless $success; say "This text WILL NEVER appear!"; } # OR sub parent_sub_with_catch { my $success = try { die; 1; } catch { # do something with $_ return undef; #see note }; return unless $success; say "This text WILL NEVER appear!"; } Note that if you have a C<catch> block, it must return C<undef> for this to work, since if a C<catch> block exists, its return value is returned in place of C<undef> when an exception is thrown. =item * C<try> introduces another caller stack frame. L<Sub::Uplevel> is not used. L<Carp> will not report this when using full stack traces, though, because C<%Carp::Internal> is used. This lack of magic is considered a feature. =for stopwords unhygienically =item * The value of C<$_> in the C<catch> block is not guaranteed to be the value of the exception thrown (C<$@>) in the C<try> block. There is no safe way to ensure this, since C<eval> may be used unhygienically in destructors. The only guarantee is that the C<catch> will be called if an exception is thrown. =item * The return value of the C<catch> block is not ignored, so if testing the result of the expression for truth on success, be sure to return a false value from the C<catch> block: my $obj = try { MightFail->new; } catch { ... return; # avoid returning a true value; }; return unless $obj; =item * C<$SIG{__DIE__}> is still in effect. Though it can be argued that C<$SIG{__DIE__}> should be disabled inside of C<eval> blocks, since it isn't people have grown to rely on it. Therefore in the interests of compatibility, C<try> does not disable C<$SIG{__DIE__}> for the scope of the error throwing code. =item * Lexical C<$_> may override the one set by C<catch>. For example Perl 5.10's C<given> form uses a lexical C<$_>, creating some confusing behavior: given ($foo) { when (...) { try { ... } catch { warn $_; # will print $foo, not the error warn $_[0]; # instead, get the error like this } } } Note that this behavior was changed once again in L<Perl5 version 18|https://metacpan.org/module/perldelta#given-now-aliases-the-global-_>. However, since the entirety of lexical C<$_> is now L<considered experimental |https://metacpan.org/module/perldelta#Lexical-_-is-now-experimental>, it is unclear whether the new version 18 behavior is final. =back =head1 SEE ALSO =over 4 =item L<Syntax::Keyword::Try> Only available on perls >= 5.14, with a slightly different syntax (e.g. no trailing C<;> because it's actually a keyword, not a sub, but this means you can C<return> and C<next> within it). Use L<Feature::Compat::Try> to automatically switch to the native C<try> syntax in newer perls (when available). See also L<Try Catch Exception Handling|perlsyn/Try-Catch-Exception-Handling>. =item L<TryCatch> Much more feature complete, more convenient semantics, but at the cost of implementation complexity. =item L<autodie> Automatic error throwing for builtin functions and more. Also designed to work well with C<given>/C<when>. =item L<Throwable> A lightweight role for rolling your own exception classes. =item L<Error> Exception object implementation with a C<try> statement. Does not localize C<$@>. =item L<Exception::Class::TryCatch> Provides a C<catch> statement, but properly calling C<eval> is your responsibility. The C<try> keyword pushes C<$@> onto an error stack, avoiding some of the issues with C<$@>, but you still need to localize to prevent clobbering. =back =head1 LIGHTNING TALK I gave a lightning talk about this module, you can see the slides (Firefox only): L<http://web.archive.org/web/20100628040134/http://nothingmuch.woobling.org/talks/takahashi.xul> Or read the source: L<http://web.archive.org/web/20100305133605/http://nothingmuch.woobling.org/talks/yapc_asia_2009/try_tiny.yml> =head1 SUPPORT Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=Try-Tiny> (or L<bug-Try-Tiny@rt.cpan.org|mailto:bug-Try-Tiny@rt.cpan.org>). =head1 AUTHORS =over 4 =item * יובל קוג'מן (Yuval Kogman) <nothingmuch@woobling.org> =item * Jesse Luehrs <doy@tozt.net> =back =head1 CONTRIBUTORS =for stopwords Karen Etheridge Peter Rabbitson Ricardo Signes Mark Fowler Graham Knop Aristotle Pagaltzis Dagfinn Ilmari Mannsåker Lukas Mai Alex anaxagoras Andrew Yates awalker chromatic cm-perl David Lowe Glenn Hans Dieter Pearcey Jens Berthold Jonathan Yu Marc Mims Stosberg Pali Paul Howarth Rudolf Leermakers =over 4 =item * Karen Etheridge <ether@cpan.org> =item * Peter Rabbitson <ribasushi@cpan.org> =item * Ricardo Signes <rjbs@cpan.org> =item * Mark Fowler <mark@twoshortplanks.com> =item * Graham Knop <haarg@haarg.org> =item * Aristotle Pagaltzis <pagaltzis@gmx.de> =item * Dagfinn Ilmari Mannsåker <ilmari@ilmari.org> =item * Lukas Mai <l.mai@web.de> =item * Alex <alex@koban.(none)> =item * anaxagoras <walkeraj@gmail.com> =item * Andrew Yates <ayates@haddock.local> =item * awalker <awalker@sourcefire.com> =item * chromatic <chromatic@wgz.org> =item * cm-perl <cm-perl@users.noreply.github.com> =item * David Lowe <davidl@lokku.com> =item * Glenn Fowler <cebjyre@cpan.org> =item * Hans Dieter Pearcey <hdp@weftsoar.net> =item * Jens Berthold <jens@jebecs.de> =item * Jonathan Yu <JAWNSY@cpan.org> =item * Marc Mims <marc@questright.com> =item * Mark Stosberg <mark@stosberg.com> =item * Pali <pali@cpan.org> =item * Paul Howarth <paul@city-fan.org> =item * Rudolf Leermakers <rudolf@hatsuseno.org> =back =head1 COPYRIGHT AND LICENCE This software is Copyright (c) 2009 by יובל קוג'מן (Yuval Kogman). This is free software, licensed under: The MIT (X11) License =cut PK�����A0]Q������perl5/App/Cpan.pmnu�6$��������package App::Cpan; use strict; use warnings; use vars qw($VERSION); use if $] < 5.008 => 'IO::Scalar'; $VERSION = '1.678'; =head1 NAME App::Cpan - easily interact with CPAN from the command line =head1 SYNOPSIS # with arguments and no switches, installs specified modules cpan module_name [ module_name ... ] # with switches, installs modules with extra behavior cpan [-cfFimtTw] module_name [ module_name ... ] # use local::lib cpan -I module_name [ module_name ... ] # one time mirror override for faster mirrors cpan -p ... # with just the dot, install from the distribution in the # current directory cpan . # without arguments, starts CPAN.pm shell cpan # without arguments, but some switches cpan [-ahpruvACDLOPX] =head1 DESCRIPTION This script provides a command interface (not a shell) to CPAN. At the moment it uses CPAN.pm to do the work, but it is not a one-shot command runner for CPAN.pm. =head2 Options =over 4 =item -a Creates a CPAN.pm autobundle with CPAN::Shell->autobundle. =item -A module [ module ... ] Shows the primary maintainers for the specified modules. =item -c module Runs a `make clean` in the specified module's directories. =item -C module [ module ... ] Show the F<Changes> files for the specified modules =item -D module [ module ... ] Show the module details. This prints one line for each out-of-date module (meaning, modules locally installed but have newer versions on CPAN). Each line has three columns: module name, local version, and CPAN version. =item -f Force the specified action, when it normally would have failed. Use this to install a module even if its tests fail. When you use this option, -i is not optional for installing a module when you need to force it: % cpan -f -i Module::Foo =item -F Turn off CPAN.pm's attempts to lock anything. You should be careful with this since you might end up with multiple scripts trying to muck in the same directory. This isn't so much of a concern if you're loading a special config with C<-j>, and that config sets up its own work directories. =item -g module [ module ... ] Downloads to the current directory the latest distribution of the module. =item -G module [ module ... ] UNIMPLEMENTED Download to the current directory the latest distribution of the modules, unpack each distribution, and create a git repository for each distribution. If you want this feature, check out Yanick Champoux's C<Git::CPAN::Patch> distribution. =item -h Print a help message and exit. When you specify C<-h>, it ignores all of the other options and arguments. =item -i module [ module ... ] Install the specified modules. With no other switches, this switch is implied. =item -I Load C<local::lib> (think like C<-I> for loading lib paths). Too bad C<-l> was already taken. =item -j Config.pm Load the file that has the CPAN configuration data. This should have the same format as the standard F<CPAN/Config.pm> file, which defines C<$CPAN::Config> as an anonymous hash. If the file does not exist, C<cpan> dies. =item -J Dump the configuration in the same format that CPAN.pm uses. This is useful for checking the configuration as well as using the dump as a starting point for a new, custom configuration. =item -l List all installed modules with their versions =item -L author [ author ... ] List the modules by the specified authors. =item -m Make the specified modules. =item -M mirror1,mirror2,... A comma-separated list of mirrors to use for just this run. The C<-P> option can find them for you automatically. =item -n Do a dry run, but don't actually install anything. (unimplemented) =item -O Show the out-of-date modules. =item -p Ping the configured mirrors and print a report =item -P Find the best mirrors you could be using and use them for the current session. =item -r Recompiles dynamically loaded modules with CPAN::Shell->recompile. =item -s Drop in the CPAN.pm shell. This command does this automatically if you don't specify any arguments. =item -t module [ module ... ] Run a `make test` on the specified modules. =item -T Do not test modules. Simply install them. =item -u Upgrade all installed modules. Blindly doing this can really break things, so keep a backup. =item -v Print the script version and CPAN.pm version then exit. =item -V Print detailed information about the cpan client. =item -w UNIMPLEMENTED Turn on cpan warnings. This checks various things, like directory permissions, and tells you about problems you might have. =item -x module [ module ... ] Find close matches to the named modules that you think you might have mistyped. This requires the optional installation of Text::Levenshtein or Text::Levenshtein::Damerau. =item -X Dump all the namespaces to standard output. =back =head2 Examples # print a help message cpan -h # print the version numbers cpan -v # create an autobundle cpan -a # recompile modules cpan -r # upgrade all installed modules cpan -u # install modules ( sole -i is optional ) cpan -i Netscape::Booksmarks Business::ISBN # force install modules ( must use -i ) cpan -fi CGI::Minimal URI # install modules but without testing them cpan -Ti CGI::Minimal URI =head2 Environment variables There are several components in CPAN.pm that use environment variables. The build tools, L<ExtUtils::MakeMaker> and L<Module::Build> use some, while others matter to the levels above them. Some of these are specified by the Perl Toolchain Gang: Lancaster Consensus: L<https://github.com/Perl-Toolchain-Gang/toolchain-site/blob/master/lancaster-consensus.md> Oslo Consensus: L<https://github.com/Perl-Toolchain-Gang/toolchain-site/blob/master/oslo-consensus.md> =over 4 =item NONINTERACTIVE_TESTING Assume no one is paying attention and skips prompts for distributions that do that correctly. C<cpan(1)> sets this to C<1> unless it already has a value (even if that value is false). =item PERL_MM_USE_DEFAULT Use the default answer for a prompted questions. C<cpan(1)> sets this to C<1> unless it already has a value (even if that value is false). =item CPAN_OPTS As with C<PERL5OPT>, a string of additional C<cpan(1)> options to add to those you specify on the command line. =item CPANSCRIPT_LOGLEVEL The log level to use, with either the embedded, minimal logger or L<Log::Log4perl> if it is installed. Possible values are the same as the C<Log::Log4perl> levels: C<TRACE>, C<DEBUG>, C<INFO>, C<WARN>, C<ERROR>, and C<FATAL>. The default is C<INFO>. =item GIT_COMMAND The path to the C<git> binary to use for the Git features. The default is C</usr/local/bin/git>. =back =head2 Methods =over 4 =cut use autouse Carp => qw(carp croak cluck); use CPAN 1.80 (); # needs no test use Config; use autouse Cwd => qw(cwd); use autouse 'Data::Dumper' => qw(Dumper); use File::Spec::Functions qw(catfile file_name_is_absolute rel2abs); use File::Basename; use Getopt::Std; # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Internal constants use constant TRUE => 1; use constant FALSE => 0; # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # The return values use constant HEY_IT_WORKED => 0; use constant I_DONT_KNOW_WHAT_HAPPENED => 1; # 0b0000_0001 use constant ITS_NOT_MY_FAULT => 2; use constant THE_PROGRAMMERS_AN_IDIOT => 4; use constant A_MODULE_FAILED_TO_INSTALL => 8; # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # set up the order of options that we layer over CPAN::Shell BEGIN { # most of this should be in methods use vars qw( @META_OPTIONS $Default %CPAN_METHODS @CPAN_OPTIONS @option_order %Method_table %Method_table_index ); @META_OPTIONS = qw( h v V I g G M: C A D O l L a r p P j: J w x X ); $Default = 'default'; %CPAN_METHODS = ( # map switches to method names in CPAN::Shell $Default => 'install', 'c' => 'clean', 'f' => 'force', 'i' => 'install', 'm' => 'make', 't' => 'test', 'u' => 'upgrade', 'T' => 'notest', 's' => 'shell', ); @CPAN_OPTIONS = grep { $_ ne $Default } sort keys %CPAN_METHODS; @option_order = ( @META_OPTIONS, @CPAN_OPTIONS ); # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # map switches to the subroutines in this script, along with other information. # use this stuff instead of hard-coded indices and values sub NO_ARGS () { 0 } sub ARGS () { 1 } sub GOOD_EXIT () { 0 } %Method_table = ( # key => [ sub ref, takes args?, exit value, description ] # options that do their thing first, then exit h => [ \&_print_help, NO_ARGS, GOOD_EXIT, 'Printing help' ], v => [ \&_print_version, NO_ARGS, GOOD_EXIT, 'Printing version' ], V => [ \&_print_details, NO_ARGS, GOOD_EXIT, 'Printing detailed version' ], X => [ \&_list_all_namespaces, NO_ARGS, GOOD_EXIT, 'Listing all namespaces' ], # options that affect other options j => [ \&_load_config, ARGS, GOOD_EXIT, 'Use specified config file' ], J => [ \&_dump_config, NO_ARGS, GOOD_EXIT, 'Dump configuration to stdout' ], F => [ \&_lock_lobotomy, NO_ARGS, GOOD_EXIT, 'Turn off CPAN.pm lock files' ], I => [ \&_load_local_lib, NO_ARGS, GOOD_EXIT, 'Loading local::lib' ], M => [ \&_use_these_mirrors, ARGS, GOOD_EXIT, 'Setting per session mirrors' ], P => [ \&_find_good_mirrors, NO_ARGS, GOOD_EXIT, 'Finding good mirrors' ], w => [ \&_turn_on_warnings, NO_ARGS, GOOD_EXIT, 'Turning on warnings' ], # options that do their one thing g => [ \&_download, ARGS, GOOD_EXIT, 'Download the latest distro' ], G => [ \&_gitify, ARGS, GOOD_EXIT, 'Down and gitify the latest distro' ], C => [ \&_show_Changes, ARGS, GOOD_EXIT, 'Showing Changes file' ], A => [ \&_show_Author, ARGS, GOOD_EXIT, 'Showing Author' ], D => [ \&_show_Details, ARGS, GOOD_EXIT, 'Showing Details' ], O => [ \&_show_out_of_date, NO_ARGS, GOOD_EXIT, 'Showing Out of date' ], l => [ \&_list_all_mods, NO_ARGS, GOOD_EXIT, 'Listing all modules' ], L => [ \&_show_author_mods, ARGS, GOOD_EXIT, 'Showing author mods' ], a => [ \&_create_autobundle, NO_ARGS, GOOD_EXIT, 'Creating autobundle' ], p => [ \&_ping_mirrors, NO_ARGS, GOOD_EXIT, 'Pinging mirrors' ], r => [ \&_recompile, NO_ARGS, GOOD_EXIT, 'Recompiling' ], u => [ \&_upgrade, NO_ARGS, GOOD_EXIT, 'Running `make test`' ], 's' => [ \&_shell, NO_ARGS, GOOD_EXIT, 'Drop into the CPAN.pm shell' ], 'x' => [ \&_guess_namespace, ARGS, GOOD_EXIT, 'Guessing namespaces' ], c => [ \&_default, ARGS, GOOD_EXIT, 'Running `make clean`' ], f => [ \&_default, ARGS, GOOD_EXIT, 'Installing with force' ], i => [ \&_default, ARGS, GOOD_EXIT, 'Running `make install`' ], 'm' => [ \&_default, ARGS, GOOD_EXIT, 'Running `make`' ], t => [ \&_default, ARGS, GOOD_EXIT, 'Running `make test`' ], T => [ \&_default, ARGS, GOOD_EXIT, 'Installing with notest' ], ); %Method_table_index = ( code => 0, takes_args => 1, exit_value => 2, description => 3, ); } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # finally, do some argument processing sub _stupid_interface_hack_for_non_rtfmers { no warnings 'uninitialized'; shift @ARGV if( $ARGV[0] eq 'install' and @ARGV > 1 ) } sub _process_options { my %options; push @ARGV, grep $_, split /\s+/, $ENV{CPAN_OPTS} || ''; # if no arguments, just drop into the shell if( 0 == @ARGV ) { CPAN::shell(); exit 0 } elsif (Getopt::Std::getopts( join( '', @option_order ), \%options )) { \%options; } else { exit 1 } } sub _process_setup_options { my( $class, $options ) = @_; if( $options->{j} ) { $Method_table{j}[ $Method_table_index{code} ]->( $options->{j} ); delete $options->{j}; } elsif ( ! $options->{h} ) { # h "ignores all of the other options and arguments" # this is what CPAN.pm would do otherwise local $CPAN::Be_Silent = 1; CPAN::HandleConfig->load( # be_silent => 1, deprecated write_file => 0, ); } $class->_turn_off_testing if $options->{T}; foreach my $o ( qw(F I w P M) ) { next unless exists $options->{$o}; $Method_table{$o}[ $Method_table_index{code} ]->( $options->{$o} ); delete $options->{$o}; } if( $options->{o} ) { my @pairs = map { [ split /=/, $_, 2 ] } split /,/, $options->{o}; foreach my $pair ( @pairs ) { my( $setting, $value ) = @$pair; $CPAN::Config->{$setting} = $value; # $logger->debug( "Setting [$setting] to [$value]" ); } delete $options->{o}; } my $option_count = grep { $options->{$_} } @option_order; no warnings 'uninitialized'; # don't count options that imply installation foreach my $opt ( qw(f T) ) { # don't count force or notest $option_count -= $options->{$opt}; } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # if there are no options, set -i (this line fixes RT ticket 16915) $options->{i}++ unless $option_count; } sub _setup_environment { # should we override or set defaults? If this were a true interactive # session, we'd be in the CPAN shell. # https://github.com/Perl-Toolchain-Gang/toolchain-site/blob/master/lancaster-consensus.md $ENV{NONINTERACTIVE_TESTING} = 1 unless defined $ENV{NONINTERACTIVE_TESTING}; $ENV{PERL_MM_USE_DEFAULT} = 1 unless defined $ENV{PERL_MM_USE_DEFAULT}; } =item run( ARGS ) Just do it. The C<run> method returns 0 on success and a positive number on failure. See the section on EXIT CODES for details on the values. =cut my $logger; sub run { my( $class, @args ) = @_; local @ARGV = @args; my $return_value = HEY_IT_WORKED; # assume that things will work $logger = $class->_init_logger; $logger->debug( "Using logger from @{[ref $logger]}" ); $class->_hook_into_CPANpm_report; $logger->debug( "Hooked into output" ); $class->_stupid_interface_hack_for_non_rtfmers; $logger->debug( "Patched cargo culting" ); my $options = $class->_process_options; $logger->debug( "Options are @{[Dumper($options)]}" ); $class->_process_setup_options( $options ); $class->_setup_environment( $options ); OPTION: foreach my $option ( @option_order ) { next unless $options->{$option}; my( $sub, $takes_args, $description ) = map { $Method_table{$option}[ $Method_table_index{$_} ] } qw( code takes_args description ); unless( ref $sub eq ref sub {} ) { $return_value = THE_PROGRAMMERS_AN_IDIOT; last OPTION; } $logger->info( "[$option] $description -- ignoring other arguments" ) if( @ARGV && ! $takes_args ); $return_value = $sub->( \ @ARGV, $options ); last; } return $return_value; } my $LEVEL; { package Local::Null::Logger; # hide from PAUSE my @LOGLEVELS = qw(TRACE DEBUG INFO WARN ERROR FATAL); $LEVEL = uc($ENV{CPANSCRIPT_LOGLEVEL} || 'INFO'); my %LL = map { $LOGLEVELS[$_] => $_ } 0..$#LOGLEVELS; unless (defined $LL{$LEVEL}){ warn "Unsupported loglevel '$LEVEL', setting to INFO"; $LEVEL = 'INFO'; } sub new { bless \ my $x, $_[0] } sub AUTOLOAD { my $autoload = our $AUTOLOAD; $autoload =~ s/.*://; return if $LL{uc $autoload} < $LL{$LEVEL}; $CPAN::Frontend->mywarn(">($autoload): $_\n") for split /[\r\n]+/, $_[1]; } sub DESTROY { 1 } } # load a module without searching the default entry for the current # directory sub _safe_load_module { my $name = shift; local @INC = @INC; pop @INC if $INC[-1] eq '.'; eval "require $name; 1"; } sub _init_logger { my $log4perl_loaded = _safe_load_module("Log::Log4perl"); unless( $log4perl_loaded ) { print STDOUT "Loading internal logger. Log::Log4perl recommended for better logging\n"; $logger = Local::Null::Logger->new; return $logger; } Log::Log4perl::init( \ <<"HERE" ); log4perl.rootLogger=$LEVEL, A1 log4perl.appender.A1=Log::Log4perl::Appender::Screen log4perl.appender.A1.layout=PatternLayout log4perl.appender.A1.layout.ConversionPattern=%m%n HERE $logger = Log::Log4perl->get_logger( 'App::Cpan' ); } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # sub _default { my( $args, $options ) = @_; my $switch = ''; # choose the option that we're going to use # we'll deal with 'f' (force) later, so skip it foreach my $option ( @CPAN_OPTIONS ) { next if ( $option eq 'f' or $option eq 'T' ); next unless $options->{$option}; $switch = $option; last; } # 1. with no switches, but arguments, use the default switch (install) # 2. with no switches and no args, start the shell # 3. With a switch but no args, die! These switches need arguments. if( not $switch and @$args ) { $switch = $Default; } elsif( not $switch and not @$args ) { return CPAN::shell() } elsif( $switch and not @$args ) { die "Nothing to $CPAN_METHODS{$switch}!\n"; } # Get and check the method from CPAN::Shell my $method = $CPAN_METHODS{$switch}; die "CPAN.pm cannot $method!\n" unless CPAN::Shell->can( $method ); # call the CPAN::Shell method, with force or notest if specified my $action = do { if( $options->{f} ) { sub { CPAN::Shell->force( $method, @_ ) } } elsif( $options->{T} ) { sub { CPAN::Shell->notest( $method, @_ ) } } else { sub { CPAN::Shell->$method( @_ ) } } }; # How do I handle exit codes for multiple arguments? my @errors = (); $options->{x} or _disable_guessers(); foreach my $arg ( @$args ) { # check the argument and perhaps capture typos my $module = _expand_module( $arg ) or do { $logger->error( "Skipping $arg because I couldn't find a matching namespace." ); next; }; _clear_cpanpm_output(); $action->( $arg ); my $error = _cpanpm_output_indicates_failure(); push @errors, $error if $error; } return do { if( @errors ) { $errors[0] } else { HEY_IT_WORKED } }; } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =for comment CPAN.pm sends all the good stuff either to STDOUT, or to a temp file if $CPAN::Be_Silent is set. I have to intercept that output so I can find out what happened. =cut BEGIN { my $scalar = ''; sub _hook_into_CPANpm_report { no warnings 'redefine'; *CPAN::Shell::myprint = sub { my($self,$what) = @_; $scalar .= $what if defined $what; $self->print_ornamented($what, $CPAN::Config->{colorize_print}||'bold blue on_white', ); }; *CPAN::Shell::mywarn = sub { my($self,$what) = @_; $scalar .= $what if defined $what; $self->print_ornamented($what, $CPAN::Config->{colorize_warn}||'bold red on_white' ); }; } sub _clear_cpanpm_output { $scalar = '' } sub _get_cpanpm_output { $scalar } # These are lines I don't care about in CPAN.pm output. If I can # filter out the informational noise, I have a better chance to # catch the error signal my @skip_lines = ( qr/^\QWarning \(usually harmless\)/, qr/\bwill not store persistent state\b/, qr(//hint//), qr/^\s+reports\s+/, qr/^Try the command/, qr/^\s+$/, qr/^to find objects/, qr/^\s*Database was generated on/, qr/^Going to read/, qr|^\s+i\s+/|, # the i /Foo::Whatever/ line when it doesn't know ); sub _get_cpanpm_last_line { my $fh; if( $] < 5.008 ) { $fh = IO::Scalar->new( \ $scalar ); } else { eval q{ open $fh, '<', \\ $scalar; }; } my @lines = <$fh>; # This is a bit ugly. Once we examine a line, we have to # examine the line before it and go through all of the same # regexes. I could do something fancy, but this works. REGEXES: { foreach my $regex ( @skip_lines ) { if( $lines[-1] =~ m/$regex/ ) { pop @lines; redo REGEXES; # we have to go through all of them for every line! } } } $logger->debug( "Last interesting line of CPAN.pm output is:\n\t$lines[-1]" ); $lines[-1]; } } BEGIN { my $epic_fail_words = join '|', qw( Error stop(?:ping)? problems force not unsupported fail(?:ed)? Cannot\s+install ); sub _cpanpm_output_indicates_failure { my $last_line = _get_cpanpm_last_line(); my $result = $last_line =~ /\b(?:$epic_fail_words)\b/i; return A_MODULE_FAILED_TO_INSTALL if $last_line =~ /\b(?:Cannot\s+install)\b/i; $result || (); } } sub _cpanpm_output_indicates_success { my $last_line = _get_cpanpm_last_line(); my $result = $last_line =~ /\b(?:\s+-- OK|PASS)\b/; $result || (); } sub _cpanpm_output_is_vague { return FALSE if _cpanpm_output_indicates_failure() || _cpanpm_output_indicates_success(); return TRUE; } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # sub _turn_on_warnings { carp "Warnings are implemented yet"; return HEY_IT_WORKED; } sub _turn_off_testing { $logger->debug( 'Trusting test report history' ); $CPAN::Config->{trust_test_report_history} = 1; return HEY_IT_WORKED; } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # sub _print_help { $logger->info( "Use perldoc to read the documentation" ); my $HAVE_PERLDOC = eval { require Pod::Perldoc; 1; }; if ($HAVE_PERLDOC) { system qq{"$^X" -e "require Pod::Perldoc; Pod::Perldoc->run()" $0}; exit; } else { warn "Please install Pod::Perldoc, maybe try 'cpan -i Pod::Perldoc'\n"; return HEY_IT_WORKED; } } sub _print_version # -v { $logger->info( "$0 script version $VERSION, CPAN.pm version " . CPAN->VERSION ); return HEY_IT_WORKED; } sub _print_details # -V { _print_version(); _check_install_dirs(); $logger->info( '-' x 50 . "\nChecking configured mirrors..." ); foreach my $mirror ( @{ $CPAN::Config->{urllist} } ) { _print_ping_report( $mirror ); } $logger->info( '-' x 50 . "\nChecking for faster mirrors..." ); { require CPAN::Mirrors; if ( $CPAN::Config->{connect_to_internet_ok} ) { $CPAN::Frontend->myprint(qq{Trying to fetch a mirror list from the Internet\n}); eval { CPAN::FTP->localize('MIRRORED.BY',File::Spec->catfile($CPAN::Config->{keep_source_where},'MIRRORED.BY'),3,1) } or $CPAN::Frontend->mywarn(<<'HERE'); We failed to get a copy of the mirror list from the Internet. You will need to provide CPAN mirror URLs yourself. HERE $CPAN::Frontend->myprint("\n"); } my $mirrors = CPAN::Mirrors->new( _mirror_file() ); my @continents = $mirrors->find_best_continents; my @mirrors = $mirrors->get_mirrors_by_continents( $continents[0] ); my @timings = $mirrors->get_mirrors_timings( \@mirrors ); foreach my $timing ( @timings ) { $logger->info( sprintf "%s (%0.2f ms)", $timing->hostname, $timing->rtt ); } } return HEY_IT_WORKED; } sub _check_install_dirs { my $makepl_arg = $CPAN::Config->{makepl_arg}; my $mbuildpl_arg = $CPAN::Config->{mbuildpl_arg}; my @custom_dirs; # PERL_MM_OPT push @custom_dirs, $makepl_arg =~ m/INSTALL_BASE\s*=\s*(\S+)/g, $mbuildpl_arg =~ m/--install_base\s*=\s*(\S+)/g; if( @custom_dirs ) { foreach my $dir ( @custom_dirs ) { _print_inc_dir_report( $dir ); } } # XXX: also need to check makepl_args, etc my @checks = ( [ 'core', [ grep $_, @Config{qw(installprivlib installarchlib)} ] ], [ 'vendor', [ grep $_, @Config{qw(installvendorlib installvendorarch)} ] ], [ 'site', [ grep $_, @Config{qw(installsitelib installsitearch)} ] ], [ 'PERL5LIB', _split_paths( $ENV{PERL5LIB} ) ], [ 'PERLLIB', _split_paths( $ENV{PERLLIB} ) ], ); $logger->info( '-' x 50 . "\nChecking install dirs..." ); foreach my $tuple ( @checks ) { my( $label ) = $tuple->[0]; $logger->info( "Checking $label" ); $logger->info( "\tno directories for $label" ) unless @{ $tuple->[1] }; foreach my $dir ( @{ $tuple->[1] } ) { _print_inc_dir_report( $dir ); } } } sub _split_paths { [ map { _expand_filename( $_ ) } split /$Config{path_sep}/, $_[0] || '' ]; } =pod Stolen from File::Path::Expand =cut sub _expand_filename { my( $path ) = @_; no warnings 'uninitialized'; $logger->debug( "Expanding path $path\n" ); $path =~ s{\A~([^/]+)?}{ _home_of( $1 || $> ) || "~$1" }e; return $path; } sub _home_of { require User::pwent; my( $user ) = @_; my $ent = User::pwent::getpw($user) or return; return $ent->dir; } sub _get_default_inc { require Config; [ @Config::Config{ _vars() }, '.' ]; } sub _vars { qw( installarchlib installprivlib installsitearch installsitelib ); } sub _ping_mirrors { my $urls = $CPAN::Config->{urllist}; require URI; foreach my $url ( @$urls ) { my( $obj ) = URI->new( $url ); next unless _is_pingable_scheme( $obj ); my $host = $obj->host; _print_ping_report( $obj ); } } sub _is_pingable_scheme { my( $uri ) = @_; $uri->scheme eq 'file' } sub _mirror_file { my $file = do { my $file = 'MIRRORED.BY'; my $local_path = File::Spec->catfile( $CPAN::Config->{keep_source_where}, $file ); if( -e $local_path ) { $local_path } else { require CPAN::FTP; CPAN::FTP->localize( $file, $local_path, 3, 1 ); $local_path; } }; } sub _find_good_mirrors { require CPAN::Mirrors; my $mirrors = CPAN::Mirrors->new( _mirror_file() ); my @mirrors = $mirrors->best_mirrors( how_many => 5, verbose => 1, ); foreach my $mirror ( @mirrors ) { next unless eval { $mirror->can( 'http' ) }; _print_ping_report( $mirror->http ); } $CPAN::Config->{urllist} = [ map { $_->http } @mirrors ]; } sub _print_inc_dir_report { my( $dir ) = shift; my $writeable = -w $dir ? '+' : '!!! (not writeable)'; $logger->info( "\t$writeable $dir" ); return -w $dir; } sub _print_ping_report { my( $mirror ) = @_; my $rtt = eval { _get_ping_report( $mirror ) }; my $result = $rtt ? sprintf "+ (%4d ms)", $rtt * 1000 : '!'; $logger->info( sprintf "\t%s %s", $result, $mirror ); } sub _get_ping_report { require URI; my( $mirror ) = @_; my( $url ) = ref $mirror ? $mirror : URI->new( $mirror ); #XXX require Net::Ping; my $ping = Net::Ping->new( 'tcp', 1 ); if( $url->scheme eq 'file' ) { return -e $url->file; } my( $port ) = $url->port; return unless $port; if ( $ping->can('port_number') ) { $ping->port_number($port); } else { $ping->{'port_num'} = $port; } $ping->hires(1) if $ping->can( 'hires' ); my( $alive, $rtt ) = eval{ $ping->ping( $url->host ) }; $alive ? $rtt : undef; } sub _load_local_lib # -I { $logger->debug( "Loading local::lib" ); my $rc = _safe_load_module("local::lib"); unless( $rc ) { $logger->logdie( "Could not load local::lib" ); } local::lib->import; return HEY_IT_WORKED; } sub _use_these_mirrors # -M { $logger->debug( "Setting per session mirrors" ); unless( $_[0] ) { $logger->logdie( "The -M switch requires a comma-separated list of mirrors" ); } $CPAN::Config->{urllist} = [ split /,/, $_[0] ]; $logger->debug( "Mirrors are @{$CPAN::Config->{urllist}}" ); } sub _create_autobundle { $logger->info( "Creating autobundle in $CPAN::Config->{cpan_home}/Bundle" ); CPAN::Shell->autobundle; return HEY_IT_WORKED; } sub _recompile { $logger->info( "Recompiling dynamically-loaded extensions" ); CPAN::Shell->recompile; return HEY_IT_WORKED; } sub _upgrade { $logger->info( "Upgrading all modules" ); CPAN::Shell->upgrade(); return HEY_IT_WORKED; } sub _shell { $logger->info( "Dropping into shell" ); CPAN::shell(); return HEY_IT_WORKED; } sub _load_config # -j { my $argument = shift; my $file = file_name_is_absolute( $argument ) ? $argument : rel2abs( $argument ); croak( "cpan config file [$file] for -j does not exist!\n" ) unless -e $file; # should I clear out any existing config here? $CPAN::Config = {}; delete $INC{'CPAN/Config.pm'}; my $rc = eval "require '$file'"; # CPAN::HandleConfig::require_myconfig_or_config looks for this $INC{'CPAN/MyConfig.pm'} = 'fake out!'; # CPAN::HandleConfig::load looks for this $CPAN::Config_loaded = 'fake out'; croak( "Could not load [$file]: $@\n") unless $rc; return HEY_IT_WORKED; } sub _dump_config # -J { my $args = shift; require Data::Dumper; my $fh = $args->[0] || \*STDOUT; local $Data::Dumper::Sortkeys = 1; my $dd = Data::Dumper->new( [$CPAN::Config], ['$CPAN::Config'] ); print $fh $dd->Dump, "\n1;\n__END__\n"; return HEY_IT_WORKED; } sub _lock_lobotomy # -F { no warnings 'redefine'; *CPAN::_flock = sub { 1 }; *CPAN::checklock = sub { 1 }; return HEY_IT_WORKED; } sub _download { my $args = shift; local $CPAN::DEBUG = 1; my %paths; foreach my $arg ( @$args ) { $logger->info( "Checking $arg" ); my $module = _expand_module( $arg ) or next; my $path = $module->cpan_file; $logger->debug( "Inst file would be $path\n" ); $paths{$module} = _get_file( _make_path( $path ) ); $logger->info( "Downloaded [$arg] to [$paths{$arg}]" ); } return \%paths; } sub _make_path { join "/", qw(authors id), $_[0] } sub _get_file { my $path = shift; my $loaded = _safe_load_module("LWP::Simple"); croak "You need LWP::Simple to use features that fetch files from CPAN\n" unless $loaded; my $file = substr $path, rindex( $path, '/' ) + 1; my $store_path = catfile( cwd(), $file ); $logger->debug( "Store path is $store_path" ); foreach my $site ( @{ $CPAN::Config->{urllist} } ) { my $fetch_path = join "/", $site, $path; $logger->debug( "Trying $fetch_path" ); my $status_code = LWP::Simple::getstore( $fetch_path, $store_path ); last if( 200 <= $status_code and $status_code <= 300 ); $logger->warn( "Could not get [$fetch_path]: Status code $status_code" ); } return $store_path; } sub _gitify { my $args = shift; my $loaded = _safe_load_module("Archive::Extract"); croak "You need Archive::Extract to use features that gitify distributions\n" unless $loaded; my $starting_dir = cwd(); foreach my $arg ( @$args ) { $logger->info( "Checking $arg" ); my $store_paths = _download( [ $arg ] ); $logger->debug( "gitify Store path is $store_paths->{$arg}" ); my $dirname = dirname( $store_paths->{$arg} ); my $ae = Archive::Extract->new( archive => $store_paths->{$arg} ); $ae->extract( to => $dirname ); chdir $ae->extract_path; my $git = $ENV{GIT_COMMAND} || '/usr/local/bin/git'; croak "Could not find $git" unless -e $git; croak "$git is not executable" unless -x $git; # can we do this in Pure Perl? system( $git, 'init' ); system( $git, qw( add . ) ); system( $git, qw( commit -a -m ), 'initial import' ); } chdir $starting_dir; return HEY_IT_WORKED; } sub _show_Changes { my $args = shift; foreach my $arg ( @$args ) { $logger->info( "Checking $arg\n" ); my $module = _expand_module( $arg ) or next; my $out = _get_cpanpm_output(); next unless eval { $module->inst_file }; #next if $module->uptodate; ( my $id = $module->id() ) =~ s/::/\-/; my $url = "http://search.cpan.org/~" . lc( $module->userid ) . "/" . $id . "-" . $module->cpan_version() . "/"; #print "URL: $url\n"; _get_changes_file($url); } return HEY_IT_WORKED; } sub _get_changes_file { croak "Reading Changes files requires LWP::Simple and URI\n" unless _safe_load_module("LWP::Simple") && _safe_load_module("URI"); my $url = shift; my $content = LWP::Simple::get( $url ); $logger->info( "Got $url ..." ) if defined $content; #print $content; my( $change_link ) = $content =~ m|<a href="(.*?)">Changes</a>|gi; my $changes_url = URI->new_abs( $change_link, $url ); $logger->debug( "Change link is: $changes_url" ); my $changes = LWP::Simple::get( $changes_url ); print $changes; return HEY_IT_WORKED; } sub _show_Author { my $args = shift; foreach my $arg ( @$args ) { my $module = _expand_module( $arg ) or next; unless( $module ) { $logger->info( "Didn't find a $arg module, so no author!" ); next; } my $author = CPAN::Shell->expand( "Author", $module->userid ); next unless $module->userid; printf "%-25s %-8s %-25s %s\n", $arg, $module->userid, $author->email, $author->name; } return HEY_IT_WORKED; } sub _show_Details { my $args = shift; foreach my $arg ( @$args ) { my $module = _expand_module( $arg ) or next; my $author = CPAN::Shell->expand( "Author", $module->userid ); next unless $module->userid; print "$arg\n", "-" x 73, "\n\t"; print join "\n\t", $module->description ? $module->description : "(no description)", $module->cpan_file ? $module->cpan_file : "(no cpanfile)", $module->inst_file ? $module->inst_file :"(no installation file)" , 'Installed: ' . ($module->inst_version ? $module->inst_version : "not installed"), 'CPAN: ' . $module->cpan_version . ' ' . ($module->uptodate ? "" : "Not ") . "up to date", $author->fullname . " (" . $module->userid . ")", $author->email; print "\n\n"; } return HEY_IT_WORKED; } BEGIN { my $modules; sub _get_all_namespaces { return $modules if $modules; $modules = [ map { $_->id } CPAN::Shell->expand( "Module", "/./" ) ]; } } sub _show_out_of_date { my $modules = _get_all_namespaces(); printf "%-40s %6s %6s\n", "Module Name", "Local", "CPAN"; print "-" x 73, "\n"; foreach my $module ( @$modules ) { next unless $module = _expand_module($module); next unless $module->inst_file; next if $module->uptodate; printf "%-40s %.4f %.4f\n", $module->id, $module->inst_version ? $module->inst_version : '', $module->cpan_version; } return HEY_IT_WORKED; } sub _show_author_mods { my $args = shift; my %hash = map { lc $_, 1 } @$args; my $modules = _get_all_namespaces(); foreach my $module ( @$modules ) { next unless exists $hash{ lc $module->userid }; print $module->id, "\n"; } return HEY_IT_WORKED; } sub _list_all_mods # -l { require File::Find; my $args = shift; my $fh = \*STDOUT; INC: foreach my $inc ( @INC ) { my( $wanted, $reporter ) = _generator(); File::Find::find( { wanted => $wanted }, $inc ); my $count = 0; FILE: foreach my $file ( @{ $reporter->() } ) { my $version = _parse_version_safely( $file ); my $module_name = _path_to_module( $inc, $file ); next FILE unless defined $module_name; print $fh "$module_name\t$version\n"; #last if $count++ > 5; } } return HEY_IT_WORKED; } sub _generator { my @files = (); sub { push @files, File::Spec->canonpath( $File::Find::name ) if m/\A\w+\.pm\z/ }, sub { \@files }, } sub _parse_version_safely # stolen from PAUSE's mldistwatch, but refactored { my( $file ) = @_; local $/ = "\n"; local $_; # don't mess with the $_ in the map calling this return unless open FILE, "<$file"; my $in_pod = 0; my $version; while( <FILE> ) { chomp; $in_pod = /^=(?!cut)/ ? 1 : /^=cut/ ? 0 : $in_pod; next if $in_pod || /^\s*#/; next unless /([\$*])(([\w\:\']*)\bVERSION)\b.*\=/; my( $sigil, $var ) = ( $1, $2 ); $version = _eval_version( $_, $sigil, $var ); last; } close FILE; return 'undef' unless defined $version; return $version; } sub _eval_version { my( $line, $sigil, $var ) = @_; # split package line to hide from PAUSE my $eval = qq{ package ExtUtils::MakeMaker::_version; local $sigil$var; \$$var=undef; do { $line }; \$$var }; my $version = do { local $^W = 0; no strict; eval( $eval ); }; return $version; } sub _path_to_module { my( $inc, $path ) = @_; return if length $path < length $inc; my $module_path = substr( $path, length $inc ); $module_path =~ s/\.pm\z//; # XXX: this is cheating and doesn't handle everything right my @dirs = grep { ! /\W/ } File::Spec->splitdir( $module_path ); shift @dirs; my $module_name = join "::", @dirs; return $module_name; } sub _expand_module { my( $module ) = @_; my $expanded = CPAN::Shell->expandany( $module ); return $expanded if $expanded; $expanded = CPAN::Shell->expand( "Module", $module ); unless( defined $expanded ) { $logger->error( "Could not expand [$module]. Check the module name." ); my $threshold = ( grep { int } sort { length $a <=> length $b } length($module)/4, 4 )[0]; my $guesses = _guess_at_module_name( $module, $threshold ); if( defined $guesses and @$guesses ) { $logger->info( "Perhaps you meant one of these:" ); foreach my $guess ( @$guesses ) { $logger->info( "\t$guess" ); } } return; } return $expanded; } my $guessers = [ [ qw( Text::Levenshtein::XS distance 7 1 ) ], [ qw( Text::Levenshtein::Damerau::XS xs_edistance 7 1 ) ], [ qw( Text::Levenshtein distance 7 1 ) ], [ qw( Text::Levenshtein::Damerau::PP pp_edistance 7 1 ) ], ]; sub _disable_guessers { $_->[-1] = 0 for @$guessers; } # for -x sub _guess_namespace { my $args = shift; foreach my $arg ( @$args ) { $logger->debug( "Checking $arg" ); my $guesses = _guess_at_module_name( $arg ); foreach my $guess ( @$guesses ) { print $guess, "\n"; } } return HEY_IT_WORKED; } sub _list_all_namespaces { my $modules = _get_all_namespaces(); foreach my $module ( @$modules ) { print $module, "\n"; } } BEGIN { my $distance; my $_threshold; my $can_guess; my $shown_help = 0; sub _guess_at_module_name { my( $target, $threshold ) = @_; unless( defined $distance ) { foreach my $try ( @$guessers ) { $can_guess = eval "require $try->[0]; 1" or next; $try->[-1] or next; # disabled no strict 'refs'; $distance = \&{ join "::", @$try[0,1] }; $threshold ||= $try->[2]; } } $_threshold ||= $threshold; unless( $distance ) { unless( $shown_help ) { my $modules = join ", ", map { $_->[0] } @$guessers; substr $modules, rindex( $modules, ',' ), 1, ', and'; # Should this be colorized? if( $can_guess ) { $logger->info( "I can suggest names if you provide the -x option on invocation." ); } else { $logger->info( "I can suggest names if you install one of $modules" ); $logger->info( "and you provide the -x option on invocation." ); } $shown_help++; } return; } my $modules = _get_all_namespaces(); $logger->info( "Checking " . @$modules . " namespaces for close match suggestions" ); my %guesses; foreach my $guess ( @$modules ) { my $distance = $distance->( $target, $guess ); next if $distance > $_threshold; $guesses{$guess} = $distance; } my @guesses = sort { $guesses{$a} <=> $guesses{$b} } keys %guesses; return [ grep { defined } @guesses[0..9] ]; } } 1; =back =head1 EXIT VALUES The script exits with zero if it thinks that everything worked, or a positive number if it thinks that something failed. Note, however, that in some cases it has to divine a failure by the output of things it does not control. For now, the exit codes are vague: 1 An unknown error 2 The was an external problem 4 There was an internal problem with the script 8 A module failed to install =head1 TO DO * There is initial support for Log4perl if it is available, but I haven't gone through everything to make the NullLogger work out correctly if Log4perl is not installed. * When I capture CPAN.pm output, I need to check for errors and report them to the user. * Warnings switch * Check then exit =head1 BUGS * none noted =head1 SEE ALSO L<CPAN>, L<App::cpanminus> =head1 SOURCE AVAILABILITY This code is in Github in the CPAN.pm repository: https://github.com/andk/cpanpm The source used to be tracked separately in another GitHub repo, but the canonical source is now in the above repo. =head1 CREDITS Japheth Cleaver added the bits to allow a forced install (C<-f>). Jim Brandt suggested and provided the initial implementation for the up-to-date and Changes features. Adam Kennedy pointed out that C<exit()> causes problems on Windows where this script ends up with a .bat extension David Golden helps integrate this into the C<CPAN.pm> repos. Jim Keenan fixed up various issues with _download =head1 AUTHOR brian d foy, C<< <bdfoy@cpan.org> >> =head1 COPYRIGHT Copyright (c) 2001-2021, brian d foy, All Rights Reserved. You may redistribute this under the same terms as Perl itself. =cut # Local Variables: # mode: cperl # indent-tabs-mode: t # cperl-indent-level: 8 # cperl-continued-statement-offset: 8 # End: PK�����A0]ڸZ������>��perl5/x86_64-linux-thread-multi/.meta/Expect-1.38/install.jsonnu�6$��������{"dist":"Expect-1.38","name":"Expect","target":"Expect","provides":{"Expect":{"version":1.38,"file":"lib/Expect.pm"}},"pathname":"J/JA/JACOBY/Expect-1.38.tar.gz","version":1.38}PK�����A0]!6��6��=��perl5/x86_64-linux-thread-multi/.meta/Expect-1.38/MYMETA.jsonnu�6$��������{ "abstract" : "automate interactions with command line programs that expose a text terminal interface.", "author" : [ "Austin Schutz <ASchutz@users.sourceforge.net>", "Roland Giersig <RGiersig@cpan.org>", "Dave Jacoby <jacoby@cpan.org>" ], "dynamic_config" : 0, "generated_by" : "ExtUtils::MakeMaker version 7.62, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Expect", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : {} }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "6.64" } }, "runtime" : { "requires" : { "Carp" : "0", "Errno" : "0", "Exporter" : "0", "Fcntl" : "0", "IO::Handle" : "0", "IO::Pty" : "1.11", "IO::Tty" : "1.11", "POSIX" : "0", "perl" : "5.006000" } }, "test" : { "requires" : { "File::Temp" : "0", "Test::More" : "1.00" } } }, "release_status" : "stable", "resources" : { "repository" : { "type" : "git", "url" : "http://github.com/jacoby/expect.pm.git", "web" : "http://github.com/jacoby/expect.pm" } }, "version" : "1.38", "x_serialization_backend" : "JSON::PP version 2.97001" } PK�����A0]Q}����>��perl5/x86_64-linux-thread-multi/.meta/IO-Tty-1.20/install.jsonnu�6$��������{"provides":{"IO::Tty":{"file":"Tty.pm","version":"1.20"},"IO::Tty::Constant":{"version":"1.20","file":"Tty/Constant.pm"},"IO::Pty":{"file":"Pty.pm","version":"1.20"}},"target":"IO::Tty","version":"1.20","pathname":"T/TO/TODDR/IO-Tty-1.20.tar.gz","dist":"IO-Tty-1.20","name":"IO::Tty"}PK�����A0]ǽA��A��=��perl5/x86_64-linux-thread-multi/.meta/IO-Tty-1.20/MYMETA.jsonnu�6$��������{ "abstract" : "Pseudo ttys and constants", "author" : [ "Roland Giersig <RGiersig@cpan.org>" ], "dynamic_config" : 0, "generated_by" : "ExtUtils::MakeMaker version 7.64, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "IO-Tty", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "Test::More" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/toddr/IO-Tty/issues" }, "license" : [ "http://dev.perl.org/licenses/" ], "repository" : { "url" : "https://github.com/toddr/IO-Tty" } }, "version" : "1.20", "x_serialization_backend" : "JSON::PP version 2.97001" } PK�����A0] Y��Y��J��perl5/x86_64-linux-thread-multi/.meta/LWP-Protocol-https-6.14/install.jsonnu�6$��������{"name":"LWP::Protocol::https","pathname":"O/OA/OALDERS/LWP-Protocol-https-6.14.tar.gz","version":"6.14","dist":"LWP-Protocol-https-6.14","provides":{"LWP::Protocol::https":{"version":"6.14","file":"lib/LWP/Protocol/https.pm"},"LWP::Protocol::https::Socket":{"version":"6.14","file":"lib/LWP/Protocol/https.pm"}},"target":"LWP::Protocol::https"}PK�����A0]Q+c��c��I��perl5/x86_64-linux-thread-multi/.meta/LWP-Protocol-https-6.14/MYMETA.jsonnu�6$��������{ "abstract" : "Provide https support for LWP::UserAgent", "author" : [ "Gisle Aas <gisle@activestate.com>" ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 6.031, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "LWP-Protocol-https", "no_index" : { "directory" : [ "t", "xt" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" }, "suggests" : { "JSON::PP" : "2.27300" } }, "develop" : { "recommends" : { "Dist::Zilla::PluginBundle::Git::VersionManager" : "0.007" }, "requires" : { "Capture::Tiny" : "0.48", "File::Spec" : "0", "IO::Handle" : "0", "IPC::Open3" : "0", "Pod::Coverage::TrustPod" : "0", "Test::CPAN::Changes" : "0.19", "Test::CPAN::Meta" : "0", "Test::CheckManifest" : "1.29", "Test::CleanNamespaces" : "0.15", "Test::EOL" : "0", "Test::Kwalitee" : "1.22", "Test::MinimumVersion" : "0", "Test::Mojibake" : "0", "Test::More" : "0.94", "Test::Pod" : "1.41", "Test::Pod::Coverage" : "1.08", "Test::Pod::Spelling::CommonMistakes" : "1.000", "Test::Portability::Files" : "0", "Test::Spelling" : "0.12", "Test::Version" : "1" } }, "runtime" : { "requires" : { "IO::Socket::SSL" : "1.970", "LWP::Protocol::http" : "0", "LWP::UserAgent" : "6.06", "Net::HTTPS" : "6", "base" : "0", "perl" : "5.008001", "strict" : "0" } }, "test" : { "recommends" : { "CPAN::Meta" : "2.120900" }, "requires" : { "ExtUtils::MakeMaker" : "0", "File::Spec" : "0", "File::Temp" : "0", "IO::Select" : "0", "IO::Socket::INET" : "0", "IO::Socket::SSL" : "1.970", "IO::Socket::SSL::Utils" : "0", "LWP::UserAgent" : "6.06", "Socket" : "0", "Test::More" : "0.96", "Test::Needs" : "0.002010", "Test::RequiresInternet" : "0", "warnings" : "0" } } }, "provides" : { "LWP::Protocol::https" : { "file" : "lib/LWP/Protocol/https.pm", "version" : "6.14" }, "LWP::Protocol::https::Socket" : { "file" : "lib/LWP/Protocol/https.pm", "version" : "6.14" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/libwww-perl/LWP-Protocol-https/issues" }, "homepage" : "https://github.com/libwww-perl/LWP-Protocol-https", "repository" : { "type" : "git", "url" : "https://github.com/libwww-perl/LWP-Protocol-https.git", "web" : "https://github.com/libwww-perl/LWP-Protocol-https" }, "x_IRC" : "irc://irc.perl.org/#lwp", "x_MailingList" : "mailto:libwww@perl.org" }, "version" : "6.14", "x_Dist_Zilla" : { "perl" : { "version" : "5.034000" }, "plugins" : [ { "class" : "Dist::Zilla::Plugin::Git::GatherDir", "config" : { "Dist::Zilla::Plugin::GatherDir" : { "exclude_filename" : [ "LICENSE", "META.json", "Makefile.PL", "README.md" ], "exclude_match" : [], "include_dotfiles" : 0, "prefix" : "", "prune_directory" : [], "root" : "." }, "Dist::Zilla::Plugin::Git::GatherDir" : { "include_untracked" : 0 } }, "name" : "Git::GatherDir", "version" : "2.049" }, { "class" : "Dist::Zilla::Plugin::PruneCruft", "name" : "PruneCruft", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::MetaConfig", "name" : "MetaConfig", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::MetaProvides::Package", "config" : { "Dist::Zilla::Plugin::MetaProvides::Package" : { "finder_objects" : [ { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : "MetaProvides::Package/AUTOVIV/:InstallModulesPM", "version" : "6.031" } ], "include_underscores" : 0 }, "Dist::Zilla::Role::MetaProvider::Provider" : { "$Dist::Zilla::Role::MetaProvider::Provider::VERSION" : "2.002004", "inherit_missing" : 1, "inherit_version" : 1, "meta_noindex" : 1 }, "Dist::Zilla::Role::ModuleMetadata" : { "Module::Metadata" : "1.000037", "version" : "0.006" } }, "name" : "MetaProvides::Package", "version" : "2.004003" }, { "class" : "Dist::Zilla::Plugin::MetaNoIndex", "name" : "MetaNoIndex", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::MetaYAML", "name" : "MetaYAML", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::MetaJSON", "name" : "MetaJSON", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::MetaResources", "name" : "MetaResources", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::Git::Check", "config" : { "Dist::Zilla::Plugin::Git::Check" : { "untracked_files" : "die" }, "Dist::Zilla::Role::Git::DirtyFiles" : { "allow_dirty" : [ "LICENSE", "META.json", "Makefile.PL", "README.md" ], "allow_dirty_match" : [], "changelog" : "Changes" }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.34.1", "repo_root" : "." } }, "name" : "Git::Check", "version" : "2.049" }, { "class" : "Dist::Zilla::Plugin::Git::Contributors", "config" : { "Dist::Zilla::Plugin::Git::Contributors" : { "git_version" : "2.34.1", "include_authors" : 0, "include_releaser" : 1, "order_by" : "name", "paths" : [] } }, "name" : "Git::Contributors", "version" : "0.036" }, { "class" : "Dist::Zilla::Plugin::GithubMeta", "name" : "GithubMeta", "version" : "0.58" }, { "class" : "Dist::Zilla::Plugin::Manifest", "name" : "Manifest", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::License", "name" : "License", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::ReadmeAnyFromPod", "config" : { "Dist::Zilla::Role::FileWatcher" : { "version" : "0.006" } }, "name" : "Markdown_Readme", "version" : "0.163250" }, { "class" : "Dist::Zilla::Plugin::Prereqs", "config" : { "Dist::Zilla::Plugin::Prereqs" : { "phase" : "develop", "type" : "recommends" } }, "name" : "@Git::VersionManager/pluginbundle version", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::RewriteVersion::Transitional", "config" : { "Dist::Zilla::Plugin::RewriteVersion" : { "add_tarball_name" : 0, "finders" : [ ":ExecFiles", ":InstallModules" ], "global" : 0, "skip_version_provider" : 0 }, "Dist::Zilla::Plugin::RewriteVersion::Transitional" : {} }, "name" : "@Git::VersionManager/RewriteVersion::Transitional", "version" : "0.009" }, { "class" : "Dist::Zilla::Plugin::MetaProvides::Update", "name" : "@Git::VersionManager/MetaProvides::Update", "version" : "0.007" }, { "class" : "Dist::Zilla::Plugin::CopyFilesFromRelease", "config" : { "Dist::Zilla::Plugin::CopyFilesFromRelease" : { "filename" : [ "Changes" ], "match" : [] } }, "name" : "@Git::VersionManager/CopyFilesFromRelease", "version" : "0.007" }, { "class" : "Dist::Zilla::Plugin::Git::Commit", "config" : { "Dist::Zilla::Plugin::Git::Commit" : { "add_files_in" : [], "commit_msg" : "v%V%n%n%c", "signoff" : 0 }, "Dist::Zilla::Role::Git::DirtyFiles" : { "allow_dirty" : [ "Changes", "LICENSE", "META.json", "Makefile.PL" ], "allow_dirty_match" : [], "changelog" : "Changes" }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.34.1", "repo_root" : "." }, "Dist::Zilla::Role::Git::StringFormatter" : { "time_zone" : "local" } }, "name" : "@Git::VersionManager/release snapshot", "version" : "2.049" }, { "class" : "Dist::Zilla::Plugin::Git::Tag", "config" : { "Dist::Zilla::Plugin::Git::Tag" : { "branch" : null, "changelog" : "Changes", "signed" : 0, "tag" : "v6.14", "tag_format" : "v%V", "tag_message" : "v%V" }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.34.1", "repo_root" : "." }, "Dist::Zilla::Role::Git::StringFormatter" : { "time_zone" : "local" } }, "name" : "@Git::VersionManager/Git::Tag", "version" : "2.049" }, { "class" : "Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional", "config" : { "Dist::Zilla::Plugin::BumpVersionAfterRelease" : { "finders" : [ ":ExecFiles", ":InstallModules" ], "global" : 0, "munge_makefile_pl" : 1 }, "Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional" : {} }, "name" : "@Git::VersionManager/BumpVersionAfterRelease::Transitional", "version" : "0.009" }, { "class" : "Dist::Zilla::Plugin::NextRelease", "name" : "@Git::VersionManager/NextRelease", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::Git::Commit", "config" : { "Dist::Zilla::Plugin::Git::Commit" : { "add_files_in" : [], "commit_msg" : "increment $VERSION after %v release", "signoff" : 0 }, "Dist::Zilla::Role::Git::DirtyFiles" : { "allow_dirty" : [ "Build.PL", "Changes", "Makefile.PL" ], "allow_dirty_match" : [ "(?^:^lib/.*\\.pm$)" ], "changelog" : "Changes" }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.34.1", "repo_root" : "." }, "Dist::Zilla::Role::Git::StringFormatter" : { "time_zone" : "local" } }, "name" : "@Git::VersionManager/post-release commit", "version" : "2.049" }, { "class" : "Dist::Zilla::Plugin::Prereqs::FromCPANfile", "name" : "Prereqs::FromCPANfile", "version" : "0.08" }, { "class" : "Dist::Zilla::Plugin::MakeMaker::Awesome", "config" : { "Dist::Zilla::Plugin::MakeMaker" : { "make_path" : "make", "version" : "6.031" }, "Dist::Zilla::Role::TestRunner" : { "default_jobs" : "8", "version" : "6.031" } }, "name" : "MakeMaker::Awesome", "version" : "0.49" }, { "class" : "Dist::Zilla::Plugin::CheckChangeLog", "name" : "CheckChangeLog", "version" : "0.05" }, { "class" : "Dist::Zilla::Plugin::CheckChangesHasContent", "name" : "CheckChangesHasContent", "version" : "0.011" }, { "class" : "Dist::Zilla::Plugin::Test::Kwalitee", "config" : { "Dist::Zilla::Plugin::Test::Kwalitee" : { "filename" : "xt/author/kwalitee.t", "skiptest" : [ "has_readme" ] } }, "name" : "Test::Kwalitee", "version" : "2.12" }, { "class" : "Dist::Zilla::Plugin::MojibakeTests", "name" : "MojibakeTests", "version" : "0.8" }, { "class" : "Dist::Zilla::Plugin::Test::Version", "name" : "Test::Version", "version" : "1.09" }, { "class" : "Dist::Zilla::Plugin::Test::ReportPrereqs", "name" : "Test::ReportPrereqs", "version" : "0.029" }, { "class" : "Dist::Zilla::Plugin::Test::Compile", "config" : { "Dist::Zilla::Plugin::Test::Compile" : { "bail_out_on_fail" : "1", "fail_on_warning" : "author", "fake_home" : 0, "filename" : "xt/author/00-compile.t", "module_finder" : [ ":InstallModules" ], "needs_display" : 0, "phase" : "develop", "script_finder" : [ ":PerlExecFiles" ], "skips" : [], "switch" : [] } }, "name" : "Test::Compile", "version" : "2.058" }, { "class" : "Dist::Zilla::Plugin::Test::Portability", "config" : { "Dist::Zilla::Plugin::Test::Portability" : { "options" : "" } }, "name" : "Test::Portability", "version" : "2.001001" }, { "class" : "Dist::Zilla::Plugin::Test::CleanNamespaces", "config" : { "Dist::Zilla::Plugin::Test::CleanNamespaces" : { "filename" : "xt/author/clean-namespaces.t", "skips" : [] } }, "name" : "Test::CleanNamespaces", "version" : "0.006" }, { "class" : "Dist::Zilla::Plugin::Test::EOL", "config" : { "Dist::Zilla::Plugin::Test::EOL" : { "filename" : "xt/author/eol.t", "finder" : [ ":ExecFiles", ":InstallModules", ":TestFiles" ], "trailing_whitespace" : 1 } }, "name" : "Test::EOL", "version" : "0.19" }, { "class" : "Dist::Zilla::Plugin::MetaTests", "name" : "MetaTests", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::Test::MinimumVersion", "config" : { "Dist::Zilla::Plugin::Test::MinimumVersion" : { "max_target_perl" : null } }, "name" : "Test::MinimumVersion", "version" : "2.000010" }, { "class" : "Dist::Zilla::Plugin::PodSyntaxTests", "name" : "PodSyntaxTests", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::Test::Pod::Coverage::Configurable", "name" : "Test::Pod::Coverage::Configurable", "version" : "0.07" }, { "class" : "Dist::Zilla::Plugin::Test::PodSpelling", "config" : { "Dist::Zilla::Plugin::Test::PodSpelling" : { "directories" : [ "bin", "lib" ], "spell_cmd" : "aspell list", "stopwords" : [], "wordlist" : "Pod::Wordlist" } }, "name" : "Test::PodSpelling", "version" : "2.007005" }, { "class" : "Dist::Zilla::Plugin::RunExtraTests", "config" : { "Dist::Zilla::Role::TestRunner" : { "default_jobs" : "8" } }, "name" : "RunExtraTests", "version" : "0.029" }, { "class" : "Dist::Zilla::Plugin::CheckStrictVersion", "name" : "CheckStrictVersion", "version" : "0.001" }, { "class" : "Dist::Zilla::Plugin::CopyFilesFromBuild", "name" : "CopyFilesFromBuild", "version" : "0.170880" }, { "class" : "Dist::Zilla::Plugin::TestRelease", "name" : "TestRelease", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::ConfirmRelease", "name" : "ConfirmRelease", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::UploadToCPAN", "name" : "UploadToCPAN", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::Git::Push", "config" : { "Dist::Zilla::Plugin::Git::Push" : { "push_to" : [ "origin" ], "remotes_must_exist" : 1 }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.34.1", "repo_root" : "." } }, "name" : "Git::Push", "version" : "2.049" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":InstallModules", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":IncModules", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":TestFiles", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ExtraTestFiles", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ExecFiles", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":PerlExecFiles", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ShareFiles", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":MainModule", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":AllFiles", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":NoFiles", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : "MetaProvides::Package/AUTOVIV/:InstallModulesPM", "version" : "6.031" } ], "zilla" : { "class" : "Dist::Zilla::Dist::Builder", "config" : { "is_trial" : 0 }, "version" : "6.031" } }, "x_contributors" : [ "Adam Kennedy <adamk@cpan.org>", "Adam Sjogren <asjo@koldfront.dk>", "Alexandr Ciornii <alexchorny@gmail.com>", "Alexey Tourbin <at@altlinux.ru>", "Alex Kapranoff <ka@nadoby.ru>", "amire80 <amir.aharoni@gmail.com>", "Andreas J. Koenig <andreas.koenig@anima.de>", "Axel Burri <axel@tty0.ch>", "Bill Mann <wfmann@alum.mit.edu>", "Bron Gondwana <brong@fastmail.fm>", "Chase Whitener <capoeirab@cpan.org>", "Christopher J. Madsen <cjm@cpan.org>", "cpansprout <cpansprout@gmail.com>", "Dan Book <grinnz@grinnz.com>", "Daniel Hedlund <Daniel.Hedlund@eprize.com>", "David E. Wheeler <david@justatheory.com>", "David Golden <dagolden@cpan.org>", "DAVIDRW <davidrw@cpan.org>", "dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>", "Dmitriy Shamatrin <dshamatrin@cloudbees.com>", "drieux <drieux@yahoo-inc.com>", "Father Chrysostomos <sprout@cpan.org>", "FWILES <FWILES@cpan.org>", "Gavin Peters <gpeters@deepsky.com>", "Gianni Ceccarelli <gianni.ceccarelli@broadbean.com>", "Gisle Aas <gisle@aas.no>", "Graeme Thompson <Graeme.Thompson@mobilecohesion.com>", "Hans-H. Froehlich <hfroehlich@co-de-co.de>", "Ian Kilgore <iank@cpan.org>", "Jacob J <waif@chaos2.org>", "Jakub Wilk <jwilk@jwilk.net>", "jefflee <shaohua@gmail.com>", "JJ Merelo <jjmerelo@gmail.com>", "john9art <john9art@yahoo.com>", "Jon Jensen <jon@endpoint.com>", "Karen Etheridge <ether@cpan.org>", "Leo Lapworth <leo@cuckoo.org>", "Marin Tsanov <btg.tsanov.marin@gmail.com>", "Mark Stosberg <mark@stosberg.com>", "Michael G. Schwern <schwern@pobox.com>", "Mike Schilli <github@perlmeister.com>", "Mohammad S Anwar <mohammad.anwar@yahoo.com>", "murphy <murphy@genome.chop.edu>", "Nicolas R <nicolas@atoomic.org>", "Olaf Alders <olaf@wundersolutions.com>", "Ondrej Hanak <ondrej.hanak@ubs.com>", "openstrike <git@openstrike.co.uk>", "Peter Rabbitson <ribasushi@cpan.org>", "phrstbrn <phrstbrn@gmail.com>", "Randy Stauner <randy@magnificent-tears.com>", "Robert Stone <talby@trap.mtview.ca.us>", "Rolf Grossmann <rg@progtech.net>", "ruff <ruff@ukrpost.net>", "sasao <sasao@yugen.org>", "Sean M. Burke <sburke@cpan.org>", "Shoichi Kaji <skaji@cpan.org>", "Slaven Rezic <slaven@rezic.de>", "Spiros Denaxas <s.denaxas@gmail.com>", "Steffen Ullrich <Steffen_Ullrich@genua.de>", "Steve Hay <SteveHay@planit.com>", "Tim Couzins <tim.couzins@sophos.com>", "Todd Lipcon <todd@amiestreet.com>", "Tom Hukins <tom@eborcom.com>", "Tony Finch <dot@dotat.at>", "Toru Yamaguchi <zigorou@cpan.org>", "uid39246 <uid39246>", "Ville Skyttä <ville.skytta@iki.fi>", "Yuri Karaban <tech@askold.net>", "Yury Zavarin <yury.zavarin@gmail.com>", "Zefram <zefram@fysh.org>" ], "x_generated_by_perl" : "v5.34.0", "x_serialization_backend" : "JSON::PP version 2.97001", "x_spdx_expression" : "Artistic-1.0-Perl OR GPL-1.0-or-later" } PK�����A0]Y������>��perl5/x86_64-linux-thread-multi/.meta/Switch-2.17/install.jsonnu�6$��������{"pathname":"C/CH/CHORNY/Switch-2.17.tar.gz","name":"Switch","dist":"Switch-2.17","target":"Switch","provides":{"Switch":{"version":2.17,"file":"Switch.pm"}},"version":2.17}PK�����A0]V ����=��perl5/x86_64-linux-thread-multi/.meta/Switch-2.17/MYMETA.jsonnu�6$��������{ "abstract" : "A switch statement for Perl, do not use if you can use given/when", "author" : [ "Damian Conway", "Rafael Garcia-Suarez", "Alexandr Ciornii" ], "dynamic_config" : 0, "generated_by" : "ExtUtils::MakeMaker version 6.78, CPAN::Meta::Converter version 2.131490, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Switch", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : { "Filter::Util::Call" : "0", "Text::Balanced" : "2", "if" : "0", "perl" : "5.005" } } }, "release_status" : "stable", "resources" : { "repository" : { "url" : "http://github.com/chorny/Switch" } }, "version" : "2.17", "x_serialization_backend" : "JSON::PP version 2.97001" } PK�����A0]J͝������B��perl5/x86_64-linux-thread-multi/.meta/HTTP-Tiny-0.088/install.jsonnu�6$��������{"dist":"HTTP-Tiny-0.088","name":"HTTP::Tiny","target":"HTTP::Tiny","provides":{"HTTP::Tiny":{"version":"0.088","file":"lib/HTTP/Tiny.pm"}},"pathname":"D/DA/DAGOLDEN/HTTP-Tiny-0.088.tar.gz","version":"0.088"}PK�����A0]�1����A��perl5/x86_64-linux-thread-multi/.meta/HTTP-Tiny-0.088/MYMETA.jsonnu�6$��������{ "abstract" : "A small, simple, correct HTTP/1.1 client", "author" : [ "Christian Hansen <chansen@cpan.org>", "David Golden <dagolden@cpan.org>" ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 6.030, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "HTTP-Tiny", "no_index" : { "directory" : [ "corpus", "examples", "t", "xt" ], "package" : [ "DB" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "6.17" }, "suggests" : { "JSON::PP" : "2.27300" } }, "develop" : { "requires" : { "Dist::Zilla" : "5", "Dist::Zilla::Plugin::Prereqs" : "0", "Dist::Zilla::Plugin::ReleaseStatus::FromVersion" : "0", "Dist::Zilla::Plugin::RemovePrereqs" : "0", "Dist::Zilla::PluginBundle::DAGOLDEN" : "0.072", "File::Spec" : "0", "File::Temp" : "0", "IO::Handle" : "0", "IPC::Open3" : "0", "Perl::Critic::Policy::Lax::ProhibitStringyEval::ExceptForRequire" : "0", "Pod::Coverage::TrustPod" : "0", "Pod::Wordlist" : "0", "Software::License::Perl_5" : "0", "Test::CPAN::Meta" : "0", "Test::MinimumVersion" : "0", "Test::More" : "0", "Test::Perl::Critic" : "0", "Test::Pod" : "1.41", "Test::Pod::Coverage" : "1.08", "Test::Portability::Files" : "0", "Test::Spelling" : "0.12", "Test::Version" : "1", "perl" : "5.006" } }, "runtime" : { "recommends" : { "HTTP::CookieJar" : "0.001", "IO::Socket::IP" : "0.32", "IO::Socket::SSL" : "1.42", "Mozilla::CA" : "20160104", "Net::SSLeay" : "1.49" }, "requires" : { "Carp" : "0", "Fcntl" : "0", "IO::Socket" : "0", "MIME::Base64" : "0", "Socket" : "0", "Time::Local" : "0", "bytes" : "0", "perl" : "5.006", "strict" : "0", "warnings" : "0" }, "suggests" : { "IO::Socket::SSL" : "1.56" } }, "test" : { "recommends" : { "CPAN::Meta" : "2.120900" }, "requires" : { "Data::Dumper" : "0", "Exporter" : "0", "ExtUtils::MakeMaker" : "0", "File::Basename" : "0", "File::Spec" : "0", "File::Temp" : "0", "IO::Dir" : "0", "IO::File" : "0", "IO::Socket::INET" : "0", "IPC::Cmd" : "0", "Test::More" : "0.96", "lib" : "0", "open" : "0" } } }, "provides" : { "HTTP::Tiny" : { "file" : "lib/HTTP/Tiny.pm", "version" : "0.088" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/Perl-Toolchain-Gang/HTTP-Tiny/issues" }, "homepage" : "https://github.com/Perl-Toolchain-Gang/HTTP-Tiny", "repository" : { "type" : "git", "url" : "https://github.com/Perl-Toolchain-Gang/HTTP-Tiny.git", "web" : "https://github.com/Perl-Toolchain-Gang/HTTP-Tiny" } }, "version" : "0.088", "x_authority" : "cpan:DAGOLDEN", "x_contributors" : [ "Alan Gardner <gardner@pythian.com>", "Alessandro Ghedini <al3xbio@gmail.com>", "A. Sinan Unur <nanis@cpan.org>", "Brad Gilbert <bgills@cpan.org>", "brian m. carlson <sandals@crustytoothpaste.net>", "Chris Nehren <apeiron@cpan.org>", "Chris Weyl <cweyl@alumni.drew.edu>", "Claes Jakobsson <claes@surfar.nu>", "Clinton Gormley <clint@traveljury.com>", "Craig A. Berry <craigberry@mac.com>", "Craig Berry <cberry@cpan.org>", "David Golden <xdg@xdg.me>", "David Mitchell <davem@iabyn.com>", "Dean Pearce <pearce@pythian.com>", "Edward Zborowski <ed@rubensteintech.com>", "Felipe Gasper <felipe@felipegasper.com>", "Graham Knop <haarg@haarg.org>", "Greg Kennedy <kennedy.greg@gmail.com>", "James E Keenan <jkeenan@cpan.org>", "James Raspass <jraspass@gmail.com>", "Jeremy Mates <jmates@cpan.org>", "Jess Robinson <castaway@desert-island.me.uk>", "Karen Etheridge <ether@cpan.org>", "Lukas Eklund <leklund@gmail.com>", "Martin J. Evans <mjegh@ntlworld.com>", "Martin-Louis Bright <mlbright@gmail.com>", "Matthew Horsfall <wolfsage@gmail.com>", "Michael R. Davis <mrdvt92@users.noreply.github.com>", "Mike Doherty <doherty@cpan.org>", "Nicolas Rochelemagne <rochelemagne@cpanel.net>", "Olaf Alders <olaf@wundersolutions.com>", "Olivier Mengué <dolmen@cpan.org>", "Petr Písař <ppisar@redhat.com>", "sanjay-cpu <snjkmr32@gmail.com>", "Serguei Trouchelle <stro@cpan.org>", "Shoichi Kaji <skaji@cpan.org>", "SkyMarshal <skymarshal1729@gmail.com>", "Sören Kornetzki <soeren.kornetzki@delti.com>", "Steve Grazzini <steve.grazzini@grantstreet.com>", "Stig Palmquist <git@stig.io>", "Syohei YOSHIDA <syohex@gmail.com>", "Tatsuhiko Miyagawa <miyagawa@bulknews.net>", "Tom Hukins <tom@eborcom.com>", "Tony Cook <tony@develop-help.com>", "Xavier Guimard <yadd@debian.org>" ], "x_generated_by_perl" : "v5.36.0", "x_serialization_backend" : "JSON::PP version 2.97001", "x_spdx_expression" : "Artistic-1.0-Perl OR GPL-1.0-or-later" } PK�����A0]����H��perl5/x86_64-linux-thread-multi/.meta/Types-Serialiser-1.01/install.jsonnu�6$��������{"dist":"Types-Serialiser-1.01","name":"Types::Serialiser","provides":{"Types::Serialiser":{"version":1.01,"file":"Serialiser.pm"},"Types::Serialiser::BooleanBase":{"file":"Serialiser.pm","version":1.01},"JSON::PP::Boolean":{"file":"Serialiser.pm","version":1.01},"Types::Serialiser::Error":{"file":"Serialiser.pm","version":1.01}},"target":"Types::Serialiser","version":1.01,"pathname":"M/ML/MLEHMANN/Types-Serialiser-1.01.tar.gz"}PK�����A0]V����G��perl5/x86_64-linux-thread-multi/.meta/Types-Serialiser-1.01/MYMETA.jsonnu�6$��������{ "abstract" : "unknown", "author" : [ "unknown" ], "dynamic_config" : 0, "generated_by" : "ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150001, CPAN::Meta::Converter version 2.150010", "license" : [ "unknown" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Types-Serialiser", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : { "common::sense" : "0" } } }, "release_status" : "stable", "version" : "1.01", "x_serialization_backend" : "JSON::PP version 2.97001" } PK�����A0]Ym��m��A��perl5/x86_64-linux-thread-multi/.meta/YAML-Syck-1.34/install.jsonnu�6$��������{"dist":"YAML-Syck-1.34","name":"YAML::Syck","target":"YAML::Syck","provides":{"JSON::Syck":{"version":1.34,"file":"lib/JSON/Syck.pm"},"YAML::Dumper::Syck":{"file":"lib/YAML/Dumper/Syck.pm"},"YAML::Loader::Syck":{"file":"lib/YAML/Loader/Syck.pm"},"YAML::Syck":{"version":1.34,"file":"lib/YAML/Syck.pm"}},"pathname":"T/TO/TODDR/YAML-Syck-1.34.tar.gz","version":1.34}PK�����A0]Q^����@��perl5/x86_64-linux-thread-multi/.meta/YAML-Syck-1.34/MYMETA.jsonnu�6$��������{ "abstract" : "Fast, lightweight YAML loader and dumper", "author" : [ "Todd Rinaldo <toddr@cpan.org>" ], "dynamic_config" : 0, "generated_by" : "ExtUtils::MakeMaker version 7.44, CPAN::Meta::Converter version 2.150010", "license" : [ "mit" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "YAML-Syck", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "Test::More" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : { "perl" : "5.006" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/toddr/YAML-Syck/issues" }, "homepage" : "http://github.com/toddr/YAML-Syck", "license" : [ "http://dev.perl.org/licenses/" ], "repository" : { "url" : "http://github.com/toddr/YAML-Syck" } }, "version" : "1.34", "x_serialization_backend" : "JSON::PP version 2.97001" } PK�����A0]�������@��perl5/x86_64-linux-thread-multi/.meta/Try-Tiny-0.31/install.jsonnu�6$��������{"dist":"Try-Tiny-0.31","name":"Try::Tiny","target":"Try::Tiny","provides":{"Try::Tiny":{"file":"lib/Try/Tiny.pm","version":"0.31"}},"pathname":"E/ET/ETHER/Try-Tiny-0.31.tar.gz","version":"0.31"}PK�����A0]}t����?��perl5/x86_64-linux-thread-multi/.meta/Try-Tiny-0.31/MYMETA.jsonnu�6$��������{ "abstract" : "Minimal try/catch with proper preservation of $@", "author" : [ "יובל קוג'מן (Yuval Kogman) <nothingmuch@woobling.org>", "Jesse Luehrs <doy@tozt.net>" ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 6.024, CPAN::Meta::Converter version 2.150010", "license" : [ "mit" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Try-Tiny", "no_index" : { "directory" : [ "t", "xt" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" }, "suggests" : { "JSON::PP" : "2.27300" } }, "develop" : { "recommends" : { "Dist::Zilla::PluginBundle::Author::ETHER" : "0.161", "Dist::Zilla::PluginBundle::Git::VersionManager" : "0.007" }, "requires" : { "Capture::Tiny" : "0.12", "Encode" : "0", "File::Spec" : "0", "IO::Handle" : "0", "IPC::Open3" : "0", "Pod::Coverage::TrustPod" : "0", "Pod::Wordlist" : "0", "Sub::Name" : "0.08", "Sub::Util" : "0", "Test::CPAN::Changes" : "0.19", "Test::CPAN::Meta" : "0", "Test::EOL" : "0", "Test::Kwalitee" : "1.21", "Test::MinimumVersion" : "0", "Test::Mojibake" : "0", "Test::More" : "0.96", "Test::NoTabs" : "0", "Test::Pod" : "1.41", "Test::Pod::Coverage" : "1.08", "Test::Pod::No404s" : "0", "Test::Portability::Files" : "0", "Test::Spelling" : "0.12" } }, "runtime" : { "requires" : { "Carp" : "0", "Exporter" : "5.57", "constant" : "0", "perl" : "5.006", "strict" : "0", "warnings" : "0" }, "suggests" : { "Sub::Name" : "0.08", "Sub::Util" : "0" } }, "test" : { "recommends" : { "CPAN::Meta" : "2.120900" }, "requires" : { "ExtUtils::MakeMaker" : "0", "File::Spec" : "0", "Test::More" : "0", "if" : "0" }, "suggests" : { "CPAN::Meta::Check" : "0.011", "CPAN::Meta::Requirements" : "0", "Capture::Tiny" : "0.12" } }, "x_Dist_Zilla" : { "requires" : { "Dist::Zilla" : "5", "Dist::Zilla::Plugin::Authority" : "1.009", "Dist::Zilla::Plugin::AutoMetaResources" : "0", "Dist::Zilla::Plugin::AutoPrereqs" : "5.038", "Dist::Zilla::Plugin::Breaks" : "0", "Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional" : "0.004", "Dist::Zilla::Plugin::CheckIssues" : "0", "Dist::Zilla::Plugin::CheckMetaResources" : "0", "Dist::Zilla::Plugin::CheckPrereqsIndexed" : "0.019", "Dist::Zilla::Plugin::CheckSelfDependency" : "0", "Dist::Zilla::Plugin::CheckStrictVersion" : "0", "Dist::Zilla::Plugin::ConfirmRelease" : "0", "Dist::Zilla::Plugin::CopyFilesFromRelease" : "0", "Dist::Zilla::Plugin::EnsureLatestPerl" : "0", "Dist::Zilla::Plugin::FileFinder::ByName" : "0", "Dist::Zilla::Plugin::FileFinder::Filter" : "0", "Dist::Zilla::Plugin::GenerateFile::FromShareDir" : "0", "Dist::Zilla::Plugin::Git::Check" : "0", "Dist::Zilla::Plugin::Git::CheckFor::CorrectBranch" : "0.004", "Dist::Zilla::Plugin::Git::CheckFor::MergeConflicts" : "0", "Dist::Zilla::Plugin::Git::Commit" : "2.020", "Dist::Zilla::Plugin::Git::Contributors" : "0.029", "Dist::Zilla::Plugin::Git::Describe" : "0.004", "Dist::Zilla::Plugin::Git::GatherDir" : "2.016", "Dist::Zilla::Plugin::Git::Push" : "0", "Dist::Zilla::Plugin::Git::Remote::Check" : "0", "Dist::Zilla::Plugin::Git::Tag" : "0", "Dist::Zilla::Plugin::GitHub::Update" : "0.40", "Dist::Zilla::Plugin::GithubMeta" : "0.54", "Dist::Zilla::Plugin::InstallGuide" : "1.200005", "Dist::Zilla::Plugin::Keywords" : "0.004", "Dist::Zilla::Plugin::License" : "5.038", "Dist::Zilla::Plugin::MakeMaker" : "0", "Dist::Zilla::Plugin::Manifest" : "0", "Dist::Zilla::Plugin::MetaConfig" : "0", "Dist::Zilla::Plugin::MetaJSON" : "0", "Dist::Zilla::Plugin::MetaNoIndex" : "0", "Dist::Zilla::Plugin::MetaProvides::Package" : "1.15000002", "Dist::Zilla::Plugin::MetaTests" : "0", "Dist::Zilla::Plugin::MetaYAML" : "0", "Dist::Zilla::Plugin::MinimumPerl" : "1.006", "Dist::Zilla::Plugin::MojibakeTests" : "0.8", "Dist::Zilla::Plugin::NextRelease" : "5.033", "Dist::Zilla::Plugin::OnlyCorePrereqs" : "0", "Dist::Zilla::Plugin::PodCoverageTests" : "5.040", "Dist::Zilla::Plugin::PodSyntaxTests" : "5.040", "Dist::Zilla::Plugin::PodWeaver" : "4.008", "Dist::Zilla::Plugin::Prereqs" : "0", "Dist::Zilla::Plugin::Prereqs::AuthorDeps" : "0.006", "Dist::Zilla::Plugin::Prereqs::Soften" : "0", "Dist::Zilla::Plugin::PromptIfStale" : "0", "Dist::Zilla::Plugin::Readme" : "0", "Dist::Zilla::Plugin::ReadmeAnyFromPod" : "0.142180", "Dist::Zilla::Plugin::RewriteVersion::Transitional" : "0.006", "Dist::Zilla::Plugin::Run::AfterBuild" : "0.041", "Dist::Zilla::Plugin::Run::AfterRelease" : "0.038", "Dist::Zilla::Plugin::RunExtraTests" : "0.024", "Dist::Zilla::Plugin::StaticInstall" : "0.005", "Dist::Zilla::Plugin::Substitute" : "0", "Dist::Zilla::Plugin::Test::CPAN::Changes" : "0.012", "Dist::Zilla::Plugin::Test::ChangesHasContent" : "0", "Dist::Zilla::Plugin::Test::CheckBreaks" : "0.018", "Dist::Zilla::Plugin::Test::Compile" : "2.039", "Dist::Zilla::Plugin::Test::EOL" : "0.17", "Dist::Zilla::Plugin::Test::Kwalitee" : "2.10", "Dist::Zilla::Plugin::Test::MinimumVersion" : "2.000010", "Dist::Zilla::Plugin::Test::NoTabs" : "0.08", "Dist::Zilla::Plugin::Test::Pod::No404s" : "1.003", "Dist::Zilla::Plugin::Test::PodSpelling" : "2.006003", "Dist::Zilla::Plugin::Test::Portability" : "2.000007", "Dist::Zilla::Plugin::Test::ReportPrereqs" : "0.022", "Dist::Zilla::Plugin::TestRelease" : "0", "Dist::Zilla::Plugin::UploadToCPAN" : "0", "Dist::Zilla::Plugin::UseUnsafeInc" : "0", "Dist::Zilla::PluginBundle::Author::ETHER" : "0.136", "Dist::Zilla::PluginBundle::Git::VersionManager" : "0.007", "Software::License::MIT" : "0" } } }, "provides" : { "Try::Tiny" : { "file" : "lib/Try/Tiny.pm", "version" : "0.31" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "mailto" : "bug-Try-Tiny@rt.cpan.org", "web" : "https://rt.cpan.org/Public/Dist/Display.html?Name=Try-Tiny" }, "homepage" : "https://github.com/p5sagit/Try-Tiny", "repository" : { "type" : "git", "url" : "https://github.com/p5sagit/Try-Tiny.git", "web" : "https://github.com/p5sagit/Try-Tiny" } }, "version" : "0.31", "x_Dist_Zilla" : { "perl" : { "version" : "5.035006" }, "plugins" : [ { "class" : "Dist::Zilla::Plugin::FileFinder::Filter", "name" : "all_files_but_using_5.10_features", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::Prereqs", "config" : { "Dist::Zilla::Plugin::Prereqs" : { "phase" : "develop", "type" : "recommends" } }, "name" : "@Author::ETHER/pluginbundle version", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::PromptIfStale", "config" : { "Dist::Zilla::Plugin::PromptIfStale" : { "check_all_plugins" : 0, "check_all_prereqs" : 0, "modules" : [ "Dist::Zilla::PluginBundle::Author::ETHER" ], "phase" : "build", "run_under_travis" : 0, "skip" : [] } }, "name" : "@Author::ETHER/stale modules, build", "version" : "0.057" }, { "class" : "Dist::Zilla::Plugin::FileFinder::ByName", "name" : "@Author::ETHER/Examples", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::Git::GatherDir", "config" : { "Dist::Zilla::Plugin::GatherDir" : { "exclude_filename" : [ "CONTRIBUTING", "INSTALL", "LICENCE", "README.pod" ], "exclude_match" : [], "follow_symlinks" : 0, "include_dotfiles" : 0, "prefix" : "", "prune_directory" : [], "root" : "." }, "Dist::Zilla::Plugin::Git::GatherDir" : { "include_untracked" : 0 } }, "name" : "@Author::ETHER/Git::GatherDir", "version" : "2.048" }, { "class" : "Dist::Zilla::Plugin::MetaYAML", "name" : "@Author::ETHER/MetaYAML", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::MetaJSON", "name" : "@Author::ETHER/MetaJSON", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::Readme", "name" : "@Author::ETHER/Readme", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::Manifest", "name" : "@Author::ETHER/Manifest", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::License", "name" : "@Author::ETHER/License", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::GenerateFile::FromShareDir", "config" : { "Dist::Zilla::Plugin::GenerateFile::FromShareDir" : { "destination_filename" : "CONTRIBUTING", "dist" : "Dist-Zilla-PluginBundle-Author-ETHER", "encoding" : "UTF-8", "has_xs" : 0, "location" : "build", "source_filename" : "CONTRIBUTING" }, "Dist::Zilla::Role::RepoFileInjector" : { "allow_overwrite" : 1, "repo_root" : ".", "version" : "0.009" } }, "name" : "@Author::ETHER/generate CONTRIBUTING", "version" : "0.015" }, { "class" : "Dist::Zilla::Plugin::InstallGuide", "config" : { "Dist::Zilla::Role::ModuleMetadata" : { "Module::Metadata" : "1.000037", "version" : "0.006" } }, "name" : "@Author::ETHER/InstallGuide", "version" : "1.200014" }, { "class" : "Dist::Zilla::Plugin::Test::Compile", "config" : { "Dist::Zilla::Plugin::Test::Compile" : { "bail_out_on_fail" : 1, "fail_on_warning" : "author", "fake_home" : 0, "filename" : "xt/author/00-compile.t", "module_finder" : [ ":InstallModules" ], "needs_display" : 0, "phase" : "develop", "script_finder" : [ ":PerlExecFiles", "@Author::ETHER/Examples" ], "skips" : [], "switch" : [] } }, "name" : "@Author::ETHER/Test::Compile", "version" : "2.058" }, { "class" : "Dist::Zilla::Plugin::Test::NoTabs", "config" : { "Dist::Zilla::Plugin::Test::NoTabs" : { "filename" : "xt/author/no-tabs.t", "finder" : [ ":InstallModules", ":ExecFiles", "@Author::ETHER/Examples", ":TestFiles", ":ExtraTestFiles" ] } }, "name" : "@Author::ETHER/Test::NoTabs", "version" : "0.15" }, { "class" : "Dist::Zilla::Plugin::Test::EOL", "config" : { "Dist::Zilla::Plugin::Test::EOL" : { "filename" : "xt/author/eol.t", "finder" : [ ":ExecFiles", ":ExtraTestFiles", ":InstallModules", ":TestFiles", "@Author::ETHER/Examples" ], "trailing_whitespace" : 1 } }, "name" : "@Author::ETHER/Test::EOL", "version" : "0.19" }, { "class" : "Dist::Zilla::Plugin::MetaTests", "name" : "@Author::ETHER/MetaTests", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::Test::CPAN::Changes", "config" : { "Dist::Zilla::Plugin::Test::CPAN::Changes" : { "changelog" : "Changes" } }, "name" : "@Author::ETHER/Test::CPAN::Changes", "version" : "0.012" }, { "class" : "Dist::Zilla::Plugin::Test::ChangesHasContent", "name" : "@Author::ETHER/Test::ChangesHasContent", "version" : "0.011" }, { "class" : "Dist::Zilla::Plugin::Test::MinimumVersion", "config" : { "Dist::Zilla::Plugin::Test::MinimumVersion" : { "max_target_perl" : "5.006" } }, "name" : "@Author::ETHER/Test::MinimumVersion", "version" : "2.000010" }, { "class" : "Dist::Zilla::Plugin::PodSyntaxTests", "name" : "@Author::ETHER/PodSyntaxTests", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::PodCoverageTests", "name" : "@Author::ETHER/PodCoverageTests", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::Test::PodSpelling", "config" : { "Dist::Zilla::Plugin::Test::PodSpelling" : { "directories" : [ "examples", "lib", "script", "t", "xt" ], "spell_cmd" : "", "stopwords" : [ "irc" ], "wordlist" : "Pod::Wordlist" } }, "name" : "@Author::ETHER/Test::PodSpelling", "version" : "2.007005" }, { "class" : "Dist::Zilla::Plugin::Test::Pod::No404s", "name" : "@Author::ETHER/Test::Pod::No404s", "version" : "1.004" }, { "class" : "Dist::Zilla::Plugin::Test::Kwalitee", "config" : { "Dist::Zilla::Plugin::Test::Kwalitee" : { "filename" : "xt/author/kwalitee.t", "skiptest" : [] } }, "name" : "@Author::ETHER/Test::Kwalitee", "version" : "2.12" }, { "class" : "Dist::Zilla::Plugin::MojibakeTests", "name" : "@Author::ETHER/MojibakeTests", "version" : "0.8" }, { "class" : "Dist::Zilla::Plugin::Test::ReportPrereqs", "name" : "@Author::ETHER/Test::ReportPrereqs", "version" : "0.028" }, { "class" : "Dist::Zilla::Plugin::Test::Portability", "config" : { "Dist::Zilla::Plugin::Test::Portability" : { "options" : "" } }, "name" : "@Author::ETHER/Test::Portability", "version" : "2.001000" }, { "class" : "Dist::Zilla::Plugin::Git::Describe", "name" : "@Author::ETHER/Git::Describe", "version" : "0.007" }, { "class" : "Dist::Zilla::Plugin::PodWeaver", "config" : { "Dist::Zilla::Plugin::PodWeaver" : { "config_plugins" : [ "@Author::ETHER" ], "finder" : [ ":InstallModules", ":ExecFiles" ], "plugins" : [ { "class" : "Pod::Weaver::Plugin::EnsurePod5", "name" : "@Author::ETHER/EnsurePod5", "version" : "4.018" }, { "class" : "Pod::Weaver::Plugin::H1Nester", "name" : "@Author::ETHER/H1Nester", "version" : "4.018" }, { "class" : "Pod::Weaver::Plugin::SingleEncoding", "name" : "@Author::ETHER/SingleEncoding", "version" : "4.018" }, { "class" : "Pod::Weaver::Plugin::Transformer", "name" : "@Author::ETHER/List", "version" : "4.018" }, { "class" : "Pod::Weaver::Plugin::Transformer", "name" : "@Author::ETHER/Verbatim", "version" : "4.018" }, { "class" : "Pod::Weaver::Section::Region", "name" : "@Author::ETHER/header", "version" : "4.018" }, { "class" : "Pod::Weaver::Section::Name", "name" : "@Author::ETHER/Name", "version" : "4.018" }, { "class" : "Pod::Weaver::Section::Version", "name" : "@Author::ETHER/Version", "version" : "4.018" }, { "class" : "Pod::Weaver::Section::Region", "name" : "@Author::ETHER/prelude", "version" : "4.018" }, { "class" : "Pod::Weaver::Section::Generic", "name" : "SYNOPSIS", "version" : "4.018" }, { "class" : "Pod::Weaver::Section::Generic", "name" : "DESCRIPTION", "version" : "4.018" }, { "class" : "Pod::Weaver::Section::Generic", "name" : "OVERVIEW", "version" : "4.018" }, { "class" : "Pod::Weaver::Section::Collect", "name" : "ATTRIBUTES", "version" : "4.018" }, { "class" : "Pod::Weaver::Section::Collect", "name" : "METHODS", "version" : "4.018" }, { "class" : "Pod::Weaver::Section::Collect", "name" : "FUNCTIONS", "version" : "4.018" }, { "class" : "Pod::Weaver::Section::Collect", "name" : "TYPES", "version" : "4.018" }, { "class" : "Pod::Weaver::Section::Leftovers", "name" : "@Author::ETHER/Leftovers", "version" : "4.018" }, { "class" : "Pod::Weaver::Section::Region", "name" : "@Author::ETHER/postlude", "version" : "4.018" }, { "class" : "Pod::Weaver::Section::GenerateSection", "name" : "@Author::ETHER/generate SUPPORT", "version" : "4.018" }, { "class" : "Pod::Weaver::Section::AllowOverride", "name" : "@Author::ETHER/allow override SUPPORT", "version" : "0.05" }, { "class" : "Pod::Weaver::Section::Authors", "name" : "@Author::ETHER/Authors", "version" : "4.018" }, { "class" : "Pod::Weaver::Section::AllowOverride", "name" : "@Author::ETHER/allow override AUTHOR", "version" : "0.05" }, { "class" : "Pod::Weaver::Section::Contributors", "name" : "@Author::ETHER/Contributors", "version" : "0.009" }, { "class" : "Pod::Weaver::Section::Legal", "name" : "@Author::ETHER/Legal", "version" : "4.018" }, { "class" : "Pod::Weaver::Section::Region", "name" : "@Author::ETHER/footer", "version" : "4.018" } ] } }, "name" : "@Author::ETHER/PodWeaver", "version" : "4.009" }, { "class" : "Dist::Zilla::Plugin::GithubMeta", "name" : "@Author::ETHER/GithubMeta", "version" : "0.58" }, { "class" : "Dist::Zilla::Plugin::AutoMetaResources", "name" : "@Author::ETHER/AutoMetaResources", "version" : "1.21" }, { "class" : "Dist::Zilla::Plugin::Authority", "name" : "@Author::ETHER/Authority", "version" : "1.009" }, { "class" : "Dist::Zilla::Plugin::MetaNoIndex", "name" : "@Author::ETHER/MetaNoIndex", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::MetaProvides::Package", "config" : { "Dist::Zilla::Plugin::MetaProvides::Package" : { "finder" : [ ":InstallModules" ], "finder_objects" : [ { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":InstallModules", "version" : "6.024" } ], "include_underscores" : 0 }, "Dist::Zilla::Role::MetaProvider::Provider" : { "$Dist::Zilla::Role::MetaProvider::Provider::VERSION" : "2.002004", "inherit_missing" : 0, "inherit_version" : 0, "meta_noindex" : 1 }, "Dist::Zilla::Role::ModuleMetadata" : { "Module::Metadata" : "1.000037", "version" : "0.006" } }, "name" : "@Author::ETHER/MetaProvides::Package", "version" : "2.004003" }, { "class" : "Dist::Zilla::Plugin::MetaConfig", "name" : "@Author::ETHER/MetaConfig", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::Keywords", "config" : { "Dist::Zilla::Plugin::Keywords" : { "keywords" : [] } }, "name" : "@Author::ETHER/Keywords", "version" : "0.007" }, { "class" : "Dist::Zilla::Plugin::UseUnsafeInc", "config" : { "Dist::Zilla::Plugin::UseUnsafeInc" : { "dot_in_INC" : 0 } }, "name" : "@Author::ETHER/UseUnsafeInc", "version" : "0.001" }, { "class" : "Dist::Zilla::Plugin::AutoPrereqs", "name" : "@Author::ETHER/AutoPrereqs", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::Prereqs::AuthorDeps", "name" : "@Author::ETHER/Prereqs::AuthorDeps", "version" : "0.007" }, { "class" : "Dist::Zilla::Plugin::MinimumPerl", "name" : "@Author::ETHER/MinimumPerl", "version" : "1.006" }, { "class" : "Dist::Zilla::Plugin::MakeMaker", "config" : { "Dist::Zilla::Role::TestRunner" : { "default_jobs" : 9 } }, "name" : "@Author::ETHER/MakeMaker", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::Git::Contributors", "config" : { "Dist::Zilla::Plugin::Git::Contributors" : { "git_version" : "2.31.1", "include_authors" : 0, "include_releaser" : 1, "order_by" : "commits", "paths" : [] } }, "name" : "@Author::ETHER/Git::Contributors", "version" : "0.036" }, { "class" : "Dist::Zilla::Plugin::StaticInstall", "config" : { "Dist::Zilla::Plugin::StaticInstall" : { "dry_run" : 1, "mode" : "auto" } }, "name" : "@Author::ETHER/StaticInstall", "version" : "0.012" }, { "class" : "Dist::Zilla::Plugin::RunExtraTests", "config" : { "Dist::Zilla::Role::TestRunner" : { "default_jobs" : 9 } }, "name" : "@Author::ETHER/RunExtraTests", "version" : "0.029" }, { "class" : "Dist::Zilla::Plugin::CheckSelfDependency", "config" : { "Dist::Zilla::Plugin::CheckSelfDependency" : { "finder" : [ ":InstallModules" ] }, "Dist::Zilla::Role::ModuleMetadata" : { "Module::Metadata" : "1.000037", "version" : "0.006" } }, "name" : "@Author::ETHER/CheckSelfDependency", "version" : "0.011" }, { "class" : "Dist::Zilla::Plugin::Run::AfterBuild", "config" : { "Dist::Zilla::Plugin::Run::Role::Runner" : { "fatal_errors" : 1, "quiet" : 1, "run" : [ "bash -c \"test -e .ackrc && grep -q -- '--ignore-dir=.latest' .ackrc || echo '--ignore-dir=.latest' >> .ackrc; if [[ `dirname '%d'` != .build ]]; then test -e .ackrc && grep -q -- '--ignore-dir=%d' .ackrc || echo '--ignore-dir=%d' >> .ackrc; fi\"" ], "version" : "0.048" } }, "name" : "@Author::ETHER/.ackrc", "version" : "0.048" }, { "class" : "Dist::Zilla::Plugin::Run::AfterBuild", "config" : { "Dist::Zilla::Plugin::Run::Role::Runner" : { "eval" : [ "if ('%d' =~ /^%n-[.[:xdigit:]]+$/) { unlink '.latest'; symlink '%d', '.latest'; }" ], "fatal_errors" : 0, "quiet" : 1, "version" : "0.048" } }, "name" : "@Author::ETHER/.latest", "version" : "0.048" }, { "class" : "Dist::Zilla::Plugin::CheckStrictVersion", "name" : "@Author::ETHER/CheckStrictVersion", "version" : "0.001" }, { "class" : "Dist::Zilla::Plugin::CheckMetaResources", "name" : "@Author::ETHER/CheckMetaResources", "version" : "0.001" }, { "class" : "Dist::Zilla::Plugin::EnsureLatestPerl", "config" : { "Dist::Zilla::Plugin::EnsureLatestPerl" : { "Module::CoreList" : "5.20211120" } }, "name" : "@Author::ETHER/EnsureLatestPerl", "version" : "0.008" }, { "class" : "Dist::Zilla::Plugin::PromptIfStale", "config" : { "Dist::Zilla::Plugin::PromptIfStale" : { "check_all_plugins" : 1, "check_all_prereqs" : 1, "modules" : [], "phase" : "release", "run_under_travis" : 0, "skip" : [] } }, "name" : "@Author::ETHER/stale modules, release", "version" : "0.057" }, { "class" : "Dist::Zilla::Plugin::Git::Check", "config" : { "Dist::Zilla::Plugin::Git::Check" : { "untracked_files" : "die" }, "Dist::Zilla::Role::Git::DirtyFiles" : { "allow_dirty" : [], "allow_dirty_match" : [], "changelog" : "Changes" }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.31.1", "repo_root" : "." } }, "name" : "@Author::ETHER/initial check", "version" : "2.048" }, { "class" : "Dist::Zilla::Plugin::Git::CheckFor::MergeConflicts", "config" : { "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.31.1", "repo_root" : "." } }, "name" : "@Author::ETHER/Git::CheckFor::MergeConflicts", "version" : "0.014" }, { "class" : "Dist::Zilla::Plugin::Git::CheckFor::CorrectBranch", "config" : { "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.31.1", "repo_root" : "." } }, "name" : "@Author::ETHER/Git::CheckFor::CorrectBranch", "version" : "0.014" }, { "class" : "Dist::Zilla::Plugin::Git::Remote::Check", "name" : "@Author::ETHER/Git::Remote::Check", "version" : "0.1.2" }, { "class" : "Dist::Zilla::Plugin::CheckPrereqsIndexed", "name" : "@Author::ETHER/CheckPrereqsIndexed", "version" : "0.021" }, { "class" : "Dist::Zilla::Plugin::TestRelease", "name" : "@Author::ETHER/TestRelease", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::Git::Check", "config" : { "Dist::Zilla::Plugin::Git::Check" : { "untracked_files" : "die" }, "Dist::Zilla::Role::Git::DirtyFiles" : { "allow_dirty" : [], "allow_dirty_match" : [], "changelog" : "Changes" }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.31.1", "repo_root" : "." } }, "name" : "@Author::ETHER/after tests", "version" : "2.048" }, { "class" : "Dist::Zilla::Plugin::CheckIssues", "name" : "@Author::ETHER/CheckIssues", "version" : "0.011" }, { "class" : "Dist::Zilla::Plugin::UploadToCPAN", "name" : "@Author::ETHER/UploadToCPAN", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::CopyFilesFromRelease", "config" : { "Dist::Zilla::Plugin::CopyFilesFromRelease" : { "filename" : [ "CONTRIBUTING", "INSTALL", "LICENCE", "LICENSE", "ppport.h" ], "match" : [] } }, "name" : "@Author::ETHER/copy generated files", "version" : "0.007" }, { "class" : "Dist::Zilla::Plugin::ReadmeAnyFromPod", "config" : { "Dist::Zilla::Role::FileWatcher" : { "version" : "0.006" } }, "name" : "@Author::ETHER/ReadmeAnyFromPod", "version" : "0.163250" }, { "class" : "Dist::Zilla::Plugin::Prereqs", "config" : { "Dist::Zilla::Plugin::Prereqs" : { "phase" : "develop", "type" : "recommends" } }, "name" : "@Author::ETHER/@Git::VersionManager/pluginbundle version", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::RewriteVersion::Transitional", "config" : { "Dist::Zilla::Plugin::RewriteVersion" : { "add_tarball_name" : 0, "finders" : [ ":ExecFiles", ":InstallModules" ], "global" : 1, "skip_version_provider" : 0 }, "Dist::Zilla::Plugin::RewriteVersion::Transitional" : {} }, "name" : "@Author::ETHER/@Git::VersionManager/RewriteVersion::Transitional", "version" : "0.009" }, { "class" : "Dist::Zilla::Plugin::MetaProvides::Update", "name" : "@Author::ETHER/@Git::VersionManager/MetaProvides::Update", "version" : "0.007" }, { "class" : "Dist::Zilla::Plugin::CopyFilesFromRelease", "config" : { "Dist::Zilla::Plugin::CopyFilesFromRelease" : { "filename" : [ "Changes" ], "match" : [] } }, "name" : "@Author::ETHER/@Git::VersionManager/CopyFilesFromRelease", "version" : "0.007" }, { "class" : "Dist::Zilla::Plugin::Git::Commit", "config" : { "Dist::Zilla::Plugin::Git::Commit" : { "add_files_in" : [ "." ], "commit_msg" : "%N-%v%t%n%n%c", "signoff" : 0 }, "Dist::Zilla::Role::Git::DirtyFiles" : { "allow_dirty" : [ "CONTRIBUTING", "Changes", "INSTALL", "LICENCE", "README.pod" ], "allow_dirty_match" : [], "changelog" : "Changes" }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.31.1", "repo_root" : "." }, "Dist::Zilla::Role::Git::StringFormatter" : { "time_zone" : "local" } }, "name" : "@Author::ETHER/@Git::VersionManager/release snapshot", "version" : "2.048" }, { "class" : "Dist::Zilla::Plugin::Git::Tag", "config" : { "Dist::Zilla::Plugin::Git::Tag" : { "branch" : null, "changelog" : "Changes", "signed" : 0, "tag" : "v0.31", "tag_format" : "v%V", "tag_message" : "v%v%t" }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.31.1", "repo_root" : "." }, "Dist::Zilla::Role::Git::StringFormatter" : { "time_zone" : "local" } }, "name" : "@Author::ETHER/@Git::VersionManager/Git::Tag", "version" : "2.048" }, { "class" : "Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional", "config" : { "Dist::Zilla::Plugin::BumpVersionAfterRelease" : { "finders" : [ ":InstallModules" ], "global" : 1, "munge_makefile_pl" : 1 }, "Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional" : {} }, "name" : "@Author::ETHER/@Git::VersionManager/BumpVersionAfterRelease::Transitional", "version" : "0.009" }, { "class" : "Dist::Zilla::Plugin::NextRelease", "name" : "@Author::ETHER/@Git::VersionManager/NextRelease", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::Git::Commit", "config" : { "Dist::Zilla::Plugin::Git::Commit" : { "add_files_in" : [], "commit_msg" : "increment $VERSION after %v release", "signoff" : 0 }, "Dist::Zilla::Role::Git::DirtyFiles" : { "allow_dirty" : [ "Build.PL", "Changes", "Makefile.PL" ], "allow_dirty_match" : [ "(?^:^lib/.*\\.pm$)" ], "changelog" : "Changes" }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.31.1", "repo_root" : "." }, "Dist::Zilla::Role::Git::StringFormatter" : { "time_zone" : "local" } }, "name" : "@Author::ETHER/@Git::VersionManager/post-release commit", "version" : "2.048" }, { "class" : "Dist::Zilla::Plugin::Prereqs", "config" : { "Dist::Zilla::Plugin::Prereqs" : { "phase" : "x_Dist_Zilla", "type" : "requires" } }, "name" : "@Author::ETHER/@Git::VersionManager/prereqs for @Git::VersionManager", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::Git::Push", "config" : { "Dist::Zilla::Plugin::Git::Push" : { "push_to" : [ "origin" ], "remotes_must_exist" : 1 }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.31.1", "repo_root" : "." } }, "name" : "@Author::ETHER/Git::Push", "version" : "2.048" }, { "class" : "Dist::Zilla::Plugin::GitHub::Update", "config" : { "Dist::Zilla::Plugin::GitHub::Update" : { "metacpan" : 1 } }, "name" : "@Author::ETHER/GitHub::Update", "version" : "0.48" }, { "class" : "Dist::Zilla::Plugin::Run::AfterRelease", "config" : { "Dist::Zilla::Plugin::Run::Role::Runner" : { "fatal_errors" : 0, "quiet" : 0, "run" : [ "REDACTED" ], "version" : "0.048" } }, "name" : "@Author::ETHER/install release", "version" : "0.048" }, { "class" : "Dist::Zilla::Plugin::Run::AfterRelease", "config" : { "Dist::Zilla::Plugin::Run::Role::Runner" : { "eval" : [ "print \"release complete!\\xa\"" ], "fatal_errors" : 1, "quiet" : 1, "version" : "0.048" } }, "name" : "@Author::ETHER/release complete", "version" : "0.048" }, { "class" : "Dist::Zilla::Plugin::ConfirmRelease", "name" : "@Author::ETHER/ConfirmRelease", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::Prereqs", "config" : { "Dist::Zilla::Plugin::Prereqs" : { "phase" : "x_Dist_Zilla", "type" : "requires" } }, "name" : "@Author::ETHER/prereqs for @Author::ETHER", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::Substitute", "name" : "Substitute", "version" : "0.006" }, { "class" : "Dist::Zilla::Plugin::Prereqs::Soften", "config" : { "Dist::Zilla::Plugin::Prereqs::Soften" : { "copy_to" : [ "develop.requires" ], "modules" : [ "Capture::Tiny", "Sub::Name", "Sub::Util" ], "modules_from_features" : null, "to_relationship" : "suggests" } }, "name" : "Prereqs::Soften", "version" : "0.006003" }, { "class" : "Dist::Zilla::Plugin::OnlyCorePrereqs", "config" : { "Dist::Zilla::Plugin::OnlyCorePrereqs" : { "also_disallow" : [], "check_dual_life_versions" : "0", "deprecated_ok" : 0, "phases" : [ "configure", "build", "runtime", "test" ], "skips" : [], "starting_version" : "to be determined from perl prereq" } }, "name" : "OnlyCorePrereqs", "version" : "0.024" }, { "class" : "Dist::Zilla::Plugin::Breaks", "name" : "Breaks", "version" : "0.004" }, { "class" : "Dist::Zilla::Plugin::Test::CheckBreaks", "config" : { "Dist::Zilla::Plugin::Test::CheckBreaks" : { "conflicts_module" : [], "no_forced_deps" : 1 }, "Dist::Zilla::Role::ModuleMetadata" : { "Module::Metadata" : "1.000037", "version" : "0.006" } }, "name" : "Test::CheckBreaks", "version" : "0.019" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":InstallModules", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":IncModules", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":TestFiles", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ExtraTestFiles", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ExecFiles", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":PerlExecFiles", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ShareFiles", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":MainModule", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":AllFiles", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":NoFiles", "version" : "6.024" }, { "class" : "Dist::Zilla::Plugin::VerifyPhases", "name" : "@Author::ETHER/PHASE VERIFICATION", "version" : "0.016" } ], "zilla" : { "class" : "Dist::Zilla::Dist::Builder", "config" : { "is_trial" : 0 }, "version" : "6.024" } }, "x_authority" : "cpan:NUFFIN", "x_breaks" : { "Try::Tiny::Except" : "<= 0.01" }, "x_contributors" : [ "Karen Etheridge <ether@cpan.org>", "Peter Rabbitson <ribasushi@cpan.org>", "Ricardo Signes <rjbs@cpan.org>", "Mark Fowler <mark@twoshortplanks.com>", "Graham Knop <haarg@haarg.org>", "Aristotle Pagaltzis <pagaltzis@gmx.de>", "Dagfinn Ilmari Mannsåker <ilmari@ilmari.org>", "Lukas Mai <l.mai@web.de>", "Alex <alex@koban.(none)>", "anaxagoras <walkeraj@gmail.com>", "Andrew Yates <ayates@haddock.local>", "awalker <awalker@sourcefire.com>", "chromatic <chromatic@wgz.org>", "cm-perl <cm-perl@users.noreply.github.com>", "David Lowe <davidl@lokku.com>", "Glenn Fowler <cebjyre@cpan.org>", "Hans Dieter Pearcey <hdp@weftsoar.net>", "Jens Berthold <jens@jebecs.de>", "Jonathan Yu <JAWNSY@cpan.org>", "Marc Mims <marc@questright.com>", "Mark Stosberg <mark@stosberg.com>", "Pali <pali@cpan.org>", "Paul Howarth <paul@city-fan.org>", "Rudolf Leermakers <rudolf@hatsuseno.org>" ], "x_generated_by_perl" : "v5.35.6", "x_serialization_backend" : "JSON::PP version 2.97001", "x_spdx_expression" : "MIT", "x_use_unsafe_inc" : 0 } PK�����A0]T �� ��<��perl5/x86_64-linux-thread-multi/.meta/CPAN-2.36/install.jsonnu�6$��������{"name":"CPAN","dist":"CPAN-2.36","pathname":"A/AN/ANDK/CPAN-2.36.tar.gz","version":2.36,"target":"CPAN","provides":{"CPAN::Index":{"version":2.29,"file":"lib/CPAN/Index.pm"},"CPAN::URL":{"version":5.5,"file":"lib/CPAN/URL.pm"},"CPAN::HandleConfig":{"file":"lib/CPAN/HandleConfig.pm","version":5.5012},"CPAN::Admin":{"file":"lib/CPAN/Admin.pm","version":5.501},"CPAN::Nox":{"file":"lib/CPAN/Nox.pm","version":5.5001},"CPAN::Distroprefs::Result::Success":{"file":"lib/CPAN/Distroprefs.pm","version":6.0001},"CPAN::Shell":{"version":5.5009,"file":"lib/CPAN/Shell.pm"},"CPAN::FTP::netrc":{"file":"lib/CPAN/FTP/netrc.pm","version":1.01},"CPAN::Bundle":{"file":"lib/CPAN/Bundle.pm","version":5.5005},"CPAN::HTTP::Credentials":{"file":"lib/CPAN/HTTP/Credentials.pm","version":1.9601},"CPAN::Version":{"file":"lib/CPAN/Version.pm","version":5.5003},"CPAN::Distroprefs::Result::Warning":{"version":6.0001,"file":"lib/CPAN/Distroprefs.pm"},"CPAN::Debug":{"version":5.5001,"file":"lib/CPAN/Debug.pm"},"CPAN::Author":{"version":5.5002,"file":"lib/CPAN/Author.pm"},"CPAN::CacheMgr":{"version":5.5002,"file":"lib/CPAN/CacheMgr.pm"},"CPAN::Exception::blocked_urllist":{"version":1.001,"file":"lib/CPAN/Exception/blocked_urllist.pm"},"CPAN::Plugin::Specfile":{"file":"lib/CPAN/Plugin/Specfile.pm","version":0.02},"CPAN::HTTP::Client":{"file":"lib/CPAN/HTTP/Client.pm","version":1.9602},"CPAN::Distroprefs::Result":{"file":"lib/CPAN/Distroprefs.pm","version":6.0001},"CPAN::Distroprefs::Result::Error":{"file":"lib/CPAN/Distroprefs.pm","version":6.0001},"CPAN::Kwalify":{"version":"5.50","file":"lib/CPAN/Kwalify.pm"},"CPAN::Exception::RecursiveDependency::na":{"file":"lib/CPAN/Exception/RecursiveDependency.pm","version":5.5001},"CPAN::Tarzip":{"version":5.5013,"file":"lib/CPAN/Tarzip.pm"},"App::Cpan":{"file":"lib/App/Cpan.pm","version":1.678},"CPAN::Module":{"version":5.5003,"file":"lib/CPAN/Module.pm"},"CPAN::Exception::yaml_not_installed":{"version":5.5,"file":"lib/CPAN/Exception/yaml_not_installed.pm"},"CPAN::Distroprefs::Pref":{"version":6.0001,"file":"lib/CPAN/Distroprefs.pm"},"CPAN::FirstTime":{"file":"lib/CPAN/FirstTime.pm","version":5.5317},"CPAN::Distroprefs":{"file":"lib/CPAN/Distroprefs.pm","version":6.0001},"CPAN::DeferredCode":{"file":"lib/CPAN/DeferredCode.pm","version":"5.50"},"CPAN::Prompt":{"version":5.5,"file":"lib/CPAN/Prompt.pm"},"CPAN::Mirrored::By":{"version":2.27,"file":"lib/CPAN/Mirrors.pm"},"CPAN::Distroprefs::Result::Fatal":{"version":6.0001,"file":"lib/CPAN/Distroprefs.pm"},"CPAN::Complete":{"version":5.5001,"file":"lib/CPAN/Complete.pm"},"CPAN::Distrostatus":{"file":"lib/CPAN/Distrostatus.pm","version":5.5},"CPAN":{"file":"lib/CPAN.pm","version":2.36},"CPAN::Plugin":{"version":0.97,"file":"lib/CPAN/Plugin.pm"},"CPAN::Mirrors":{"file":"lib/CPAN/Mirrors.pm","version":2.27},"CPAN::Exception::yaml_process_error":{"file":"lib/CPAN/Exception/yaml_process_error.pm","version":5.5},"CPAN::Distribution":{"file":"lib/CPAN/Distribution.pm","version":2.34},"CPAN::Eval":{"file":"lib/CPAN/Distroprefs.pm","version":6.0001},"CPAN::Queue":{"file":"lib/CPAN/Queue.pm","version":5.5003},"CPAN::LWP::UserAgent":{"version":1.9601,"file":"lib/CPAN/LWP/UserAgent.pm"},"CPAN::FTP":{"file":"lib/CPAN/FTP.pm","version":5.5016},"CPAN::Exception::RecursiveDependency":{"file":"lib/CPAN/Exception/RecursiveDependency.pm","version":5.5001},"CPAN::Queue::Item":{"file":"lib/CPAN/Queue.pm","version":5.5003},"CPAN::InfoObj":{"file":"lib/CPAN/InfoObj.pm","version":5.5},"CPAN::Distroprefs::Iterator":{"file":"lib/CPAN/Distroprefs.pm","version":6.0001}}}PK�����A0]qA �� ��;��perl5/x86_64-linux-thread-multi/.meta/CPAN-2.36/MYMETA.jsonnu�6$��������{ "abstract" : "query, download and build perl modules from CPAN sites", "author" : [ "Andreas Koenig <andreas.koenig.gmwojprw@franz.ak.mind.de>" ], "dynamic_config" : 0, "generated_by" : "ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150010", "keywords" : [ "CPAN", "module", "module installation" ], "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "CPAN", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : { "Archive::Tar" : "0", "Archive::Zip" : "0", "CPAN::Meta" : "0", "CPAN::Meta::Requirements" : "2.121", "CPAN::Meta::YAML" : "0", "Compress::Bzip2" : "0", "Compress::Zlib" : "0", "Data::Dumper" : "0", "Digest::MD5" : "0", "Digest::SHA" : "0", "Exporter" : "0", "Exporter::Heavy" : "0", "ExtUtils::CBuilder" : "0", "File::Copy" : "0", "File::HomeDir" : "0", "File::Spec" : "0", "File::Temp" : "0", "File::Which" : "0", "HTTP::Tiny" : "0.005", "IO::Compress::Base" : "0", "IO::Zlib" : "0", "JSON::PP" : "0", "MIME::Base64" : "0", "Module::Build" : "0", "Net::FTP" : "0", "Net::Ping" : "0", "Parse::CPAN::Meta" : "0", "Pod::Perldoc" : "0", "Pod::Perldoc::ToMan" : "0", "Scalar::Util" : "0", "Socket" : "0", "Term::ReadKey" : "0", "Test::Harness" : "2.62", "Test::More" : "0", "Text::Glob" : "0", "Text::ParseWords" : "0", "Text::Wrap" : "0", "perl" : "5.006002" } } }, "release_status" : "stable", "resources" : { "repository" : { "type" : "git", "url" : "https://github.com/andk/cpanpm" } }, "version" : "2.36", "x_serialization_backend" : "JSON::PP version 2.97001" } PK�����A0]0ޟ����A��perl5/x86_64-linux-thread-multi/.meta/version-0.9930/install.jsonnu�6$��������{"dist":"version-0.9930","name":"version","provides":{"version::vpp":{"version":"0.9930","file":"vperl/vpp.pm"},"version":{"file":"lib/version.pm","version":"0.9930"},"version::vxs":{"file":"vutil/lib/version/vxs.pm","version":"0.9930"},"version::regex":{"file":"lib/version/regex.pm","version":"0.9930"}},"target":"version","version":"0.9930","pathname":"L/LE/LEONT/version-0.9930.tar.gz"}PK�����A0]uv����@��perl5/x86_64-linux-thread-multi/.meta/version-0.9930/MYMETA.jsonnu�6$��������{ "abstract" : "Structured version objects", "author" : [ "John Peacock <jpeacock@cpan.org>" ], "dynamic_config" : 0, "generated_by" : "ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "version", "no_index" : { "directory" : [ "t", "inc" ], "package" : [ "charstar" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : { "perl" : "5.006002" } }, "test" : { "requires" : { "File::Temp" : "0.13", "Test::More" : "0.45", "base" : "0" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "mailto" : "bug-version@rt.cpan.org", "web" : "https://rt.cpan.org/Public/Dist/Display.html?Name=version" }, "repository" : { "type" : "git", "url" : "git://github.com/Perl/version.pm.git", "web" : "https://github.com/Perl/version.pm" } }, "version" : "0.9930", "x_serialization_backend" : "JSON::PP version 2.97001" } PK�����A0]A9������H��perl5/x86_64-linux-thread-multi/.meta/Canary-Stability-2013/install.jsonnu�6$��������{"name":"Canary::Stability","dist":"Canary-Stability-2013","pathname":"M/ML/MLEHMANN/Canary-Stability-2013.tar.gz","version":2013,"target":"Canary::Stability","provides":{"Canary::Stability":{"version":2013,"file":"Stability.pm"}}}PK�����A0]QE����G��perl5/x86_64-linux-thread-multi/.meta/Canary-Stability-2013/MYMETA.jsonnu�6$��������{ "abstract" : "unknown", "author" : [ "unknown" ], "dynamic_config" : 0, "generated_by" : "ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150010", "license" : [ "unknown" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Canary-Stability", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } } }, "release_status" : "stable", "version" : "2013", "x_serialization_backend" : "JSON::PP version 2.97001" } PK�����A0]{������?��perl5/x86_64-linux-thread-multi/.meta/JSON-XS-4.03/install.jsonnu�6$��������{"dist":"JSON-XS-4.03","name":"JSON::XS","provides":{"JSON::XS":{"file":"XS.pm","version":4.03}},"target":"JSON::XS","version":4.03,"pathname":"M/ML/MLEHMANN/JSON-XS-4.03.tar.gz"}PK�����A0]| ����>��perl5/x86_64-linux-thread-multi/.meta/JSON-XS-4.03/MYMETA.jsonnu�6$��������{ "abstract" : "unknown", "author" : [ "unknown" ], "dynamic_config" : 0, "generated_by" : "ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150001, CPAN::Meta::Converter version 2.150010", "license" : [ "unknown" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "JSON-XS", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "Canary::Stability" : "0", "ExtUtils::MakeMaker" : "6.52" } }, "runtime" : { "requires" : { "Types::Serialiser" : "0", "common::sense" : "0" } } }, "release_status" : "stable", "version" : "4.03", "x_serialization_backend" : "JSON::PP version 2.97001" } PK�����A0]s������D��perl5/x86_64-linux-thread-multi/.meta/common-sense-3.75/install.jsonnu�6$��������{"name":"common::sense","dist":"common-sense-3.75","pathname":"M/ML/MLEHMANN/common-sense-3.75.tar.gz","version":3.75,"target":"common::sense","provides":{"common::sense":{"version":3.75,"file":"sense.pm.PL"}}}PK�����A0]PU?��?��C��perl5/x86_64-linux-thread-multi/.meta/common-sense-3.75/MYMETA.jsonnu�6$��������{ "abstract" : "unknown", "author" : [ "unknown" ], "dynamic_config" : 0, "generated_by" : "ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150001, CPAN::Meta::Converter version 2.150010", "license" : [ "unknown" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "common-sense", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } } }, "release_status" : "stable", "version" : 3.75, "x_serialization_backend" : "JSON::PP version 2.97001" } PK�����A0]cT����E��perl5/x86_64-linux-thread-multi/.meta/local-lib-2.000029/install.jsonnu�6$��������{"provides":{"local::lib":{"version":2.000029,"file":"lib/local/lib.pm"},"lib::core::only":{"file":"lib/lib/core/only.pm"}},"target":"local::lib","version":2.000029,"pathname":"H/HA/HAARG/local-lib-2.000029.tar.gz","dist":"local-lib-2.000029","name":"local::lib"}PK�����A0]�VJ����D��perl5/x86_64-linux-thread-multi/.meta/local-lib-2.000029/MYMETA.jsonnu�6$��������{ "abstract" : "create and use a local lib/ for perl modules with PERL5LIB", "author" : [ "mst - Matt S. Trout (cpan:MSTROUT) <mst@shadowcat.co.uk>" ], "dynamic_config" : 0, "generated_by" : "ExtUtils::MakeMaker version 7.64, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "local-lib", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : {} }, "configure" : { "requires" : {} }, "develop" : { "requires" : { "Capture::Tiny" : "0", "Module::Build" : "0.36", "Test::CPAN::Changes" : "0", "Test::EOL" : "0", "Test::More" : "0.8101", "Test::NoTabs" : "0", "Test::Pod" : "0" } }, "runtime" : { "requires" : { "perl" : "5.006" } }, "test" : { "requires" : { "Test::More" : "0" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "mailto" : "bug-local-lib@rt.cpan.org", "web" : "https://rt.cpan.org/Public/Dist/Display.html?Name=local-lib" }, "license" : [ "http://dev.perl.org/licenses/" ], "repository" : { "type" : "git", "url" : "git://github.com/Perl-Toolchain-Gang/local-lib", "web" : "https://github.com/Perl-Toolchain-Gang/local-lib" }, "x_IRC" : "irc://irc.perl.org/#local-lib" }, "version" : "2.000029", "x_serialization_backend" : "JSON::PP version 2.97001" } PK�����A0]z€����O��perl5/x86_64-linux-thread-multi/.meta/CPAN-Meta-Requirements-2.143/install.jsonnu�6$��������{"version":"2.143","pathname":"R/RJ/RJBS/CPAN-Meta-Requirements-2.143.tar.gz","provides":{"CPAN::Meta::Requirements::Range":{"file":"lib/CPAN/Meta/Requirements/Range.pm","version":"2.143"},"CPAN::Meta::Requirements":{"version":"2.143","file":"lib/CPAN/Meta/Requirements.pm"}},"target":"CPAN::Meta::Requirements","name":"CPAN::Meta::Requirements","dist":"CPAN-Meta-Requirements-2.143"}PK�����A0]&9~��~��N��perl5/x86_64-linux-thread-multi/.meta/CPAN-Meta-Requirements-2.143/MYMETA.jsonnu�6$��������{ "abstract" : "a set of version requirements for a CPAN dist", "author" : [ "David Golden <dagolden@cpan.org>", "Ricardo Signes <rjbs@cpan.org>" ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 6.030, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "CPAN-Meta-Requirements", "no_index" : { "directory" : [ "corpus", "examples", "t", "xt" ], "package" : [ "DB" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "6.17" } }, "develop" : { "requires" : { "Dist::Zilla" : "5", "Dist::Zilla::Plugin::Authority" : "0", "Dist::Zilla::Plugin::AutoPrereqs" : "0", "Dist::Zilla::Plugin::BumpVersionAfterRelease" : "0", "Dist::Zilla::Plugin::CPANFile" : "0", "Dist::Zilla::Plugin::CheckChangesHasContent" : "0", "Dist::Zilla::Plugin::CheckMetaResources" : "0", "Dist::Zilla::Plugin::CheckPrereqsIndexed" : "0", "Dist::Zilla::Plugin::ConfirmRelease" : "0", "Dist::Zilla::Plugin::CopyFilesFromBuild::Filtered" : "0", "Dist::Zilla::Plugin::ExecDir" : "0", "Dist::Zilla::Plugin::Git::Check" : "0", "Dist::Zilla::Plugin::Git::CheckFor::CorrectBranch" : "0", "Dist::Zilla::Plugin::Git::Commit" : "0", "Dist::Zilla::Plugin::Git::Contributors" : "0", "Dist::Zilla::Plugin::Git::GatherDir" : "0", "Dist::Zilla::Plugin::Git::Push" : "0", "Dist::Zilla::Plugin::Git::Tag" : "0", "Dist::Zilla::Plugin::GithubMeta" : "0", "Dist::Zilla::Plugin::InsertCopyright" : "0", "Dist::Zilla::Plugin::License" : "0", "Dist::Zilla::Plugin::MakeMaker" : "0", "Dist::Zilla::Plugin::MakeMaker::Highlander" : "0.003", "Dist::Zilla::Plugin::Manifest" : "0", "Dist::Zilla::Plugin::ManifestSkip" : "0", "Dist::Zilla::Plugin::MetaJSON" : "0", "Dist::Zilla::Plugin::MetaNoIndex" : "0", "Dist::Zilla::Plugin::MetaProvides::Package" : "0", "Dist::Zilla::Plugin::MetaTests" : "0", "Dist::Zilla::Plugin::MetaYAML" : "0", "Dist::Zilla::Plugin::MinimumPerl" : "0", "Dist::Zilla::Plugin::NextRelease" : "0", "Dist::Zilla::Plugin::OnlyCorePrereqs" : "0.014", "Dist::Zilla::Plugin::Pod2Readme" : "0", "Dist::Zilla::Plugin::PodCoverageTests" : "0", "Dist::Zilla::Plugin::PodSyntaxTests" : "0", "Dist::Zilla::Plugin::Prereqs" : "0", "Dist::Zilla::Plugin::Prereqs::AuthorDeps" : "0", "Dist::Zilla::Plugin::PromptIfStale" : "0", "Dist::Zilla::Plugin::PruneCruft" : "0", "Dist::Zilla::Plugin::RewriteVersion" : "0", "Dist::Zilla::Plugin::RunExtraTests" : "0", "Dist::Zilla::Plugin::ShareDir" : "0", "Dist::Zilla::Plugin::SurgicalPodWeaver" : "0", "Dist::Zilla::Plugin::Test::Compile" : "0", "Dist::Zilla::Plugin::Test::MinimumVersion" : "0", "Dist::Zilla::Plugin::Test::Perl::Critic" : "0", "Dist::Zilla::Plugin::Test::PodSpelling" : "0", "Dist::Zilla::Plugin::Test::Portability" : "0", "Dist::Zilla::Plugin::Test::ReportPrereqs" : "0", "Dist::Zilla::Plugin::Test::Version" : "0", "Dist::Zilla::Plugin::TestRelease" : "0", "Dist::Zilla::Plugin::UploadToCPAN" : "0", "File::Spec" : "0", "File::Temp" : "0", "IO::Handle" : "0", "IPC::Open3" : "0", "Pod::Coverage::TrustPod" : "0", "Pod::Weaver::PluginBundle::DAGOLDEN" : "0", "Pod::Wordlist" : "0", "Software::License::Perl_5" : "0", "Test::CPAN::Meta" : "0", "Test::MinimumVersion" : "0", "Test::More" : "0", "Test::Perl::Critic" : "0", "Test::Pod" : "1.41", "Test::Pod::Coverage" : "1.08", "Test::Portability::Files" : "0", "Test::Spelling" : "0.12", "Test::Version" : "1" } }, "runtime" : { "requires" : { "B" : "0", "Carp" : "0", "perl" : "5.010000", "strict" : "0", "version" : "0.88", "warnings" : "0" } }, "test" : { "recommends" : { "CPAN::Meta" : "2.120900" }, "requires" : { "ExtUtils::MakeMaker" : "0", "File::Spec" : "0", "Test::More" : "0.88", "version" : "0.88" } } }, "provides" : { "CPAN::Meta::Requirements" : { "file" : "lib/CPAN/Meta/Requirements.pm", "version" : "2.143" }, "CPAN::Meta::Requirements::Range" : { "file" : "lib/CPAN/Meta/Requirements/Range.pm", "version" : "2.143" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/Perl-Toolchain-Gang/CPAN-Meta-Requirements/issues" }, "homepage" : "https://github.com/Perl-Toolchain-Gang/CPAN-Meta-Requirements", "repository" : { "type" : "git", "url" : "https://github.com/Perl-Toolchain-Gang/CPAN-Meta-Requirements.git", "web" : "https://github.com/Perl-Toolchain-Gang/CPAN-Meta-Requirements" } }, "version" : "2.143", "x_authority" : "cpan:DAGOLDEN", "x_contributors" : [ "Ed J <mohawk2@users.noreply.github.com>", "Graham Knop <haarg@haarg.org>", "Karen Etheridge <ether@cpan.org>", "Leon Timmermans <fawaka@gmail.com>", "Paul Howarth <paul@city-fan.org>", "Ricardo Signes <rjbs@semiotic.systems>", "robario <webmaster@robario.com>", "Tatsuhiko Miyagawa <miyagawa@bulknews.net>", "Tatsuhiko Miyagawa <miyagawa@gmail.com>" ], "x_generated_by_perl" : "v5.37.10", "x_serialization_backend" : "JSON::PP version 2.97001", "x_spdx_expression" : "Artistic-1.0-Perl OR GPL-1.0-or-later" } PK�����A0] O��O����perl5/local/lib.pmnu�6$��������package local::lib; use 5.006; BEGIN { if ($ENV{RELEASE_TESTING}) { require strict; strict->import; require warnings; warnings->import; } } use Config (); our $VERSION = '2.000029'; $VERSION =~ tr/_//d; BEGIN { *_WIN32 = ($^O eq 'MSWin32' || $^O eq 'NetWare' || $^O eq 'symbian') ? sub(){1} : sub(){0}; # punt on these systems *_USE_FSPEC = ($^O eq 'MacOS' || $^O eq 'VMS' || $INC{'File/Spec.pm'}) ? sub(){1} : sub(){0}; } my $_archname = $Config::Config{archname}; my $_version = $Config::Config{version}; my @_inc_version_list = reverse split / /, $Config::Config{inc_version_list}; my $_path_sep = $Config::Config{path_sep}; our $_DIR_JOIN = _WIN32 ? '\\' : '/'; our $_DIR_SPLIT = (_WIN32 || $^O eq 'cygwin') ? qr{[\\/]} : qr{/}; our $_ROOT = _WIN32 ? do { my $UNC = qr{[\\/]{2}[^\\/]+[\\/][^\\/]+}; qr{^(?:$UNC|[A-Za-z]:|)$_DIR_SPLIT}; } : qr{^/}; our $_PERL; sub _perl { if (!$_PERL) { # untaint and validate ($_PERL, my $exe) = $^X =~ /((?:.*$_DIR_SPLIT)?(.+))/; $_PERL = 'perl' if $exe !~ /perl/; if (_is_abs($_PERL)) { } elsif (-x $Config::Config{perlpath}) { $_PERL = $Config::Config{perlpath}; } elsif ($_PERL =~ $_DIR_SPLIT && -x $_PERL) { $_PERL = _rel2abs($_PERL); } else { ($_PERL) = map { /(.*)/ } grep { -x $_ } map { ($_, _WIN32 ? ("$_.exe") : ()) } map { join($_DIR_JOIN, $_, $_PERL) } split /\Q$_path_sep\E/, $ENV{PATH}; } } $_PERL; } sub _cwd { if (my $cwd = defined &Cwd::sys_cwd ? \&Cwd::sys_cwd : defined &Cwd::cwd ? \&Cwd::cwd : undef ) { no warnings 'redefine'; *_cwd = $cwd; goto &$cwd; } my $drive = shift; return Win32::GetCwd() if _WIN32 && defined &Win32::GetCwd && !$drive; local @ENV{qw(PATH IFS CDPATH ENV BASH_ENV)}; delete @ENV{qw(PATH IFS CDPATH ENV BASH_ENV)}; my $cmd = $drive ? "eval { Cwd::getdcwd(q($drive)) }" : 'getcwd'; my $perl = _perl; my $cwd = `"$perl" -MCwd -le "print $cmd"`; chomp $cwd; if (!length $cwd && $drive) { $cwd = $drive; } $cwd =~ s/$_DIR_SPLIT?$/$_DIR_JOIN/; $cwd; } sub _catdir { if (_USE_FSPEC) { require File::Spec; File::Spec->catdir(@_); } else { my $dir = join($_DIR_JOIN, @_); $dir =~ s{($_DIR_SPLIT)(?:\.?$_DIR_SPLIT)+}{$1}g; $dir; } } sub _is_abs { if (_USE_FSPEC) { require File::Spec; File::Spec->file_name_is_absolute($_[0]); } else { $_[0] =~ $_ROOT; } } sub _rel2abs { my ($dir, $base) = @_; return $dir if _is_abs($dir); $base = _WIN32 && $dir =~ s/^([A-Za-z]:)// ? _cwd("$1") : $base ? _rel2abs($base) : _cwd; return _catdir($base, $dir); } our $_DEVNULL; sub _devnull { return $_DEVNULL ||= _USE_FSPEC ? (require File::Spec, File::Spec->devnull) : _WIN32 ? 'nul' : $^O eq 'os2' ? '/dev/nul' : '/dev/null'; } sub import { my ($class, @args) = @_; if ($0 eq '-') { push @args, @ARGV; require Cwd; } my @steps; my %opts; my %attr; my $shelltype; while (@args) { my $arg = shift @args; # check for lethal dash first to stop processing before causing problems # the fancy dash is U+2212 or \xE2\x88\x92 if ($arg =~ /\xE2\x88\x92/) { die <<'DEATH'; WHOA THERE! It looks like you've got some fancy dashes in your commandline! These are *not* the traditional -- dashes that software recognizes. You probably got these by copy-pasting from the perldoc for this module as rendered by a UTF8-capable formatter. This most typically happens on an OS X terminal, but can happen elsewhere too. Please try again after replacing the dashes with normal minus signs. DEATH } elsif ($arg eq '--self-contained') { die <<'DEATH'; FATAL: The local::lib --self-contained flag has never worked reliably and the original author, Mark Stosberg, was unable or unwilling to maintain it. As such, this flag has been removed from the local::lib codebase in order to prevent misunderstandings and potentially broken builds. The local::lib authors recommend that you look at the lib::core::only module shipped with this distribution in order to create a more robust environment that is equivalent to what --self-contained provided (although quite possibly not what you originally thought it provided due to the poor quality of the documentation, for which we apologise). DEATH } elsif( $arg =~ /^--deactivate(?:=(.*))?$/ ) { my $path = defined $1 ? $1 : shift @args; push @steps, ['deactivate', $path]; } elsif ( $arg eq '--deactivate-all' ) { push @steps, ['deactivate_all']; } elsif ( $arg =~ /^--shelltype(?:=(.*))?$/ ) { $shelltype = defined $1 ? $1 : shift @args; } elsif ( $arg eq '--no-create' ) { $opts{no_create} = 1; } elsif ( $arg eq '--quiet' ) { $attr{quiet} = 1; } elsif ( $arg eq '--always' ) { $attr{always} = 1; } elsif ( $arg =~ /^--/ ) { die "Unknown import argument: $arg"; } else { push @steps, ['activate', $arg, \%opts]; } } if (!@steps) { push @steps, ['activate', undef, \%opts]; } my $self = $class->new(%attr); for (@steps) { my ($method, @args) = @$_; $self = $self->$method(@args); } if ($0 eq '-') { print $self->environment_vars_string($shelltype); exit 0; } else { $self->setup_local_lib; } } sub new { my $class = shift; bless {@_}, $class; } sub clone { my $self = shift; bless {%$self, @_}, ref $self; } sub inc { $_[0]->{inc} ||= \@INC } sub libs { $_[0]->{libs} ||= [ \'PERL5LIB' ] } sub bins { $_[0]->{bins} ||= [ \'PATH' ] } sub roots { $_[0]->{roots} ||= [ \'PERL_LOCAL_LIB_ROOT' ] } sub extra { $_[0]->{extra} ||= {} } sub quiet { $_[0]->{quiet} } sub _as_list { my $list = shift; grep length, map { !(ref $_ && ref $_ eq 'SCALAR') ? $_ : ( defined $ENV{$$_} ? split(/\Q$_path_sep/, $ENV{$$_}) : () ) } ref $list ? @$list : $list; } sub _remove_from { my ($list, @remove) = @_; return @$list if !@remove; my %remove = map { $_ => 1 } @remove; grep !$remove{$_}, _as_list($list); } my @_lib_subdirs = ( [$_version, $_archname], [$_version], [$_archname], (map [$_], @_inc_version_list), [], ); sub install_base_bin_path { my ($class, $path) = @_; return _catdir($path, 'bin'); } sub install_base_perl_path { my ($class, $path) = @_; return _catdir($path, 'lib', 'perl5'); } sub install_base_arch_path { my ($class, $path) = @_; _catdir($class->install_base_perl_path($path), $_archname); } sub lib_paths_for { my ($class, $path) = @_; my $base = $class->install_base_perl_path($path); return map { _catdir($base, @$_) } @_lib_subdirs; } sub _mm_escape_path { my $path = shift; $path =~ s/\\/\\\\/g; if ($path =~ s/ /\\ /g) { $path = qq{"$path"}; } return $path; } sub _mb_escape_path { my $path = shift; $path =~ s/\\/\\\\/g; return qq{"$path"}; } sub installer_options_for { my ($class, $path) = @_; return ( PERL_MM_OPT => defined $path ? "INSTALL_BASE="._mm_escape_path($path) : undef, PERL_MB_OPT => defined $path ? "--install_base "._mb_escape_path($path) : undef, ); } sub active_paths { my ($self) = @_; $self = ref $self ? $self : $self->new; return grep { # screen out entries that aren't actually reflected in @INC my $active_ll = $self->install_base_perl_path($_); grep { $_ eq $active_ll } @{$self->inc}; } _as_list($self->roots); } sub deactivate { my ($self, $path) = @_; $self = $self->new unless ref $self; $path = $self->resolve_path($path); $path = $self->normalize_path($path); my @active_lls = $self->active_paths; if (!grep { $_ eq $path } @active_lls) { warn "Tried to deactivate inactive local::lib '$path'\n"; return $self; } my %args = ( bins => [ _remove_from($self->bins, $self->install_base_bin_path($path)) ], libs => [ _remove_from($self->libs, $self->install_base_perl_path($path)) ], inc => [ _remove_from($self->inc, $self->lib_paths_for($path)) ], roots => [ _remove_from($self->roots, $path) ], ); $args{extra} = { $self->installer_options_for($args{roots}[0]) }; $self->clone(%args); } sub deactivate_all { my ($self) = @_; $self = $self->new unless ref $self; my @active_lls = $self->active_paths; my %args; if (@active_lls) { %args = ( bins => [ _remove_from($self->bins, map $self->install_base_bin_path($_), @active_lls) ], libs => [ _remove_from($self->libs, map $self->install_base_perl_path($_), @active_lls) ], inc => [ _remove_from($self->inc, map $self->lib_paths_for($_), @active_lls) ], roots => [ _remove_from($self->roots, @active_lls) ], ); } $args{extra} = { $self->installer_options_for(undef) }; $self->clone(%args); } sub activate { my ($self, $path, $opts) = @_; $opts ||= {}; $self = $self->new unless ref $self; $path = $self->resolve_path($path); $self->ensure_dir_structure_for($path, { quiet => $self->quiet }) unless $opts->{no_create}; $path = $self->normalize_path($path); my @active_lls = $self->active_paths; if (grep { $_ eq $path } @active_lls[1 .. $#active_lls]) { $self = $self->deactivate($path); } my %args; if ($opts->{always} || !@active_lls || $active_lls[0] ne $path) { %args = ( bins => [ $self->install_base_bin_path($path), @{$self->bins} ], libs => [ $self->install_base_perl_path($path), @{$self->libs} ], inc => [ $self->lib_paths_for($path), @{$self->inc} ], roots => [ $path, @{$self->roots} ], ); } $args{extra} = { $self->installer_options_for($path) }; $self->clone(%args); } sub normalize_path { my ($self, $path) = @_; $path = ( Win32::GetShortPathName($path) || $path ) if $^O eq 'MSWin32'; return $path; } sub build_environment_vars_for { my $self = $_[0]->new->activate($_[1], { always => 1 }); $self->build_environment_vars; } sub build_activate_environment_vars_for { my $self = $_[0]->new->activate($_[1], { always => 1 }); $self->build_environment_vars; } sub build_deactivate_environment_vars_for { my $self = $_[0]->new->deactivate($_[1]); $self->build_environment_vars; } sub build_deact_all_environment_vars_for { my $self = $_[0]->new->deactivate_all; $self->build_environment_vars; } sub build_environment_vars { my $self = shift; ( PATH => join($_path_sep, _as_list($self->bins)), PERL5LIB => join($_path_sep, _as_list($self->libs)), PERL_LOCAL_LIB_ROOT => join($_path_sep, _as_list($self->roots)), %{$self->extra}, ); } sub setup_local_lib_for { my $self = $_[0]->new->activate($_[1]); $self->setup_local_lib; } sub setup_local_lib { my $self = shift; # if Carp is already loaded, ensure Carp::Heavy is also loaded, to avoid # $VERSION mismatch errors (Carp::Heavy loads Carp, so we do not need to # check in the other direction) require Carp::Heavy if $INC{'Carp.pm'}; $self->setup_env_hash; @INC = @{$self->inc}; } sub setup_env_hash_for { my $self = $_[0]->new->activate($_[1]); $self->setup_env_hash; } sub setup_env_hash { my $self = shift; my %env = $self->build_environment_vars; for my $key (keys %env) { if (defined $env{$key}) { $ENV{$key} = $env{$key}; } else { delete $ENV{$key}; } } } sub print_environment_vars_for { print $_[0]->environment_vars_string_for(@_[1..$#_]); } sub environment_vars_string_for { my $self = $_[0]->new->activate($_[1], { always => 1}); $self->environment_vars_string; } sub environment_vars_string { my ($self, $shelltype) = @_; $shelltype ||= $self->guess_shelltype; my $extra = $self->extra; my @envs = ( PATH => $self->bins, PERL5LIB => $self->libs, PERL_LOCAL_LIB_ROOT => $self->roots, map { $_ => $extra->{$_} } sort keys %$extra, ); $self->_build_env_string($shelltype, \@envs); } sub _build_env_string { my ($self, $shelltype, $envs) = @_; my @envs = @$envs; my $build_method = "build_${shelltype}_env_declaration"; my $out = ''; while (@envs) { my ($name, $value) = (shift(@envs), shift(@envs)); if ( ref $value && @$value == 1 && ref $value->[0] && ref $value->[0] eq 'SCALAR' && ${$value->[0]} eq $name) { next; } $out .= $self->$build_method($name, $value); } my $wrap_method = "wrap_${shelltype}_output"; if ($self->can($wrap_method)) { return $self->$wrap_method($out); } return $out; } sub build_bourne_env_declaration { my ($class, $name, $args) = @_; my $value = $class->_interpolate($args, '${%s:-}', qr/["\\\$!`]/, '\\%s'); if (!defined $value) { return qq{unset $name;\n}; } $value =~ s/(^|\G|$_path_sep)\$\{$name:-\}$_path_sep/$1\${$name}\${$name:+$_path_sep}/g; $value =~ s/$_path_sep\$\{$name:-\}$/\${$name:+$_path_sep\${$name}}/; qq{${name}="$value"; export ${name};\n} } sub build_csh_env_declaration { my ($class, $name, $args) = @_; my ($value, @vars) = $class->_interpolate($args, '${%s}', qr/["\$]/, '"\\%s"'); if (!defined $value) { return qq{unsetenv $name;\n}; } my $out = ''; for my $var (@vars) { $out .= qq{if ! \$?$name setenv $name '';\n}; } my $value_without = $value; if ($value_without =~ s/(?:^|$_path_sep)\$\{$name\}(?:$_path_sep|$)//g) { $out .= qq{if "\${$name}" != '' setenv $name "$value";\n}; $out .= qq{if "\${$name}" == '' }; } $out .= qq{setenv $name "$value_without";\n}; return $out; } sub build_cmd_env_declaration { my ($class, $name, $args) = @_; my $value = $class->_interpolate($args, '%%%s%%', qr(%), '%s'); if (!$value) { return qq{\@set $name=\n}; } my $out = ''; my $value_without = $value; if ($value_without =~ s/(?:^|$_path_sep)%$name%(?:$_path_sep|$)//g) { $out .= qq{\@if not "%$name%"=="" set "$name=$value"\n}; $out .= qq{\@if "%$name%"=="" }; } $out .= qq{\@set "$name=$value_without"\n}; return $out; } sub build_powershell_env_declaration { my ($class, $name, $args) = @_; my $value = $class->_interpolate($args, '$env:%s', qr/["\$]/, '`%s'); if (!$value) { return qq{Remove-Item -ErrorAction 0 Env:\\$name;\n}; } my $maybe_path_sep = qq{\$(if("\$env:$name"-eq""){""}else{"$_path_sep"})}; $value =~ s/(^|\G|$_path_sep)\$env:$name$_path_sep/$1\$env:$name"+$maybe_path_sep+"/g; $value =~ s/$_path_sep\$env:$name$/"+$maybe_path_sep+\$env:$name+"/; qq{\$env:$name = \$("$value");\n}; } sub wrap_powershell_output { my ($class, $out) = @_; return $out || " \n"; } sub build_fish_env_declaration { my ($class, $name, $args) = @_; my $value = $class->_interpolate($args, '$%s', qr/[\\"'$ ]/, '\\%s'); if (!defined $value) { return qq{set -e $name;\n}; } # fish has special handling for PATH, CDPATH, and MANPATH. They are always # treated as arrays, and joined with ; when storing the environment. Other # env vars can be arrays, but will be joined without a separator. We only # really care about PATH, but might as well make this routine more general. if ($name =~ /^(?:CD|MAN)?PATH$/) { $value =~ s/$_path_sep/ /g; my $silent = $name =~ /^(?:CD)?PATH$/ ? " 2>"._devnull : ''; return qq{set -x $name $value$silent;\n}; } my $out = ''; my $value_without = $value; if ($value_without =~ s/(?:^|$_path_sep)\$$name(?:$_path_sep|$)//g) { $out .= qq{set -q $name; and set -x $name $value;\n}; $out .= qq{set -q $name; or }; } $out .= qq{set -x $name $value_without;\n}; $out; } sub _interpolate { my ($class, $args, $var_pat, $escape, $escape_pat) = @_; return unless defined $args; my @args = ref $args ? @$args : $args; return unless @args; my @vars = map { $$_ } grep { ref $_ eq 'SCALAR' } @args; my $string = join $_path_sep, map { ref $_ eq 'SCALAR' ? sprintf($var_pat, $$_) : do { s/($escape)/sprintf($escape_pat, $1)/ge; $_; }; } @args; return wantarray ? ($string, \@vars) : $string; } sub pipeline; sub pipeline { my @methods = @_; my $last = pop(@methods); if (@methods) { \sub { my ($obj, @args) = @_; $obj->${pipeline @methods}( $obj->$last(@args) ); }; } else { \sub { shift->$last(@_); }; } } sub resolve_path { my ($class, $path) = @_; $path = $class->${pipeline qw( resolve_relative_path resolve_home_path resolve_empty_path )}($path); $path; } sub resolve_empty_path { my ($class, $path) = @_; if (defined $path) { $path; } else { '~/perl5'; } } sub resolve_home_path { my ($class, $path) = @_; $path =~ /^~([^\/]*)/ or return $path; my $user = $1; my $homedir = do { if (! length($user) && defined $ENV{HOME}) { $ENV{HOME}; } else { require File::Glob; File::Glob::bsd_glob("~$user", File::Glob::GLOB_TILDE()); } }; unless (defined $homedir) { require Carp; require Carp::Heavy; Carp::croak( "Couldn't resolve homedir for " .(defined $user ? $user : 'current user') ); } $path =~ s/^~[^\/]*/$homedir/; $path; } sub resolve_relative_path { my ($class, $path) = @_; _rel2abs($path); } sub ensure_dir_structure_for { my ($class, $path, $opts) = @_; $opts ||= {}; my @dirs; foreach my $dir ( $class->lib_paths_for($path), $class->install_base_bin_path($path), ) { my $d = $dir; while (!-d $d) { push @dirs, $d; require File::Basename; $d = File::Basename::dirname($d); } } warn "Attempting to create directory ${path}\n" if !$opts->{quiet} && @dirs; my %seen; foreach my $dir (reverse @dirs) { next if $seen{$dir}++; mkdir $dir or -d $dir or die "Unable to create $dir: $!" } return; } sub guess_shelltype { my $shellbin = defined $ENV{SHELL} && length $ENV{SHELL} ? ($ENV{SHELL} =~ /([\w.]+)$/)[-1] : ( $^O eq 'MSWin32' && exists $ENV{'!EXITCODE'} ) ? 'bash' : ( $^O eq 'MSWin32' && $ENV{PROMPT} && $ENV{COMSPEC} ) ? ($ENV{COMSPEC} =~ /([\w.]+)$/)[-1] : ( $^O eq 'MSWin32' && !$ENV{PROMPT} ) ? 'powershell.exe' : 'sh'; for ($shellbin) { return /csh$/ ? 'csh' : /fish$/ ? 'fish' : /command(?:\.com)?$/i ? 'cmd' : /cmd(?:\.exe)?$/i ? 'cmd' : /4nt(?:\.exe)?$/i ? 'cmd' : /powershell(?:\.exe)?$/i ? 'powershell' : 'bourne'; } } 1; __END__ =encoding utf8 =head1 NAME local::lib - create and use a local lib/ for perl modules with PERL5LIB =head1 SYNOPSIS In code - use local::lib; # sets up a local lib at ~/perl5 use local::lib '~/foo'; # same, but ~/foo # Or... use FindBin; use local::lib "$FindBin::Bin/../support"; # app-local support library From the shell - # Install LWP and its missing dependencies to the '~/perl5' directory perl -MCPAN -Mlocal::lib -e 'CPAN::install(LWP)' # Just print out useful shell commands $ perl -Mlocal::lib PERL_MB_OPT='--install_base /home/username/perl5'; export PERL_MB_OPT; PERL_MM_OPT='INSTALL_BASE=/home/username/perl5'; export PERL_MM_OPT; PERL5LIB="/home/username/perl5/lib/perl5"; export PERL5LIB; PATH="/home/username/perl5/bin:$PATH"; export PATH; PERL_LOCAL_LIB_ROOT="/home/usename/perl5:$PERL_LOCAL_LIB_ROOT"; export PERL_LOCAL_LIB_ROOT; From a F<.bash_profile> or F<.bashrc> file - eval "$(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib)" =head2 The bootstrapping technique A typical way to install local::lib is using what is known as the "bootstrapping" technique. You would do this if your system administrator hasn't already installed local::lib. In this case, you'll need to install local::lib in your home directory. Even if you do have administrative privileges, you will still want to set up your environment variables, as discussed in step 4. Without this, you would still install the modules into the system CPAN installation and also your Perl scripts will not use the lib/ path you bootstrapped with local::lib. By default local::lib installs itself and the CPAN modules into ~/perl5. Windows users must also see L</Differences when using this module under Win32>. =over 4 =item 1. Download and unpack the local::lib tarball from CPAN (search for "Download" on the CPAN page about local::lib). Do this as an ordinary user, not as root or administrator. Unpack the file in your home directory or in any other convenient location. =item 2. Run this: perl Makefile.PL --bootstrap If the system asks you whether it should automatically configure as much as possible, you would typically answer yes. =item 3. Run this: (local::lib assumes you have make installed on your system) make test && make install =item 4. Now we need to setup the appropriate environment variables, so that Perl starts using our newly generated lib/ directory. If you are using bash or any other Bourne shells, you can add this to your shell startup script this way: echo 'eval "$(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib)"' >>~/.bashrc If you are using C shell, you can do this as follows: % echo $SHELL /bin/csh $ echo 'eval `perl -I$HOME/perl5/lib/perl5 -Mlocal::lib`' >> ~/.cshrc After writing your shell configuration file, be sure to re-read it to get the changed settings into your current shell's environment. Bourne shells use C<. ~/.bashrc> for this, whereas C shells use C<source ~/.cshrc>. =back =head3 Bootstrapping into an alternate directory In order to install local::lib into a directory other than the default, you need to specify the name of the directory when you call bootstrap. Then, when setting up the environment variables, both perl and local::lib must be told the location of the bootstrap directory. The setup process would look as follows: perl Makefile.PL --bootstrap=~/foo make test && make install echo 'eval "$(perl -I$HOME/foo/lib/perl5 -Mlocal::lib=$HOME/foo)"' >>~/.bashrc . ~/.bashrc =head3 Other bootstrapping options If you're on a slower machine, or are operating under draconian disk space limitations, you can disable the automatic generation of manpages from POD when installing modules by using the C<--no-manpages> argument when bootstrapping: perl Makefile.PL --bootstrap --no-manpages To avoid doing several bootstrap for several Perl module environments on the same account, for example if you use it for several different deployed applications independently, you can use one bootstrapped local::lib installation to install modules in different directories directly this way: cd ~/mydir1 perl -Mlocal::lib=./ eval $(perl -Mlocal::lib=./) ### To set the environment for this shell alone printenv ### You will see that ~/mydir1 is in the PERL5LIB perl -MCPAN -e install ... ### whatever modules you want cd ../mydir2 ... REPEAT ... If you use F<.bashrc> to activate a local::lib automatically, the local::lib will be re-enabled in any sub-shells used, overriding adjustments you may have made in the parent shell. To avoid this, you can initialize the local::lib in F<.bash_profile> rather than F<.bashrc>, or protect the local::lib invocation with a C<$SHLVL> check: [ $SHLVL -eq 1 ] && eval "$(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib)" If you are working with several C<local::lib> environments, you may want to remove some of them from the current environment without disturbing the others. You can deactivate one environment like this (using bourne sh): eval $(perl -Mlocal::lib=--deactivate,~/path) which will generate and run the commands needed to remove C<~/path> from your various search paths. Whichever environment was B<activated most recently> will remain the target for module installations. That is, if you activate C<~/path_A> and then you activate C<~/path_B>, new modules you install will go in C<~/path_B>. If you deactivate C<~/path_B> then modules will be installed into C<~/pathA> -- but if you deactivate C<~/path_A> then they will still be installed in C<~/pathB> because pathB was activated later. You can also ask C<local::lib> to clean itself completely out of the current shell's environment with the C<--deactivate-all> option. For multiple environments for multiple apps you may need to include a modified version of the C<< use FindBin >> instructions in the "In code" sample above. If you did something like the above, you have a set of Perl modules at C<< ~/mydir1/lib >>. If you have a script at C<< ~/mydir1/scripts/myscript.pl >>, you need to tell it where to find the modules you installed for it at C<< ~/mydir1/lib >>. In C<< ~/mydir1/scripts/myscript.pl >>: use strict; use warnings; use local::lib "$FindBin::Bin/.."; ### points to ~/mydir1 and local::lib finds lib use lib "$FindBin::Bin/../lib"; ### points to ~/mydir1/lib Put this before any BEGIN { ... } blocks that require the modules you installed. =head2 Differences when using this module under Win32 To set up the proper environment variables for your current session of C<CMD.exe>, you can use this: C:\>perl -Mlocal::lib set PERL_MB_OPT=--install_base C:\DOCUME~1\ADMINI~1\perl5 set PERL_MM_OPT=INSTALL_BASE=C:\DOCUME~1\ADMINI~1\perl5 set PERL5LIB=C:\DOCUME~1\ADMINI~1\perl5\lib\perl5 set PATH=C:\DOCUME~1\ADMINI~1\perl5\bin;%PATH% ### To set the environment for this shell alone C:\>perl -Mlocal::lib > %TEMP%\tmp.bat && %TEMP%\tmp.bat && del %TEMP%\tmp.bat ### instead of $(perl -Mlocal::lib=./) If you want the environment entries to persist, you'll need to add them to the Control Panel's System applet yourself or use L<App::local::lib::Win32Helper>. The "~" is translated to the user's profile directory (the directory named for the user under "Documents and Settings" (Windows XP or earlier) or "Users" (Windows Vista or later)) unless $ENV{HOME} exists. After that, the home directory is translated to a short name (which means the directory must exist) and the subdirectories are created. =head3 PowerShell local::lib also supports PowerShell, and can be used with the C<Invoke-Expression> cmdlet. Invoke-Expression "$(perl -Mlocal::lib)" =head1 RATIONALE The version of a Perl package on your machine is not always the version you need. Obviously, the best thing to do would be to update to the version you need. However, you might be in a situation where you're prevented from doing this. Perhaps you don't have system administrator privileges; or perhaps you are using a package management system such as Debian, and nobody has yet gotten around to packaging up the version you need. local::lib solves this problem by allowing you to create your own directory of Perl packages downloaded from CPAN (in a multi-user system, this would typically be within your own home directory). The existing system Perl installation is not affected; you simply invoke Perl with special options so that Perl uses the packages in your own local package directory rather than the system packages. local::lib arranges things so that your locally installed version of the Perl packages takes precedence over the system installation. If you are using a package management system (such as Debian), you don't need to worry about Debian and CPAN stepping on each other's toes. Your local version of the packages will be written to an entirely separate directory from those installed by Debian. =head1 DESCRIPTION This module provides a quick, convenient way of bootstrapping a user-local Perl module library located within the user's home directory. It also constructs and prints out for the user the list of environment variables using the syntax appropriate for the user's current shell (as specified by the C<SHELL> environment variable), suitable for directly adding to one's shell configuration file. More generally, local::lib allows for the bootstrapping and usage of a directory containing Perl modules outside of Perl's C<@INC>. This makes it easier to ship an application with an app-specific copy of a Perl module, or collection of modules. Useful in cases like when an upstream maintainer hasn't applied a patch to a module of theirs that you need for your application. On import, local::lib sets the following environment variables to appropriate values: =over 4 =item PERL_MB_OPT =item PERL_MM_OPT =item PERL5LIB =item PATH =item PERL_LOCAL_LIB_ROOT =back When possible, these will be appended to instead of overwritten entirely. These values are then available for reference by any code after import. =head1 CREATING A SELF-CONTAINED SET OF MODULES See L<lib::core::only> for one way to do this - but note that there are a number of caveats, and the best approach is always to perform a build against a clean perl (i.e. site and vendor as close to empty as possible). =head1 IMPORT OPTIONS Options are values that can be passed to the C<local::lib> import besides the directory to use. They are specified as C<use local::lib '--option'[, path];> or C<perl -Mlocal::lib=--option[,path]>. =head2 --deactivate Remove the chosen path (or the default path) from the module search paths if it was added by C<local::lib>, instead of adding it. =head2 --deactivate-all Remove all directories that were added to search paths by C<local::lib> from the search paths. =head2 --quiet Don't output any messages about directories being created. =head2 --always Always add directories to environment variables, ignoring if they are already included. =head2 --shelltype Specify the shell type to use for output. By default, the shell will be detected based on the environment. Should be one of: C<bourne>, C<csh>, C<cmd>, or C<powershell>. =head2 --no-create Prevents C<local::lib> from creating directories when activating dirs. This is likely to cause issues on Win32 systems. =head1 CLASS METHODS =head2 ensure_dir_structure_for =over 4 =item Arguments: $path =item Return value: None =back Attempts to create a local::lib directory, including subdirectories and all required parent directories. Throws an exception on failure. =head2 print_environment_vars_for =over 4 =item Arguments: $path =item Return value: None =back Prints to standard output the variables listed above, properly set to use the given path as the base directory. =head2 build_environment_vars_for =over 4 =item Arguments: $path =item Return value: %environment_vars =back Returns a hash with the variables listed above, properly set to use the given path as the base directory. =head2 setup_env_hash_for =over 4 =item Arguments: $path =item Return value: None =back Constructs the C<%ENV> keys for the given path, by calling L</build_environment_vars_for>. =head2 active_paths =over 4 =item Arguments: None =item Return value: @paths =back Returns a list of active C<local::lib> paths, according to the C<PERL_LOCAL_LIB_ROOT> environment variable and verified against what is really in C<@INC>. =head2 install_base_perl_path =over 4 =item Arguments: $path =item Return value: $install_base_perl_path =back Returns a path describing where to install the Perl modules for this local library installation. Appends the directories C<lib> and C<perl5> to the given path. =head2 lib_paths_for =over 4 =item Arguments: $path =item Return value: @lib_paths =back Returns the list of paths perl will search for libraries, given a base path. This includes the base path itself, the architecture specific subdirectory, and perl version specific subdirectories. These paths may not all exist. =head2 install_base_bin_path =over 4 =item Arguments: $path =item Return value: $install_base_bin_path =back Returns a path describing where to install the executable programs for this local library installation. Appends the directory C<bin> to the given path. =head2 installer_options_for =over 4 =item Arguments: $path =item Return value: %installer_env_vars =back Returns a hash of environment variables that should be set to cause installation into the given path. =head2 resolve_empty_path =over 4 =item Arguments: $path =item Return value: $base_path =back Builds and returns the base path into which to set up the local module installation. Defaults to C<~/perl5>. =head2 resolve_home_path =over 4 =item Arguments: $path =item Return value: $home_path =back Attempts to find the user's home directory. If no definite answer is available, throws an exception. =head2 resolve_relative_path =over 4 =item Arguments: $path =item Return value: $absolute_path =back Translates the given path into an absolute path. =head2 resolve_path =over 4 =item Arguments: $path =item Return value: $absolute_path =back Calls the following in a pipeline, passing the result from the previous to the next, in an attempt to find where to configure the environment for a local library installation: L</resolve_empty_path>, L</resolve_home_path>, L</resolve_relative_path>. Passes the given path argument to L</resolve_empty_path> which then returns a result that is passed to L</resolve_home_path>, which then has its result passed to L</resolve_relative_path>. The result of this final call is returned from L</resolve_path>. =head1 OBJECT INTERFACE =head2 new =over 4 =item Arguments: %attributes =item Return value: $local_lib =back Constructs a new C<local::lib> object, representing the current state of C<@INC> and the relevant environment variables. =head1 ATTRIBUTES =head2 roots An arrayref representing active C<local::lib> directories. =head2 inc An arrayref representing C<@INC>. =head2 libs An arrayref representing the PERL5LIB environment variable. =head2 bins An arrayref representing the PATH environment variable. =head2 extra A hashref of extra environment variables (e.g. C<PERL_MM_OPT> and C<PERL_MB_OPT>) =head2 no_create If set, C<local::lib> will not try to create directories when activating them. =head1 OBJECT METHODS =head2 clone =over 4 =item Arguments: %attributes =item Return value: $local_lib =back Constructs a new C<local::lib> object based on the existing one, overriding the specified attributes. =head2 activate =over 4 =item Arguments: $path =item Return value: $new_local_lib =back Constructs a new instance with the specified path active. =head2 deactivate =over 4 =item Arguments: $path =item Return value: $new_local_lib =back Constructs a new instance with the specified path deactivated. =head2 deactivate_all =over 4 =item Arguments: None =item Return value: $new_local_lib =back Constructs a new instance with all C<local::lib> directories deactivated. =head2 environment_vars_string =over 4 =item Arguments: [ $shelltype ] =item Return value: $shell_env_string =back Returns a string to set up the C<local::lib>, meant to be run by a shell. =head2 build_environment_vars =over 4 =item Arguments: None =item Return value: %environment_vars =back Returns a hash with the variables listed above, properly set to use the given path as the base directory. =head2 setup_env_hash =over 4 =item Arguments: None =item Return value: None =back Constructs the C<%ENV> keys for the given path, by calling L</build_environment_vars>. =head2 setup_local_lib Constructs the C<%ENV> hash using L</setup_env_hash>, and set up C<@INC>. =head1 A WARNING ABOUT UNINST=1 Be careful about using local::lib in combination with "make install UNINST=1". The idea of this feature is that will uninstall an old version of a module before installing a new one. However it lacks a safety check that the old version and the new version will go in the same directory. Used in combination with local::lib, you can potentially delete a globally accessible version of a module while installing the new version in a local place. Only combine "make install UNINST=1" and local::lib if you understand these possible consequences. =head1 LIMITATIONS =over 4 =item * Directory names with spaces in them are not well supported by the perl toolchain and the programs it uses. Pure-perl distributions should support spaces, but problems are more likely with dists that require compilation. A workaround you can do is moving your local::lib to a directory with spaces B<after> you installed all modules inside your local::lib bootstrap. But be aware that you can't update or install CPAN modules after the move. =item * Rather basic shell detection. Right now anything with csh in its name is assumed to be a C shell or something compatible, and everything else is assumed to be Bourne, except on Win32 systems. If the C<SHELL> environment variable is not set, a Bourne-compatible shell is assumed. =item * Kills any existing PERL_MM_OPT or PERL_MB_OPT. =item * Should probably auto-fixup CPAN config if not already done. =item * On VMS and MacOS Classic (pre-OS X), local::lib loads L<File::Spec>. This means any L<File::Spec> version installed in the local::lib will be ignored by scripts using local::lib. A workaround for this is using C<use lib "$local_lib/lib/perl5";> instead of using C<local::lib> directly. =item * Conflicts with L<ExtUtils::MakeMaker>'s C<PREFIX> option. C<local::lib> uses the C<INSTALL_BASE> option, as it has more predictable and sane behavior. If something attempts to use the C<PREFIX> option when running a F<Makefile.PL>, L<ExtUtils::MakeMaker> will refuse to run, as the two options conflict. This can be worked around by temporarily unsetting the C<PERL_MM_OPT> environment variable. =item * Conflicts with L<Module::Build>'s C<--prefix> option. Similar to the previous limitation, but any C<--prefix> option specified will be ignored. This can be worked around by temporarily unsetting the C<PERL_MB_OPT> environment variable. =back Patches very much welcome for any of the above. =over 4 =item * On Win32 systems, does not have a way to write the created environment variables to the registry, so that they can persist through a reboot. =back =head1 TROUBLESHOOTING If you've configured local::lib to install CPAN modules somewhere in to your home directory, and at some point later you try to install a module with C<cpan -i Foo::Bar>, but it fails with an error like: C<Warning: You do not have permissions to install into /usr/lib64/perl5/site_perl/5.8.8/x86_64-linux at /usr/lib64/perl5/5.8.8/Foo/Bar.pm> and buried within the install log is an error saying C<'INSTALL_BASE' is not a known MakeMaker parameter name>, then you've somehow lost your updated ExtUtils::MakeMaker module. To remedy this situation, rerun the bootstrapping procedure documented above. Then, run C<rm -r ~/.cpan/build/Foo-Bar*> Finally, re-run C<cpan -i Foo::Bar> and it should install without problems. =head1 ENVIRONMENT =over 4 =item SHELL =item COMSPEC local::lib looks at the user's C<SHELL> environment variable when printing out commands to add to the shell configuration file. On Win32 systems, C<COMSPEC> is also examined. =back =head1 SEE ALSO =over 4 =item * L<Perl Advent article, 2011|http://perladvent.org/2011/2011-12-01.html> =back =head1 SUPPORT IRC: Join #toolchain on irc.perl.org. =head1 AUTHOR Matt S Trout <mst@shadowcat.co.uk> http://www.shadowcat.co.uk/ auto_install fixes kindly sponsored by http://www.takkle.com/ =head1 CONTRIBUTORS Patches to correctly output commands for csh style shells, as well as some documentation additions, contributed by Christopher Nehren <apeiron@cpan.org>. Doc patches for a custom local::lib directory, more cleanups in the english documentation and a L<german documentation|POD2::DE::local::lib> contributed by Torsten Raudssus <torsten@raudssus.de>. Hans Dieter Pearcey <hdp@cpan.org> sent in some additional tests for ensuring things will install properly, submitted a fix for the bug causing problems with writing Makefiles during bootstrapping, contributed an example program, and submitted yet another fix to ensure that local::lib can install and bootstrap properly. Many, many thanks! pattern of Freenode IRC contributed the beginnings of the Troubleshooting section. Many thanks! Patch to add Win32 support contributed by Curtis Jewell <csjewell@cpan.org>. Warnings for missing PATH/PERL5LIB (as when not running interactively) silenced by a patch from Marco Emilio Poleggi. Mark Stosberg <mark@summersault.com> provided the code for the now deleted '--self-contained' option. Documentation patches to make win32 usage clearer by David Mertens <dcmertens.perl@gmail.com> (run4flat). Brazilian L<portuguese translation|POD2::PT_BR::local::lib> and minor doc patches contributed by Breno G. de Oliveira <garu@cpan.org>. Improvements to stacking multiple local::lib dirs and removing them from the environment later on contributed by Andrew Rodland <arodland@cpan.org>. Patch for Carp version mismatch contributed by Hakim Cassimally <osfameron@cpan.org>. Rewrite of internals and numerous bug fixes and added features contributed by Graham Knop <haarg@haarg.org>. =head1 COPYRIGHT Copyright (c) 2007 - 2013 the local::lib L</AUTHOR> and L</CONTRIBUTORS> as listed above. =head1 LICENSE This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut PK�����A0]H�A�A� ��perl5/CPAN.pmnu�6$��������# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: use strict; package CPAN; $CPAN::VERSION = '2.36'; $CPAN::VERSION =~ s/_//; # we need to run chdir all over and we would get at wrong libraries # there use File::Spec (); BEGIN { if (File::Spec->can("rel2abs")) { for my $inc (@INC) { $inc = File::Spec->rel2abs($inc) unless ref $inc; } } $SIG{WINCH} = 'IGNORE' if exists $SIG{WINCH}; } use CPAN::Author; use CPAN::HandleConfig; use CPAN::Version; use CPAN::Bundle; use CPAN::CacheMgr; use CPAN::Complete; use CPAN::Debug; use CPAN::Distribution; use CPAN::Distrostatus; use CPAN::FTP; use CPAN::Index 1.93; # https://rt.cpan.org/Ticket/Display.html?id=43349 use CPAN::InfoObj; use CPAN::Module; use CPAN::Prompt; use CPAN::URL; use CPAN::Queue; use CPAN::Tarzip; use CPAN::DeferredCode; use CPAN::Shell; use CPAN::LWP::UserAgent; use CPAN::Exception::RecursiveDependency; use CPAN::Exception::yaml_not_installed; use CPAN::Exception::yaml_process_error; use Carp (); use Config (); use Cwd qw(chdir); use DirHandle (); use Exporter (); use ExtUtils::MakeMaker qw(prompt); # for some unknown reason, # 5.005_04 does not work without # this use File::Basename (); use File::Copy (); use File::Find; use File::Path (); use FileHandle (); use Fcntl qw(:flock); use Safe (); use Sys::Hostname qw(hostname); use Text::ParseWords (); use Text::Wrap (); # protect against "called too early" sub find_perl (); sub anycwd (); sub _uniq; no lib "."; require Mac::BuildTools if $^O eq 'MacOS'; if ($ENV{PERL5_CPAN_IS_RUNNING} && $$ != $ENV{PERL5_CPAN_IS_RUNNING}) { $ENV{PERL5_CPAN_IS_RUNNING_IN_RECURSION} ||= $ENV{PERL5_CPAN_IS_RUNNING}; my @rec = _uniq split(/,/, $ENV{PERL5_CPAN_IS_RUNNING_IN_RECURSION}), $$; $ENV{PERL5_CPAN_IS_RUNNING_IN_RECURSION} = join ",", @rec; # warn "# Note: Recursive call of CPAN.pm detected\n"; my $w = sprintf "# Note: CPAN.pm is running in process %d now", pop @rec; my %sleep = ( 5 => 30, 6 => 60, 7 => 120, ); my $sleep = @rec > 7 ? 300 : ($sleep{scalar @rec}||0); my $verbose = @rec >= 4; while (@rec) { $w .= sprintf " which has been called by process %d", pop @rec; } if ($sleep) { $w .= ".\n\n# Sleeping $sleep seconds to protect other processes\n"; } if ($verbose) { warn $w; } local $| = 1; my $have_been_sleeping = 0; while ($sleep > 0) { printf "\r#%5d", --$sleep; sleep 1; ++$have_been_sleeping; } print "\n" if $have_been_sleeping; } $ENV{PERL5_CPAN_IS_RUNNING}=$$; $ENV{PERL5_CPANPLUS_IS_RUNNING}=$$; # https://rt.cpan.org/Ticket/Display.html?id=23735 END { $CPAN::End++; &cleanup; } $CPAN::Signal ||= 0; $CPAN::Frontend ||= "CPAN::Shell"; unless (@CPAN::Defaultsites) { @CPAN::Defaultsites = map { CPAN::URL->new(TEXT => $_, FROM => "DEF") } "http://www.perl.org/CPAN/", "ftp://ftp.perl.org/pub/CPAN/"; } # $CPAN::iCwd (i for initial) $CPAN::iCwd ||= CPAN::anycwd(); $CPAN::Perl ||= CPAN::find_perl(); $CPAN::Defaultdocs ||= "http://search.cpan.org/perldoc?"; $CPAN::Defaultrecent ||= "http://search.cpan.org/uploads.rdf"; $CPAN::Defaultrecent ||= "http://cpan.uwinnipeg.ca/htdocs/cpan.xml"; # our globals are getting a mess use vars qw( $AUTOLOAD $Be_Silent $CONFIG_DIRTY $Defaultdocs $Echo_readline $Frontend $GOTOSHELL $HAS_USABLE $Have_warned $MAX_RECURSION $META $RUN_DEGRADED $Signal $SQLite $Suppress_readline $VERSION $autoload_recursion $term @Defaultsites @EXPORT ); $MAX_RECURSION = 32; @CPAN::ISA = qw(CPAN::Debug Exporter); # note that these functions live in CPAN::Shell and get executed via # AUTOLOAD when called directly @EXPORT = qw( autobundle bundle clean cvs_import expand force fforce get install install_tested is_tested make mkmyconfig notest perldoc readme recent recompile report shell smoke test upgrade ); sub soft_chdir_with_alternatives ($); { $autoload_recursion ||= 0; #-> sub CPAN::AUTOLOAD ; sub AUTOLOAD { ## no critic $autoload_recursion++; my($l) = $AUTOLOAD; $l =~ s/.*:://; if ($CPAN::Signal) { warn "Refusing to autoload '$l' while signal pending"; $autoload_recursion--; return; } if ($autoload_recursion > 1) { my $fullcommand = join " ", map { "'$_'" } $l, @_; warn "Refusing to autoload $fullcommand in recursion\n"; $autoload_recursion--; return; } my(%export); @export{@EXPORT} = ''; CPAN::HandleConfig->load unless $CPAN::Config_loaded++; if (exists $export{$l}) { CPAN::Shell->$l(@_); } else { die(qq{Unknown CPAN command "$AUTOLOAD". }. qq{Type ? for help.\n}); } $autoload_recursion--; } } { my $x = *SAVEOUT; # avoid warning open($x,">&STDOUT") or die "dup failed"; my $redir = 0; sub _redirect(@) { #die if $redir; local $_; push(@_,undef); while(defined($_=shift)) { if (s/^\s*>//){ my ($m) = s/^>// ? ">" : ""; s/\s+//; $_=shift unless length; die "no dest" unless defined; open(STDOUT,">$m$_") or die "open:$_:$!\n"; $redir=1; } elsif ( s/^\s*\|\s*// ) { my $pipe="| $_"; while(defined($_[0])){ $pipe .= ' ' . shift; } open(STDOUT,$pipe) or die "open:$pipe:$!\n"; $redir=1; } else { push(@_,$_); } } return @_; } sub _unredirect { return unless $redir; $redir = 0; ## redirect: unredirect and propagate errors. explicit close to wait for pipe. close(STDOUT); open(STDOUT,">&SAVEOUT"); die "$@" if "$@"; ## redirect: done } } sub _uniq { my(@list) = @_; my %seen; return grep { !$seen{$_}++ } @list; } #-> sub CPAN::shell ; sub shell { my($self) = @_; $Suppress_readline = ! -t STDIN unless defined $Suppress_readline; CPAN::HandleConfig->load unless $CPAN::Config_loaded++; my $oprompt = shift || CPAN::Prompt->new; my $prompt = $oprompt; my $commandline = shift || ""; $CPAN::CurrentCommandId ||= 1; local($^W) = 1; unless ($Suppress_readline) { require Term::ReadLine; if (! $term or $term->ReadLine eq "Term::ReadLine::Stub" ) { $term = Term::ReadLine->new('CPAN Monitor'); } if ($term->ReadLine eq "Term::ReadLine::Gnu") { my $attribs = $term->Attribs; $attribs->{attempted_completion_function} = sub { &CPAN::Complete::gnu_cpl; } } else { $readline::rl_completion_function = $readline::rl_completion_function = 'CPAN::Complete::cpl'; } if (my $histfile = $CPAN::Config->{'histfile'}) {{ unless ($term->can("AddHistory")) { $CPAN::Frontend->mywarn("Terminal does not support AddHistory.\n"); unless ($CPAN::META->has_inst('Term::ReadLine::Perl')) { $CPAN::Frontend->mywarn("\nTo fix that, maybe try> install Term::ReadLine::Perl\n\n"); } last; } $META->readhist($term,$histfile); }} for ($CPAN::Config->{term_ornaments}) { # alias local $Term::ReadLine::termcap_nowarn = 1; $term->ornaments($_) if defined; } # $term->OUT is autoflushed anyway my $odef = select STDERR; $| = 1; select STDOUT; $| = 1; select $odef; } $META->checklock(); my @cwd = grep { defined $_ and length $_ } CPAN::anycwd(), File::Spec->can("tmpdir") ? File::Spec->tmpdir() : (), File::Spec->rootdir(); my $try_detect_readline; $try_detect_readline = $term->ReadLine eq "Term::ReadLine::Stub" if $term; unless ($CPAN::Config->{inhibit_startup_message}) { my $rl_avail = $Suppress_readline ? "suppressed" : ($term->ReadLine ne "Term::ReadLine::Stub") ? "enabled" : "available (maybe install Bundle::CPAN or Bundle::CPANxxl?)"; $CPAN::Frontend->myprint( sprintf qq{ cpan shell -- CPAN exploration and modules installation (v%s) Enter 'h' for help. }, $CPAN::VERSION, ) } my($continuation) = ""; my $last_term_ornaments; SHELLCOMMAND: while () { if ($Suppress_readline) { if ($Echo_readline) { $|=1; } print $prompt; last SHELLCOMMAND unless defined ($_ = <> ); if ($Echo_readline) { # backdoor: I could not find a way to record sessions print $_; } chomp; } else { last SHELLCOMMAND unless defined ($_ = $term->readline($prompt, $commandline)); } $_ = "$continuation$_" if $continuation; s/^\s+//; next SHELLCOMMAND if /^$/; s/^\s*\?\s*/help /; if (/^(?:q(?:uit)?|bye|exit)\s*$/i) { last SHELLCOMMAND; } elsif (s/\\$//s) { chomp; $continuation = $_; $prompt = " > "; } elsif (/^\!/) { s/^\!//; my($eval) = $_; package CPAN::Eval; # hide from the indexer use strict; use vars qw($import_done); CPAN->import(':DEFAULT') unless $import_done++; CPAN->debug("eval[$eval]") if $CPAN::DEBUG; eval($eval); warn $@ if $@; $continuation = ""; $prompt = $oprompt; } elsif (/./) { my(@line); eval { @line = Text::ParseWords::shellwords($_) }; warn($@), next SHELLCOMMAND if $@; warn("Text::Parsewords could not parse the line [$_]"), next SHELLCOMMAND unless @line; $CPAN::META->debug("line[".join("|",@line)."]") if $CPAN::DEBUG; my $command = shift @line; eval { local (*STDOUT)=*STDOUT; @line = _redirect(@line); CPAN::Shell->$command(@line) }; my $command_error = $@; _unredirect; my $reported_error; if ($command_error) { my $err = $command_error; if (ref $err and $err->isa('CPAN::Exception::blocked_urllist')) { $CPAN::Frontend->mywarn("Client not fully configured, please proceed with configuring.$err"); $reported_error = ref $err; } else { # I'd prefer never to arrive here and make all errors exception objects if ($err =~ /\S/) { require Carp; require Dumpvalue; my $dv = Dumpvalue->new(tick => '"'); Carp::cluck(sprintf "Catching error: %s", $dv->stringify($err)); } } } if ($command =~ /^( # classic commands make |test |install |clean # pragmas for classic commands |ff?orce |notest # compounds |report |smoke |upgrade )$/x) { # only commands that tell us something about failed distros # eval necessary for people without an urllist eval {CPAN::Shell->failed($CPAN::CurrentCommandId,1);}; if (my $err = $@) { unless (ref $err and $reported_error eq ref $err) { die $@; } } } soft_chdir_with_alternatives(\@cwd); $CPAN::Frontend->myprint("\n"); $continuation = ""; $CPAN::CurrentCommandId++; $prompt = $oprompt; } } continue { $commandline = ""; # I do want to be able to pass a default to # shell, but on the second command I see no # use in that $Signal=0; CPAN::Queue->nullify_queue; if ($try_detect_readline) { if ($CPAN::META->has_inst("Term::ReadLine::Gnu") || $CPAN::META->has_inst("Term::ReadLine::Perl") ) { delete $INC{"Term/ReadLine.pm"}; my $redef = 0; local($SIG{__WARN__}) = CPAN::Shell::paintdots_onreload(\$redef); require Term::ReadLine; $CPAN::Frontend->myprint("\n$redef subroutines in ". "Term::ReadLine redefined\n"); $GOTOSHELL = 1; } } if ($term and $term->can("ornaments")) { for ($CPAN::Config->{term_ornaments}) { # alias if (defined $_) { if (not defined $last_term_ornaments or $_ != $last_term_ornaments ) { local $Term::ReadLine::termcap_nowarn = 1; $term->ornaments($_); $last_term_ornaments = $_; } } else { undef $last_term_ornaments; } } } for my $class (qw(Module Distribution)) { # again unsafe meta access? for my $dm (sort keys %{$CPAN::META->{readwrite}{"CPAN::$class"}}) { next unless $CPAN::META->{readwrite}{"CPAN::$class"}{$dm}{incommandcolor}; CPAN->debug("BUG: $class '$dm' was in command state, resetting"); delete $CPAN::META->{readwrite}{"CPAN::$class"}{$dm}{incommandcolor}; } } if ($GOTOSHELL) { $GOTOSHELL = 0; # not too often $META->savehist if $CPAN::term && $CPAN::term->can("GetHistory"); @_ = ($oprompt,""); goto &shell; } } soft_chdir_with_alternatives(\@cwd); } #-> CPAN::soft_chdir_with_alternatives ; sub soft_chdir_with_alternatives ($) { my($cwd) = @_; unless (@$cwd) { my $root = File::Spec->rootdir(); $CPAN::Frontend->mywarn(qq{Warning: no good directory to chdir to! Trying '$root' as temporary haven. }); push @$cwd, $root; } while () { if (chdir "$cwd->[0]") { return; } else { if (@$cwd>1) { $CPAN::Frontend->mywarn(qq{Could not chdir to "$cwd->[0]": $! Trying to chdir to "$cwd->[1]" instead. }); shift @$cwd; } else { $CPAN::Frontend->mydie(qq{Could not chdir to "$cwd->[0]": $!}); } } } } sub _flock { my($fh,$mode) = @_; if ( $Config::Config{d_flock} || $Config::Config{d_fcntl_can_lock} ) { return flock $fh, $mode; } elsif (!$Have_warned->{"d_flock"}++) { $CPAN::Frontend->mywarn("Your OS does not seem to support locking; continuing and ignoring all locking issues\n"); $CPAN::Frontend->mysleep(5); return 1; } else { return 1; } } sub _yaml_module () { my $yaml_module = $CPAN::Config->{yaml_module} || "YAML"; if ( $yaml_module ne "YAML" && !$CPAN::META->has_inst($yaml_module) ) { # $CPAN::Frontend->mywarn("'$yaml_module' not installed, falling back to 'YAML'\n"); $yaml_module = "YAML"; } if ($yaml_module eq "YAML" && $CPAN::META->has_inst($yaml_module) && $YAML::VERSION < 0.60 && !$Have_warned->{"YAML"}++ ) { $CPAN::Frontend->mywarn("Warning: YAML version '$YAML::VERSION' is too low, please upgrade!\n". "I'll continue but problems are *very* likely to happen.\n" ); $CPAN::Frontend->mysleep(5); } return $yaml_module; } # CPAN::_yaml_loadfile sub _yaml_loadfile { my($self,$local_file,$opt) = @_; return +[] unless -s $local_file; my $opt_loadblessed = $opt->{loadblessed} || $CPAN::Config->{yaml_load_code} || 0; my $yaml_module = _yaml_module; if ($CPAN::META->has_inst($yaml_module)) { # temporarily enable yaml code deserialisation no strict 'refs'; # 5.6.2 could not do the local() with the reference # so we do it manually instead my $old_loadcode = ${"$yaml_module\::LoadCode"}; my $old_loadblessed = ${"$yaml_module\::LoadBlessed"}; ${ "$yaml_module\::LoadCode" } = $CPAN::Config->{yaml_load_code} || 0; ${ "$yaml_module\::LoadBlessed" } = $opt_loadblessed ? 1 : 0; my ($code, @yaml); if ($code = UNIVERSAL::can($yaml_module, "LoadFile")) { eval { @yaml = $code->($local_file); }; if ($@) { # this shall not be done by the frontend die CPAN::Exception::yaml_process_error->new($yaml_module,$local_file,"parse",$@); } } elsif ($code = UNIVERSAL::can($yaml_module, "Load")) { local *FH; if (open FH, $local_file) { local $/; my $ystream = <FH>; eval { @yaml = $code->($ystream); }; if ($@) { # this shall not be done by the frontend die CPAN::Exception::yaml_process_error->new($yaml_module,$local_file,"parse",$@); } } else { $CPAN::Frontend->mywarn("Could not open '$local_file': $!"); } } ${"$yaml_module\::LoadCode"} = $old_loadcode; ${"$yaml_module\::LoadBlessed"} = $old_loadblessed; return \@yaml; } else { # this shall not be done by the frontend die CPAN::Exception::yaml_not_installed->new($yaml_module, $local_file, "parse"); } return +[]; } # CPAN::_yaml_dumpfile sub _yaml_dumpfile { my($self,$local_file,@what) = @_; my $yaml_module = _yaml_module; if ($CPAN::META->has_inst($yaml_module)) { my $code; if (UNIVERSAL::isa($local_file, "FileHandle")) { $code = UNIVERSAL::can($yaml_module, "Dump"); eval { print $local_file $code->(@what) }; } elsif ($code = UNIVERSAL::can($yaml_module, "DumpFile")) { eval { $code->($local_file,@what); }; } elsif ($code = UNIVERSAL::can($yaml_module, "Dump")) { local *FH; open FH, ">$local_file" or die "Could not open '$local_file': $!"; print FH $code->(@what); } if ($@) { die CPAN::Exception::yaml_process_error->new($yaml_module,$local_file,"dump",$@); } } else { if (UNIVERSAL::isa($local_file, "FileHandle")) { # I think this case does not justify a warning at all } else { die CPAN::Exception::yaml_not_installed->new($yaml_module, $local_file, "dump"); } } } sub _init_sqlite () { unless ($CPAN::META->has_inst("CPAN::SQLite")) { $CPAN::Frontend->mywarn(qq{CPAN::SQLite not installed, trying to work without\n}) unless $Have_warned->{"CPAN::SQLite"}++; return; } require CPAN::SQLite::META; # not needed since CVS version of 2006-12-17 $CPAN::SQLite ||= CPAN::SQLite::META->new($CPAN::META); } { my $negative_cache = {}; sub _sqlite_running { if ($negative_cache->{time} && time < $negative_cache->{time} + 60) { # need to cache the result, otherwise too slow return $negative_cache->{fact}; } else { $negative_cache = {}; # reset } my $ret = $CPAN::Config->{use_sqlite} && ($CPAN::SQLite || _init_sqlite()); return $ret if $ret; # fast anyway $negative_cache->{time} = time; return $negative_cache->{fact} = $ret; } } $META ||= CPAN->new; # In case we re-eval ourselves we need the || # from here on only subs. ################################################################################ sub _perl_fingerprint { my($self,$other_fingerprint) = @_; my $dll = eval {OS2::DLLname()}; my $mtime_dll = 0; if (defined $dll) { $mtime_dll = (-f $dll ? (stat(_))[9] : '-1'); } my $mtime_perl = (-f CPAN::find_perl ? (stat(_))[9] : '-1'); my $this_fingerprint = { '$^X' => CPAN::find_perl, sitearchexp => $Config::Config{sitearchexp}, 'mtime_$^X' => $mtime_perl, 'mtime_dll' => $mtime_dll, }; if ($other_fingerprint) { if (exists $other_fingerprint->{'stat($^X)'}) { # repair fp from rev. 1.88_57 $other_fingerprint->{'mtime_$^X'} = $other_fingerprint->{'stat($^X)'}[9]; } # mandatory keys since 1.88_57 for my $key (qw($^X sitearchexp mtime_dll mtime_$^X)) { return unless $other_fingerprint->{$key} eq $this_fingerprint->{$key}; } return 1; } else { return $this_fingerprint; } } sub suggest_myconfig () { SUGGEST_MYCONFIG: if(!$INC{'CPAN/MyConfig.pm'}) { $CPAN::Frontend->myprint("You don't seem to have a user ". "configuration (MyConfig.pm) yet.\n"); my $new = CPAN::Shell::colorable_makemaker_prompt("Do you want to create a ". "user configuration now? (Y/n)", "yes"); if($new =~ m{^y}i) { CPAN::Shell->mkmyconfig(); return &checklock; } else { $CPAN::Frontend->mydie("OK, giving up."); } } } #-> sub CPAN::all_objects ; sub all_objects { my($mgr,$class) = @_; CPAN::HandleConfig->load unless $CPAN::Config_loaded++; CPAN->debug("mgr[$mgr] class[$class]") if $CPAN::DEBUG; CPAN::Index->reload; values %{ $META->{readwrite}{$class} }; # unsafe meta access, ok } # Called by shell, not in batch mode. In batch mode I see no risk in # having many processes updating something as installations are # continually checked at runtime. In shell mode I suspect it is # unintentional to open more than one shell at a time #-> sub CPAN::checklock ; sub checklock { my($self) = @_; my $lockfile = File::Spec->catfile($CPAN::Config->{cpan_home},".lock"); if (-f $lockfile && -M _ > 0) { my $fh = FileHandle->new($lockfile) or $CPAN::Frontend->mydie("Could not open lockfile '$lockfile': $!"); my $otherpid = <$fh>; my $otherhost = <$fh>; $fh->close; if (defined $otherpid && length $otherpid) { chomp $otherpid; } if (defined $otherhost && length $otherhost) { chomp $otherhost; } my $thishost = hostname(); my $ask_if_degraded_wanted = 0; if (defined $otherhost && defined $thishost && $otherhost ne '' && $thishost ne '' && $otherhost ne $thishost) { $CPAN::Frontend->mydie(sprintf("CPAN.pm panic: Lockfile '$lockfile'\n". "reports other host $otherhost and other ". "process $otherpid.\n". "Cannot proceed.\n")); } elsif ($RUN_DEGRADED) { $CPAN::Frontend->mywarn("Running in downgraded mode (experimental)\n"); } elsif (defined $otherpid && $otherpid) { return if $$ == $otherpid; # should never happen $CPAN::Frontend->mywarn( qq{ There seems to be running another CPAN process (pid $otherpid). Contacting... }); if (kill 0, $otherpid or $!{EPERM}) { $CPAN::Frontend->mywarn(qq{Other job is running.\n}); $ask_if_degraded_wanted = 1; } elsif (-w $lockfile) { my($ans) = CPAN::Shell::colorable_makemaker_prompt (qq{Other job not responding. Shall I overwrite }. qq{the lockfile '$lockfile'? (Y/n)},"y"); $CPAN::Frontend->myexit("Ok, bye\n") unless $ans =~ /^y/i; } else { Carp::croak( qq{Lockfile '$lockfile' not writable by you. }. qq{Cannot proceed.\n}. qq{ On UNIX try:\n}. qq{ rm '$lockfile'\n}. qq{ and then rerun us.\n} ); } } elsif ($^O eq "MSWin32") { $CPAN::Frontend->mywarn( qq{ There seems to be running another CPAN process according to '$lockfile'. }); $ask_if_degraded_wanted = 1; } else { $CPAN::Frontend->mydie(sprintf("CPAN.pm panic: Found invalid lockfile ". "'$lockfile', please remove. Cannot proceed.\n")); } if ($ask_if_degraded_wanted) { my($ans) = CPAN::Shell::colorable_makemaker_prompt (qq{Shall I try to run in downgraded }. qq{mode? (Y/n)},"y"); if ($ans =~ /^y/i) { $CPAN::Frontend->mywarn("Running in downgraded mode (experimental). Please report if something unexpected happens\n"); $RUN_DEGRADED = 1; for ($CPAN::Config) { # XXX # $_->{build_dir_reuse} = 0; # 2006-11-17 akoenig Why was that? $_->{commandnumber_in_prompt} = 0; # visibility $_->{histfile} = ""; # who should win otherwise? $_->{cache_metadata} = 0; # better would be a lock? $_->{use_sqlite} = 0; # better would be a write lock! $_->{auto_commit} = 0; # we are violent, do not persist $_->{test_report} = 0; # Oliver Paukstadt had sent wrong reports in degraded mode } } else { my $msg = "You may want to kill the other job and delete the lockfile."; if (defined $otherpid) { $msg .= " Something like: kill $otherpid rm $lockfile "; } $CPAN::Frontend->mydie("\n$msg"); } } } my $dotcpan = $CPAN::Config->{cpan_home}; eval { File::Path::mkpath($dotcpan);}; if ($@) { # A special case at least for Jarkko. my $firsterror = $@; my $seconderror; my $symlinkcpan; if (-l $dotcpan) { $symlinkcpan = readlink $dotcpan; die "readlink $dotcpan failed: $!" unless defined $symlinkcpan; eval { File::Path::mkpath($symlinkcpan); }; if ($@) { $seconderror = $@; } else { $CPAN::Frontend->mywarn(qq{ Working directory $symlinkcpan created. }); } } unless (-d $dotcpan) { my $mess = qq{ Your configuration suggests "$dotcpan" as your CPAN.pm working directory. I could not create this directory due to this error: $firsterror\n}; $mess .= qq{ As "$dotcpan" is a symlink to "$symlinkcpan", I tried to create that, but I failed with this error: $seconderror } if $seconderror; $mess .= qq{ Please make sure the directory exists and is writable. }; $CPAN::Frontend->mywarn($mess); return suggest_myconfig; } } # $@ after eval mkpath $dotcpan if (0) { # to test what happens when a race condition occurs for (reverse 1..10) { print $_, "\n"; sleep 1; } } # locking if (!$RUN_DEGRADED && !$self->{LOCKFH}) { my $fh; unless ($fh = FileHandle->new("+>>$lockfile")) { $CPAN::Frontend->mywarn(qq{ Your configuration suggests that CPAN.pm should use a working directory of $CPAN::Config->{cpan_home} Unfortunately we could not create the lock file $lockfile due to '$!'. Please make sure that the configuration variable \$CPAN::Config->{cpan_home} points to a directory where you can write a .lock file. You can set this variable in either a CPAN/MyConfig.pm or a CPAN/Config.pm in your \@INC path; }); return suggest_myconfig; } my $sleep = 1; while (!CPAN::_flock($fh, LOCK_EX|LOCK_NB)) { my $err = $! || "unknown error"; if ($sleep>3) { $CPAN::Frontend->mydie("Could not lock '$lockfile' with flock: $err; giving up\n"); } $CPAN::Frontend->mysleep($sleep+=0.1); $CPAN::Frontend->mywarn("Could not lock '$lockfile' with flock: $err; retrying\n"); } seek $fh, 0, 0; truncate $fh, 0; $fh->autoflush(1); $fh->print($$, "\n"); $fh->print(hostname(), "\n"); $self->{LOCK} = $lockfile; $self->{LOCKFH} = $fh; } $SIG{TERM} = sub { my $sig = shift; &cleanup; $CPAN::Frontend->mydie("Got SIG$sig, leaving"); }; $SIG{INT} = sub { # no blocks!!! my $sig = shift; &cleanup if $Signal; die "Got yet another signal" if $Signal > 1; $CPAN::Frontend->mydie("Got another SIG$sig") if $Signal; $CPAN::Frontend->mywarn("Caught SIG$sig, trying to continue\n"); $Signal++; }; # From: Larry Wall <larry@wall.org> # Subject: Re: deprecating SIGDIE # To: perl5-porters@perl.org # Date: Thu, 30 Sep 1999 14:58:40 -0700 (PDT) # # The original intent of __DIE__ was only to allow you to substitute one # kind of death for another on an application-wide basis without respect # to whether you were in an eval or not. As a global backstop, it should # not be used any more lightly (or any more heavily :-) than class # UNIVERSAL. Any attempt to build a general exception model on it should # be politely squashed. Any bug that causes every eval {} to have to be # modified should be not so politely squashed. # # Those are my current opinions. It is also my opinion that polite # arguments degenerate to personal arguments far too frequently, and that # when they do, it's because both people wanted it to, or at least didn't # sufficiently want it not to. # # Larry # global backstop to cleanup if we should really die $SIG{__DIE__} = \&cleanup; $self->debug("Signal handler set.") if $CPAN::DEBUG; } #-> sub CPAN::DESTROY ; sub DESTROY { &cleanup; # need an eval? } #-> sub CPAN::anycwd ; sub anycwd () { my $getcwd; $getcwd = $CPAN::Config->{'getcwd'} || 'cwd'; CPAN->$getcwd(); } #-> sub CPAN::cwd ; sub cwd {Cwd::cwd();} #-> sub CPAN::getcwd ; sub getcwd {Cwd::getcwd();} #-> sub CPAN::fastcwd ; sub fastcwd {Cwd::fastcwd();} #-> sub CPAN::getdcwd ; sub getdcwd {Cwd::getdcwd();} #-> sub CPAN::backtickcwd ; sub backtickcwd {my $cwd = `cwd`; chomp $cwd; $cwd} # Adapted from Probe::Perl #-> sub CPAN::_perl_is_same sub _perl_is_same { my ($perl) = @_; return MM->maybe_command($perl) && `$perl -MConfig=myconfig -e print -e myconfig` eq Config->myconfig; } # Adapted in part from Probe::Perl #-> sub CPAN::find_perl ; sub find_perl () { if ( File::Spec->file_name_is_absolute($^X) ) { return $^X; } else { my $exe = $Config::Config{exe_ext}; my @candidates = ( File::Spec->catfile($CPAN::iCwd,$^X), $Config::Config{'perlpath'}, ); for my $perl_name ($^X, 'perl', 'perl5', "perl$]") { for my $path (File::Spec->path(), $Config::Config{'binexp'}) { if ( defined($path) && length $path && -d $path ) { my $perl = File::Spec->catfile($path,$perl_name); push @candidates, $perl; # try with extension if not provided already if ($^O eq 'VMS') { # VMS might have a file version at the end push @candidates, $perl . $exe unless $perl =~ m/$exe(;\d+)?$/i; } elsif (defined $exe && length $exe) { push @candidates, $perl . $exe unless $perl =~ m/$exe$/i; } } } } for my $perl ( @candidates ) { if (MM->maybe_command($perl) && _perl_is_same($perl)) { $^X = $perl; return $perl; } } } return $^X; # default fall back } #-> sub CPAN::exists ; sub exists { my($mgr,$class,$id) = @_; CPAN::HandleConfig->load unless $CPAN::Config_loaded++; CPAN::Index->reload; ### Carp::croak "exists called without class argument" unless $class; $id ||= ""; $id =~ s/:+/::/g if $class eq "CPAN::Module"; my $exists; if (CPAN::_sqlite_running) { $exists = (exists $META->{readonly}{$class}{$id} or $CPAN::SQLite->set($class, $id)); } else { $exists = exists $META->{readonly}{$class}{$id}; } $exists ||= exists $META->{readwrite}{$class}{$id}; # unsafe meta access, ok } #-> sub CPAN::delete ; sub delete { my($mgr,$class,$id) = @_; delete $META->{readonly}{$class}{$id}; # unsafe meta access, ok delete $META->{readwrite}{$class}{$id}; # unsafe meta access, ok } #-> sub CPAN::has_usable # has_inst is sometimes too optimistic, we should replace it with this # has_usable whenever a case is given sub has_usable { my($self,$mod,$message) = @_; return 1 if $HAS_USABLE->{$mod}; my $has_inst = $self->has_inst($mod,$message); return unless $has_inst; my $usable; $usable = { # # most of these subroutines warn on the frontend, then # die if the installed version is unusable for some # reason; has_usable() then returns false when it caught # an exception, otherwise returns true and caches that; # 'CPAN::Meta' => [ sub { require CPAN::Meta; unless (CPAN::Version->vge(CPAN::Meta->VERSION, 2.110350)) { for ("Will not use CPAN::Meta, need version 2.110350\n") { $CPAN::Frontend->mywarn($_); die $_; } } }, ], 'CPAN::Meta::Requirements' => [ sub { if (defined $CPAN::Meta::Requirements::VERSION && CPAN::Version->vlt($CPAN::Meta::Requirements::VERSION, "2.120920") ) { delete $INC{"CPAN/Meta/Requirements.pm"}; } require CPAN::Meta::Requirements; unless (CPAN::Version->vge(CPAN::Meta::Requirements->VERSION, 2.120920)) { for ("Will not use CPAN::Meta::Requirements, need version 2.120920\n") { $CPAN::Frontend->mywarn($_); die $_; } } }, ], 'CPAN::Reporter' => [ sub { if (defined $CPAN::Reporter::VERSION && CPAN::Version->vlt($CPAN::Reporter::VERSION, "1.2011") ) { delete $INC{"CPAN/Reporter.pm"}; } require CPAN::Reporter; unless (CPAN::Version->vge(CPAN::Reporter->VERSION, "1.2011")) { for ("Will not use CPAN::Reporter, need version 1.2011\n") { $CPAN::Frontend->mywarn($_); die $_; } } }, ], LWP => [ # we frequently had "Can't locate object # method "new" via package "LWP::UserAgent" at # (eval 69) line 2006 sub {require LWP}, sub {require LWP::UserAgent}, sub {require HTTP::Request}, sub {require URI::URL; unless (CPAN::Version->vge(URI::URL::->VERSION,0.08)) { for ("Will not use URI::URL, need 0.08\n") { $CPAN::Frontend->mywarn($_); die $_; } } }, ], 'Net::FTP' => [ sub { my $var = $CPAN::Config->{ftp_proxy} || $ENV{ftp_proxy}; if ($var and $var =~ /^http:/i) { # rt #110833 for ("Net::FTP cannot handle http proxy") { $CPAN::Frontend->mywarn($_); die $_; } } }, sub {require Net::FTP}, sub {require Net::Config}, ], 'IO::Socket::SSL' => [ sub { require IO::Socket::SSL; unless (CPAN::Version->vge(IO::Socket::SSL::->VERSION,1.56)) { for ("Will not use IO::Socket::SSL, need 1.56\n") { $CPAN::Frontend->mywarn($_); die $_; } } } ], 'Net::SSLeay' => [ sub { require Net::SSLeay; unless (CPAN::Version->vge(Net::SSLeay::->VERSION,1.49)) { for ("Will not use Net::SSLeay, need 1.49\n") { $CPAN::Frontend->mywarn($_); die $_; } } } ], 'HTTP::Tiny' => [ sub { require HTTP::Tiny; unless (CPAN::Version->vge(HTTP::Tiny->VERSION, 0.005)) { for ("Will not use HTTP::Tiny, need version 0.005\n") { $CPAN::Frontend->mywarn($_); die $_; } } }, ], 'File::HomeDir' => [ sub {require File::HomeDir; unless (CPAN::Version->vge(File::HomeDir::->VERSION, 0.52)) { for ("Will not use File::HomeDir, need 0.52\n") { $CPAN::Frontend->mywarn($_); die $_; } } }, ], 'Archive::Tar' => [ sub {require Archive::Tar; my $demand = "1.50"; unless (CPAN::Version->vge(Archive::Tar::->VERSION, $demand)) { my $atv = Archive::Tar->VERSION; for ("You have Archive::Tar $atv, but $demand or later is recommended. Please upgrade.\n") { $CPAN::Frontend->mywarn($_); # don't die, because we may need # Archive::Tar to upgrade } } }, ], 'File::Temp' => [ # XXX we should probably delete from # %INC too so we can load after we # installed a new enough version -- # I'm not sure. sub {require File::Temp; unless (CPAN::Version->vge(File::Temp::->VERSION,0.16)) { for ("Will not use File::Temp, need 0.16\n") { $CPAN::Frontend->mywarn($_); die $_; } } }, ] }; if ($usable->{$mod}) { local @INC = @INC; pop @INC if $INC[-1] eq '.'; for my $c (0..$#{$usable->{$mod}}) { my $code = $usable->{$mod}[$c]; my $ret = eval { &$code() }; $ret = "" unless defined $ret; if ($@) { # warn "DEBUG: c[$c]\$\@[$@]ret[$ret]"; return; } } } return $HAS_USABLE->{$mod} = 1; } sub frontend { shift; $CPAN::Frontend = shift if @_; $CPAN::Frontend; } sub use_inst { my ($self, $module) = @_; unless ($self->has_inst($module)) { $self->frontend->mydie("$module not installed, cannot continue"); } } #-> sub CPAN::has_inst sub has_inst { my($self,$mod,$message) = @_; Carp::croak("CPAN->has_inst() called without an argument") unless defined $mod; my %dont = map { $_ => 1 } keys %{$CPAN::META->{dontload_hash}||{}}, keys %{$CPAN::Config->{dontload_hash}||{}}, @{$CPAN::Config->{dontload_list}||[]}; if (defined $message && $message eq "no" # as far as I remember only used by Nox || $dont{$mod} ) { $CPAN::META->{dontload_hash}{$mod}||=1; # unsafe meta access, ok return 0; } local @INC = @INC; pop @INC if $INC[-1] eq '.'; my $file = $mod; my $obj; $file =~ s|::|/|g; $file .= ".pm"; if ($INC{$file}) { # checking %INC is wrong, because $INC{LWP} may be true # although $INC{"URI/URL.pm"} may have failed. But as # I really want to say "blah loaded OK", I have to somehow # cache results. ### warn "$file in %INC"; #debug return 1; } elsif (eval { require $file }) { # eval is good: if we haven't yet read the database it's # perfect and if we have installed the module in the meantime, # it tries again. The second require is only a NOOP returning # 1 if we had success, otherwise it's retrying my $mtime = (stat $INC{$file})[9]; # privileged files loaded by has_inst; Note: we use $mtime # as a proxy for a checksum. $CPAN::Shell::reload->{$file} = $mtime; my $v = eval "\$$mod\::VERSION"; $v = $v ? " (v$v)" : ""; CPAN::Shell->optprint("load_module","CPAN: $mod loaded ok$v\n"); if ($mod eq "CPAN::WAIT") { push @CPAN::Shell::ISA, 'CPAN::WAIT'; } return 1; } elsif ($mod eq "Net::FTP") { $CPAN::Frontend->mywarn(qq{ Please, install Net::FTP as soon as possible. CPAN.pm installs it for you if you just type install Bundle::libnet }) unless $Have_warned->{"Net::FTP"}++; $CPAN::Frontend->mysleep(3); } elsif ($mod eq "Digest::SHA") { if ($Have_warned->{"Digest::SHA"}++) { $CPAN::Frontend->mywarn(qq{CPAN: checksum security checks disabled }. qq{because Digest::SHA not installed.\n}); } else { $CPAN::Frontend->mywarn(qq{ CPAN: checksum security checks disabled because Digest::SHA not installed. Please consider installing the Digest::SHA module. }); $CPAN::Frontend->mysleep(2); } } elsif ($mod eq "Module::Signature") { # NOT prefs_lookup, we are not a distro my $check_sigs = $CPAN::Config->{check_sigs}; if (not $check_sigs) { # they do not want us:-( } elsif (not $Have_warned->{"Module::Signature"}++) { # No point in complaining unless the user can # reasonably install and use it. if (eval { require Crypt::OpenPGP; 1 } || ( defined $CPAN::Config->{'gpg'} && $CPAN::Config->{'gpg'} =~ /\S/ ) ) { $CPAN::Frontend->mywarn(qq{ CPAN: Module::Signature security checks disabled because Module::Signature not installed. Please consider installing the Module::Signature module. You may also need to be able to connect over the Internet to the public key servers like pool.sks-keyservers.net or pgp.mit.edu. }); $CPAN::Frontend->mysleep(2); } } } else { delete $INC{$file}; # if it inc'd LWP but failed during, say, URI } return 0; } #-> sub CPAN::instance ; sub instance { my($mgr,$class,$id) = @_; CPAN::Index->reload; $id ||= ""; # unsafe meta access, ok? return $META->{readwrite}{$class}{$id} if exists $META->{readwrite}{$class}{$id}; $META->{readwrite}{$class}{$id} ||= $class->new(ID => $id); } #-> sub CPAN::new ; sub new { bless {}, shift; } #-> sub CPAN::_exit_messages ; sub _exit_messages { my ($self) = @_; $self->{exit_messages} ||= []; } #-> sub CPAN::cleanup ; sub cleanup { # warn "cleanup called with arg[@_] End[$CPAN::End] Signal[$Signal]"; local $SIG{__DIE__} = ''; my($message) = @_; my $i = 0; my $ineval = 0; my($subroutine); while ((undef,undef,undef,$subroutine) = caller(++$i)) { $ineval = 1, last if $subroutine eq '(eval)'; } return if $ineval && !$CPAN::End; return unless defined $META->{LOCK}; return unless -f $META->{LOCK}; $META->savehist; $META->{cachemgr} ||= CPAN::CacheMgr->new('atexit'); close $META->{LOCKFH}; unlink $META->{LOCK}; # require Carp; # Carp::cluck("DEBUGGING"); if ( $CPAN::CONFIG_DIRTY ) { $CPAN::Frontend->mywarn("Warning: Configuration not saved.\n"); } $CPAN::Frontend->myprint("Lockfile removed.\n"); for my $msg ( @{ $META->_exit_messages } ) { $CPAN::Frontend->myprint($msg); } } #-> sub CPAN::readhist sub readhist { my($self,$term,$histfile) = @_; my $histsize = $CPAN::Config->{'histsize'} || 100; $term->Attribs->{'MaxHistorySize'} = $histsize if (defined($term->Attribs->{'MaxHistorySize'})); my($fh) = FileHandle->new; open $fh, "<$histfile" or return; local $/ = "\n"; while (<$fh>) { chomp; $term->AddHistory($_); } close $fh; } #-> sub CPAN::savehist sub savehist { my($self) = @_; my($histfile,$histsize); unless ($histfile = $CPAN::Config->{'histfile'}) { $CPAN::Frontend->mywarn("No history written (no histfile specified).\n"); return; } $histsize = $CPAN::Config->{'histsize'} || 100; if ($CPAN::term) { unless ($CPAN::term->can("GetHistory")) { $CPAN::Frontend->mywarn("Terminal does not support GetHistory.\n"); return; } } else { return; } my @h = $CPAN::term->GetHistory; splice @h, 0, @h-$histsize if @h>$histsize; my($fh) = FileHandle->new; open $fh, ">$histfile" or $CPAN::Frontend->mydie("Couldn't open >$histfile: $!"); local $\ = local $, = "\n"; print $fh @h; close $fh; } #-> sub CPAN::is_tested sub is_tested { my($self,$what,$when) = @_; unless ($what) { Carp::cluck("DEBUG: empty what"); return; } $self->{is_tested}{$what} = $when; } #-> sub CPAN::reset_tested # forget all distributions tested -- resets what gets included in PERL5LIB sub reset_tested { my ($self) = @_; $self->{is_tested} = {}; } #-> sub CPAN::is_installed # unsets the is_tested flag: as soon as the thing is installed, it is # not needed in set_perl5lib anymore sub is_installed { my($self,$what) = @_; delete $self->{is_tested}{$what}; } sub _list_sorted_descending_is_tested { my($self) = @_; my $foul = 0; my @sorted = sort { ($self->{is_tested}{$b}||0) <=> ($self->{is_tested}{$a}||0) } grep { if ($foul){ 0 } elsif (-e) { 1 } else { $foul = $_; 0 } } keys %{$self->{is_tested}}; if ($foul) { $CPAN::Frontend->mywarn("Lost build_dir detected ($foul), giving up all cached test results of currently running session.\n"); for my $dbd (sort keys %{$self->{is_tested}}) { # distro-build-dir SEARCH: for my $d (sort { $a->id cmp $b->id } $CPAN::META->all_objects("CPAN::Distribution")) { if ($d->{build_dir} && $d->{build_dir} eq $dbd) { $CPAN::Frontend->mywarn(sprintf "Flushing cache for %s\n", $d->pretty_id); $d->fforce(""); last SEARCH; } } delete $self->{is_tested}{$dbd}; } return (); } else { return @sorted; } } #-> sub CPAN::set_perl5lib # Notes on max environment variable length: # - Win32 : XP or later, 8191; Win2000 or NT4, 2047 { my $fh; sub set_perl5lib { my($self,$for) = @_; unless ($for) { (undef,undef,undef,$for) = caller(1); $for =~ s/.*://; } $self->{is_tested} ||= {}; return unless %{$self->{is_tested}}; my $env = $ENV{PERL5LIB}; $env = $ENV{PERLLIB} unless defined $env; my @env; push @env, split /\Q$Config::Config{path_sep}\E/, $env if defined $env and length $env; #my @dirs = map {("$_/blib/arch", "$_/blib/lib")} keys %{$self->{is_tested}}; #$CPAN::Frontend->myprint("Prepending @dirs to PERL5LIB.\n"); my @dirs = map {("$_/blib/arch", "$_/blib/lib")} $self->_list_sorted_descending_is_tested; return if !@dirs; if (@dirs < 12) { $CPAN::Frontend->optprint('perl5lib', "Prepending @dirs to PERL5LIB for '$for'\n"); $ENV{PERL5LIB} = join $Config::Config{path_sep}, @dirs, @env; } elsif (@dirs < 24 ) { my @d = map {my $cp = $_; $cp =~ s/^\Q$CPAN::Config->{build_dir}\E/%BUILDDIR%/; $cp } @dirs; $CPAN::Frontend->optprint('perl5lib', "Prepending @d to PERL5LIB; ". "%BUILDDIR%=$CPAN::Config->{build_dir} ". "for '$for'\n" ); $ENV{PERL5LIB} = join $Config::Config{path_sep}, @dirs, @env; } else { my $cnt = keys %{$self->{is_tested}}; my $newenv = join $Config::Config{path_sep}, @dirs, @env; $CPAN::Frontend->optprint('perl5lib', sprintf ("Prepending blib/arch and blib/lib of ". "%d build dirs to PERL5LIB, reaching size %d; ". "for '%s'\n", $cnt, length($newenv), $for) ); $ENV{PERL5LIB} = $newenv; } }} 1; __END__ =head1 NAME CPAN - query, download and build perl modules from CPAN sites =head1 SYNOPSIS Interactive mode: perl -MCPAN -e shell --or-- cpan Basic commands: # Modules: cpan> install Acme::Meta # in the shell CPAN::Shell->install("Acme::Meta"); # in perl # Distributions: cpan> install NWCLARK/Acme-Meta-0.02.tar.gz # in the shell CPAN::Shell-> install("NWCLARK/Acme-Meta-0.02.tar.gz"); # in perl # module objects: $mo = CPAN::Shell->expandany($mod); $mo = CPAN::Shell->expand("Module",$mod); # same thing # distribution objects: $do = CPAN::Shell->expand("Module",$mod)->distribution; $do = CPAN::Shell->expandany($distro); # same thing $do = CPAN::Shell->expand("Distribution", $distro); # same thing =head1 DESCRIPTION The CPAN module automates or at least simplifies the make and install of perl modules and extensions. It includes some primitive searching capabilities and knows how to use LWP, HTTP::Tiny, Net::FTP and certain external download clients to fetch distributions from the net. These are fetched from one or more mirrored CPAN (Comprehensive Perl Archive Network) sites and unpacked in a dedicated directory. The CPAN module also supports named and versioned I<bundles> of modules. Bundles simplify handling of sets of related modules. See Bundles below. The package contains a session manager and a cache manager. The session manager keeps track of what has been fetched, built, and installed in the current session. The cache manager keeps track of the disk space occupied by the make processes and deletes excess space using a simple FIFO mechanism. All methods provided are accessible in a programmer style and in an interactive shell style. =head2 CPAN::shell([$prompt, $command]) Starting Interactive Mode Enter interactive mode by running perl -MCPAN -e shell or cpan which puts you into a readline interface. If C<Term::ReadKey> and either of C<Term::ReadLine::Perl> or C<Term::ReadLine::Gnu> are installed, history and command completion are supported. Once at the command line, type C<h> for one-page help screen; the rest should be self-explanatory. The function call C<shell> takes two optional arguments: one the prompt, the second the default initial command line (the latter only works if a real ReadLine interface module is installed). The most common uses of the interactive modes are =over 2 =item Searching for authors, bundles, distribution files and modules There are corresponding one-letter commands C<a>, C<b>, C<d>, and C<m> for each of the four categories and another, C<i> for any of the mentioned four. Each of the four entities is implemented as a class with slightly differing methods for displaying an object. Arguments to these commands are either strings exactly matching the identification string of an object, or regular expressions matched case-insensitively against various attributes of the objects. The parser only recognizes a regular expression when you enclose it with slashes. The principle is that the number of objects found influences how an item is displayed. If the search finds one item, the result is displayed with the rather verbose method C<as_string>, but if more than one is found, each object is displayed with the terse method C<as_glimpse>. Examples: cpan> m Acme::MetaSyntactic Module id = Acme::MetaSyntactic CPAN_USERID BOOK (Philippe Bruhat (BooK) <[...]>) CPAN_VERSION 0.99 CPAN_FILE B/BO/BOOK/Acme-MetaSyntactic-0.99.tar.gz UPLOAD_DATE 2006-11-06 MANPAGE Acme::MetaSyntactic - Themed metasyntactic variables names INST_FILE /usr/local/lib/perl/5.10.0/Acme/MetaSyntactic.pm INST_VERSION 0.99 cpan> a BOOK Author id = BOOK EMAIL [...] FULLNAME Philippe Bruhat (BooK) cpan> d BOOK/Acme-MetaSyntactic-0.99.tar.gz Distribution id = B/BO/BOOK/Acme-MetaSyntactic-0.99.tar.gz CPAN_USERID BOOK (Philippe Bruhat (BooK) <[...]>) CONTAINSMODS Acme::MetaSyntactic Acme::MetaSyntactic::Alias [...] UPLOAD_DATE 2006-11-06 cpan> m /lorem/ Module = Acme::MetaSyntactic::loremipsum (BOOK/Acme-MetaSyntactic-0.99.tar.gz) Module Text::Lorem (ADEOLA/Text-Lorem-0.3.tar.gz) Module Text::Lorem::More (RKRIMEN/Text-Lorem-More-0.12.tar.gz) Module Text::Lorem::More::Source (RKRIMEN/Text-Lorem-More-0.12.tar.gz) cpan> i /berlin/ Distribution BEATNIK/Filter-NumberLines-0.02.tar.gz Module = DateTime::TimeZone::Europe::Berlin (DROLSKY/DateTime-TimeZone-0.7904.tar.gz) Module Filter::NumberLines (BEATNIK/Filter-NumberLines-0.02.tar.gz) Author [...] The examples illustrate several aspects: the first three queries target modules, authors, or distros directly and yield exactly one result. The last two use regular expressions and yield several results. The last one targets all of bundles, modules, authors, and distros simultaneously. When more than one result is available, they are printed in one-line format. =item C<get>, C<make>, C<test>, C<install>, C<clean> modules or distributions These commands take any number of arguments and investigate what is necessary to perform the action. Argument processing is as follows: known module name in format Foo/Bar.pm module other embedded slash distribution - with trailing slash dot directory enclosing slashes regexp known module name in format Foo::Bar module If the argument is a distribution file name (recognized by embedded slashes), it is processed. If it is a module, CPAN determines the distribution file in which this module is included and processes that, following any dependencies named in the module's META.yml or Makefile.PL (this behavior is controlled by the configuration parameter C<prerequisites_policy>). If an argument is enclosed in slashes it is treated as a regular expression: it is expanded and if the result is a single object (distribution, bundle or module), this object is processed. Example: install Dummy::Perl # installs the module install AUXXX/Dummy-Perl-3.14.tar.gz # installs that distribution install /Dummy-Perl-3.14/ # same if the regexp is unambiguous C<get> downloads a distribution file and untars or unzips it, C<make> builds it, C<test> runs the test suite, and C<install> installs it. Any C<make> or C<test> is run unconditionally. An install <distribution_file> is also run unconditionally. But for install <module> CPAN checks whether an install is needed and prints I<module up to date> if the distribution file containing the module doesn't need updating. CPAN also keeps track of what it has done within the current session and doesn't try to build a package a second time regardless of whether it succeeded or not. It does not repeat a test run if the test has been run successfully before. Same for install runs. The C<force> pragma may precede another command (currently: C<get>, C<make>, C<test>, or C<install>) to execute the command from scratch and attempt to continue past certain errors. See the section below on the C<force> and the C<fforce> pragma. The C<notest> pragma skips the test part in the build process. Example: cpan> notest install Tk A C<clean> command results in a make clean being executed within the distribution file's working directory. =item C<readme>, C<perldoc>, C<look> module or distribution C<readme> displays the README file of the associated distribution. C<Look> gets and untars (if not yet done) the distribution file, changes to the appropriate directory and opens a subshell process in that directory. C<perldoc> displays the module's pod documentation in html or plain text format. =item C<ls> author =item C<ls> globbing_expression The first form lists all distribution files in and below an author's CPAN directory as stored in the CHECKSUMS files distributed on CPAN. The listing recurses into subdirectories. The second form limits or expands the output with shell globbing as in the following examples: ls JV/make* ls GSAR/*make* ls */*make* The last example is very slow and outputs extra progress indicators that break the alignment of the result. Note that globbing only lists directories explicitly asked for, for example FOO/* will not list FOO/bar/Acme-Sthg-n.nn.tar.gz. This may be regarded as a bug that may be changed in some future version. =item C<failed> The C<failed> command reports all distributions that failed on one of C<make>, C<test> or C<install> for some reason in the currently running shell session. =item Persistence between sessions If the C<YAML> or the C<YAML::Syck> module is installed a record of the internal state of all modules is written to disk after each step. The files contain a signature of the currently running perl version for later perusal. If the configurations variable C<build_dir_reuse> is set to a true value, then CPAN.pm reads the collected YAML files. If the stored signature matches the currently running perl, the stored state is loaded into memory such that persistence between sessions is effectively established. =item The C<force> and the C<fforce> pragma To speed things up in complex installation scenarios, CPAN.pm keeps track of what it has already done and refuses to do some things a second time. A C<get>, a C<make>, and an C<install> are not repeated. A C<test> is repeated only if the previous test was unsuccessful. The diagnostic message when CPAN.pm refuses to do something a second time is one of I<Has already been >C<unwrapped|made|tested successfully> or something similar. Another situation where CPAN refuses to act is an C<install> if the corresponding C<test> was not successful. In all these cases, the user can override this stubborn behaviour by prepending the command with the word force, for example: cpan> force get Foo cpan> force make AUTHOR/Bar-3.14.tar.gz cpan> force test Baz cpan> force install Acme::Meta Each I<forced> command is executed with the corresponding part of its memory erased. The C<fforce> pragma is a variant that emulates a C<force get> which erases the entire memory followed by the action specified, effectively restarting the whole get/make/test/install procedure from scratch. =item Lockfile Interactive sessions maintain a lockfile, by default C<~/.cpan/.lock>. Batch jobs can run without a lockfile and not disturb each other. The shell offers to run in I<downgraded mode> when another process is holding the lockfile. This is an experimental feature that is not yet tested very well. This second shell then does not write the history file, does not use the metadata file, and has a different prompt. =item Signals CPAN.pm installs signal handlers for SIGINT and SIGTERM. While you are in the cpan-shell, it is intended that you can press C<^C> anytime and return to the cpan-shell prompt. A SIGTERM will cause the cpan-shell to clean up and leave the shell loop. You can emulate the effect of a SIGTERM by sending two consecutive SIGINTs, which usually means by pressing C<^C> twice. CPAN.pm ignores SIGPIPE. If the user sets C<inactivity_timeout>, a SIGALRM is used during the run of the C<perl Makefile.PL> or C<perl Build.PL> subprocess. A SIGALRM is also used during module version parsing, and is controlled by C<version_timeout>. =back =head2 CPAN::Shell The commands available in the shell interface are methods in the package CPAN::Shell. If you enter the shell command, your input is split by the Text::ParseWords::shellwords() routine, which acts like most shells do. The first word is interpreted as the method to be invoked, and the rest of the words are treated as the method's arguments. Continuation lines are supported by ending a line with a literal backslash. =head2 autobundle C<autobundle> writes a bundle file into the C<$CPAN::Config-E<gt>{cpan_home}/Bundle> directory. The file contains a list of all modules that are both available from CPAN and currently installed within @INC. Duplicates of each distribution are suppressed. The name of the bundle file is based on the current date and a counter, e.g. F<Bundle/Snapshot_2012_05_21_00.pm>. This is installed again by running C<cpan Bundle::Snapshot_2012_05_21_00>, or installing C<Bundle::Snapshot_2012_05_21_00> from the CPAN shell. Return value: path to the written file. =head2 hosts Note: this feature is still in alpha state and may change in future versions of CPAN.pm This commands provides a statistical overview over recent download activities. The data for this is collected in the YAML file C<FTPstats.yml> in your C<cpan_home> directory. If no YAML module is configured or YAML not installed, no stats are provided. =over =item install_tested Install all distributions that have been tested successfully but have not yet been installed. See also C<is_tested>. =item is_tested List all build directories of distributions that have been tested successfully but have not yet been installed. See also C<install_tested>. =back =head2 mkmyconfig mkmyconfig() writes your own CPAN::MyConfig file into your C<~/.cpan/> directory so that you can save your own preferences instead of the system-wide ones. =head2 r [Module|/Regexp/]... scans current perl installation for modules that have a newer version available on CPAN and provides a list of them. If called without argument, all potential upgrades are listed; if called with arguments the list is filtered to the modules and regexps given as arguments. The listing looks something like this: Package namespace installed latest in CPAN file CPAN 1.94_64 1.9600 ANDK/CPAN-1.9600.tar.gz CPAN::Reporter 1.1801 1.1902 DAGOLDEN/CPAN-Reporter-1.1902.tar.gz YAML 0.70 0.73 INGY/YAML-0.73.tar.gz YAML::Syck 1.14 1.17 AVAR/YAML-Syck-1.17.tar.gz YAML::Tiny 1.44 1.50 ADAMK/YAML-Tiny-1.50.tar.gz CGI 3.43 3.55 MARKSTOS/CGI.pm-3.55.tar.gz Module::Build::YAML 1.40 1.41 DAGOLDEN/Module-Build-0.3800.tar.gz TAP::Parser::Result::YAML 3.22 3.23 ANDYA/Test-Harness-3.23.tar.gz YAML::XS 0.34 0.35 INGY/YAML-LibYAML-0.35.tar.gz It suppresses duplicates in the column C<in CPAN file> such that distributions with many upgradeable modules are listed only once. Note that the list is not sorted. =head2 recent ***EXPERIMENTAL COMMAND*** The C<recent> command downloads a list of recent uploads to CPAN and displays them I<slowly>. While the command is running, a $SIG{INT} exits the loop after displaying the current item. B<Note>: This command requires XML::LibXML installed. B<Note>: This whole command currently is just a hack and will probably change in future versions of CPAN.pm, but the general approach will likely remain. B<Note>: See also L<smoke> =head2 recompile recompile() is a special command that takes no argument and runs the make/test/install cycle with brute force over all installed dynamically loadable extensions (a.k.a. XS modules) with 'force' in effect. The primary purpose of this command is to finish a network installation. Imagine you have a common source tree for two different architectures. You decide to do a completely independent fresh installation. You start on one architecture with the help of a Bundle file produced earlier. CPAN installs the whole Bundle for you, but when you try to repeat the job on the second architecture, CPAN responds with a C<"Foo up to date"> message for all modules. So you invoke CPAN's recompile on the second architecture and you're done. Another popular use for C<recompile> is to act as a rescue in case your perl breaks binary compatibility. If one of the modules that CPAN uses is in turn depending on binary compatibility (so you cannot run CPAN commands), then you should try the CPAN::Nox module for recovery. =head2 report Bundle|Distribution|Module The C<report> command temporarily turns on the C<test_report> config variable, then runs the C<force test> command with the given arguments. The C<force> pragma reruns the tests and repeats every step that might have failed before. =head2 smoke ***EXPERIMENTAL COMMAND*** B<*** WARNING: this command downloads and executes software from CPAN to your computer of completely unknown status. You should never do this with your normal account and better have a dedicated well separated and secured machine to do this. ***> The C<smoke> command takes the list of recent uploads to CPAN as provided by the C<recent> command and tests them all. While the command is running $SIG{INT} is defined to mean that the current item shall be skipped. B<Note>: This whole command currently is just a hack and will probably change in future versions of CPAN.pm, but the general approach will likely remain. B<Note>: See also L<recent> =head2 upgrade [Module|/Regexp/]... The C<upgrade> command first runs an C<r> command with the given arguments and then installs the newest versions of all modules that were listed by that. =head2 The four C<CPAN::*> Classes: Author, Bundle, Module, Distribution Although it may be considered internal, the class hierarchy does matter for both users and programmer. CPAN.pm deals with the four classes mentioned above, and those classes all share a set of methods. Classical single polymorphism is in effect. A metaclass object registers all objects of all kinds and indexes them with a string. The strings referencing objects have a separated namespace (well, not completely separated): Namespace Class words containing a "/" (slash) Distribution words starting with Bundle:: Bundle everything else Module or Author Modules know their associated Distribution objects. They always refer to the most recent official release. Developers may mark their releases as unstable development versions (by inserting an underscore into the module version number which will also be reflected in the distribution name when you run 'make dist'), so the really hottest and newest distribution is not always the default. If a module Foo circulates on CPAN in both version 1.23 and 1.23_90, CPAN.pm offers a convenient way to install version 1.23 by saying install Foo This would install the complete distribution file (say BAR/Foo-1.23.tar.gz) with all accompanying material. But if you would like to install version 1.23_90, you need to know where the distribution file resides on CPAN relative to the authors/id/ directory. If the author is BAR, this might be BAR/Foo-1.23_90.tar.gz; so you would have to say install BAR/Foo-1.23_90.tar.gz The first example will be driven by an object of the class CPAN::Module, the second by an object of class CPAN::Distribution. =head2 Integrating local directories Note: this feature is still in alpha state and may change in future versions of CPAN.pm Distribution objects are normally distributions from the CPAN, but there is a slightly degenerate case for Distribution objects, too, of projects held on the local disk. These distribution objects have the same name as the local directory and end with a dot. A dot by itself is also allowed for the current directory at the time CPAN.pm was used. All actions such as C<make>, C<test>, and C<install> are applied directly to that directory. This gives the command C<cpan .> an interesting touch: while the normal mantra of installing a CPAN module without CPAN.pm is one of perl Makefile.PL perl Build.PL ( go and get prerequisites ) make ./Build make test ./Build test make install ./Build install the command C<cpan .> does all of this at once. It figures out which of the two mantras is appropriate, fetches and installs all prerequisites, takes care of them recursively, and finally finishes the installation of the module in the current directory, be it a CPAN module or not. The typical usage case is for private modules or working copies of projects from remote repositories on the local disk. =head2 Redirection The usual shell redirection symbols C< | > and C<< > >> are recognized by the cpan shell B<only when surrounded by whitespace>. So piping to pager or redirecting output into a file works somewhat as in a normal shell, with the stipulation that you must type extra spaces. =head2 Plugin support ***EXPERIMENTAL*** Plugins are objects that implement any of currently eight methods: pre_get post_get pre_make post_make pre_test post_test pre_install post_install The C<plugin_list> configuration parameter holds a list of strings of the form Modulename=arg0,arg1,arg2,arg3,... eg: CPAN::Plugin::Flurb=dir,/opt/pkgs/flurb/raw,verbose,1 At run time, each listed plugin is instantiated as a singleton object by running the equivalent of this pseudo code: my $plugin = <string representation from config>; <generate Modulename and arguments from $plugin>; my $p = $instance{$plugin} ||= Modulename->new($arg0,$arg1,...); The generated singletons are kept around from instantiation until the end of the shell session. <plugin_list> can be reconfigured at any time at run time. While the cpan shell is running, it checks all activated plugins at each of the 8 reference points listed above and runs the respective method if it is implemented for that object. The method is called with the active CPAN::Distribution object passed in as an argument. =head1 CONFIGURATION When the CPAN module is used for the first time, a configuration dialogue tries to determine a couple of site specific options. The result of the dialog is stored in a hash reference C< $CPAN::Config > in a file CPAN/Config.pm. Default values defined in the CPAN/Config.pm file can be overridden in a user specific file: CPAN/MyConfig.pm. Such a file is best placed in C<$HOME/.cpan/CPAN/MyConfig.pm>, because C<$HOME/.cpan> is added to the search path of the CPAN module before the use() or require() statements. The mkmyconfig command writes this file for you. The C<o conf> command has various bells and whistles: =over =item completion support If you have a ReadLine module installed, you can hit TAB at any point of the commandline and C<o conf> will offer you completion for the built-in subcommands and/or config variable names. =item displaying some help: o conf help Displays a short help =item displaying current values: o conf [KEY] Displays the current value(s) for this config variable. Without KEY, displays all subcommands and config variables. Example: o conf shell If KEY starts and ends with a slash, the string in between is treated as a regular expression and only keys matching this regexp are displayed Example: o conf /color/ =item changing of scalar values: o conf KEY VALUE Sets the config variable KEY to VALUE. The empty string can be specified as usual in shells, with C<''> or C<""> Example: o conf wget /usr/bin/wget =item changing of list values: o conf KEY SHIFT|UNSHIFT|PUSH|POP|SPLICE|LIST If a config variable name ends with C<list>, it is a list. C<o conf KEY shift> removes the first element of the list, C<o conf KEY pop> removes the last element of the list. C<o conf KEYS unshift LIST> prepends a list of values to the list, C<o conf KEYS push LIST> appends a list of valued to the list. Likewise, C<o conf KEY splice LIST> passes the LIST to the corresponding splice command. Finally, any other list of arguments is taken as a new list value for the KEY variable discarding the previous value. Examples: o conf urllist unshift http://cpan.dev.local/CPAN o conf urllist splice 3 1 o conf urllist http://cpan1.local http://cpan2.local ftp://ftp.perl.org =item reverting to saved: o conf defaults Reverts all config variables to the state in the saved config file. =item saving the config: o conf commit Saves all config variables to the current config file (CPAN/Config.pm or CPAN/MyConfig.pm that was loaded at start). =back The configuration dialog can be started any time later again by issuing the command C< o conf init > in the CPAN shell. A subset of the configuration dialog can be run by issuing C<o conf init WORD> where WORD is any valid config variable or a regular expression. =head2 Config Variables The following keys in the hash reference $CPAN::Config are currently defined: allow_installing_module_downgrades allow or disallow installing module downgrades allow_installing_outdated_dists allow or disallow installing modules that are indexed in the cpan index pointing to a distro with a higher distro-version number applypatch path to external prg auto_commit commit all changes to config variables to disk build_cache size of cache for directories to build modules build_dir locally accessible directory to build modules build_dir_reuse boolean if distros in build_dir are persistent build_requires_install_policy to install or not to install when a module is only needed for building. yes|no|ask/yes|ask/no bzip2 path to external prg cache_metadata use serializer to cache metadata check_sigs if signatures should be verified cleanup_after_install remove build directory immediately after a successful install and remember that for the duration of the session colorize_debug Term::ANSIColor attributes for debugging output colorize_output boolean if Term::ANSIColor should colorize output colorize_print Term::ANSIColor attributes for normal output colorize_warn Term::ANSIColor attributes for warnings commandnumber_in_prompt boolean if you want to see current command number commands_quote preferred character to use for quoting external commands when running them. Defaults to double quote on Windows, single tick everywhere else; can be set to space to disable quoting connect_to_internet_ok whether to ask if opening a connection is ok before urllist is specified cpan_home local directory reserved for this package curl path to external prg dontload_hash DEPRECATED dontload_list arrayref: modules in the list will not be loaded by the CPAN::has_inst() routine ftp path to external prg ftp_passive if set, the environment variable FTP_PASSIVE is set for downloads ftp_proxy proxy host for ftp requests ftpstats_period max number of days to keep download statistics ftpstats_size max number of items to keep in the download statistics getcwd see below gpg path to external prg gzip location of external program gzip halt_on_failure stop processing after the first failure of queued items or dependencies histfile file to maintain history between sessions histsize maximum number of lines to keep in histfile http_proxy proxy host for http requests inactivity_timeout breaks interactive Makefile.PLs or Build.PLs after this many seconds inactivity. Set to 0 to disable timeouts. index_expire refetch index files after this many days inhibit_startup_message if true, suppress the startup message keep_source_where directory in which to keep the source (if we do) load_module_verbosity report loading of optional modules used by CPAN.pm lynx path to external prg make location of external make program make_arg arguments that should always be passed to 'make' make_install_make_command the make command for running 'make install', for example 'sudo make' make_install_arg same as make_arg for 'make install' makepl_arg arguments passed to 'perl Makefile.PL' mbuild_arg arguments passed to './Build' mbuild_install_arg arguments passed to './Build install' mbuild_install_build_command command to use instead of './Build' when we are in the install stage, for example 'sudo ./Build' mbuildpl_arg arguments passed to 'perl Build.PL' ncftp path to external prg ncftpget path to external prg no_proxy don't proxy to these hosts/domains (comma separated list) pager location of external program more (or any pager) password your password if you CPAN server wants one patch path to external prg patches_dir local directory containing patch files perl5lib_verbosity verbosity level for PERL5LIB additions plugin_list list of active hooks (see Plugin support above and the CPAN::Plugin module) prefer_external_tar per default all untar operations are done with Archive::Tar; by setting this variable to true the external tar command is used if available prefer_installer legal values are MB and EUMM: if a module comes with both a Makefile.PL and a Build.PL, use the former (EUMM) or the latter (MB); if the module comes with only one of the two, that one will be used no matter the setting prerequisites_policy what to do if you are missing module prerequisites ('follow' automatically, 'ask' me, or 'ignore') For 'follow', also sets PERL_AUTOINSTALL and PERL_EXTUTILS_AUTOINSTALL for "--defaultdeps" if not already set prefs_dir local directory to store per-distro build options proxy_user username for accessing an authenticating proxy proxy_pass password for accessing an authenticating proxy pushy_https use https to cpan.org when possible, otherwise use http to cpan.org and issue a warning randomize_urllist add some randomness to the sequence of the urllist recommends_policy whether recommended prerequisites should be included scan_cache controls scanning of cache ('atstart', 'atexit' or 'never') shell your favorite shell show_unparsable_versions boolean if r command tells which modules are versionless show_upload_date boolean if commands should try to determine upload date show_zero_versions boolean if r command tells for which modules $version==0 suggests_policy whether suggested prerequisites should be included tar location of external program tar tar_verbosity verbosity level for the tar command term_is_latin deprecated: if true Unicode is translated to ISO-8859-1 (and nonsense for characters outside latin range) term_ornaments boolean to turn ReadLine ornamenting on/off test_report email test reports (if CPAN::Reporter is installed) trust_test_report_history skip testing when previously tested ok (according to CPAN::Reporter history) unzip location of external program unzip urllist arrayref to nearby CPAN sites (or equivalent locations) urllist_ping_external use external ping command when autoselecting mirrors urllist_ping_verbose increase verbosity when autoselecting mirrors use_prompt_default set PERL_MM_USE_DEFAULT for configure/make/test/install use_sqlite use CPAN::SQLite for metadata storage (fast and lean) username your username if you CPAN server wants one version_timeout stops version parsing after this many seconds. Default is 15 secs. Set to 0 to disable. wait_list arrayref to a wait server to try (See CPAN::WAIT) wget path to external prg yaml_load_code enable YAML code deserialisation via CPAN::DeferredCode yaml_module which module to use to read/write YAML files You can set and query each of these options interactively in the cpan shell with the C<o conf> or the C<o conf init> command as specified below. =over 2 =item C<o conf E<lt>scalar optionE<gt>> prints the current value of the I<scalar option> =item C<o conf E<lt>scalar optionE<gt> E<lt>valueE<gt>> Sets the value of the I<scalar option> to I<value> =item C<o conf E<lt>list optionE<gt>> prints the current value of the I<list option> in MakeMaker's neatvalue format. =item C<o conf E<lt>list optionE<gt> [shift|pop]> shifts or pops the array in the I<list option> variable =item C<o conf E<lt>list optionE<gt> [unshift|push|splice] E<lt>listE<gt>> works like the corresponding perl commands. =item interactive editing: o conf init [MATCH|LIST] Runs an interactive configuration dialog for matching variables. Without argument runs the dialog over all supported config variables. To specify a MATCH the argument must be enclosed by slashes. Examples: o conf init ftp_passive ftp_proxy o conf init /color/ Note: this method of setting config variables often provides more explanation about the functioning of a variable than the manpage. =back =head2 CPAN::anycwd($path): Note on config variable getcwd CPAN.pm changes the current working directory often and needs to determine its own current working directory. By default it uses Cwd::cwd, but if for some reason this doesn't work on your system, configure alternatives according to the following table: =over 4 =item cwd Calls Cwd::cwd =item getcwd Calls Cwd::getcwd =item fastcwd Calls Cwd::fastcwd =item getdcwd Calls Cwd::getdcwd =item backtickcwd Calls the external command cwd. =back =head2 Note on the format of the urllist parameter urllist parameters are URLs according to RFC 1738. We do a little guessing if your URL is not compliant, but if you have problems with C<file> URLs, please try the correct format. Either: file://localhost/whatever/ftp/pub/CPAN/ or file:///home/ftp/pub/CPAN/ =head2 The urllist parameter has CD-ROM support The C<urllist> parameter of the configuration table contains a list of URLs used for downloading. If the list contains any C<file> URLs, CPAN always tries there first. This feature is disabled for index files. So the recommendation for the owner of a CD-ROM with CPAN contents is: include your local, possibly outdated CD-ROM as a C<file> URL at the end of urllist, e.g. o conf urllist push file://localhost/CDROM/CPAN CPAN.pm will then fetch the index files from one of the CPAN sites that come at the beginning of urllist. It will later check for each module to see whether there is a local copy of the most recent version. Another peculiarity of urllist is that the site that we could successfully fetch the last file from automatically gets a preference token and is tried as the first site for the next request. So if you add a new site at runtime it may happen that the previously preferred site will be tried another time. This means that if you want to disallow a site for the next transfer, it must be explicitly removed from urllist. =head2 Maintaining the urllist parameter If you have YAML.pm (or some other YAML module configured in C<yaml_module>) installed, CPAN.pm collects a few statistical data about recent downloads. You can view the statistics with the C<hosts> command or inspect them directly by looking into the C<FTPstats.yml> file in your C<cpan_home> directory. To get some interesting statistics, it is recommended that C<randomize_urllist> be set; this introduces some amount of randomness into the URL selection. =head2 The C<requires> and C<build_requires> dependency declarations Since CPAN.pm version 1.88_51 modules declared as C<build_requires> by a distribution are treated differently depending on the config variable C<build_requires_install_policy>. By setting C<build_requires_install_policy> to C<no>, such a module is not installed. It is only built and tested, and then kept in the list of tested but uninstalled modules. As such, it is available during the build of the dependent module by integrating the path to the C<blib/arch> and C<blib/lib> directories in the environment variable PERL5LIB. If C<build_requires_install_policy> is set to C<yes>, then both modules declared as C<requires> and those declared as C<build_requires> are treated alike. By setting to C<ask/yes> or C<ask/no>, CPAN.pm asks the user and sets the default accordingly. =head2 Configuration of the allow_installing_* parameters The C<allow_installing_*> parameters are evaluated during the C<make> phase. If set to C<yes>, they allow the testing and the installation of the current distro and otherwise have no effect. If set to C<no>, they may abort the build (preventing testing and installing), depending on the contents of the C<blib/> directory. The C<blib/> directory is the directory that holds all the files that would usually be installed in the C<install> phase. C<allow_installing_outdated_dists> compares the C<blib/> directory with the CPAN index. If it finds something there that belongs, according to the index, to a different dist, it aborts the current build. C<allow_installing_module_downgrades> compares the C<blib/> directory with already installed modules, actually their version numbers, as determined by ExtUtils::MakeMaker or equivalent. If a to-be-installed module would downgrade an already installed module, the current build is aborted. An interesting twist occurs when a distroprefs document demands the installation of an outdated dist via goto while C<allow_installing_outdated_dists> forbids it. Without additional provisions, this would let the C<allow_installing_outdated_dists> win and the distroprefs lose. So the proper arrangement in such a case is to write a second distroprefs document for the distro that C<goto> points to and overrule the C<cpanconfig> there. E.g.: --- match: distribution: "^MAUKE/Keyword-Simple-0.04.tar.gz" goto: "MAUKE/Keyword-Simple-0.03.tar.gz" --- match: distribution: "^MAUKE/Keyword-Simple-0.03.tar.gz" cpanconfig: allow_installing_outdated_dists: yes =head2 Configuration for individual distributions (I<Distroprefs>) (B<Note:> This feature has been introduced in CPAN.pm 1.8854) Distributions on CPAN usually behave according to what we call the CPAN mantra. Or since the advent of Module::Build we should talk about two mantras: perl Makefile.PL perl Build.PL make ./Build make test ./Build test make install ./Build install But some modules cannot be built with this mantra. They try to get some extra data from the user via the environment, extra arguments, or interactively--thus disturbing the installation of large bundles like Phalanx100 or modules with many dependencies like Plagger. The distroprefs system of C<CPAN.pm> addresses this problem by allowing the user to specify extra informations and recipes in YAML files to either =over =item pass additional arguments to one of the four commands, =item set environment variables =item instantiate an Expect object that reads from the console, waits for some regular expressions and enters some answers =item temporarily override assorted C<CPAN.pm> configuration variables =item specify dependencies the original maintainer forgot =item disable the installation of an object altogether =back See the YAML and Data::Dumper files that come with the C<CPAN.pm> distribution in the C<distroprefs/> directory for examples. =head2 Filenames The YAML files themselves must have the C<.yml> extension; all other files are ignored (for two exceptions see I<Fallback Data::Dumper and Storable> below). The containing directory can be specified in C<CPAN.pm> in the C<prefs_dir> config variable. Try C<o conf init prefs_dir> in the CPAN shell to set and activate the distroprefs system. Every YAML file may contain arbitrary documents according to the YAML specification, and every document is treated as an entity that can specify the treatment of a single distribution. Filenames can be picked arbitrarily; C<CPAN.pm> always reads all files (in alphabetical order) and takes the key C<match> (see below in I<Language Specs>) as a hashref containing match criteria that determine if the current distribution matches the YAML document or not. =head2 Fallback Data::Dumper and Storable If neither your configured C<yaml_module> nor YAML.pm is installed, CPAN.pm falls back to using Data::Dumper and Storable and looks for files with the extensions C<.dd> or C<.st> in the C<prefs_dir> directory. These files are expected to contain one or more hashrefs. For Data::Dumper generated files, this is expected to be done with by defining C<$VAR1>, C<$VAR2>, etc. The YAML shell would produce these with the command ysh < somefile.yml > somefile.dd For Storable files the rule is that they must be constructed such that C<Storable::retrieve(file)> returns an array reference and the array elements represent one distropref object each. The conversion from YAML would look like so: perl -MYAML=LoadFile -MStorable=nstore -e ' @y=LoadFile(shift); nstore(\@y, shift)' somefile.yml somefile.st In bootstrapping situations it is usually sufficient to translate only a few YAML files to Data::Dumper for crucial modules like C<YAML::Syck>, C<YAML.pm> and C<Expect.pm>. If you prefer Storable over Data::Dumper, remember to pull out a Storable version that writes an older format than all the other Storable versions that will need to read them. =head2 Blueprint The following example contains all supported keywords and structures with the exception of C<eexpect> which can be used instead of C<expect>. --- comment: "Demo" match: module: "Dancing::Queen" distribution: "^CHACHACHA/Dancing-" not_distribution: "\.zip$" perl: "/usr/local/cariba-perl/bin/perl" perlconfig: archname: "freebsd" not_cc: "gcc" env: DANCING_FLOOR: "Shubiduh" disabled: 1 cpanconfig: make: gmake pl: args: - "--somearg=specialcase" env: {} expect: - "Which is your favorite fruit" - "apple\n" make: args: - all - extra-all env: {} expect: [] commandline: "echo SKIPPING make" test: args: [] env: {} expect: [] install: args: [] env: WANT_TO_INSTALL: YES expect: - "Do you really want to install" - "y\n" patches: - "ABCDE/Fedcba-3.14-ABCDE-01.patch" depends: configure_requires: LWP: 5.8 build_requires: Test::Exception: 0.25 requires: Spiffy: 0.30 =head2 Language Specs Every YAML document represents a single hash reference. The valid keys in this hash are as follows: =over =item comment [scalar] A comment =item cpanconfig [hash] Temporarily override assorted C<CPAN.pm> configuration variables. Supported are: C<build_requires_install_policy>, C<check_sigs>, C<make>, C<make_install_make_command>, C<prefer_installer>, C<test_report>. Please report as a bug when you need another one supported. =item depends [hash] *** EXPERIMENTAL FEATURE *** All three types, namely C<configure_requires>, C<build_requires>, and C<requires> are supported in the way specified in the META.yml specification. The current implementation I<merges> the specified dependencies with those declared by the package maintainer. In a future implementation this may be changed to override the original declaration. =item disabled [boolean] Specifies that this distribution shall not be processed at all. =item features [array] *** EXPERIMENTAL FEATURE *** Experimental implementation to deal with optional_features from META.yml. Still needs coordination with installer software and currently works only for META.yml declaring C<dynamic_config=0>. Use with caution. =item goto [string] The canonical name of a delegate distribution to install instead. Useful when a new version, although it tests OK itself, breaks something else or a developer release or a fork is already uploaded that is better than the last released version. =item install [hash] Processing instructions for the C<make install> or C<./Build install> phase of the CPAN mantra. See below under I<Processing Instructions>. =item make [hash] Processing instructions for the C<make> or C<./Build> phase of the CPAN mantra. See below under I<Processing Instructions>. =item match [hash] A hashref with one or more of the keys C<distribution>, C<module>, C<perl>, C<perlconfig>, and C<env> that specify whether a document is targeted at a specific CPAN distribution or installation. Keys prefixed with C<not_> negates the corresponding match. The corresponding values are interpreted as regular expressions. The C<distribution> related one will be matched against the canonical distribution name, e.g. "AUTHOR/Foo-Bar-3.14.tar.gz". The C<module> related one will be matched against I<all> modules contained in the distribution until one module matches. The C<perl> related one will be matched against C<$^X> (but with the absolute path). The value associated with C<perlconfig> is itself a hashref that is matched against corresponding values in the C<%Config::Config> hash living in the C<Config.pm> module. Keys prefixed with C<not_> negates the corresponding match. The value associated with C<env> is itself a hashref that is matched against corresponding values in the C<%ENV> hash. Keys prefixed with C<not_> negates the corresponding match. If more than one restriction of C<module>, C<distribution>, etc. is specified, the results of the separately computed match values must all match. If so, the hashref represented by the YAML document is returned as the preference structure for the current distribution. =item patches [array] An array of patches on CPAN or on the local disk to be applied in order via an external patch program. If the value for the C<-p> parameter is C<0> or C<1> is determined by reading the patch beforehand. The path to each patch is either an absolute path on the local filesystem or relative to a patch directory specified in the C<patches_dir> configuration variable or in the format of a canonical distro name. For examples please consult the distroprefs/ directory in the CPAN.pm distribution (these examples are not installed by default). Note: if the C<applypatch> program is installed and C<CPAN::Config> knows about it B<and> a patch is written by the C<makepatch> program, then C<CPAN.pm> lets C<applypatch> apply the patch. Both C<makepatch> and C<applypatch> are available from CPAN in the C<JV/makepatch-*> distribution. =item pl [hash] Processing instructions for the C<perl Makefile.PL> or C<perl Build.PL> phase of the CPAN mantra. See below under I<Processing Instructions>. =item test [hash] Processing instructions for the C<make test> or C<./Build test> phase of the CPAN mantra. See below under I<Processing Instructions>. =back =head2 Processing Instructions =over =item args [array] Arguments to be added to the command line =item commandline A full commandline to run via C<system()>. During execution, the environment variable PERL is set to $^X (but with an absolute path). If C<commandline> is specified, C<args> is not used. =item eexpect [hash] Extended C<expect>. This is a hash reference with four allowed keys, C<mode>, C<timeout>, C<reuse>, and C<talk>. You must install the C<Expect> module to use C<eexpect>. CPAN.pm does not install it for you. C<mode> may have the values C<deterministic> for the case where all questions come in the order written down and C<anyorder> for the case where the questions may come in any order. The default mode is C<deterministic>. C<timeout> denotes a timeout in seconds. Floating-point timeouts are OK. With C<mode=deterministic>, the timeout denotes the timeout per question; with C<mode=anyorder> it denotes the timeout per byte received from the stream or questions. C<talk> is a reference to an array that contains alternating questions and answers. Questions are regular expressions and answers are literal strings. The Expect module watches the stream from the execution of the external program (C<perl Makefile.PL>, C<perl Build.PL>, C<make>, etc.). For C<mode=deterministic>, the CPAN.pm injects the corresponding answer as soon as the stream matches the regular expression. For C<mode=anyorder> CPAN.pm answers a question as soon as the timeout is reached for the next byte in the input stream. In this mode you can use the C<reuse> parameter to decide what will happen with a question-answer pair after it has been used. In the default case (reuse=0) it is removed from the array, avoiding being used again accidentally. If you want to answer the question C<Do you really want to do that> several times, then it must be included in the array at least as often as you want this answer to be given. Setting the parameter C<reuse> to 1 makes this repetition unnecessary. =item env [hash] Environment variables to be set during the command =item expect [array] You must install the C<Expect> module to use C<expect>. CPAN.pm does not install it for you. C<< expect: <array> >> is a short notation for this C<eexpect>: eexpect: mode: deterministic timeout: 15 talk: <array> =back =head2 Schema verification with C<Kwalify> If you have the C<Kwalify> module installed (which is part of the Bundle::CPANxxl), then all your distroprefs files are checked for syntactic correctness. =head2 Example Distroprefs Files C<CPAN.pm> comes with a collection of example YAML files. Note that these are really just examples and should not be used without care because they cannot fit everybody's purpose. After all, the authors of the packages that ask questions had a need to ask, so you should watch their questions and adjust the examples to your environment and your needs. You have been warned:-) =head1 PROGRAMMER'S INTERFACE If you do not enter the shell, shell commands are available both as methods (C<CPAN::Shell-E<gt>install(...)>) and as functions in the calling package (C<install(...)>). Before calling low-level commands, it makes sense to initialize components of CPAN you need, e.g.: CPAN::HandleConfig->load; CPAN::Shell::setup_output; CPAN::Index->reload; High-level commands do such initializations automatically. There's currently only one class that has a stable interface - CPAN::Shell. All commands that are available in the CPAN shell are methods of the class CPAN::Shell. The arguments on the commandline are passed as arguments to the method. So if you take for example the shell command notest install A B C the actually executed command is CPAN::Shell->notest("install","A","B","C"); Each of the commands that produce listings of modules (C<r>, C<autobundle>, C<u>) also return a list of the IDs of all modules within the list. =over 2 =item expand($type,@things) The IDs of all objects available within a program are strings that can be expanded to the corresponding real objects with the C<CPAN::Shell-E<gt>expand("Module",@things)> method. Expand returns a list of CPAN::Module objects according to the C<@things> arguments given. In scalar context, it returns only the first element of the list. =item expandany(@things) Like expand, but returns objects of the appropriate type, i.e. CPAN::Bundle objects for bundles, CPAN::Module objects for modules, and CPAN::Distribution objects for distributions. Note: it does not expand to CPAN::Author objects. =item Programming Examples This enables the programmer to do operations that combine functionalities that are available in the shell. # install everything that is outdated on my disk: perl -MCPAN -e 'CPAN::Shell->install(CPAN::Shell->r)' # install my favorite programs if necessary: for $mod (qw(Net::FTP Digest::SHA Data::Dumper)) { CPAN::Shell->install($mod); } # list all modules on my disk that have no VERSION number for $mod (CPAN::Shell->expand("Module","/./")) { next unless $mod->inst_file; # MakeMaker convention for undefined $VERSION: next unless $mod->inst_version eq "undef"; print "No VERSION in ", $mod->id, "\n"; } # find out which distribution on CPAN contains a module: print CPAN::Shell->expand("Module","Apache::Constants")->cpan_file Or if you want to schedule a I<cron> job to watch CPAN, you could list all modules that need updating. First a quick and dirty way: perl -e 'use CPAN; CPAN::Shell->r;' If you don't want any output should all modules be up to date, parse the output of above command for the regular expression C</modules are up to date/> and decide to mail the output only if it doesn't match. If you prefer to do it more in a programmerish style in one single process, something like this may better suit you: # list all modules on my disk that have newer versions on CPAN for $mod (CPAN::Shell->expand("Module","/./")) { next unless $mod->inst_file; next if $mod->uptodate; printf "Module %s is installed as %s, could be updated to %s from CPAN\n", $mod->id, $mod->inst_version, $mod->cpan_version; } If that gives too much output every day, you may want to watch only for three modules. You can write for $mod (CPAN::Shell->expand("Module","/Apache|LWP|CGI/")) { as the first line instead. Or you can combine some of the above tricks: # watch only for a new mod_perl module $mod = CPAN::Shell->expand("Module","mod_perl"); exit if $mod->uptodate; # new mod_perl arrived, let me know all update recommendations CPAN::Shell->r; =back =head2 Methods in the other Classes =over 4 =item CPAN::Author::as_glimpse() Returns a one-line description of the author =item CPAN::Author::as_string() Returns a multi-line description of the author =item CPAN::Author::email() Returns the author's email address =item CPAN::Author::fullname() Returns the author's name =item CPAN::Author::name() An alias for fullname =item CPAN::Bundle::as_glimpse() Returns a one-line description of the bundle =item CPAN::Bundle::as_string() Returns a multi-line description of the bundle =item CPAN::Bundle::clean() Recursively runs the C<clean> method on all items contained in the bundle. =item CPAN::Bundle::contains() Returns a list of objects' IDs contained in a bundle. The associated objects may be bundles, modules or distributions. =item CPAN::Bundle::force($method,@args) Forces CPAN to perform a task that it normally would have refused to do. Force takes as arguments a method name to be called and any number of additional arguments that should be passed to the called method. The internals of the object get the needed changes so that CPAN.pm does not refuse to take the action. The C<force> is passed recursively to all contained objects. See also the section above on the C<force> and the C<fforce> pragma. =item CPAN::Bundle::get() Recursively runs the C<get> method on all items contained in the bundle =item CPAN::Bundle::inst_file() Returns the highest installed version of the bundle in either @INC or C<< $CPAN::Config->{cpan_home} >>. Note that this is different from CPAN::Module::inst_file. =item CPAN::Bundle::inst_version() Like CPAN::Bundle::inst_file, but returns the $VERSION =item CPAN::Bundle::uptodate() Returns 1 if the bundle itself and all its members are up-to-date. =item CPAN::Bundle::install() Recursively runs the C<install> method on all items contained in the bundle =item CPAN::Bundle::make() Recursively runs the C<make> method on all items contained in the bundle =item CPAN::Bundle::readme() Recursively runs the C<readme> method on all items contained in the bundle =item CPAN::Bundle::test() Recursively runs the C<test> method on all items contained in the bundle =item CPAN::Distribution::as_glimpse() Returns a one-line description of the distribution =item CPAN::Distribution::as_string() Returns a multi-line description of the distribution =item CPAN::Distribution::author Returns the CPAN::Author object of the maintainer who uploaded this distribution =item CPAN::Distribution::pretty_id() Returns a string of the form "AUTHORID/TARBALL", where AUTHORID is the author's PAUSE ID and TARBALL is the distribution filename. =item CPAN::Distribution::base_id() Returns the distribution filename without any archive suffix. E.g "Foo-Bar-0.01" =item CPAN::Distribution::clean() Changes to the directory where the distribution has been unpacked and runs C<make clean> there. =item CPAN::Distribution::containsmods() Returns a list of IDs of modules contained in a distribution file. Works only for distributions listed in the 02packages.details.txt.gz file. This typically means that just most recent version of a distribution is covered. =item CPAN::Distribution::cvs_import() Changes to the directory where the distribution has been unpacked and runs something like cvs -d $cvs_root import -m $cvs_log $cvs_dir $userid v$version there. =item CPAN::Distribution::dir() Returns the directory into which this distribution has been unpacked. =item CPAN::Distribution::force($method,@args) Forces CPAN to perform a task that it normally would have refused to do. Force takes as arguments a method name to be called and any number of additional arguments that should be passed to the called method. The internals of the object get the needed changes so that CPAN.pm does not refuse to take the action. See also the section above on the C<force> and the C<fforce> pragma. =item CPAN::Distribution::get() Downloads the distribution from CPAN and unpacks it. Does nothing if the distribution has already been downloaded and unpacked within the current session. =item CPAN::Distribution::install() Changes to the directory where the distribution has been unpacked and runs the external command C<make install> there. If C<make> has not yet been run, it will be run first. A C<make test> is issued in any case and if this fails, the install is cancelled. The cancellation can be avoided by letting C<force> run the C<install> for you. This install method only has the power to install the distribution if there are no dependencies in the way. To install an object along with all its dependencies, use CPAN::Shell->install. Note that install() gives no meaningful return value. See uptodate(). =item CPAN::Distribution::isa_perl() Returns 1 if this distribution file seems to be a perl distribution. Normally this is derived from the file name only, but the index from CPAN can contain a hint to achieve a return value of true for other filenames too. =item CPAN::Distribution::look() Changes to the directory where the distribution has been unpacked and opens a subshell there. Exiting the subshell returns. =item CPAN::Distribution::make() First runs the C<get> method to make sure the distribution is downloaded and unpacked. Changes to the directory where the distribution has been unpacked and runs the external commands C<perl Makefile.PL> or C<perl Build.PL> and C<make> there. =item CPAN::Distribution::perldoc() Downloads the pod documentation of the file associated with a distribution (in HTML format) and runs it through the external command I<lynx> specified in C<< $CPAN::Config->{lynx} >>. If I<lynx> isn't available, it converts it to plain text with the external command I<html2text> and runs it through the pager specified in C<< $CPAN::Config->{pager} >>. =item CPAN::Distribution::prefs() Returns the hash reference from the first matching YAML file that the user has deposited in the C<prefs_dir/> directory. The first succeeding match wins. The files in the C<prefs_dir/> are processed alphabetically, and the canonical distro name (e.g. AUTHOR/Foo-Bar-3.14.tar.gz) is matched against the regular expressions stored in the $root->{match}{distribution} attribute value. Additionally all module names contained in a distribution are matched against the regular expressions in the $root->{match}{module} attribute value. The two match values are ANDed together. Each of the two attributes are optional. =item CPAN::Distribution::prereq_pm() Returns the hash reference that has been announced by a distribution as the C<requires> and C<build_requires> elements. These can be declared either by the C<META.yml> (if authoritative) or can be deposited after the run of C<Build.PL> in the file C<./_build/prereqs> or after the run of C<Makfile.PL> written as the C<PREREQ_PM> hash in a comment in the produced C<Makefile>. I<Note>: this method only works after an attempt has been made to C<make> the distribution. Returns undef otherwise. =item CPAN::Distribution::readme() Downloads the README file associated with a distribution and runs it through the pager specified in C<< $CPAN::Config->{pager} >>. =item CPAN::Distribution::reports() Downloads report data for this distribution from www.cpantesters.org and displays a subset of them. =item CPAN::Distribution::read_yaml() Returns the content of the META.yml of this distro as a hashref. Note: works only after an attempt has been made to C<make> the distribution. Returns undef otherwise. Also returns undef if the content of META.yml is not authoritative. (The rules about what exactly makes the content authoritative are still in flux.) =item CPAN::Distribution::test() Changes to the directory where the distribution has been unpacked and runs C<make test> there. =item CPAN::Distribution::uptodate() Returns 1 if all the modules contained in the distribution are up-to-date. Relies on containsmods. =item CPAN::Index::force_reload() Forces a reload of all indices. =item CPAN::Index::reload() Reloads all indices if they have not been read for more than C<< $CPAN::Config->{index_expire} >> days. =item CPAN::InfoObj::dump() CPAN::Author, CPAN::Bundle, CPAN::Module, and CPAN::Distribution inherit this method. It prints the data structure associated with an object. Useful for debugging. Note: the data structure is considered internal and thus subject to change without notice. =item CPAN::Module::as_glimpse() Returns a one-line description of the module in four columns: The first column contains the word C<Module>, the second column consists of one character: an equals sign if this module is already installed and up-to-date, a less-than sign if this module is installed but can be upgraded, and a space if the module is not installed. The third column is the name of the module and the fourth column gives maintainer or distribution information. =item CPAN::Module::as_string() Returns a multi-line description of the module =item CPAN::Module::clean() Runs a clean on the distribution associated with this module. =item CPAN::Module::cpan_file() Returns the filename on CPAN that is associated with the module. =item CPAN::Module::cpan_version() Returns the latest version of this module available on CPAN. =item CPAN::Module::cvs_import() Runs a cvs_import on the distribution associated with this module. =item CPAN::Module::description() Returns a 44 character description of this module. Only available for modules listed in The Module List (CPAN/modules/00modlist.long.html or 00modlist.long.txt.gz) =item CPAN::Module::distribution() Returns the CPAN::Distribution object that contains the current version of this module. =item CPAN::Module::dslip_status() Returns a hash reference. The keys of the hash are the letters C<D>, C<S>, C<L>, C<I>, and <P>, for development status, support level, language, interface and public licence respectively. The data for the DSLIP status are collected by pause.perl.org when authors register their namespaces. The values of the 5 hash elements are one-character words whose meaning is described in the table below. There are also 5 hash elements C<DV>, C<SV>, C<LV>, C<IV>, and <PV> that carry a more verbose value of the 5 status variables. Where the 'DSLIP' characters have the following meanings: D - Development Stage (Note: *NO IMPLIED TIMESCALES*): i - Idea, listed to gain consensus or as a placeholder c - under construction but pre-alpha (not yet released) a/b - Alpha/Beta testing R - Released M - Mature (no rigorous definition) S - Standard, supplied with Perl 5 S - Support Level: m - Mailing-list d - Developer u - Usenet newsgroup comp.lang.perl.modules n - None known, try comp.lang.perl.modules a - abandoned; volunteers welcome to take over maintenance L - Language Used: p - Perl-only, no compiler needed, should be platform independent c - C and perl, a C compiler will be needed h - Hybrid, written in perl with optional C code, no compiler needed + - C++ and perl, a C++ compiler will be needed o - perl and another language other than C or C++ I - Interface Style f - plain Functions, no references used h - hybrid, object and function interfaces available n - no interface at all (huh?) r - some use of unblessed References or ties O - Object oriented using blessed references and/or inheritance P - Public License p - Standard-Perl: user may choose between GPL and Artistic g - GPL: GNU General Public License l - LGPL: "GNU Lesser General Public License" (previously known as "GNU Library General Public License") b - BSD: The BSD License a - Artistic license alone 2 - Artistic license 2.0 or later o - open source: approved by www.opensource.org d - allows distribution without restrictions r - restricted distribution n - no license at all =item CPAN::Module::force($method,@args) Forces CPAN to perform a task it would normally refuse to do. Force takes as arguments a method name to be invoked and any number of additional arguments to pass that method. The internals of the object get the needed changes so that CPAN.pm does not refuse to take the action. See also the section above on the C<force> and the C<fforce> pragma. =item CPAN::Module::get() Runs a get on the distribution associated with this module. =item CPAN::Module::inst_file() Returns the filename of the module found in @INC. The first file found is reported, just as perl itself stops searching @INC once it finds a module. =item CPAN::Module::available_file() Returns the filename of the module found in PERL5LIB or @INC. The first file found is reported. The advantage of this method over C<inst_file> is that modules that have been tested but not yet installed are included because PERL5LIB keeps track of tested modules. =item CPAN::Module::inst_version() Returns the version number of the installed module in readable format. =item CPAN::Module::available_version() Returns the version number of the available module in readable format. =item CPAN::Module::install() Runs an C<install> on the distribution associated with this module. =item CPAN::Module::look() Changes to the directory where the distribution associated with this module has been unpacked and opens a subshell there. Exiting the subshell returns. =item CPAN::Module::make() Runs a C<make> on the distribution associated with this module. =item CPAN::Module::manpage_headline() If module is installed, peeks into the module's manpage, reads the headline, and returns it. Moreover, if the module has been downloaded within this session, does the equivalent on the downloaded module even if it hasn't been installed yet. =item CPAN::Module::perldoc() Runs a C<perldoc> on this module. =item CPAN::Module::readme() Runs a C<readme> on the distribution associated with this module. =item CPAN::Module::reports() Calls the reports() method on the associated distribution object. =item CPAN::Module::test() Runs a C<test> on the distribution associated with this module. =item CPAN::Module::uptodate() Returns 1 if the module is installed and up-to-date. =item CPAN::Module::userid() Returns the author's ID of the module. =back =head2 Cache Manager Currently the cache manager only keeps track of the build directory ($CPAN::Config->{build_dir}). It is a simple FIFO mechanism that deletes complete directories below C<build_dir> as soon as the size of all directories there gets bigger than $CPAN::Config->{build_cache} (in MB). The contents of this cache may be used for later re-installations that you intend to do manually, but will never be trusted by CPAN itself. This is due to the fact that the user might use these directories for building modules on different architectures. There is another directory ($CPAN::Config->{keep_source_where}) where the original distribution files are kept. This directory is not covered by the cache manager and must be controlled by the user. If you choose to have the same directory as build_dir and as keep_source_where directory, then your sources will be deleted with the same fifo mechanism. =head2 Bundles A bundle is just a perl module in the namespace Bundle:: that does not define any functions or methods. It usually only contains documentation. It starts like a perl module with a package declaration and a $VERSION variable. After that the pod section looks like any other pod with the only difference being that I<one special pod section> exists starting with (verbatim): =head1 CONTENTS In this pod section each line obeys the format Module_Name [Version_String] [- optional text] The only required part is the first field, the name of a module (e.g. Foo::Bar, i.e. I<not> the name of the distribution file). The rest of the line is optional. The comment part is delimited by a dash just as in the man page header. The distribution of a bundle should follow the same convention as other distributions. Bundles are treated specially in the CPAN package. If you say 'install Bundle::Tkkit' (assuming such a bundle exists), CPAN will install all the modules in the CONTENTS section of the pod. You can install your own Bundles locally by placing a conformant Bundle file somewhere into your @INC path. The autobundle() command which is available in the shell interface does that for you by including all currently installed modules in a snapshot bundle file. =head1 PREREQUISITES The CPAN program is trying to depend on as little as possible so the user can use it in hostile environment. It works better the more goodies the environment provides. For example if you try in the CPAN shell install Bundle::CPAN or install Bundle::CPANxxl you will find the shell more convenient than the bare shell before. If you have a local mirror of CPAN and can access all files with "file:" URLs, then you only need a perl later than perl5.003 to run this module. Otherwise Net::FTP is strongly recommended. LWP may be required for non-UNIX systems, or if your nearest CPAN site is associated with a URL that is not C<ftp:>. If you have neither Net::FTP nor LWP, there is a fallback mechanism implemented for an external ftp command or for an external lynx command. =head1 UTILITIES =head2 Finding packages and VERSION This module presumes that all packages on CPAN =over 2 =item * declare their $VERSION variable in an easy to parse manner. This prerequisite can hardly be relaxed because it consumes far too much memory to load all packages into the running program just to determine the $VERSION variable. Currently all programs that are dealing with version use something like this perl -MExtUtils::MakeMaker -le \ 'print MM->parse_version(shift)' filename If you are author of a package and wonder if your $VERSION can be parsed, please try the above method. =item * come as compressed or gzipped tarfiles or as zip files and contain a C<Makefile.PL> or C<Build.PL> (well, we try to handle a bit more, but with little enthusiasm). =back =head2 Debugging Debugging this module is more than a bit complex due to interference from the software producing the indices on CPAN, the mirroring process on CPAN, packaging, configuration, synchronicity, and even (gasp!) due to bugs within the CPAN.pm module itself. For debugging the code of CPAN.pm itself in interactive mode, some debugging aid can be turned on for most packages within CPAN.pm with one of =over 2 =item o debug package... sets debug mode for packages. =item o debug -package... unsets debug mode for packages. =item o debug all turns debugging on for all packages. =item o debug number =back which sets the debugging packages directly. Note that C<o debug 0> turns debugging off. What seems a successful strategy is the combination of C<reload cpan> and the debugging switches. Add a new debug statement while running in the shell and then issue a C<reload cpan> and see the new debugging messages immediately without losing the current context. C<o debug> without an argument lists the valid package names and the current set of packages in debugging mode. C<o debug> has built-in completion support. For debugging of CPAN data there is the C<dump> command which takes the same arguments as make/test/install and outputs each object's Data::Dumper dump. If an argument looks like a perl variable and contains one of C<$>, C<@> or C<%>, it is eval()ed and fed to Data::Dumper directly. =head2 Floppy, Zip, Offline Mode CPAN.pm works nicely without network access, too. If you maintain machines that are not networked at all, you should consider working with C<file:> URLs. You'll have to collect your modules somewhere first. So you might use CPAN.pm to put together all you need on a networked machine. Then copy the $CPAN::Config->{keep_source_where} (but not $CPAN::Config->{build_dir}) directory on a floppy. This floppy is kind of a personal CPAN. CPAN.pm on the non-networked machines works nicely with this floppy. See also below the paragraph about CD-ROM support. =head2 Basic Utilities for Programmers =over 2 =item has_inst($module) Returns true if the module is installed. Used to load all modules into the running CPAN.pm that are considered optional. The config variable C<dontload_list> intercepts the C<has_inst()> call such that an optional module is not loaded despite being available. For example, the following command will prevent C<YAML.pm> from being loaded: cpan> o conf dontload_list push YAML See the source for details. =item use_inst($module) Similary to L<has_inst()> tries to load optional library but also dies if library is not available =item has_usable($module) Returns true if the module is installed and in a usable state. Only useful for a handful of modules that are used internally. See the source for details. =item instance($module) The constructor for all the singletons used to represent modules, distributions, authors, and bundles. If the object already exists, this method returns the object; otherwise, it calls the constructor. =item frontend() =item frontend($new_frontend) Getter/setter for frontend object. Method just allows to subclass CPAN.pm. =back =head1 SECURITY There's no strong security layer in CPAN.pm. CPAN.pm helps you to install foreign, unmasked, unsigned code on your machine. We compare to a checksum that comes from the net just as the distribution file itself. But we try to make it easy to add security on demand: =head2 Cryptographically signed modules Since release 1.77, CPAN.pm has been able to verify cryptographically signed module distributions using Module::Signature. The CPAN modules can be signed by their authors, thus giving more security. The simple unsigned MD5 checksums that were used before by CPAN protect mainly against accidental file corruption. You will need to have Module::Signature installed, which in turn requires that you have at least one of Crypt::OpenPGP module or the command-line F<gpg> tool installed. You will also need to be able to connect over the Internet to the public key servers, like pgp.mit.edu, and their port 11731 (the HKP protocol). The configuration parameter check_sigs is there to turn signature checking on or off. =head1 EXPORT Most functions in package CPAN are exported by default. The reason for this is that the primary use is intended for the cpan shell or for one-liners. =head1 ENVIRONMENT When the CPAN shell enters a subshell via the look command, it sets the environment CPAN_SHELL_LEVEL to 1, or increments that variable if it is already set. When CPAN runs, it sets the environment variable PERL5_CPAN_IS_RUNNING to the ID of the running process. It also sets PERL5_CPANPLUS_IS_RUNNING to prevent runaway processes which could happen with older versions of Module::Install. When running C<perl Makefile.PL>, the environment variable C<PERL5_CPAN_IS_EXECUTING> is set to the full path of the C<Makefile.PL> that is being executed. This prevents runaway processes with newer versions of Module::Install. When the config variable ftp_passive is set, all downloads will be run with the environment variable FTP_PASSIVE set to this value. This is in general a good idea as it influences both Net::FTP and LWP based connections. The same effect can be achieved by starting the cpan shell with this environment variable set. For Net::FTP alone, one can also always set passive mode by running libnetcfg. =head1 POPULATE AN INSTALLATION WITH LOTS OF MODULES Populating a freshly installed perl with one's favorite modules is pretty easy if you maintain a private bundle definition file. To get a useful blueprint of a bundle definition file, the command autobundle can be used on the CPAN shell command line. This command writes a bundle definition file for all modules installed for the current perl interpreter. It's recommended to run this command once only, and from then on maintain the file manually under a private name, say Bundle/my_bundle.pm. With a clever bundle file you can then simply say cpan> install Bundle::my_bundle then answer a few questions and go out for coffee (possibly even in a different city). Maintaining a bundle definition file means keeping track of two things: dependencies and interactivity. CPAN.pm sometimes fails on calculating dependencies because not all modules define all MakeMaker attributes correctly, so a bundle definition file should specify prerequisites as early as possible. On the other hand, it's annoying that so many distributions need some interactive configuring. So what you can try to accomplish in your private bundle file is to have the packages that need to be configured early in the file and the gentle ones later, so you can go out for coffee after a few minutes and leave CPAN.pm to churn away unattended. =head1 WORKING WITH CPAN.pm BEHIND FIREWALLS Thanks to Graham Barr for contributing the following paragraphs about the interaction between perl, and various firewall configurations. For further information on firewalls, it is recommended to consult the documentation that comes with the I<ncftp> program. If you are unable to go through the firewall with a simple Perl setup, it is likely that you can configure I<ncftp> so that it works through your firewall. =head2 Three basic types of firewalls Firewalls can be categorized into three basic types. =over 4 =item http firewall This is when the firewall machine runs a web server, and to access the outside world, you must do so via that web server. If you set environment variables like http_proxy or ftp_proxy to values beginning with http://, or in your web browser you've proxy information set, then you know you are running behind an http firewall. To access servers outside these types of firewalls with perl (even for ftp), you need LWP or HTTP::Tiny. =item ftp firewall This where the firewall machine runs an ftp server. This kind of firewall will only let you access ftp servers outside the firewall. This is usually done by connecting to the firewall with ftp, then entering a username like "user@outside.host.com". To access servers outside these type of firewalls with perl, you need Net::FTP. =item One-way visibility One-way visibility means these firewalls try to make themselves invisible to users inside the firewall. An FTP data connection is normally created by sending your IP address to the remote server and then listening for the return connection. But the remote server will not be able to connect to you because of the firewall. For these types of firewall, FTP connections need to be done in a passive mode. There are two that I can think off. =over 4 =item SOCKS If you are using a SOCKS firewall, you will need to compile perl and link it with the SOCKS library. This is what is normally called a 'socksified' perl. With this executable you will be able to connect to servers outside the firewall as if it were not there. =item IP Masquerade This is when the firewall implemented in the kernel (via NAT, or networking address translation), it allows you to hide a complete network behind one IP address. With this firewall no special compiling is needed as you can access hosts directly. For accessing ftp servers behind such firewalls you usually need to set the environment variable C<FTP_PASSIVE> or the config variable ftp_passive to a true value. =back =back =head2 Configuring lynx or ncftp for going through a firewall If you can go through your firewall with e.g. lynx, presumably with a command such as /usr/local/bin/lynx -pscott:tiger then you would configure CPAN.pm with the command o conf lynx "/usr/local/bin/lynx -pscott:tiger" That's all. Similarly for ncftp or ftp, you would configure something like o conf ncftp "/usr/bin/ncftp -f /home/scott/ncftplogin.cfg" Your mileage may vary... =head1 FAQ =over 4 =item 1) I installed a new version of module X but CPAN keeps saying, I have the old version installed Probably you B<do> have the old version installed. This can happen if a module installs itself into a different directory in the @INC path than it was previously installed. This is not really a CPAN.pm problem, you would have the same problem when installing the module manually. The easiest way to prevent this behaviour is to add the argument C<UNINST=1> to the C<make install> call, and that is why many people add this argument permanently by configuring o conf make_install_arg UNINST=1 =item 2) So why is UNINST=1 not the default? Because there are people who have their precise expectations about who may install where in the @INC path and who uses which @INC array. In fine tuned environments C<UNINST=1> can cause damage. =item 3) I want to clean up my mess, and install a new perl along with all modules I have. How do I go about it? Run the autobundle command for your old perl and optionally rename the resulting bundle file (e.g. Bundle/mybundle.pm), install the new perl with the Configure option prefix, e.g. ./Configure -Dprefix=/usr/local/perl-5.6.78.9 Install the bundle file you produced in the first step with something like cpan> install Bundle::mybundle and you're done. =item 4) When I install bundles or multiple modules with one command there is too much output to keep track of. You may want to configure something like o conf make_arg "| tee -ai /root/.cpan/logs/make.out" o conf make_install_arg "| tee -ai /root/.cpan/logs/make_install.out" so that STDOUT is captured in a file for later inspection. =item 5) I am not root, how can I install a module in a personal directory? As of CPAN 1.9463, if you do not have permission to write the default perl library directories, CPAN's configuration process will ask you whether you want to bootstrap <local::lib>, which makes keeping a personal perl library directory easy. Another thing you should bear in mind is that the UNINST parameter can be dangerous when you are installing into a private area because you might accidentally remove modules that other people depend on that are not using the private area. =item 6) How to get a package, unwrap it, and make a change before building it? Have a look at the C<look> (!) command. =item 7) I installed a Bundle and had a couple of fails. When I retried, everything resolved nicely. Can this be fixed to work on first try? The reason for this is that CPAN does not know the dependencies of all modules when it starts out. To decide about the additional items to install, it just uses data found in the META.yml file or the generated Makefile. An undetected missing piece breaks the process. But it may well be that your Bundle installs some prerequisite later than some depending item and thus your second try is able to resolve everything. Please note, CPAN.pm does not know the dependency tree in advance and cannot sort the queue of things to install in a topologically correct order. It resolves perfectly well B<if> all modules declare the prerequisites correctly with the PREREQ_PM attribute to MakeMaker or the C<requires> stanza of Module::Build. For bundles which fail and you need to install often, it is recommended to sort the Bundle definition file manually. =item 8) In our intranet, we have many modules for internal use. How can I integrate these modules with CPAN.pm but without uploading the modules to CPAN? Have a look at the CPAN::Site module. =item 9) When I run CPAN's shell, I get an error message about things in my C</etc/inputrc> (or C<~/.inputrc>) file. These are readline issues and can only be fixed by studying readline configuration on your architecture and adjusting the referenced file accordingly. Please make a backup of the C</etc/inputrc> or C<~/.inputrc> and edit them. Quite often harmless changes like uppercasing or lowercasing some arguments solves the problem. =item 10) Some authors have strange characters in their names. Internally CPAN.pm uses the UTF-8 charset. If your terminal is expecting ISO-8859-1 charset, a converter can be activated by setting term_is_latin to a true value in your config file. One way of doing so would be cpan> o conf term_is_latin 1 If other charset support is needed, please file a bug report against CPAN.pm at rt.cpan.org and describe your needs. Maybe we can extend the support or maybe UTF-8 terminals become widely available. Note: this config variable is deprecated and will be removed in a future version of CPAN.pm. It will be replaced with the conventions around the family of $LANG and $LC_* environment variables. =item 11) When an install fails for some reason and then I correct the error condition and retry, CPAN.pm refuses to install the module, saying C<Already tried without success>. Use the force pragma like so force install Foo::Bar Or you can use look Foo::Bar and then C<make install> directly in the subshell. =item 12) How do I install a "DEVELOPER RELEASE" of a module? By default, CPAN will install the latest non-developer release of a module. If you want to install a dev release, you have to specify the partial path starting with the author id to the tarball you wish to install, like so: cpan> install KWILLIAMS/Module-Build-0.27_07.tar.gz Note that you can use the C<ls> command to get this path listed. =item 13) How do I install a module and all its dependencies from the commandline, without being prompted for anything, despite my CPAN configuration (or lack thereof)? CPAN uses ExtUtils::MakeMaker's prompt() function to ask its questions, so if you set the PERL_MM_USE_DEFAULT environment variable, you shouldn't be asked any questions at all (assuming the modules you are installing are nice about obeying that variable as well): % PERL_MM_USE_DEFAULT=1 perl -MCPAN -e 'install My::Module' =item 14) How do I create a Module::Build based Build.PL derived from an ExtUtils::MakeMaker focused Makefile.PL? http://search.cpan.org/dist/Module-Build-Convert/ =item 15) I'm frequently irritated with the CPAN shell's inability to help me select a good mirror. CPAN can now help you select a "good" mirror, based on which ones have the lowest 'ping' round-trip times. From the shell, use the command 'o conf init urllist' and allow CPAN to automatically select mirrors for you. Beyond that help, the urllist config parameter is yours. You can add and remove sites at will. You should find out which sites have the best up-to-dateness, bandwidth, reliability, etc. and are topologically close to you. Some people prefer fast downloads, others up-to-dateness, others reliability. You decide which to try in which order. Henk P. Penning maintains a site that collects data about CPAN sites: http://mirrors.cpan.org/ Also, feel free to play with experimental features. Run o conf init randomize_urllist ftpstats_period ftpstats_size and choose your favorite parameters. After a few downloads running the C<hosts> command will probably assist you in choosing the best mirror sites. =item 16) Why do I get asked the same questions every time I start the shell? You can make your configuration changes permanent by calling the command C<o conf commit>. Alternatively set the C<auto_commit> variable to true by running C<o conf init auto_commit> and answering the following question with yes. =item 17) Older versions of CPAN.pm had the original root directory of all tarballs in the build directory. Now there are always random characters appended to these directory names. Why was this done? The random characters are provided by File::Temp and ensure that each module's individual build directory is unique. This makes running CPAN.pm in concurrent processes simultaneously safe. =item 18) Speaking of the build directory. Do I have to clean it up myself? You have the choice to set the config variable C<scan_cache> to C<never>. Then you must clean it up yourself. The other possible values, C<atstart> and C<atexit> clean up the build directory when you start (or more precisely, after the first extraction into the build directory) or exit the CPAN shell, respectively. If you never start up the CPAN shell, you probably also have to clean up the build directory yourself. =item 19) How can I switch to sudo instead of local::lib? The following 5 environment veriables need to be reset to the previous values: PATH, PERL5LIB, PERL_LOCAL_LIB_ROOT, PERL_MB_OPT, PERL_MM_OPT; and these two CPAN.pm config variables must be reconfigured: make_install_make_command and mbuild_install_build_command. The five env variables have probably been overwritten in your $HOME/.bashrc or some equivalent. You either find them there and delete their traces and logout/login or you override them temporarily, depending on your exact desire. The two cpanpm config variables can be set with: o conf init /install_.*_command/ probably followed by o conf commit =back =head1 COMPATIBILITY =head2 OLD PERL VERSIONS CPAN.pm is regularly tested to run under 5.005 and assorted newer versions. It is getting more and more difficult to get the minimal prerequisites working on older perls. It is close to impossible to get the whole Bundle::CPAN working there. If you're in the position to have only these old versions, be advised that CPAN is designed to work fine without the Bundle::CPAN installed. To get things going, note that GBARR/Scalar-List-Utils-1.18.tar.gz is compatible with ancient perls and that File::Temp is listed as a prerequisite but CPAN has reasonable workarounds if it is missing. =head2 CPANPLUS This module and its competitor, the CPANPLUS module, are both much cooler than the other. CPAN.pm is older. CPANPLUS was designed to be more modular, but it was never intended to be compatible with CPAN.pm. =head2 CPANMINUS In the year 2010 App::cpanminus was launched as a new approach to a cpan shell with a considerably smaller footprint. Very cool stuff. =head1 SECURITY ADVICE This software enables you to upgrade software on your computer and so is inherently dangerous because the newly installed software may contain bugs and may alter the way your computer works or even make it unusable. Please consider backing up your data before every upgrade. =head1 BUGS Please report bugs via L<http://rt.cpan.org/> Before submitting a bug, please make sure that the traditional method of building a Perl module package from a shell by following the installation instructions of that package still works in your environment. =head1 AUTHOR Andreas Koenig C<< <andk@cpan.org> >> =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L<http://www.perl.com/perl/misc/Artistic.html> =head1 TRANSLATIONS Kawai,Takanori provides a Japanese translation of a very old version of this manpage at L<http://homepage3.nifty.com/hippo2000/perltips/CPAN.htm> =head1 SEE ALSO Many people enter the CPAN shell by running the L<cpan> utility program which is installed in the same directory as perl itself. So if you have this directory in your PATH variable (or some equivalent in your operating system) then typing C<cpan> in a console window will work for you as well. Above that the utility provides several commandline shortcuts. melezhik (Alexey) sent me a link where he published a chef recipe to work with CPAN.pm: http://community.opscode.com/cookbooks/cpan. =cut PK�����A0]$m6��m6����man/man3/version.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "version 3" .TH version 3 "2022-07-15" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" version \- Perl extension for Version Objects .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& # Parsing version strings (decimal or dotted\-decimal) \& \& use version 0.77; # get latest bug\-fixes and API \& $ver = version\->parse($string) \& \& # Declaring a dotted\-decimal $VERSION (keep on one line!) \& \& use version; our $VERSION = version\->declare("v1.2.3"); # formal \& use version; our $VERSION = qv("v1.2.3"); # deprecated \& use version; our $VERSION = qv("v1.2_3"); # deprecated \& \& # Declaring an old\-style decimal $VERSION (use quotes!) \& \& our $VERSION = "1.0203"; # recommended \& use version; our $VERSION = version\->parse("1.0203"); # formal \& use version; our $VERSION = version\->parse("1.02_03"); # alpha \& \& # Comparing mixed version styles (decimals, dotted\-decimals, objects) \& \& if ( version\->parse($v1) == version\->parse($v2) ) { \& # do stuff \& } \& \& # Sorting mixed version styles \& \& @ordered = sort { version\->parse($a) <=> version\->parse($b) } @list; .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" Version objects were added to Perl in 5.10. This module implements version objects for older version of Perl and provides the version object \s-1API\s0 for all versions of Perl. All previous releases before 0.74 are deprecated and should not be used due to incompatible \s-1API\s0 changes. Version 0.77 introduces the new \&'parse' and 'declare' methods to standardize usage. You are strongly urged to set 0.77 as a minimum in your code, e.g. .PP .Vb 1 \& use version 0.77; # even for Perl v.5.10.0 .Ve .SH "TYPES OF VERSION OBJECTS" .IX Header "TYPES OF VERSION OBJECTS" There are two different types of version objects, corresponding to the two different styles of versions in use: .IP "Decimal Versions" 2 .IX Item "Decimal Versions" The classic floating-point number \f(CW$VERSION\fR. The advantage to this style is that you don't need to do anything special, just type a number into your source file. Quoting is recommended, as it ensures that trailing zeroes (\*(L"1.50\*(R") are preserved in any warnings or other output. .IP "Dotted Decimal Versions" 2 .IX Item "Dotted Decimal Versions" The more modern form of version assignment, with 3 (or potentially more) integers separated by decimal points (e.g. v1.2.3). This is the form that Perl itself has used since 5.6.0 was released. The leading 'v' is now strongly recommended for clarity, and will throw a warning in a future release if omitted. A leading 'v' character is required to pass the \&\*(L"\fBis_strict()\fR\*(R" test. .SH "DECLARING VERSIONS" .IX Header "DECLARING VERSIONS" If you have a module that uses a decimal \f(CW$VERSION\fR (floating point), and you do not intend to ever change that, this module is not for you. There is nothing that version.pm gains you over a simple \f(CW$VERSION\fR assignment: .PP .Vb 1 \& our $VERSION = "1.02"; .Ve .PP Since Perl v5.10.0 includes the version.pm comparison logic anyways, you don't need to do anything at all. .SS "How to convert a module from decimal to dotted-decimal" .IX Subsection "How to convert a module from decimal to dotted-decimal" If you have used a decimal \f(CW$VERSION\fR in the past and wish to switch to a dotted-decimal \f(CW$VERSION\fR, then you need to make a one-time conversion to the new format. .PP \&\fBImportant Note\fR: you must ensure that your new \f(CW$VERSION\fR is numerically greater than your current decimal \f(CW$VERSION\fR; this is not always obvious. First, convert your old decimal version (e.g. 1.02) to a normalized dotted-decimal form: .PP .Vb 2 \& $ perl \-Mversion \-e \*(Aqprint version\->parse("1.02")\->normal\*(Aq \& v1.20.0 .Ve .PP Then increment any of the dotted-decimal components (v1.20.1 or v1.21.0). .ie n .SS "How to ""declare()"" a dotted-decimal version" .el .SS "How to \f(CWdeclare()\fP a dotted-decimal version" .IX Subsection "How to declare() a dotted-decimal version" .Vb 1 \& use version; our $VERSION = version\->declare("v1.2.3"); .Ve .PP The \f(CW\*(C`declare()\*(C'\fR method always creates dotted-decimal version objects. When used in a module, you \fBmust\fR put it on the same line as \*(L"use version\*(R" to ensure that \f(CW$VERSION\fR is read correctly by \s-1PAUSE\s0 and installer tools. You should also add 'version' to the 'configure_requires' section of your module metadata file. See instructions in ExtUtils::MakeMaker or Module::Build for details. .PP \&\fBImportant Note\fR: Even if you pass in what looks like a decimal number (\*(L"1.2\*(R"), a dotted-decimal will be created (\*(L"v1.200.0\*(R"). To avoid confusion or unintentional errors on older Perls, follow these guidelines: .IP "\(bu" 2 Always use a dotted-decimal with (at least) three components .IP "\(bu" 2 Always use a leading-v .IP "\(bu" 2 Always quote the version .PP If you really insist on using version.pm with an ordinary decimal version, use \f(CW\*(C`parse()\*(C'\fR instead of declare. See the \*(L"\s-1PARSING AND COMPARING VERSIONS\*(R"\s0 for details. .PP See also version::Internals for more on version number conversion, quoting, calculated version numbers and declaring developer or \*(L"alpha\*(R" version numbers. .SH "PARSING AND COMPARING VERSIONS" .IX Header "PARSING AND COMPARING VERSIONS" If you need to compare version numbers, but can't be sure whether they are expressed as numbers, strings, v\-strings or version objects, then you should use version.pm to parse them all into objects for comparison. .ie n .SS "How to ""parse()"" a version" .el .SS "How to \f(CWparse()\fP a version" .IX Subsection "How to parse() a version" The \f(CW\*(C`parse()\*(C'\fR method takes in anything that might be a version and returns a corresponding version object, doing any necessary conversion along the way. .IP "\(bu" 2 Dotted-decimal: bare v\-strings (v1.2.3) and strings with more than one decimal point and a leading 'v' (\*(L"v1.2.3\*(R"); \s-1NOTE\s0 you can technically use a v\-string or strings with a leading-v and only one decimal point (v1.2 or \&\*(L"v1.2\*(R"), but you will confuse both yourself and others. .IP "\(bu" 2 Decimal: regular decimal numbers (literal or in a string) .PP Some examples: .PP .Vb 8 \& $variable version\->parse($variable) \& \-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \& 1.23 v1.230.0 \& "1.23" v1.230.0 \& v1.23 v1.23.0 \& "v1.23" v1.23.0 \& "1.2.3" v1.2.3 \& "v1.2.3" v1.2.3 .Ve .PP See version::Internals for more on version number conversion. .SS "How to check for a legal version string" .IX Subsection "How to check for a legal version string" If you do not want to actually create a full blown version object, but would still like to verify that a given string meets the criteria to be parsed as a version, there are two helper functions that can be employed directly: .ie n .IP """is_lax()""" 4 .el .IP "\f(CWis_lax()\fR" 4 .IX Item "is_lax()" The lax criteria corresponds to what is currently allowed by the version parser. All of the following formats are acceptable for dotted-decimal formats strings: .Sp .Vb 5 \& v1.2 \& 1.2345.6 \& v1.23_4 \& 1.2345 \& 1.2345_01 .Ve .ie n .IP """is_strict()""" 4 .el .IP "\f(CWis_strict()\fR" 4 .IX Item "is_strict()" If you want to limit yourself to a much more narrow definition of what a version string constitutes, \f(CW\*(C`is_strict()\*(C'\fR is limited to version strings like the following list: .Sp .Vb 2 \& v1.234.5 \& 2.3456 .Ve .PP See version::Internals for details of the regular expressions that define the legal version string forms, as well as how to use those regular expressions in your own code if \f(CW\*(C`is_lax()\*(C'\fR and \&\f(CW\*(C`is_strict()\*(C'\fR are not sufficient for your needs. .SS "How to compare version objects" .IX Subsection "How to compare version objects" Version objects overload the \f(CW\*(C`cmp\*(C'\fR and \f(CW\*(C`<=>\*(C'\fR operators. Perl automatically generates all of the other comparison operators based on those two so all the normal logical comparisons will work. .PP .Vb 3 \& if ( version\->parse($v1) == version\->parse($v2) ) { \& # do stuff \& } .Ve .PP If a version object is compared against a non-version object, the non-object term will be converted to a version object using \f(CW\*(C`parse()\*(C'\fR. This may give surprising results: .PP .Vb 2 \& $v1 = version\->parse("v0.95.0"); \& $bool = $v1 < 0.94; # TRUE since 0.94 is v0.940.0 .Ve .PP Always comparing to a version object will help avoid surprises: .PP .Vb 1 \& $bool = $v1 < version\->parse("v0.94.0"); # FALSE .Ve .PP Note that \*(L"alpha\*(R" version objects (where the version string contains a trailing underscore segment) compare as less than the equivalent version without an underscore: .PP .Vb 1 \& $bool = version\->parse("1.23_45") < version\->parse("1.2345"); # TRUE .Ve .PP See version::Internals for more details on \*(L"alpha\*(R" versions. .SH "OBJECT METHODS" .IX Header "OBJECT METHODS" .SS "\fBis_alpha()\fP" .IX Subsection "is_alpha()" True if and only if the version object was created with a underscore, e.g. .PP .Vb 2 \& version\->parse(\*(Aq1.002_03\*(Aq)\->is_alpha; # TRUE \& version\->declare(\*(Aq1.2.3_4\*(Aq)\->is_alpha; # TRUE .Ve .SS "\fBis_qv()\fP" .IX Subsection "is_qv()" True only if the version object is a dotted-decimal version, e.g. .PP .Vb 4 \& version\->parse(\*(Aqv1.2.0\*(Aq)\->is_qv; # TRUE \& version\->declare(\*(Aqv1.2\*(Aq)\->is_qv; # TRUE \& qv(\*(Aq1.2\*(Aq)\->is_qv; # TRUE \& version\->parse(\*(Aq1.2\*(Aq)\->is_qv; # FALSE .Ve .SS "\fBnormal()\fP" .IX Subsection "normal()" Returns a string with a standard 'normalized' dotted-decimal form with a leading-v and at least 3 components. .PP .Vb 2 \& version\->declare(\*(Aqv1.2\*(Aq)\->normal; # v1.2.0 \& version\->parse(\*(Aq1.2\*(Aq)\->normal; # v1.200.0 .Ve .SS "\fBnumify()\fP" .IX Subsection "numify()" Returns a value representing the object in a pure decimal. .PP .Vb 2 \& version\->declare(\*(Aqv1.2\*(Aq)\->numify; # 1.002000 \& version\->parse(\*(Aq1.2\*(Aq)\->numify; # 1.200 .Ve .SS "\fBstringify()\fP" .IX Subsection "stringify()" Returns a string that is as close to the original representation as possible. If the original representation was a numeric literal, it will be returned the way perl would normally represent it in a string. This method is used whenever a version object is interpolated into a string. .PP .Vb 3 \& version\->declare(\*(Aqv1.2\*(Aq)\->stringify; # v1.2 \& version\->parse(\*(Aq1.200\*(Aq)\->stringify; # 1.2 \& version\->parse(1.02_30)\->stringify; # 1.023 .Ve .SH "EXPORTED FUNCTIONS" .IX Header "EXPORTED FUNCTIONS" .SS "\fBqv()\fP" .IX Subsection "qv()" This function is no longer recommended for use, but is maintained for compatibility with existing code. If you do not want to have it exported to your namespace, use this form: .PP .Vb 1 \& use version 0.77 (); .Ve .SS "\fBis_lax()\fP" .IX Subsection "is_lax()" (Not exported by default) .PP This function takes a scalar argument and returns a boolean value indicating whether the argument meets the \*(L"lax\*(R" rules for a version number. Leading and trailing spaces are not allowed. .SS "\fBis_strict()\fP" .IX Subsection "is_strict()" (Not exported by default) .PP This function takes a scalar argument and returns a boolean value indicating whether the argument meets the \*(L"strict\*(R" rules for a version number. Leading and trailing spaces are not allowed. .SH "AUTHOR" .IX Header "AUTHOR" John Peacock <jpeacock@cpan.org> .SH "SEE ALSO" .IX Header "SEE ALSO" version::Internals. .PP perl. PK�����A0]u�Tf ��f ����man/man3/CPAN::Queue.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "CPAN::Queue 3" .TH CPAN::Queue 3 "2020-05-19" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" CPAN::Queue \- internal queue support for CPAN.pm .SH "LICENSE" .IX Header "LICENSE" This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. PK�����A0] �� ����man/man3/IO::Tty.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "Tty 3" .TH Tty 3 "2023-12-28" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" IO::Tty \- Low\-level allocate a pseudo\-Tty, import constants. .SH "VERSION" .IX Header "VERSION" 1.20 .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 3 \& use IO::Tty qw(TIOCNOTTY); \& ... \& # use only to import constants, see IO::Pty to create ptys. .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\f(CW\*(C`IO::Tty\*(C'\fR is used internally by \f(CW\*(C`IO::Pty\*(C'\fR to create a pseudo-tty. You wouldn't want to use it directly except to import constants, use \&\f(CW\*(C`IO::Pty\*(C'\fR. For a list of importable constants, see IO::Tty::Constant. .PP Windows is now supported, but \s-1ONLY\s0 under the Cygwin environment, see <http://sources.redhat.com/cygwin/>. .PP Please note that pty creation is very system-dependend. From my experience, any modern \s-1POSIX\s0 system should be fine. Find below a list of systems that \f(CW\*(C`IO::Tty\*(C'\fR should work on. A more detailed table (which is slowly getting out-of-date) is available from the project pages document manager at SourceForge <http://sourceforge.net/projects/expectperl/>. .PP If you have problems on your system and your system is listed in the \&\*(L"verified\*(R" list, you probably have some non-standard setup, e.g. you compiled your Linux-kernel yourself and disabled ptys (bummer!). Please ask your friendly sysadmin for help. .PP If your system is not listed, unpack the latest version of \f(CW\*(C`IO::Tty\*(C'\fR, do a \f(CW\*(Aqperl Makefile.PL; make; make test; uname \-a\*(Aq\fR and send me (\fIRGiersig@cpan.org\fR) the results and I'll see what I can deduce from that. There are chances that it will work right out-of-the-box... .PP If it's working on your system, please send me a short note with details (version number, distribution, etc. 'uname \-a' and 'perl \-V' is a good start; also, the output from \*(L"perl Makefile.PL\*(R" contains a lot of interesting info, so please include that as well) so I can get an overview. Thanks! .SH "VERIFIED SYSTEMS, KNOWN ISSUES" .IX Header "VERIFIED SYSTEMS, KNOWN ISSUES" This is a list of systems that \f(CW\*(C`IO::Tty\*(C'\fR seems to work on ('make test' passes) with comments about \*(L"features\*(R": .IP "\(bu" 4 \&\s-1AIX 4.3\s0 .Sp Returns \s-1EIO\s0 instead of \s-1EOF\s0 when the slave is closed. Benign. .IP "\(bu" 4 \&\s-1AIX 5\s0.x .IP "\(bu" 4 FreeBSD 4.4 .Sp \&\s-1EOF\s0 on the slave tty is not reported back to the master. .IP "\(bu" 4 OpenBSD 2.8 .Sp The ioctl \s-1TIOCSCTTY\s0 sometimes fails. This is also known in Tcl/Expect, see http://expect.nist.gov/FAQ.html .Sp \&\s-1EOF\s0 on the slave tty is not reported back to the master. .IP "\(bu" 4 Darwin 7.9.0 .IP "\(bu" 4 \&\s-1HPUX 10.20 & 11.00\s0 .Sp \&\s-1EOF\s0 on the slave tty is not reported back to the master. .IP "\(bu" 4 \&\s-1IRIX 6.5\s0 .IP "\(bu" 4 Linux 2.2.x & 2.4.x .Sp Returns \s-1EIO\s0 instead of \s-1EOF\s0 when the slave is closed. Benign. .IP "\(bu" 4 \&\s-1OSF 4.0\s0 .Sp \&\s-1EOF\s0 on the slave tty is not reported back to the master. .IP "\(bu" 4 Solaris 8, 2.7, 2.6 .Sp Has the \*(L"feature\*(R" of returning \s-1EOF\s0 just once?! .Sp \&\s-1EOF\s0 on the slave tty is not reported back to the master. .IP "\(bu" 4 Windows NT/2k/XP (under Cygwin) .Sp When you send (print) a too long line (>160 chars) to a non-raw pty, the call just hangs forever and even \fBalarm()\fR cannot get you out. Don't complain to me... .Sp \&\s-1EOF\s0 on the slave tty is not reported back to the master. .IP "\(bu" 4 z/OS .PP The following systems have not been verified yet for this version, but a previous version worked on them: .IP "\(bu" 4 \&\s-1SCO\s0 Unix .IP "\(bu" 4 NetBSD .Sp probably the same as the other *BSDs... .PP If you have additions to these lists, please mail them to <\fIRGiersig@cpan.org\fR>. .SH "SEE ALSO" .IX Header "SEE ALSO" IO::Pty, IO::Tty::Constant .SH "MAILING LISTS" .IX Header "MAILING LISTS" As this module is mainly used by Expect, support for it is available via the two Expect mailing lists, expectperl-announce and expectperl-discuss, at .PP .Vb 1 \& http://lists.sourceforge.net/lists/listinfo/expectperl\-announce .Ve .PP and .PP .Vb 1 \& http://lists.sourceforge.net/lists/listinfo/expectperl\-discuss .Ve .SH "AUTHORS" .IX Header "AUTHORS" Originally by Graham Barr <\fIgbarr@pobox.com\fR>, based on the Ptty module by Nick Ing-Simmons <\fInik@tiuk.ti.com\fR>. .PP Now maintained and heavily rewritten by Roland Giersig <\fIRGiersig@cpan.org\fR>. .PP Contains copyrighted stuff from openssh v3.0p1, authored by Tatu Ylonen <ylo@cs.hut.fi>, Markus Friedl and Todd C. Miller <Todd.Miller@courtesan.com>. I also got a lot of inspiration from the pty code in Xemacs. .SH "COPYRIGHT" .IX Header "COPYRIGHT" Now all code is free software; you can redistribute it and/or modify it under the same terms as Perl itself. .PP Nevertheless the above \s-1AUTHORS\s0 retain their copyrights to the various parts and want to receive credit if their source code is used. See the source for details. .SH "DISCLAIMER" .IX Header "DISCLAIMER" \&\s-1THIS SOFTWARE IS PROVIDED\s0 ``\s-1AS IS\s0'' \s-1AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\s0 (\s-1INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES\s0; \s-1LOSS OF USE, DATA, OR PROFITS\s0; \s-1OR BUSINESS INTERRUPTION\s0) \s-1HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\s0 (\s-1INCLUDING NEGLIGENCE OR OTHERWISE\s0) \s-1ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\s0 .PP In other words: Use at your own risk. Provided as is. Your mileage may vary. Read the source, Luke! .PP And finally, just to be sure: .PP Any Use of This Product, in Any Manner Whatsoever, Will Increase the Amount of Disorder in the Universe. Although No Liability Is Implied Herein, the Consumer Is Warned That This Process Will Ultimately Lead to the Heat Death of the Universe. PK�����A0]IyG����!��man/man3/LWP::Protocol::https.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "LWP::Protocol::https 3" .TH LWP::Protocol::https 3 "2024-03-11" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" LWP::Protocol::https \- Provide https support for LWP::UserAgent .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use LWP::UserAgent; \& \& $ua = LWP::UserAgent\->new(ssl_opts => { verify_hostname => 1 }); \& $res = $ua\->get("https://www.example.com"); \& \& # specify a CA path \& $ua = LWP::UserAgent\->new( \& ssl_opts => { \& SSL_ca_path => \*(Aq/etc/ssl/certs\*(Aq, \& verify_hostname => 1, \& } \& ); .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" The LWP::Protocol::https module provides support for using https schemed URLs with \s-1LWP.\s0 This module is a plug-in to the \s-1LWP\s0 protocol handling, so you don't use it directly. Once the module is installed \s-1LWP\s0 is able to access sites using \s-1HTTP\s0 over \s-1SSL/TLS.\s0 .PP If hostname verification is requested by LWP::UserAgent's \f(CW\*(C`ssl_opts\*(C'\fR, and neither \f(CW\*(C`SSL_ca_file\*(C'\fR nor \f(CW\*(C`SSL_ca_path\*(C'\fR is set, then \f(CW\*(C`SSL_ca_file\*(C'\fR is implied to be the one provided by Mozilla::CA. If the Mozilla::CA module isn't available \s-1SSL\s0 requests will fail. Either install this module, set up an alternative \f(CW\*(C`SSL_ca_file\*(C'\fR or disable hostname verification. .PP This module used to be bundled with the libwww-perl, but it was unbundled in v6.02 in order to be able to declare its dependencies properly for the \s-1CPAN\s0 tool-chain. Applications that need https support can just declare their dependency on LWP::Protocol::https and will no longer need to know what underlying modules to install. .SH "SEE ALSO" .IX Header "SEE ALSO" IO::Socket::SSL, Crypt::SSLeay, Mozilla::CA .SH "COPYRIGHT & LICENSE" .IX Header "COPYRIGHT & LICENSE" Copyright (c) 1997\-2011 Gisle Aas. .PP This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. PK�����A0]�I*��I*����man/man3/YAML::Syck.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "YAML::Syck 3" .TH YAML::Syck 3 "2020-10-26" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" YAML::Syck \- Fast, lightweight YAML loader and dumper .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use YAML::Syck; \& \& # Set this for interoperability with other YAML/Syck bindings: \& # e.g. Load(\*(AqYes\*(Aq) becomes 1 and Load(\*(AqNo\*(Aq) becomes \*(Aq\*(Aq. \& $YAML::Syck::ImplicitTyping = 1; \& \& $data = Load($yaml); \& $yaml = Dump($data); \& \& # $file can be an IO object, or a filename \& $data = LoadFile($file); \& DumpFile($file, $data); \& \& # A string with multiple YAML streams in it \& $yaml = Dump(@data); \& @data = Load($yaml); \& \& # Dumping into a pre\-existing output buffer \& my $yaml; \& DumpInto(\e$yaml, @data); .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" This module provides a Perl interface to the \fBlibsyck\fR data serialization library. It exports the \f(CW\*(C`Dump\*(C'\fR and \f(CW\*(C`Load\*(C'\fR functions for converting Perl data structures to \s-1YAML\s0 strings, and the other way around. .PP \&\fB\s-1NOTE\s0\fR: If you are working with other language's YAML/Syck bindings (such as Ruby), please set \f(CW$YAML::Syck::ImplicitTyping\fR to \f(CW1\fR before calling the \f(CW\*(C`Load\*(C'\fR/\f(CW\*(C`Dump\*(C'\fR functions. The default setting is for preserving backward-compatibility with \f(CW\*(C`YAML.pm\*(C'\fR. .SH "Differences Between YAML::Syck and YAML" .IX Header "Differences Between YAML::Syck and YAML" .SS "Error handling" .IX Subsection "Error handling" Some calls are designed to die rather than returning \s-1YAML.\s0 You should wrap your calls in eval to assure you do not get unexpected results. .SH "FLAGS" .IX Header "FLAGS" .ie n .SS "$YAML::Syck::Headless" .el .SS "\f(CW$YAML::Syck::Headless\fP" .IX Subsection "$YAML::Syck::Headless" Defaults to false. Setting this to a true value will make \f(CW\*(C`Dump\*(C'\fR omit the leading \f(CW\*(C`\-\-\-\en\*(C'\fR marker. .ie n .SS "$YAML::Syck::SortKeys" .el .SS "\f(CW$YAML::Syck::SortKeys\fP" .IX Subsection "$YAML::Syck::SortKeys" Defaults to false. Setting this to a true value will make \f(CW\*(C`Dump\*(C'\fR sort hash keys. .ie n .SS "$YAML::Syck::SingleQuote" .el .SS "\f(CW$YAML::Syck::SingleQuote\fP" .IX Subsection "$YAML::Syck::SingleQuote" Defaults to false. Setting this to a true value will make \f(CW\*(C`Dump\*(C'\fR always emit single quotes instead of bare strings. .ie n .SS "$YAML::Syck::ImplicitTyping" .el .SS "\f(CW$YAML::Syck::ImplicitTyping\fP" .IX Subsection "$YAML::Syck::ImplicitTyping" Defaults to false. Setting this to a true value will make \f(CW\*(C`Load\*(C'\fR recognize various implicit types in \s-1YAML,\s0 such as unquoted \f(CW\*(C`true\*(C'\fR, \f(CW\*(C`false\*(C'\fR, as well as integers and floating-point numbers. Otherwise, only \f(CW\*(C`~\*(C'\fR is recognized to be \f(CW\*(C`undef\*(C'\fR. .ie n .SS "$YAML::Syck::ImplicitUnicode" .el .SS "\f(CW$YAML::Syck::ImplicitUnicode\fP" .IX Subsection "$YAML::Syck::ImplicitUnicode" Defaults to false. For Perl 5.8.0 or later, setting this to a true value will make \f(CW\*(C`Load\*(C'\fR set Unicode flag on for every string that contains valid \s-1UTF8\s0 sequences, and make \f(CW\*(C`Dump\*(C'\fR return a unicode string. .PP Regardless of this flag, Unicode strings are dumped verbatim without escaping; byte strings with high-bit set will be dumped with backslash escaping. .PP However, because \s-1YAML\s0 does not distinguish between these two kinds of strings, so this flag will affect loading of both variants of strings. .PP If you want to use LoadFile or DumpFile with unicode, you are required to open your own file in order to assure it's \s-1UTF8\s0 encoded: .PP .Vb 2 \& open(my $fh, ">:encoding(UTF\-8)", "out.yml"); \& DumpFile($fh, $hashref); .Ve .ie n .SS "$YAML::Syck::ImplicitBinary" .el .SS "\f(CW$YAML::Syck::ImplicitBinary\fP" .IX Subsection "$YAML::Syck::ImplicitBinary" Defaults to false. For Perl 5.8.0 or later, setting this to a true value will make \f(CW\*(C`Dump\*(C'\fR generate Base64\-encoded \f(CW\*(C`!!binary\*(C'\fR data for all non-Unicode scalars containing high-bit bytes. .ie n .SS "$YAML::Syck::UseCode / $YAML::Syck::LoadCode / $YAML::Syck::DumpCode" .el .SS "\f(CW$YAML::Syck::UseCode\fP / \f(CW$YAML::Syck::LoadCode\fP / \f(CW$YAML::Syck::DumpCode\fP" .IX Subsection "$YAML::Syck::UseCode / $YAML::Syck::LoadCode / $YAML::Syck::DumpCode" These flags control whether or not to try and eval/deparse perl source code; each of them defaults to false. .PP Setting \f(CW$YAML::Syck::UseCode\fR to a true value is equivalent to setting both \f(CW$YAML::Syck::LoadCode\fR and \f(CW$YAML::Syck::DumpCode\fR to true. .ie n .SS "$YAML::Syck::LoadBlessed" .el .SS "\f(CW$YAML::Syck::LoadBlessed\fP" .IX Subsection "$YAML::Syck::LoadBlessed" Defaults to false. Setting to true will allow YAML::Syck to bless objects as it imports objects. This default changed in 1.32. .PP You can create any kind of object with \s-1YAML.\s0 The creation itself is not the critical part. If the class has a \s-1DESTROY\s0 method, it will be called once the object is deleted. An example with File::Temp removing files can be found at <https://bugs.debian.org/cgi\-bin/bugreport.cgi?bug=862373> .SH "BUGS" .IX Header "BUGS" Dumping Glob/IO values do not work yet. .PP Dumping of Tied variables is unsupported. .PP Dumping into tied (or other magic variables) with \f(CW\*(C`DumpInto\*(C'\fR might not work properly in all cases. .SH "CAVEATS" .IX Header "CAVEATS" This module implements the \s-1YAML 1.0\s0 spec. To deal with data in \s-1YAML 1.1,\s0 please use the \f(CW\*(C`YAML::XS\*(C'\fR module instead. .PP The current implementation bundles libsyck source code; if your system has a site-wide shared libsyck, it will \fInot\fR be used. .PP Tag names such as \f(CW\*(C`!!perl/hash:Foo\*(C'\fR is blessed into the package \f(CW\*(C`Foo\*(C'\fR, but the \f(CW\*(C`!hs/foo\*(C'\fR and \f(CW\*(C`!!hs/Foo\*(C'\fR tags are blessed into \f(CW\*(C`hs::Foo\*(C'\fR. Note that this holds true even if the tag contains non-word characters; for example, \&\f(CW\*(C`!haskell.org/Foo\*(C'\fR is blessed into \f(CW\*(C`haskell.org::Foo\*(C'\fR. Please use Class::Rebless to cast it into other user-defined packages. You can also set the LoadBlessed flag false to disable all blessing. .PP This module has a lot of known issues <https://rt.cpan.org/Public/Dist/Display.html?Name=YAML-Syck> and has only been semi-actively maintained since 2007. If you encounter an issue with it probably won't be fixed unless you offer up a patch <http://github.com/toddr/YAML-Syck> in Git that's ready for release. .PP There are still good reasons to use this module, such as better interoperability with other syck wrappers (like Ruby's), or some edge case of \s-1YAML\s0's syntax that it handles better. It'll probably work perfectly for you, but if it doesn't you may want to look at \&\s-1YAML::XS\s0, or perhaps at looking another serialization format like \&\s-1JSON\s0. .SH "SEE ALSO" .IX Header "SEE ALSO" \&\s-1YAML\s0, JSON::Syck .PP <http://www.yaml.org/> .SH "AUTHORS" .IX Header "AUTHORS" Audrey Tang <cpan@audreyt.org> .SH "COPYRIGHT" .IX Header "COPYRIGHT" Copyright 2005\-2009 by Audrey Tang <cpan@audreyt.org>. .PP This software is released under the \s-1MIT\s0 license cited below. .PP The \fIlibsyck\fR code bundled with this library is released by \&\*(L"why the lucky stiff\*(R", under a BSD-style license. See the \fI\s-1COPYING\s0\fR file for details. .ie n .SS "The ""\s-1MIT""\s0 License" .el .SS "The ``\s-1MIT''\s0 License" .IX Subsection "The MIT License" Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \*(L"Software\*(R"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: .PP The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. .PP \&\s-1THE SOFTWARE IS PROVIDED \*(L"AS IS\*(R", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\s0 PK�����A0]5ܚ1��1��%��man/man3/CPAN::Meta::Requirements.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "CPAN::Meta::Requirements 3" .TH CPAN::Meta::Requirements 3 "2023-06-19" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" CPAN::Meta::Requirements \- a set of version requirements for a CPAN dist .SH "VERSION" .IX Header "VERSION" version 2.143 .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use CPAN::Meta::Requirements; \& \& my $build_requires = CPAN::Meta::Requirements\->new; \& \& $build_requires\->add_minimum(\*(AqLibrary::Foo\*(Aq => 1.208); \& \& $build_requires\->add_minimum(\*(AqLibrary::Foo\*(Aq => 2.602); \& \& $build_requires\->add_minimum(\*(AqModule::Bar\*(Aq => \*(Aqv1.2.3\*(Aq); \& \& $METAyml\->{build_requires} = $build_requires\->as_string_hash; .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" A CPAN::Meta::Requirements object models a set of version constraints like those specified in the \fI\s-1META\s0.yml\fR or \fI\s-1META\s0.json\fR files in \s-1CPAN\s0 distributions, and as defined by CPAN::Meta::Spec. It can be built up by adding more and more constraints, and it will reduce them to the simplest representation. .PP Logically impossible constraints will be identified immediately by thrown exceptions. .SH "METHODS" .IX Header "METHODS" .SS "new" .IX Subsection "new" .Vb 1 \& my $req = CPAN::Meta::Requirements\->new; .Ve .PP This returns a new CPAN::Meta::Requirements object. It takes an optional hash reference argument. Currently, only one key is supported: .IP "\(bu" 4 \&\f(CW\*(C`bad_version_hook\*(C'\fR \*(-- if provided, when a version cannot be parsed into a version object, this code reference will be called with the invalid version string as first argument, and the module name as second argument. It must return a valid version object. .PP All other keys are ignored. .SS "add_minimum" .IX Subsection "add_minimum" .Vb 1 \& $req\->add_minimum( $module => $version ); .Ve .PP This adds a new minimum version requirement. If the new requirement is redundant to the existing specification, this has no effect. .PP Minimum requirements are inclusive. \f(CW$version\fR is required, along with any greater version number. .PP This method returns the requirements object. .SS "add_maximum" .IX Subsection "add_maximum" .Vb 1 \& $req\->add_maximum( $module => $version ); .Ve .PP This adds a new maximum version requirement. If the new requirement is redundant to the existing specification, this has no effect. .PP Maximum requirements are inclusive. No version strictly greater than the given version is allowed. .PP This method returns the requirements object. .SS "add_exclusion" .IX Subsection "add_exclusion" .Vb 1 \& $req\->add_exclusion( $module => $version ); .Ve .PP This adds a new excluded version. For example, you might use these three method calls: .PP .Vb 2 \& $req\->add_minimum( $module => \*(Aq1.00\*(Aq ); \& $req\->add_maximum( $module => \*(Aq1.82\*(Aq ); \& \& $req\->add_exclusion( $module => \*(Aq1.75\*(Aq ); .Ve .PP Any version between 1.00 and 1.82 inclusive would be acceptable, except for 1.75. .PP This method returns the requirements object. .SS "exact_version" .IX Subsection "exact_version" .Vb 1 \& $req\->exact_version( $module => $version ); .Ve .PP This sets the version required for the given module to \fIexactly\fR the given version. No other version would be considered acceptable. .PP This method returns the requirements object. .SS "version_range_for_module" .IX Subsection "version_range_for_module" .Vb 1 \& $req\->version_range_for_module( $another_req_object ); .Ve .SS "add_requirements" .IX Subsection "add_requirements" .Vb 1 \& $req\->add_requirements( $another_req_object ); .Ve .PP This method adds all the requirements in the given CPAN::Meta::Requirements object to the requirements object on which it was called. If there are any conflicts, an exception is thrown. .PP This method returns the requirements object. .SS "accepts_module" .IX Subsection "accepts_module" .Vb 1 \& my $bool = $req\->accepts_module($module => $version); .Ve .PP Given an module and version, this method returns true if the version specification for the module accepts the provided version. In other words, given: .PP .Vb 1 \& Module => \*(Aq>= 1.00, < 2.00\*(Aq .Ve .PP We will accept 1.00 and 1.75 but not 0.50 or 2.00. .PP For modules that do not appear in the requirements, this method will return true. .SS "clear_requirement" .IX Subsection "clear_requirement" .Vb 1 \& $req\->clear_requirement( $module ); .Ve .PP This removes the requirement for a given module from the object. .PP This method returns the requirements object. .SS "requirements_for_module" .IX Subsection "requirements_for_module" .Vb 1 \& $req\->requirements_for_module( $module ); .Ve .PP This returns a string containing the version requirements for a given module in the format described in CPAN::Meta::Spec or undef if the given module has no requirements. This should only be used for informational purposes such as error messages and should not be interpreted or used for comparison (see \&\*(L"accepts_module\*(R" instead). .SS "structured_requirements_for_module" .IX Subsection "structured_requirements_for_module" .Vb 1 \& $req\->structured_requirements_for_module( $module ); .Ve .PP This returns a data structure containing the version requirements for a given module or undef if the given module has no requirements. This should not be used for version checks (see \*(L"accepts_module\*(R" instead). .PP Added in version 2.134. .SS "required_modules" .IX Subsection "required_modules" This method returns a list of all the modules for which requirements have been specified. .SS "clone" .IX Subsection "clone" .Vb 1 \& $req\->clone; .Ve .PP This method returns a clone of the invocant. The clone and the original object can then be changed independent of one another. .SS "is_simple" .IX Subsection "is_simple" This method returns true if and only if all requirements are inclusive minimums \&\*(-- that is, if their string expression is just the version number. .SS "is_finalized" .IX Subsection "is_finalized" This method returns true if the requirements have been finalized by having the \&\f(CW\*(C`finalize\*(C'\fR method called on them. .SS "finalize" .IX Subsection "finalize" This method marks the requirements finalized. Subsequent attempts to change the requirements will be fatal, \fIif\fR they would result in a change. If they would not alter the requirements, they have no effect. .PP If a finalized set of requirements is cloned, the cloned requirements are not also finalized. .SS "as_string_hash" .IX Subsection "as_string_hash" This returns a reference to a hash describing the requirements using the strings in the CPAN::Meta::Spec specification. .PP For example after the following program: .PP .Vb 1 \& my $req = CPAN::Meta::Requirements\->new; \& \& $req\->add_minimum(\*(AqCPAN::Meta::Requirements\*(Aq => 0.102); \& \& $req\->add_minimum(\*(AqLibrary::Foo\*(Aq => 1.208); \& \& $req\->add_maximum(\*(AqLibrary::Foo\*(Aq => 2.602); \& \& $req\->add_minimum(\*(AqModule::Bar\*(Aq => \*(Aqv1.2.3\*(Aq); \& \& $req\->add_exclusion(\*(AqModule::Bar\*(Aq => \*(Aqv1.2.8\*(Aq); \& \& $req\->exact_version(\*(AqXyzzy\*(Aq => \*(Aq6.01\*(Aq); \& \& my $hashref = $req\->as_string_hash; .Ve .PP \&\f(CW$hashref\fR would contain: .PP .Vb 6 \& { \& \*(AqCPAN::Meta::Requirements\*(Aq => \*(Aq0.102\*(Aq, \& \*(AqLibrary::Foo\*(Aq => \*(Aq>= 1.208, <= 2.206\*(Aq, \& \*(AqModule::Bar\*(Aq => \*(Aq>= v1.2.3, != v1.2.8\*(Aq, \& \*(AqXyzzy\*(Aq => \*(Aq== 6.01\*(Aq, \& } .Ve .SS "add_string_requirement" .IX Subsection "add_string_requirement" .Vb 2 \& $req\->add_string_requirement(\*(AqLibrary::Foo\*(Aq => \*(Aq>= 1.208, <= 2.206\*(Aq); \& $req\->add_string_requirement(\*(AqLibrary::Foo\*(Aq => v1.208); .Ve .PP This method parses the passed in string and adds the appropriate requirement for the given module. A version can be a Perl \*(L"v\-string\*(R". It understands version ranges as described in the \*(L"Version Ranges\*(R" in CPAN::Meta::Spec. For example: .IP "1.3" 4 .IX Item "1.3" .PD 0 .IP ">= 1.3" 4 .IX Item ">= 1.3" .IP "<= 1.3" 4 .IX Item "<= 1.3" .IP "== 1.3" 4 .IX Item "== 1.3" .IP "!= 1.3" 4 .IX Item "!= 1.3" .IP "> 1.3" 4 .IX Item "> 1.3" .IP "< 1.3" 4 .IX Item "< 1.3" .IP ">= 1.3, != 1.5, <= 2.0" 4 .IX Item ">= 1.3, != 1.5, <= 2.0" .PD A version number without an operator is equivalent to specifying a minimum (\f(CW\*(C`>=\*(C'\fR). Extra whitespace is allowed. .SS "from_string_hash" .IX Subsection "from_string_hash" .Vb 2 \& my $req = CPAN::Meta::Requirements\->from_string_hash( \e%hash ); \& my $req = CPAN::Meta::Requirements\->from_string_hash( \e%hash, \e%opts ); .Ve .PP This is an alternate constructor for a CPAN::Meta::Requirements object. It takes a hash of module names and version requirement strings and returns a new CPAN::Meta::Requirements object. As with add_string_requirement, a version can be a Perl \*(L"v\-string\*(R". Optionally, you can supply a hash-reference of options, exactly as with the \*(L"new\*(R" method. .SH "SUPPORT" .IX Header "SUPPORT" .SS "Bugs / Feature Requests" .IX Subsection "Bugs / Feature Requests" Please report any bugs or feature requests through the issue tracker at <https://github.com/Perl\-Toolchain\-Gang/CPAN\-Meta\-Requirements/issues>. You will be notified automatically of any progress on your issue. .SS "Source Code" .IX Subsection "Source Code" This is open source software. The code repository is available for public review and contribution under the terms of the license. .PP <https://github.com/Perl\-Toolchain\-Gang/CPAN\-Meta\-Requirements> .PP .Vb 1 \& git clone https://github.com/Perl\-Toolchain\-Gang/CPAN\-Meta\-Requirements.git .Ve .SH "AUTHORS" .IX Header "AUTHORS" .IP "\(bu" 4 David Golden <dagolden@cpan.org> .IP "\(bu" 4 Ricardo Signes <rjbs@cpan.org> .SH "CONTRIBUTORS" .IX Header "CONTRIBUTORS" .IP "\(bu" 4 Ed J <mohawk2@users.noreply.github.com> .IP "\(bu" 4 Graham Knop <haarg@haarg.org> .IP "\(bu" 4 Karen Etheridge <ether@cpan.org> .IP "\(bu" 4 Leon Timmermans <fawaka@gmail.com> .IP "\(bu" 4 Paul Howarth <paul@city\-fan.org> .IP "\(bu" 4 Ricardo Signes <rjbs@semiotic.systems> .IP "\(bu" 4 robario <webmaster@robario.com> .IP "\(bu" 4 Tatsuhiko Miyagawa <miyagawa@bulknews.net> .IP "\(bu" 4 Tatsuhiko Miyagawa <miyagawa@gmail.com> .SH "COPYRIGHT AND LICENSE" .IX Header "COPYRIGHT AND LICENSE" This software is copyright (c) 2010 by David Golden and Ricardo Signes. .PP This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. PK�����A0] Y�� Y��!��man/man3/POD2::DE::local::lib.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "POD2::DE::local::lib 3" .TH POD2::DE::local::lib 3 "2019-11-14" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" local::lib~[de] \- Erschaffen und benutzen von Perl Modulen in einem lokalen lib/ Verzeichnis mit PERL5LIB .SH "SYNOPSIS" .IX Header "SYNOPSIS" Im Code \- .PP .Vb 1 \& use local::lib; # Benutzt das Verzeichnis ~/perl5 zum anlegen des lokalen lib/ Verzeichnisses \& \& use local::lib \*(Aq~/foo\*(Aq; # das selbe, aber mit ~/foo \& \& # Oder... \& use FindBin; \& use local::lib "$FindBin::Bin/../support"; # Applikationsspezifische Sammlung von Modulen .Ve .PP Von der Shell \- .PP .Vb 2 \& # Installiert LWP und alle notwendigen Abhängigkeiten in das \*(Aq~/perl5\*(Aq Verzeichnis \& perl \-MCPAN \-Mlocal::lib \-e \*(AqCPAN::install(LWP)\*(Aq \& \& # Gibt die Shell Kommandos aus um die Umgebung vorzubereiten \& $ perl \-Mlocal::lib \& export PERL_MB_OPT=\*(Aq\-\-install_base /home/username/perl5\*(Aq \& export PERL_MM_OPT=\*(AqINSTALL_BASE=/home/username/perl5\*(Aq \& export PERL5LIB=\*(Aq/home/username/perl5/lib/perl5/i386\-linux:/home/username/perl5/lib/perl5\*(Aq \& export PATH="/home/username/perl5/bin:$PATH" .Ve .SS "Die Bootstrapping Methode" .IX Subsection "Die Bootstrapping Methode" Ein typischer Weg um local::lib zu benutzen ist die sogenannte \*(L"Bootstrapping\*(R" Methode. Diese Methode wird benutzt wenn noch kein local::lib auf dem System installiert ist. In diesem Fall kannst du einfach local::lib direkt in deinem Home-Verzeichnis installieren. .PP Selbst wenn du administrative Rechte hast, ist es wichtig das die Umgebungsvariablen von Schritt 4 in deinem Shell Startup Skript gesetzt werden. Ohne diesen Schritt werden die Module von \s-1CPAN\s0 weiterhin im System installiert und auch Perl Skripte die du startest würden das von local::lib erstellte lib/ Verzeichnis nicht nutzen. .PP Standardmäßig installiert sich local::lib in ~/perl5. .PP Windows Benutzern müssen ausserdem dies hier lesen: \&\*(L"Unterschiede bei Benutzung dieses Module mit Win32\*(R". .PP 1. Lade das Tar-Archiv von \s-1CPAN\s0 runter (Suche nach \*(L"Download\*(R" auf der \s-1CPAN\s0 Seite von local::lib) und entpacke es in einem beliebigem Verzeichnis. Um das obige Problem zu vermeiden, sollte man dies als normaler User tun und nicht als root oder Administrator. .PP 2. Starte in dem entstandenen Verzeichnis folgenden Befehl: .PP .Vb 1 \& perl Makefile.PL \-\-bootstrap .Ve .PP Wenn das System dir vorschlägt gewisse Dinge eigenständig zu konfigurieren ist es in fast allen Fällen vollkommen in Ordnung einfach \*(L"yes\*(R" zu antworten. .PP Falls du local::lib nicht in das Standard Verzeichnis installieren willst, musst du dieses Verzeichnis als Parameter angeben: .PP .Vb 1 \& perl Makefile.PL \-\-bootstrap=~/foo .Ve .PP 3. Danach folgenden Befehl starten: (local::lib erwartet make auf dem System) .PP .Vb 1 \& make test && make install .Ve .PP 4. Nun müssen wir die benötigten Umgebungsvariablen, damit Perl unser neu generiertes lib/ Verzeichnis benutzt. Wenn du bash oder eine andere Bourne Shell benutzt, kannst du es über diesen Weg zu deinem Shell Startup Skript hinzufügen: .PP .Vb 1 \& echo \*(Aqeval $(perl \-I$HOME/perl5/lib/perl5 \-Mlocal::lib)\*(Aq >>~/.bashrc .Ve .PP Wenn du C Shell benutzt, du kannst das gleiche hiermit erreichen: .PP .Vb 4 \& /bin/csh \& echo $SHELL \& /bin/csh \& perl \-I$HOME/perl5/lib/perl5 \-Mlocal::lib >> ~/.cshrc .Ve .PP Wenn du beim bootstrappen ein anderes Verzeichnis benutzt als das Standardverzeichnis, dann musst du dieses Verzeichnis als Parameter beim Laden des Modules local::lib angeben: .PP .Vb 1 \& echo \*(Aqeval $(perl \-I$HOME/foo/lib/perl5 \-Mlocal::lib=$HOME/foo)\*(Aq >>~/.bashrc .Ve .PP Nachdem diese Änderungen in deinem Shell Startup Skript gemacht wurden, ist es nun wichtig das diese Umgebungsvariablen auch gesetzt sind in deiner aktuellen Umgebung. In Bourne Shells macht man dies z.B. mit \f(CW\*(C`. ~/.bashrc\*(C'\fR, und in C Shell würde man es mit: \&\f(CW\*(C`source ~/.cshrc\*(C'\fR mit. .PP Wenn du eine sehr langsames System hast, oder du unter drakonischen Regulierungen des Plattenplatz leben musst, kann man die automatische Generierung der manpages vom \s-1POD\s0 bei der Installation des Moduls deaktivieren beim bootstrapping mit dem \f(CW\*(C`\-\-no\-manpages\*(C'\fR Parameter: .PP .Vb 1 \& perl Makefile.PL \-\-bootstrap \-\-no\-manpages .Ve .PP Um zu vermeiden das man mehrere bootstraps macht um z.B. für verschiedene Applikationen eigene local::lib Installationen zu nutzen, kann man eine dieser Umgebungen benutzen um einfach in beliebigen anderen Verzeichnis Module zu installieren und somit weitere eigenständige lib/ Umgebungen zu bekommen: .PP .Vb 4 \& cd ~/mydir1 \& perl \-Mlocal::lib=./ \& eval $(perl \-Mlocal::lib=./) ### Um die Umgebungsvariablen für die \& ### aktuelle Shell zusetzen \& \& printenv ### Hier kannst du sehen das ~/mydir1 \& ### in der PERL5LIB Umgebungsvariable \& ### steht \& \& perl \-MCPAN \-e install ... ### welche Module auch immer ... \& cd ../mydir2 \& \& ... WIEDERHOLEN ... .Ve .PP Für mehrere Umgebungen in dieser Form brauch man eine Modifikation in der Benutzung von \f(CW\*(C`use FindBin\*(C'\fR in dem \*(L"Im Code\*(R" Beispiel oben. Wenn du sowas machst, und du hast damit z.B. Perl Module nach \f(CW\*(C`~/mydir1/lib\*(C'\fR installiert und du hast ein Script in \f(CW\*(C`~/mydir1/scripts/myscript.pl\*(C'\fR, du musst dort angeben das die Module die es braucht im Verzeichnis \f(CW\*(C`~/mydir1/lib\*(C'\fR liegen. .PP In \f(CW\*(C`~/mydir1/scripts/myscript.pl\*(C'\fR steht dann: .PP .Vb 5 \& use strict; \& use warnings; \& use local::lib "$FindBin::Bin/.."; ### zeigt auf ~/mydir1 und local::lib \& ### findet dort lib \& use lib "$FindBin::Bin/../lib"; ### zeigt auf ~/mydir1/lib .Ve .PP Setze das vor jeden \s-1BEGIN\s0 { ... } Block der die Module braucht die du installiert hast. .SS "Unterschiede bei Benutzung dieses Module mit Win32" .IX Subsection "Unterschiede bei Benutzung dieses Module mit Win32" Um die nötigen Umgebungsvariablen für diese Variablen in der derzeitigen Sitzung mit \f(CW\*(C`CMD.EXE\*(C'\fR zu setzen, kann man folgendes kommando nutzen: .PP .Vb 5 \& C:\e>perl \-Mlocal::lib \& set PERL_MB_OPT=\-\-install_base C:\eDOCUME~1\eADMINI~1\eperl5 \& set PERL_MM_OPT=INSTALL_BASE=C:\eDOCUME~1\eADMINI~1\eperl5 \& set PERL5LIB=C:\eDOCUME~1\eADMINI~1\eperl5\elib\eperl5;C:\eDOCUME~1\eADMINI~1\eperl5\elib\eperl5\eMSWin32\-x86\-multi\-thread \& set PATH=C:\eDOCUME~1\eADMINI~1\eperl5\ebin;%PATH% \& \& ### Um die Umgebungsvariablen für diese Shell alleine zu setzen \& C:\e>perl \-Mlocal::lib > %TEMP%\etmp.bat && %TEMP%\etmp.bat && del %TEMP%\etemp.bat \& ### anstelle von $(perl \-Mlocal::lib=./) in bash. .Ve .PP Wenn du willst das die Umgebungsvariablen dauerhaft gesetzt sind, musst du diese in Systemsteuerung / System dauerhaft selber eintragen oder App::local::lib::Win32Helper benutzen. .PP Die \*(L"~\*(R" wird übersetzt zu dem Benutzer Profil Verzeichnis (das Verzeichnis was beim User als \*(L"Dokumente und Einstellungen\*(R" bekannt ist unter Windows \s-1XP\s0 und vorher oder das \*(L"Benutzer\*(R" Verzeichnis bei Windows Vista und später), solange \&\f(CW$ENV\fR{\s-1HOME\s0} nicht gesetzt ist. Das Verzeichnis wird hierbei zu dem korrekten Kurznamen umgewandelt, und muss daher definitiv existieren, und wird um die nötigen Unterverzeichnise erweitert. .SH "GRUNDPRINZIP" .IX Header "GRUNDPRINZIP" Die Version von den Perl Paketen die man benötigt für spezifische Aufgaben sind sehr häufig nicht die richtigen oder korrekten Versionen auf dem System vorinstalliert. Ein Updaten von diesen Modulen ist in vielen Fällen einfach nicht möglich weil die nötigen Rechte fehlen. Ausserdem ist es generell nicht gut eigenständig die Versionen der Module auf dem System auszutauschen, weil natürlich der Rest des Systems genau die Version erwartet die von der Systemverwaltung auch installiert wurde. .PP local::lib löst dieses Problem, es erlaubt dir dein komplett eigenes Verzeichnis für deine \s-1CPAN\s0 Module zu haben und bist so nicht genötigt die Module vom System zu nutzen oder andersrum andere User nicht mit individuellen Modulwünschen zu Überarbeitung ihres Codes zu zwingen, weil bestimmte Module zentral für alle auf neuere Version upgedatet werden. Die Installation findet hierbei dann z.B. im Home Verzeichnis statt. Es werden nur Umgebungsvariablen gesetzt die das installierte Perl dazu bewegen die im Homeverzeichnis installierten Module zu benutzen, zusätzlich und vorgezogen zu denen auf dem System. .PP Daher muss man sich wenn man ein Paket System benutzt, wie z.b. Debian, garnicht mehr Sorgen machen, irgendwas auf dem System zu verletzten nur durch die Installation von Perl Modulen. .SH "BESCHREIBUNG" .IX Header "BESCHREIBUNG" Dieses Modul bietet eine schnelle und legitime Art und Weise ein sogenanntes bootstrapping zu machen um in einem User Homeverzeichnis eine Sammlung von Modulen zu installieren. Es erstellt auch die nötigen Umgebungsvariablen die benötigt werden um diese Module zu nutzen, passend zu der Shell die der User in der Umgebungsvariable \f(CW\*(C`SHELL\*(C'\fR angegeben hat, um dann direkt passend in die entsprechenden Konfigurationsdateien der Shell einfügt zu werden. .PP Weitergehend ist local::lib in der Lage Module zu nutzen die nicht im standardmäßigen \f(CW@INC\fR Pfad von Perl enthalten sind. Das macht es einfacher für bestimmte Applikationen ein bestimmtes Set von Modulen zu installieren ohne die anderen Module auf dem System in irgendeiner Art anzufassen. Damit es z.B. auch sicherer Module zu installieren die vom Maintainer noch nicht als Release verfügbar sind. .PP Beim Import setzt local::lib die folgenden Umgebungsvariablen zu den nötigen Werten: .IP "\s-1PERL_MB_OPT\s0" 4 .IX Item "PERL_MB_OPT" .PD 0 .IP "\s-1PERL_MM_OPT\s0" 4 .IX Item "PERL_MM_OPT" .IP "\s-1PERL5LIB\s0" 4 .IX Item "PERL5LIB" .IP "\s-1PATH\s0" 4 .IX Item "PATH" .PD Am \s-1PATH\s0 wird natürlich angehangen, und nicht ersetzt. .PP Diese Werte sind dann verfügbar für jeden Code der danach importiert wurde. .SH "ERSTELLEN EINES EIGENSTÄNDIGE SAMMLUNG VON MODULEN" .IX Header "ERSTELLEN EINES EIGENSTÄNDIGE SAMMLUNG VON MODULEN" Mit lib::core::only besteht eine Möglichkeit dieses zutun, aber beachte das hier eine Menge von Fallstricken und Problemen existieren, und man sollte immer darauf achten das man auf einem Perl aufbaut was sowenig wie möglich verändert wurde (d.h. site und vendor Verzeichnis so leer wie möglich). .SH "METHODEN" .IX Header "METHODEN" .SS "ensure_dir_structure_for" .IX Subsection "ensure_dir_structure_for" .ie n .IP "Parameter: $path" 4 .el .IP "Parameter: \f(CW$path\fR" 4 .IX Item "Parameter: $path" .PD 0 .IP "Rückgabewert: Keiner" 4 .IX Item "Rückgabewert: Keiner" .PD .PP Versucht den angegebenen Pfad anzulegen, mit allen nötigen drüberliegenden Verzeichnissen. Im Fehlerfall wird eine Exception geworfen. .SS "print_environment_vars_for" .IX Subsection "print_environment_vars_for" .ie n .IP "Parameter: $pfad" 4 .el .IP "Parameter: \f(CW$pfad\fR" 4 .IX Item "Parameter: $pfad" .PD 0 .IP "Rückgabewert: Keiner" 4 .IX Item "Rückgabewert: Keiner" .PD .PP Gibt die Umgebungsvariablen aus, die benötigt werden um den angegebenen Pfad als Basis Verzeichnis zu nutzen. .SS "build_environment_vars_for" .IX Subsection "build_environment_vars_for" .ie n .IP "Parameter: $pfad, $interpolate" 4 .el .IP "Parameter: \f(CW$pfad\fR, \f(CW$interpolate\fR" 4 .IX Item "Parameter: $pfad, $interpolate" .PD 0 .IP "Rückgabewert: \e%umgebungs_variablen" 4 .IX Item "Rückgabewert: %umgebungs_variablen" .PD .PP Gibt ein Hash zurück mit den Variablen die nötig sind in den Umgebungsvariablen um eine Installation in dem gegebenen Pfad zu benutzen. .SS "setup_env_hash_for" .IX Subsection "setup_env_hash_for" .ie n .IP "Parameter: $pfad" 4 .el .IP "Parameter: \f(CW$pfad\fR" 4 .IX Item "Parameter: $pfad" .PD 0 .IP "Rückgabewert: Keiner" 4 .IX Item "Rückgabewert: Keiner" .PD .PP Setzt die \f(CW%ENV\fR Einträge basierend auf dem Aufruf von \&\*(L"build_environment_vars_for\*(R". .SS "install_base_perl_path" .IX Subsection "install_base_perl_path" .ie n .IP "Parameter: $pfad" 4 .el .IP "Parameter: \f(CW$pfad\fR" 4 .IX Item "Parameter: $pfad" .PD 0 .ie n .IP "Rückgabewert: $module_installations_pfad" 4 .el .IP "Rückgabewert: \f(CW$module_installations_pfad\fR" 4 .IX Item "Rückgabewert: $module_installations_pfad" .PD .PP Gibt den Pfad zurück der benutzt wird um Perl Module zu installieren bei dem gegebenen Pfad als Basis. Prinzipiell wird nur \f(CW\*(C`lib\*(C'\fR und \f(CW\*(C`perl5\*(C'\fR als Pfadelemente angehangen. .SS "install_base_arch_path" .IX Subsection "install_base_arch_path" .ie n .IP "Parameter: $pfad" 4 .el .IP "Parameter: \f(CW$pfad\fR" 4 .IX Item "Parameter: $pfad" .PD 0 .ie n .IP "Rückgabewert: $architektur_module_installations_pfad" 4 .el .IP "Rückgabewert: \f(CW$architektur_module_installations_pfad\fR" 4 .IX Item "Rückgabewert: $architektur_module_installations_pfad" .PD .PP Gibt den Pfad zurück der benutzt wird um die Architektur\-abhängigen Perl Module zu installieren basirend auf dem angegebenen Pfad als Basis. Basierend auf dem was \*(L"install_base_perl_path\*(R" zurückgibt, and appends the value of \&\f(CW$Config{archname}\fR.asis. .SS "install_base_bin_path" .IX Subsection "install_base_bin_path" .ie n .IP "Parameter: $pfad" 4 .el .IP "Parameter: \f(CW$pfad\fR" 4 .IX Item "Parameter: $pfad" .PD 0 .ie n .IP "Rückgabewert: $ausfuehrbare_programme_installations_pfad" 4 .el .IP "Rückgabewert: \f(CW$ausfuehrbare_programme_installations_pfad\fR" 4 .IX Item "Rückgabewert: $ausfuehrbare_programme_installations_pfad" .PD .PP Gibt den Pfad zurück, wo ausführbare Programme installiert werden, basierend auf der Basis des angegebenen Pfad. Basierend auf \*(L"install_base_perl_path\*(R" Rückgabewert, hängt diese Methode noch \f(CW\*(C`bin\*(C'\fR an. .SS "resolve_empty_path" .IX Subsection "resolve_empty_path" .ie n .IP "Parameter: $pfad" 4 .el .IP "Parameter: \f(CW$pfad\fR" 4 .IX Item "Parameter: $pfad" .PD 0 .ie n .IP "Rückgabewert: $basis_pfad" 4 .el .IP "Rückgabewert: \f(CW$basis_pfad\fR" 4 .IX Item "Rückgabewert: $basis_pfad" .PD .PP Erstellt und gibt zurück den Pfad der benutzt wird als Basis zur Installation der Module. Standardmäßig dies ist \f(CW\*(C`~/perl5\*(C'\fR. .ie n .SS "resolve_home_path( $path )" .el .SS "resolve_home_path( \f(CW$path\fP )" .IX Subsection "resolve_home_path( $path )" .ie n .IP "Parameter: $pfad" 4 .el .IP "Parameter: \f(CW$pfad\fR" 4 .IX Item "Parameter: $pfad" .PD 0 .ie n .IP "Rückgabewert: $home" 4 .el .IP "Rückgabewert: \f(CW$home\fR" 4 .IX Item "Rückgabewert: $home" .PD .PP Versucht das Home Verzeichnis vom aktullen User zu finden. Es wird eine Exception geworfen, wenn kein Home Verzeichnis ermittelt werden konnte. .SS "resolve_relative_path" .IX Subsection "resolve_relative_path" .ie n .IP "Parameter: $pfad" 4 .el .IP "Parameter: \f(CW$pfad\fR" 4 .IX Item "Parameter: $pfad" .PD 0 .ie n .IP "Rückgabewert: $absoluter_pfad" 4 .el .IP "Rückgabewert: \f(CW$absoluter_pfad\fR" 4 .IX Item "Rückgabewert: $absoluter_pfad" .PD .PP Macht aus dem angegebenen Pfad einen absoluten Pfad. .SS "resolve_path" .IX Subsection "resolve_path" .ie n .IP "Parameter: $pfad" 4 .el .IP "Parameter: \f(CW$pfad\fR" 4 .IX Item "Parameter: $pfad" .PD 0 .ie n .IP "Rückgabewert: $absoluter_pfad" 4 .el .IP "Rückgabewert: \f(CW$absoluter_pfad\fR" 4 .IX Item "Rückgabewert: $absoluter_pfad" .PD .PP Hierbei wird der Pfad durch die folgende Methoden gegeben, wobei der Rückgabewert der ersten an die nächste weitergeben wird, um die Umgebung zu konfigurieren für die lokale Bibliotheks Installation: \*(L"resolve_empty_path\*(R", \&\*(L"resolve_home_path\*(R", \*(L"resolve_relative_path\*(R". Der daraus resultierende Pfad wird zu \*(L"resolve_empty_path\*(R" übergeben, dessen Resultat dann weitergegeben wird an \*(L"resolve_home_path\*(R", wessen Resultat dann weitergegeben wird an \*(L"resolve_relative_path\*(R". Dieses Resultat wird dann final an \*(L"resolve_path\*(R" übergeben, welches dann den Rückgabewert stellt. .SH "EINE WARNUNG VOR UNINST=1" .IX Header "EINE WARNUNG VOR UNINST=1" Wenn man local::lib in Kombination mit \*(L"make install UNINST=1\*(R" benutzt, muss man vorsichtig sein über die Tatsache das der Prozess über die Neuinstallation eine nicht ausreichende Sicherheit hat bezüglich wo er nun installieren muss. Hierdurch mann es passieren das beim deinstallieren eines Modul u.U. das globale Modul deinstalliert wird (wenn die Rechte vorhanden sind) aber die neue Version nur in der lokalen Version installiert ist. Es ist hier also sehr wichtig das man \*(L"make install UNINST=1\*(R" und local::lib nur gleichzeitig benutzt wenn man sehr sicher darüber ist welche Konsequenzen einem entgegenkommen. .SH "EINSCHRÄNKUNGEN" .IX Header "EINSCHRÄNKUNGEN" Die Werkzeuge von perl, die benutzt werden um die Pakete zu installieren (die sogenannte toolchain), sind leider nicht in der Lage sauber mit Verzeichnissen umzugehen die Leerzeichen enthalten und können daher local::lib nicht direkt in ein solches Verzeichnis installieren. Was du machen kannst ist \fBnach\fR der Installation von local::lib und der Module die du in deiner local::lib haben willst, das gesamte Verzeichnis dahin zu bewegen. local::lib kann mit dem Verzeichnis mit Leerzeichen umgehen. Bitte aufpassen das natürlich eine weitere Installation oder ein Erneuern von Paketen mit dem \s-1CPAN\s0 Programm nicht mehr möglich ist. .PP Die Shell Erkennung ist sehr primitiv. Derzeit ist es so das alles was \*(L"csh\*(R" im Namen hat auch als C Shell eingeordnet wird, und alles andere wird als Bourne Shell betrachet, ausser auf Win32 Systemen. Wenn die \f(CW\*(C`SHELL\*(C'\fR Variable nicht gesetzt ist, eine Bourne Shell wird angenommen. .PP Bootstrap ist leider ein Hack, und wird auf jedenfall \s-1CPAN\s0.pm benutzen für ExtUtils::MakeMaker, auch wenn \s-1CPANPLUS\s0 installiert ist. .PP Es setzt definitiv \s-1PERL5LIB, PERL_MM_OPT\s0 und \s-1PERL_MB_OPT\s0 neu und vernichtet jeden Wert der vorher gesetzt war. .PP Es sollte vielleicht eine automatische Korrektur der \s-1CPAN\s0 Config machen, wenn das nicht schon gemacht wurde. .PP \&\*(L"Patches Welcome\*(R" \- Patches sind immer willkommen beim Autor oder den anderen Mitwirkenden. .PP Auf Win32 Systemen werden die Umgebungsvariablen nicht direkt in die Registrierung geschrieben damit sie auch nach dem Neustarten erhalten bleiben. .SH "FEHLERANALYSE" .IX Header "FEHLERANALYSE" Wenn du local::lib konfiguriert hast \s-1CPAN\s0 Module in deinem Home Verzeichnis zu installieren, und du danach versuchst mit \f(CW\*(C`cpan \-i Foo::Bar\*(C'\fR ein Modul zu installieren, und dabei einen Fehler bekommst, wie: \f(CW\*(C`Warning: You do not have permissions to install into /usr/lib64/perl5/site_perl/5.8.8/x86_64\-linux at /usr/lib64/perl5/5.8.8/Foo/Bar.pm\*(C'\fR und in der installationsausgabe steht irgendwo ein Fehler der sagt \f(CW\*(C`\*(AqINSTALL_BASE\*(Aq is not a known MakeMaker parameter name\*(C'\fR, dann hast du aus irgendeinem Grund dein neue Version von ExtUtils::MakeMaker verloren. .PP Um dies zu korrigieren, einfach nochmal die bootstrapping Methode laufen lassen, wie oben beschrieben. .PP Dann starte \f(CW\*(C`rm \-r ~/.cpan/build/Foo\-Bar*\*(C'\fR .PP Abschliessend dann nochmal mit \f(CW\*(C`cpan \-i Foo::Bar\*(C'\fR installieren und die Probleme sollten verschwunden sein. .SH "UMGEBUNGSVARIABLEN" .IX Header "UMGEBUNGSVARIABLEN" .IP "\s-1SHELL\s0" 4 .IX Item "SHELL" .PD 0 .IP "\s-1COMSPEC\s0" 4 .IX Item "COMSPEC" .PD local::lib schaut in die \f(CW\*(C`SHELL\*(C'\fR Umgebungsvariable um die korrekten Kommandos zu der Shell Konfiguration hinzuzufügen. .Sp Auf Win32 Systemen, \f(CW\*(C`COMSPEC\*(C'\fR wird auch analysiert. .SH "SUPPORT" .IX Header "SUPPORT" \&\s-1IRC:\s0 .PP .Vb 1 \& Wir sind im Channel #local\-lib auf dem Server irc.perl.org. .Ve .SH "AUTOR DER ÜBERSETZUNG" .IX Header "AUTOR DER ÜBERSETZUNG" Torsten Raudssus <torsten@raudssus.de> http://www.raudssus.de/ .SH "URHEBERRECHT" .IX Header "URHEBERRECHT" Copyright (c) 2007 \- 2010 von den local::lib \*(L"\s-1AUTHOR\*(R"\s0 in local::lib und \*(L"\s-1CONTRIBUTORS\*(R"\s0 in local::lib aufgelistet in local::lib. .SH "LIZENZ" .IX Header "LIZENZ" Diese Sammlung ist freie Software und kann unter der selben Lizenz verbreitet werden wie Perl selber. PK�����A0]Hy �� ����man/man3/CPAN::Version.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "CPAN::Version 3" .TH CPAN::Version 3 "2018-09-22" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" CPAN::Version \- utility functions to compare CPAN versions .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use CPAN::Version; \& \& CPAN::Version\->vgt("1.1","1.1.1"); # 1 bc. 1.1 > 1.001001 \& \& CPAN::Version\->vlt("1.1","1.1"); # 0 bc. 1.1 not < 1.1 \& \& CPAN::Version\->vcmp("1.1","1.1.1"); # 1 bc. first is larger \& \& CPAN::Version\->vcmp("1.1.1","1.1"); # \-1 bc. first is smaller \& \& CPAN::Version\->readable(v1.2.3); # "v1.2.3" \& \& CPAN::Version\->vstring("v1.2.3"); # v1.2.3 \& \& CPAN::Version\->float2vv(1.002003); # "v1.2.3" .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" This module mediates between some version that perl sees in a package and the version that is published by the \s-1CPAN\s0 indexer. .PP It's only written as a helper module for both \s-1CPAN\s0.pm and \s-1CPANPLUS\s0.pm. .PP As it stands it predates version.pm but has the same goal: make version strings visible and comparable. .SH "LICENSE" .IX Header "LICENSE" This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. PK�����A0]/L��L����man/man3/Canary::Stability.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "Stability 3" .TH Stability 3 "2019-04-22" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" Canary::Stability \- canary to check perl compatibility for schmorp's modules .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 2 \& # in Makefile.PL \& use Canary::Stability DISTNAME => 2001, MINIMUM_PERL_VERSION; .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" This module is used by Schmorp's modules during configuration stage to test the installed perl for compatibility with his modules. .PP It's not, at this stage, meant as a tool for other module authors, although in principle nothing prevents them from subscribing to the same ideas. .PP See the \fIMakefile.PL\fR in Coro or AnyEvent for usage examples. .SH "ENVIRONMENT VARIABLES" .IX Header "ENVIRONMENT VARIABLES" .ie n .IP """PERL_CANARY_STABILITY_NOPROMPT=1""" 4 .el .IP "\f(CWPERL_CANARY_STABILITY_NOPROMPT=1\fR" 4 .IX Item "PERL_CANARY_STABILITY_NOPROMPT=1" Do not prompt the user on alert messages. .ie n .IP """PERL_CANARY_STABILITY_COLOUR=0""" 4 .el .IP "\f(CWPERL_CANARY_STABILITY_COLOUR=0\fR" 4 .IX Item "PERL_CANARY_STABILITY_COLOUR=0" Disable use of colour. .ie n .IP """PERL_CANARY_STABILITY_COLOUR=1""" 4 .el .IP "\f(CWPERL_CANARY_STABILITY_COLOUR=1\fR" 4 .IX Item "PERL_CANARY_STABILITY_COLOUR=1" Force use of colour. .ie n .IP """PERL_CANARY_STABILITY_DISABLE=1""" 4 .el .IP "\f(CWPERL_CANARY_STABILITY_DISABLE=1\fR" 4 .IX Item "PERL_CANARY_STABILITY_DISABLE=1" Disable this modules functionality completely. .ie n .IP """AUTOMATED_TESTING=1""" 4 .el .IP "\f(CWAUTOMATED_TESTING=1\fR" 4 .IX Item "AUTOMATED_TESTING=1" When this variable is set to a true value and the perl minimum version requirement is not met, the module will exit, which should skip testing under automated testing environments. .Sp This is done to avoid false failure or success reports when the chances of success are already quite low and the failures are not supported by the author. .SH "AUTHOR" .IX Header "AUTHOR" .Vb 2 \& Marc Lehmann <schmorp@schmorp.de> \& http://software.schmorp.de/pkg/Canary\-Stability.html .Ve PK�����A0]W]# %��%��,��man/man3/CPAN::Meta::Requirements::Range.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "CPAN::Meta::Requirements::Range 3" .TH CPAN::Meta::Requirements::Range 3 "2023-06-19" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" CPAN::Meta::Requirements::Range \- a set of version requirements for a CPAN dist .SH "VERSION" .IX Header "VERSION" version 2.143 .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use CPAN::Meta::Requirements::Range; \& \& my $range = CPAN::Meta::Requirements::Range\->with_minimum(1); \& \& $range = $range\->with_maximum(\*(Aqv2.2\*(Aq); \& \& my $stringified = $range\->as_string; .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" A CPAN::Meta::Requirements::Range object models a set of version constraints like those specified in the \fI\s-1META\s0.yml\fR or \fI\s-1META\s0.json\fR files in \s-1CPAN\s0 distributions, and as defined by CPAN::Meta::Spec; It can be built up by adding more and more constraints, and it will reduce them to the simplest representation. .PP Logically impossible constraints will be identified immediately by thrown exceptions. .SH "METHODS" .IX Header "METHODS" .SS "with_string_requirement" .IX Subsection "with_string_requirement" .Vb 2 \& $req\->with_string_requirement(\*(Aq>= 1.208, <= 2.206\*(Aq); \& $req\->with_string_requirement(v1.208); .Ve .PP This method parses the passed in string and adds the appropriate requirement. A version can be a Perl \*(L"v\-string\*(R". It understands version ranges as described in the \*(L"Version Ranges\*(R" in CPAN::Meta::Spec. For example: .IP "1.3" 4 .IX Item "1.3" .PD 0 .IP ">= 1.3" 4 .IX Item ">= 1.3" .IP "<= 1.3" 4 .IX Item "<= 1.3" .IP "== 1.3" 4 .IX Item "== 1.3" .IP "!= 1.3" 4 .IX Item "!= 1.3" .IP "> 1.3" 4 .IX Item "> 1.3" .IP "< 1.3" 4 .IX Item "< 1.3" .IP ">= 1.3, != 1.5, <= 2.0" 4 .IX Item ">= 1.3, != 1.5, <= 2.0" .PD A version number without an operator is equivalent to specifying a minimum (\f(CW\*(C`>=\*(C'\fR). Extra whitespace is allowed. .SS "with_range" .IX Subsection "with_range" .Vb 1 \& $range\->with_range($other_range) .Ve .PP This creates a new range object that is a merge two others. .SS "with_exact_version" .IX Subsection "with_exact_version" .Vb 1 \& $range\->with_exact_version( $version ); .Ve .PP This sets the version required to \fIexactly\fR the given version. No other version would be considered acceptable. .PP This method returns the version range object. .SS "with_minimum" .IX Subsection "with_minimum" .Vb 1 \& $range\->with_minimum( $version ); .Ve .PP This adds a new minimum version requirement. If the new requirement is redundant to the existing specification, this has no effect. .PP Minimum requirements are inclusive. \f(CW$version\fR is required, along with any greater version number. .PP This method returns the version range object. .SS "with_maximum" .IX Subsection "with_maximum" .Vb 1 \& $range\->with_maximum( $version ); .Ve .PP This adds a new maximum version requirement. If the new requirement is redundant to the existing specification, this has no effect. .PP Maximum requirements are inclusive. No version strictly greater than the given version is allowed. .PP This method returns the version range object. .SS "with_exclusion" .IX Subsection "with_exclusion" .Vb 1 \& $range\->with_exclusion( $version ); .Ve .PP This adds a new excluded version. For example, you might use these three method calls: .PP .Vb 2 \& $range\->with_minimum( \*(Aq1.00\*(Aq ); \& $range\->with_maximum( \*(Aq1.82\*(Aq ); \& \& $range\->with_exclusion( \*(Aq1.75\*(Aq ); .Ve .PP Any version between 1.00 and 1.82 inclusive would be acceptable, except for 1.75. .PP This method returns the requirements object. .SS "as_struct" .IX Subsection "as_struct" .Vb 1 \& $range\->as_struct( $module ); .Ve .PP This returns a data structure containing the version requirements. This should not be used for version checks (see \*(L"accepts_module\*(R" instead). .SS "as_string" .IX Subsection "as_string" .Vb 1 \& $range\->as_string; .Ve .PP This returns a string containing the version requirements in the format described in CPAN::Meta::Spec. This should only be used for informational purposes such as error messages and should not be interpreted or used for comparison (see \*(L"accepts\*(R" instead). .SS "accepts" .IX Subsection "accepts" .Vb 1 \& my $bool = $range\->accepts($version); .Ve .PP Given a version, this method returns true if the version specification accepts the provided version. In other words, given: .PP .Vb 1 \& \*(Aq>= 1.00, < 2.00\*(Aq .Ve .PP We will accept 1.00 and 1.75 but not 0.50 or 2.00. .SS "is_simple" .IX Subsection "is_simple" This method returns true if and only if the range is an inclusive minimum \&\*(-- that is, if their string expression is just the version number. .SH "AUTHORS" .IX Header "AUTHORS" .IP "\(bu" 4 David Golden <dagolden@cpan.org> .IP "\(bu" 4 Ricardo Signes <rjbs@cpan.org> .SH "COPYRIGHT AND LICENSE" .IX Header "COPYRIGHT AND LICENSE" This software is copyright (c) 2010 by David Golden and Ricardo Signes. .PP This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. PK�����A0]stb ��b ����man/man3/CPAN::Debug.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "CPAN::Debug 3" .TH CPAN::Debug 3 "2016-08-17" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" CPAN::Debug \- internal debugging for CPAN.pm .SH "LICENSE" .IX Header "LICENSE" This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. PK�����A0]r4J|��|����man/man3/local::lib.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "local::lib 3" .TH local::lib 3 "2022-04-20" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" local::lib \- create and use a local lib/ for perl modules with PERL5LIB .SH "SYNOPSIS" .IX Header "SYNOPSIS" In code \- .PP .Vb 1 \& use local::lib; # sets up a local lib at ~/perl5 \& \& use local::lib \*(Aq~/foo\*(Aq; # same, but ~/foo \& \& # Or... \& use FindBin; \& use local::lib "$FindBin::Bin/../support"; # app\-local support library .Ve .PP From the shell \- .PP .Vb 2 \& # Install LWP and its missing dependencies to the \*(Aq~/perl5\*(Aq directory \& perl \-MCPAN \-Mlocal::lib \-e \*(AqCPAN::install(LWP)\*(Aq \& \& # Just print out useful shell commands \& $ perl \-Mlocal::lib \& PERL_MB_OPT=\*(Aq\-\-install_base /home/username/perl5\*(Aq; export PERL_MB_OPT; \& PERL_MM_OPT=\*(AqINSTALL_BASE=/home/username/perl5\*(Aq; export PERL_MM_OPT; \& PERL5LIB="/home/username/perl5/lib/perl5"; export PERL5LIB; \& PATH="/home/username/perl5/bin:$PATH"; export PATH; \& PERL_LOCAL_LIB_ROOT="/home/usename/perl5:$PERL_LOCAL_LIB_ROOT"; export PERL_LOCAL_LIB_ROOT; .Ve .PP From a \fI.bash_profile\fR or \fI.bashrc\fR file \- .PP .Vb 1 \& eval "$(perl \-I$HOME/perl5/lib/perl5 \-Mlocal::lib)" .Ve .SS "The bootstrapping technique" .IX Subsection "The bootstrapping technique" A typical way to install local::lib is using what is known as the \&\*(L"bootstrapping\*(R" technique. You would do this if your system administrator hasn't already installed local::lib. In this case, you'll need to install local::lib in your home directory. .PP Even if you do have administrative privileges, you will still want to set up your environment variables, as discussed in step 4. Without this, you would still install the modules into the system \s-1CPAN\s0 installation and also your Perl scripts will not use the lib/ path you bootstrapped with local::lib. .PP By default local::lib installs itself and the \s-1CPAN\s0 modules into ~/perl5. .PP Windows users must also see \*(L"Differences when using this module under Win32\*(R". .IP "1." 4 Download and unpack the local::lib tarball from \s-1CPAN\s0 (search for \*(L"Download\*(R" on the \s-1CPAN\s0 page about local::lib). Do this as an ordinary user, not as root or administrator. Unpack the file in your home directory or in any other convenient location. .IP "2." 4 Run this: .Sp .Vb 1 \& perl Makefile.PL \-\-bootstrap .Ve .Sp If the system asks you whether it should automatically configure as much as possible, you would typically answer yes. .IP "3." 4 Run this: (local::lib assumes you have make installed on your system) .Sp .Vb 1 \& make test && make install .Ve .IP "4." 4 Now we need to setup the appropriate environment variables, so that Perl starts using our newly generated lib/ directory. If you are using bash or any other Bourne shells, you can add this to your shell startup script this way: .Sp .Vb 1 \& echo \*(Aqeval "$(perl \-I$HOME/perl5/lib/perl5 \-Mlocal::lib)"\*(Aq >>~/.bashrc .Ve .Sp If you are using C shell, you can do this as follows: .Sp .Vb 3 \& % echo $SHELL \& /bin/csh \& $ echo \*(Aqeval \`perl \-I$HOME/perl5/lib/perl5 \-Mlocal::lib\`\*(Aq >> ~/.cshrc .Ve .Sp After writing your shell configuration file, be sure to re-read it to get the changed settings into your current shell's environment. Bourne shells use \&\f(CW\*(C`. ~/.bashrc\*(C'\fR for this, whereas C shells use \f(CW\*(C`source ~/.cshrc\*(C'\fR. .PP \fIBootstrapping into an alternate directory\fR .IX Subsection "Bootstrapping into an alternate directory" .PP In order to install local::lib into a directory other than the default, you need to specify the name of the directory when you call bootstrap. Then, when setting up the environment variables, both perl and local::lib must be told the location of the bootstrap directory. The setup process would look as follows: .PP .Vb 4 \& perl Makefile.PL \-\-bootstrap=~/foo \& make test && make install \& echo \*(Aqeval "$(perl \-I$HOME/foo/lib/perl5 \-Mlocal::lib=$HOME/foo)"\*(Aq >>~/.bashrc \& . ~/.bashrc .Ve .PP \fIOther bootstrapping options\fR .IX Subsection "Other bootstrapping options" .PP If you're on a slower machine, or are operating under draconian disk space limitations, you can disable the automatic generation of manpages from \s-1POD\s0 when installing modules by using the \f(CW\*(C`\-\-no\-manpages\*(C'\fR argument when bootstrapping: .PP .Vb 1 \& perl Makefile.PL \-\-bootstrap \-\-no\-manpages .Ve .PP To avoid doing several bootstrap for several Perl module environments on the same account, for example if you use it for several different deployed applications independently, you can use one bootstrapped local::lib installation to install modules in different directories directly this way: .PP .Vb 7 \& cd ~/mydir1 \& perl \-Mlocal::lib=./ \& eval $(perl \-Mlocal::lib=./) ### To set the environment for this shell alone \& printenv ### You will see that ~/mydir1 is in the PERL5LIB \& perl \-MCPAN \-e install ... ### whatever modules you want \& cd ../mydir2 \& ... REPEAT ... .Ve .PP If you use \fI.bashrc\fR to activate a local::lib automatically, the local::lib will be re-enabled in any sub-shells used, overriding adjustments you may have made in the parent shell. To avoid this, you can initialize the local::lib in \&\fI.bash_profile\fR rather than \fI.bashrc\fR, or protect the local::lib invocation with a \f(CW$SHLVL\fR check: .PP .Vb 1 \& [ $SHLVL \-eq 1 ] && eval "$(perl \-I$HOME/perl5/lib/perl5 \-Mlocal::lib)" .Ve .PP If you are working with several \f(CW\*(C`local::lib\*(C'\fR environments, you may want to remove some of them from the current environment without disturbing the others. You can deactivate one environment like this (using bourne sh): .PP .Vb 1 \& eval $(perl \-Mlocal::lib=\-\-deactivate,~/path) .Ve .PP which will generate and run the commands needed to remove \f(CW\*(C`~/path\*(C'\fR from your various search paths. Whichever environment was \fBactivated most recently\fR will remain the target for module installations. That is, if you activate \&\f(CW\*(C`~/path_A\*(C'\fR and then you activate \f(CW\*(C`~/path_B\*(C'\fR, new modules you install will go in \f(CW\*(C`~/path_B\*(C'\fR. If you deactivate \f(CW\*(C`~/path_B\*(C'\fR then modules will be installed into \f(CW\*(C`~/pathA\*(C'\fR \*(-- but if you deactivate \f(CW\*(C`~/path_A\*(C'\fR then they will still be installed in \f(CW\*(C`~/pathB\*(C'\fR because pathB was activated later. .PP You can also ask \f(CW\*(C`local::lib\*(C'\fR to clean itself completely out of the current shell's environment with the \f(CW\*(C`\-\-deactivate\-all\*(C'\fR option. For multiple environments for multiple apps you may need to include a modified version of the \f(CW\*(C`use FindBin\*(C'\fR instructions in the \*(L"In code\*(R" sample above. If you did something like the above, you have a set of Perl modules at \f(CW\*(C`~/mydir1/lib\*(C'\fR. If you have a script at \f(CW\*(C`~/mydir1/scripts/myscript.pl\*(C'\fR, you need to tell it where to find the modules you installed for it at \f(CW\*(C`~/mydir1/lib\*(C'\fR. .PP In \f(CW\*(C`~/mydir1/scripts/myscript.pl\*(C'\fR: .PP .Vb 4 \& use strict; \& use warnings; \& use local::lib "$FindBin::Bin/.."; ### points to ~/mydir1 and local::lib finds lib \& use lib "$FindBin::Bin/../lib"; ### points to ~/mydir1/lib .Ve .PP Put this before any \s-1BEGIN\s0 { ... } blocks that require the modules you installed. .SS "Differences when using this module under Win32" .IX Subsection "Differences when using this module under Win32" To set up the proper environment variables for your current session of \&\f(CW\*(C`CMD.exe\*(C'\fR, you can use this: .PP .Vb 5 \& C:\e>perl \-Mlocal::lib \& set PERL_MB_OPT=\-\-install_base C:\eDOCUME~1\eADMINI~1\eperl5 \& set PERL_MM_OPT=INSTALL_BASE=C:\eDOCUME~1\eADMINI~1\eperl5 \& set PERL5LIB=C:\eDOCUME~1\eADMINI~1\eperl5\elib\eperl5 \& set PATH=C:\eDOCUME~1\eADMINI~1\eperl5\ebin;%PATH% \& \& ### To set the environment for this shell alone \& C:\e>perl \-Mlocal::lib > %TEMP%\etmp.bat && %TEMP%\etmp.bat && del %TEMP%\etmp.bat \& ### instead of $(perl \-Mlocal::lib=./) .Ve .PP If you want the environment entries to persist, you'll need to add them to the Control Panel's System applet yourself or use App::local::lib::Win32Helper. .PP The \*(L"~\*(R" is translated to the user's profile directory (the directory named for the user under \*(L"Documents and Settings\*(R" (Windows \s-1XP\s0 or earlier) or \*(L"Users\*(R" (Windows Vista or later)) unless \f(CW$ENV\fR{\s-1HOME\s0} exists. After that, the home directory is translated to a short name (which means the directory must exist) and the subdirectories are created. .PP \fIPowerShell\fR .IX Subsection "PowerShell" .PP local::lib also supports PowerShell, and can be used with the \&\f(CW\*(C`Invoke\-Expression\*(C'\fR cmdlet. .PP .Vb 1 \& Invoke\-Expression "$(perl \-Mlocal::lib)" .Ve .SH "RATIONALE" .IX Header "RATIONALE" The version of a Perl package on your machine is not always the version you need. Obviously, the best thing to do would be to update to the version you need. However, you might be in a situation where you're prevented from doing this. Perhaps you don't have system administrator privileges; or perhaps you are using a package management system such as Debian, and nobody has yet gotten around to packaging up the version you need. .PP local::lib solves this problem by allowing you to create your own directory of Perl packages downloaded from \s-1CPAN\s0 (in a multi-user system, this would typically be within your own home directory). The existing system Perl installation is not affected; you simply invoke Perl with special options so that Perl uses the packages in your own local package directory rather than the system packages. local::lib arranges things so that your locally installed version of the Perl packages takes precedence over the system installation. .PP If you are using a package management system (such as Debian), you don't need to worry about Debian and \s-1CPAN\s0 stepping on each other's toes. Your local version of the packages will be written to an entirely separate directory from those installed by Debian. .SH "DESCRIPTION" .IX Header "DESCRIPTION" This module provides a quick, convenient way of bootstrapping a user-local Perl module library located within the user's home directory. It also constructs and prints out for the user the list of environment variables using the syntax appropriate for the user's current shell (as specified by the \f(CW\*(C`SHELL\*(C'\fR environment variable), suitable for directly adding to one's shell configuration file. .PP More generally, local::lib allows for the bootstrapping and usage of a directory containing Perl modules outside of Perl's \f(CW@INC\fR. This makes it easier to ship an application with an app-specific copy of a Perl module, or collection of modules. Useful in cases like when an upstream maintainer hasn't applied a patch to a module of theirs that you need for your application. .PP On import, local::lib sets the following environment variables to appropriate values: .IP "\s-1PERL_MB_OPT\s0" 4 .IX Item "PERL_MB_OPT" .PD 0 .IP "\s-1PERL_MM_OPT\s0" 4 .IX Item "PERL_MM_OPT" .IP "\s-1PERL5LIB\s0" 4 .IX Item "PERL5LIB" .IP "\s-1PATH\s0" 4 .IX Item "PATH" .IP "\s-1PERL_LOCAL_LIB_ROOT\s0" 4 .IX Item "PERL_LOCAL_LIB_ROOT" .PD .PP When possible, these will be appended to instead of overwritten entirely. .PP These values are then available for reference by any code after import. .SH "CREATING A SELF-CONTAINED SET OF MODULES" .IX Header "CREATING A SELF-CONTAINED SET OF MODULES" See lib::core::only for one way to do this \- but note that there are a number of caveats, and the best approach is always to perform a build against a clean perl (i.e. site and vendor as close to empty as possible). .SH "IMPORT OPTIONS" .IX Header "IMPORT OPTIONS" Options are values that can be passed to the \f(CW\*(C`local::lib\*(C'\fR import besides the directory to use. They are specified as \f(CW\*(C`use local::lib \*(Aq\-\-option\*(Aq[, path];\*(C'\fR or \f(CW\*(C`perl \-Mlocal::lib=\-\-option[,path]\*(C'\fR. .SS "\-\-deactivate" .IX Subsection "--deactivate" Remove the chosen path (or the default path) from the module search paths if it was added by \f(CW\*(C`local::lib\*(C'\fR, instead of adding it. .SS "\-\-deactivate\-all" .IX Subsection "--deactivate-all" Remove all directories that were added to search paths by \f(CW\*(C`local::lib\*(C'\fR from the search paths. .SS "\-\-quiet" .IX Subsection "--quiet" Don't output any messages about directories being created. .SS "\-\-always" .IX Subsection "--always" Always add directories to environment variables, ignoring if they are already included. .SS "\-\-shelltype" .IX Subsection "--shelltype" Specify the shell type to use for output. By default, the shell will be detected based on the environment. Should be one of: \f(CW\*(C`bourne\*(C'\fR, \f(CW\*(C`csh\*(C'\fR, \&\f(CW\*(C`cmd\*(C'\fR, or \f(CW\*(C`powershell\*(C'\fR. .SS "\-\-no\-create" .IX Subsection "--no-create" Prevents \f(CW\*(C`local::lib\*(C'\fR from creating directories when activating dirs. This is likely to cause issues on Win32 systems. .SH "CLASS METHODS" .IX Header "CLASS METHODS" .SS "ensure_dir_structure_for" .IX Subsection "ensure_dir_structure_for" .ie n .IP "Arguments: $path" 4 .el .IP "Arguments: \f(CW$path\fR" 4 .IX Item "Arguments: $path" .PD 0 .IP "Return value: None" 4 .IX Item "Return value: None" .PD .PP Attempts to create a local::lib directory, including subdirectories and all required parent directories. Throws an exception on failure. .SS "print_environment_vars_for" .IX Subsection "print_environment_vars_for" .ie n .IP "Arguments: $path" 4 .el .IP "Arguments: \f(CW$path\fR" 4 .IX Item "Arguments: $path" .PD 0 .IP "Return value: None" 4 .IX Item "Return value: None" .PD .PP Prints to standard output the variables listed above, properly set to use the given path as the base directory. .SS "build_environment_vars_for" .IX Subsection "build_environment_vars_for" .ie n .IP "Arguments: $path" 4 .el .IP "Arguments: \f(CW$path\fR" 4 .IX Item "Arguments: $path" .PD 0 .ie n .IP "Return value: %environment_vars" 4 .el .IP "Return value: \f(CW%environment_vars\fR" 4 .IX Item "Return value: %environment_vars" .PD .PP Returns a hash with the variables listed above, properly set to use the given path as the base directory. .SS "setup_env_hash_for" .IX Subsection "setup_env_hash_for" .ie n .IP "Arguments: $path" 4 .el .IP "Arguments: \f(CW$path\fR" 4 .IX Item "Arguments: $path" .PD 0 .IP "Return value: None" 4 .IX Item "Return value: None" .PD .PP Constructs the \f(CW%ENV\fR keys for the given path, by calling \&\*(L"build_environment_vars_for\*(R". .SS "active_paths" .IX Subsection "active_paths" .IP "Arguments: None" 4 .IX Item "Arguments: None" .PD 0 .ie n .IP "Return value: @paths" 4 .el .IP "Return value: \f(CW@paths\fR" 4 .IX Item "Return value: @paths" .PD .PP Returns a list of active \f(CW\*(C`local::lib\*(C'\fR paths, according to the \&\f(CW\*(C`PERL_LOCAL_LIB_ROOT\*(C'\fR environment variable and verified against what is really in \f(CW@INC\fR. .SS "install_base_perl_path" .IX Subsection "install_base_perl_path" .ie n .IP "Arguments: $path" 4 .el .IP "Arguments: \f(CW$path\fR" 4 .IX Item "Arguments: $path" .PD 0 .ie n .IP "Return value: $install_base_perl_path" 4 .el .IP "Return value: \f(CW$install_base_perl_path\fR" 4 .IX Item "Return value: $install_base_perl_path" .PD .PP Returns a path describing where to install the Perl modules for this local library installation. Appends the directories \f(CW\*(C`lib\*(C'\fR and \f(CW\*(C`perl5\*(C'\fR to the given path. .SS "lib_paths_for" .IX Subsection "lib_paths_for" .ie n .IP "Arguments: $path" 4 .el .IP "Arguments: \f(CW$path\fR" 4 .IX Item "Arguments: $path" .PD 0 .ie n .IP "Return value: @lib_paths" 4 .el .IP "Return value: \f(CW@lib_paths\fR" 4 .IX Item "Return value: @lib_paths" .PD .PP Returns the list of paths perl will search for libraries, given a base path. This includes the base path itself, the architecture specific subdirectory, and perl version specific subdirectories. These paths may not all exist. .SS "install_base_bin_path" .IX Subsection "install_base_bin_path" .ie n .IP "Arguments: $path" 4 .el .IP "Arguments: \f(CW$path\fR" 4 .IX Item "Arguments: $path" .PD 0 .ie n .IP "Return value: $install_base_bin_path" 4 .el .IP "Return value: \f(CW$install_base_bin_path\fR" 4 .IX Item "Return value: $install_base_bin_path" .PD .PP Returns a path describing where to install the executable programs for this local library installation. Appends the directory \f(CW\*(C`bin\*(C'\fR to the given path. .SS "installer_options_for" .IX Subsection "installer_options_for" .ie n .IP "Arguments: $path" 4 .el .IP "Arguments: \f(CW$path\fR" 4 .IX Item "Arguments: $path" .PD 0 .ie n .IP "Return value: %installer_env_vars" 4 .el .IP "Return value: \f(CW%installer_env_vars\fR" 4 .IX Item "Return value: %installer_env_vars" .PD .PP Returns a hash of environment variables that should be set to cause installation into the given path. .SS "resolve_empty_path" .IX Subsection "resolve_empty_path" .ie n .IP "Arguments: $path" 4 .el .IP "Arguments: \f(CW$path\fR" 4 .IX Item "Arguments: $path" .PD 0 .ie n .IP "Return value: $base_path" 4 .el .IP "Return value: \f(CW$base_path\fR" 4 .IX Item "Return value: $base_path" .PD .PP Builds and returns the base path into which to set up the local module installation. Defaults to \f(CW\*(C`~/perl5\*(C'\fR. .SS "resolve_home_path" .IX Subsection "resolve_home_path" .ie n .IP "Arguments: $path" 4 .el .IP "Arguments: \f(CW$path\fR" 4 .IX Item "Arguments: $path" .PD 0 .ie n .IP "Return value: $home_path" 4 .el .IP "Return value: \f(CW$home_path\fR" 4 .IX Item "Return value: $home_path" .PD .PP Attempts to find the user's home directory. If no definite answer is available, throws an exception. .SS "resolve_relative_path" .IX Subsection "resolve_relative_path" .ie n .IP "Arguments: $path" 4 .el .IP "Arguments: \f(CW$path\fR" 4 .IX Item "Arguments: $path" .PD 0 .ie n .IP "Return value: $absolute_path" 4 .el .IP "Return value: \f(CW$absolute_path\fR" 4 .IX Item "Return value: $absolute_path" .PD .PP Translates the given path into an absolute path. .SS "resolve_path" .IX Subsection "resolve_path" .ie n .IP "Arguments: $path" 4 .el .IP "Arguments: \f(CW$path\fR" 4 .IX Item "Arguments: $path" .PD 0 .ie n .IP "Return value: $absolute_path" 4 .el .IP "Return value: \f(CW$absolute_path\fR" 4 .IX Item "Return value: $absolute_path" .PD .PP Calls the following in a pipeline, passing the result from the previous to the next, in an attempt to find where to configure the environment for a local library installation: \*(L"resolve_empty_path\*(R", \*(L"resolve_home_path\*(R", \&\*(L"resolve_relative_path\*(R". Passes the given path argument to \&\*(L"resolve_empty_path\*(R" which then returns a result that is passed to \&\*(L"resolve_home_path\*(R", which then has its result passed to \&\*(L"resolve_relative_path\*(R". The result of this final call is returned from \&\*(L"resolve_path\*(R". .SH "OBJECT INTERFACE" .IX Header "OBJECT INTERFACE" .SS "new" .IX Subsection "new" .ie n .IP "Arguments: %attributes" 4 .el .IP "Arguments: \f(CW%attributes\fR" 4 .IX Item "Arguments: %attributes" .PD 0 .ie n .IP "Return value: $local_lib" 4 .el .IP "Return value: \f(CW$local_lib\fR" 4 .IX Item "Return value: $local_lib" .PD .PP Constructs a new \f(CW\*(C`local::lib\*(C'\fR object, representing the current state of \&\f(CW@INC\fR and the relevant environment variables. .SH "ATTRIBUTES" .IX Header "ATTRIBUTES" .SS "roots" .IX Subsection "roots" An arrayref representing active \f(CW\*(C`local::lib\*(C'\fR directories. .SS "inc" .IX Subsection "inc" An arrayref representing \f(CW@INC\fR. .SS "libs" .IX Subsection "libs" An arrayref representing the \s-1PERL5LIB\s0 environment variable. .SS "bins" .IX Subsection "bins" An arrayref representing the \s-1PATH\s0 environment variable. .SS "extra" .IX Subsection "extra" A hashref of extra environment variables (e.g. \f(CW\*(C`PERL_MM_OPT\*(C'\fR and \&\f(CW\*(C`PERL_MB_OPT\*(C'\fR) .SS "no_create" .IX Subsection "no_create" If set, \f(CW\*(C`local::lib\*(C'\fR will not try to create directories when activating them. .SH "OBJECT METHODS" .IX Header "OBJECT METHODS" .SS "clone" .IX Subsection "clone" .ie n .IP "Arguments: %attributes" 4 .el .IP "Arguments: \f(CW%attributes\fR" 4 .IX Item "Arguments: %attributes" .PD 0 .ie n .IP "Return value: $local_lib" 4 .el .IP "Return value: \f(CW$local_lib\fR" 4 .IX Item "Return value: $local_lib" .PD .PP Constructs a new \f(CW\*(C`local::lib\*(C'\fR object based on the existing one, overriding the specified attributes. .SS "activate" .IX Subsection "activate" .ie n .IP "Arguments: $path" 4 .el .IP "Arguments: \f(CW$path\fR" 4 .IX Item "Arguments: $path" .PD 0 .ie n .IP "Return value: $new_local_lib" 4 .el .IP "Return value: \f(CW$new_local_lib\fR" 4 .IX Item "Return value: $new_local_lib" .PD .PP Constructs a new instance with the specified path active. .SS "deactivate" .IX Subsection "deactivate" .ie n .IP "Arguments: $path" 4 .el .IP "Arguments: \f(CW$path\fR" 4 .IX Item "Arguments: $path" .PD 0 .ie n .IP "Return value: $new_local_lib" 4 .el .IP "Return value: \f(CW$new_local_lib\fR" 4 .IX Item "Return value: $new_local_lib" .PD .PP Constructs a new instance with the specified path deactivated. .SS "deactivate_all" .IX Subsection "deactivate_all" .IP "Arguments: None" 4 .IX Item "Arguments: None" .PD 0 .ie n .IP "Return value: $new_local_lib" 4 .el .IP "Return value: \f(CW$new_local_lib\fR" 4 .IX Item "Return value: $new_local_lib" .PD .PP Constructs a new instance with all \f(CW\*(C`local::lib\*(C'\fR directories deactivated. .SS "environment_vars_string" .IX Subsection "environment_vars_string" .ie n .IP "Arguments: [ $shelltype ]" 4 .el .IP "Arguments: [ \f(CW$shelltype\fR ]" 4 .IX Item "Arguments: [ $shelltype ]" .PD 0 .ie n .IP "Return value: $shell_env_string" 4 .el .IP "Return value: \f(CW$shell_env_string\fR" 4 .IX Item "Return value: $shell_env_string" .PD .PP Returns a string to set up the \f(CW\*(C`local::lib\*(C'\fR, meant to be run by a shell. .SS "build_environment_vars" .IX Subsection "build_environment_vars" .IP "Arguments: None" 4 .IX Item "Arguments: None" .PD 0 .ie n .IP "Return value: %environment_vars" 4 .el .IP "Return value: \f(CW%environment_vars\fR" 4 .IX Item "Return value: %environment_vars" .PD .PP Returns a hash with the variables listed above, properly set to use the given path as the base directory. .SS "setup_env_hash" .IX Subsection "setup_env_hash" .IP "Arguments: None" 4 .IX Item "Arguments: None" .PD 0 .IP "Return value: None" 4 .IX Item "Return value: None" .PD .PP Constructs the \f(CW%ENV\fR keys for the given path, by calling \&\*(L"build_environment_vars\*(R". .SS "setup_local_lib" .IX Subsection "setup_local_lib" Constructs the \f(CW%ENV\fR hash using \*(L"setup_env_hash\*(R", and set up \f(CW@INC\fR. .SH "A WARNING ABOUT UNINST=1" .IX Header "A WARNING ABOUT UNINST=1" Be careful about using local::lib in combination with \*(L"make install UNINST=1\*(R". The idea of this feature is that will uninstall an old version of a module before installing a new one. However it lacks a safety check that the old version and the new version will go in the same directory. Used in combination with local::lib, you can potentially delete a globally accessible version of a module while installing the new version in a local place. Only combine \*(L"make install UNINST=1\*(R" and local::lib if you understand these possible consequences. .SH "LIMITATIONS" .IX Header "LIMITATIONS" .IP "\(bu" 4 Directory names with spaces in them are not well supported by the perl toolchain and the programs it uses. Pure-perl distributions should support spaces, but problems are more likely with dists that require compilation. A workaround you can do is moving your local::lib to a directory with spaces \&\fBafter\fR you installed all modules inside your local::lib bootstrap. But be aware that you can't update or install \s-1CPAN\s0 modules after the move. .IP "\(bu" 4 Rather basic shell detection. Right now anything with csh in its name is assumed to be a C shell or something compatible, and everything else is assumed to be Bourne, except on Win32 systems. If the \f(CW\*(C`SHELL\*(C'\fR environment variable is not set, a Bourne-compatible shell is assumed. .IP "\(bu" 4 Kills any existing \s-1PERL_MM_OPT\s0 or \s-1PERL_MB_OPT.\s0 .IP "\(bu" 4 Should probably auto-fixup \s-1CPAN\s0 config if not already done. .IP "\(bu" 4 On \s-1VMS\s0 and MacOS Classic (pre-OS X), local::lib loads File::Spec. This means any File::Spec version installed in the local::lib will be ignored by scripts using local::lib. A workaround for this is using \&\f(CW\*(C`use lib "$local_lib/lib/perl5";\*(C'\fR instead of using \f(CW\*(C`local::lib\*(C'\fR directly. .IP "\(bu" 4 Conflicts with ExtUtils::MakeMaker's \f(CW\*(C`PREFIX\*(C'\fR option. \&\f(CW\*(C`local::lib\*(C'\fR uses the \f(CW\*(C`INSTALL_BASE\*(C'\fR option, as it has more predictable and sane behavior. If something attempts to use the \f(CW\*(C`PREFIX\*(C'\fR option when running a \fIMakefile.PL\fR, ExtUtils::MakeMaker will refuse to run, as the two options conflict. This can be worked around by temporarily unsetting the \&\f(CW\*(C`PERL_MM_OPT\*(C'\fR environment variable. .IP "\(bu" 4 Conflicts with Module::Build's \f(CW\*(C`\-\-prefix\*(C'\fR option. Similar to the previous limitation, but any \f(CW\*(C`\-\-prefix\*(C'\fR option specified will be ignored. This can be worked around by temporarily unsetting the \f(CW\*(C`PERL_MB_OPT\*(C'\fR environment variable. .PP Patches very much welcome for any of the above. .IP "\(bu" 4 On Win32 systems, does not have a way to write the created environment variables to the registry, so that they can persist through a reboot. .SH "TROUBLESHOOTING" .IX Header "TROUBLESHOOTING" If you've configured local::lib to install \s-1CPAN\s0 modules somewhere in to your home directory, and at some point later you try to install a module with \f(CW\*(C`cpan \&\-i Foo::Bar\*(C'\fR, but it fails with an error like: \f(CW\*(C`Warning: You do not have permissions to install into /usr/lib64/perl5/site_perl/5.8.8/x86_64\-linux at /usr/lib64/perl5/5.8.8/Foo/Bar.pm\*(C'\fR and buried within the install log is an error saying \f(CW\*(C`\*(AqINSTALL_BASE\*(Aq is not a known MakeMaker parameter name\*(C'\fR, then you've somehow lost your updated ExtUtils::MakeMaker module. .PP To remedy this situation, rerun the bootstrapping procedure documented above. .PP Then, run \f(CW\*(C`rm \-r ~/.cpan/build/Foo\-Bar*\*(C'\fR .PP Finally, re-run \f(CW\*(C`cpan \-i Foo::Bar\*(C'\fR and it should install without problems. .SH "ENVIRONMENT" .IX Header "ENVIRONMENT" .IP "\s-1SHELL\s0" 4 .IX Item "SHELL" .PD 0 .IP "\s-1COMSPEC\s0" 4 .IX Item "COMSPEC" .PD local::lib looks at the user's \f(CW\*(C`SHELL\*(C'\fR environment variable when printing out commands to add to the shell configuration file. .Sp On Win32 systems, \f(CW\*(C`COMSPEC\*(C'\fR is also examined. .SH "SEE ALSO" .IX Header "SEE ALSO" .IP "\(bu" 4 Perl Advent article, 2011 <http://perladvent.org/2011/2011-12-01.html> .SH "SUPPORT" .IX Header "SUPPORT" \&\s-1IRC:\s0 .PP .Vb 1 \& Join #toolchain on irc.perl.org. .Ve .SH "AUTHOR" .IX Header "AUTHOR" Matt S Trout <mst@shadowcat.co.uk> http://www.shadowcat.co.uk/ .PP auto_install fixes kindly sponsored by http://www.takkle.com/ .SH "CONTRIBUTORS" .IX Header "CONTRIBUTORS" Patches to correctly output commands for csh style shells, as well as some documentation additions, contributed by Christopher Nehren <apeiron@cpan.org>. .PP Doc patches for a custom local::lib directory, more cleanups in the english documentation and a german documentation contributed by Torsten Raudssus <torsten@raudssus.de>. .PP Hans Dieter Pearcey <hdp@cpan.org> sent in some additional tests for ensuring things will install properly, submitted a fix for the bug causing problems with writing Makefiles during bootstrapping, contributed an example program, and submitted yet another fix to ensure that local::lib can install and bootstrap properly. Many, many thanks! .PP pattern of Freenode \s-1IRC\s0 contributed the beginnings of the Troubleshooting section. Many thanks! .PP Patch to add Win32 support contributed by Curtis Jewell <csjewell@cpan.org>. .PP Warnings for missing \s-1PATH/PERL5LIB\s0 (as when not running interactively) silenced by a patch from Marco Emilio Poleggi. .PP Mark Stosberg <mark@summersault.com> provided the code for the now deleted \&'\-\-self\-contained' option. .PP Documentation patches to make win32 usage clearer by David Mertens <dcmertens.perl@gmail.com> (run4flat). .PP Brazilian portuguese translation and minor doc patches contributed by Breno G. de Oliveira <garu@cpan.org>. .PP Improvements to stacking multiple local::lib dirs and removing them from the environment later on contributed by Andrew Rodland <arodland@cpan.org>. .PP Patch for Carp version mismatch contributed by Hakim Cassimally <osfameron@cpan.org>. .PP Rewrite of internals and numerous bug fixes and added features contributed by Graham Knop <haarg@haarg.org>. .SH "COPYRIGHT" .IX Header "COPYRIGHT" Copyright (c) 2007 \- 2013 the local::lib \*(L"\s-1AUTHOR\*(R"\s0 and \*(L"\s-1CONTRIBUTORS\*(R"\s0 as listed above. .SH "LICENSE" .IX Header "LICENSE" This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. PK�����A0]+ihA@Q��@Q����man/man3/Try::Tiny.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "Try::Tiny 3" .TH Try::Tiny 3 "2021-11-23" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" Try::Tiny \- Minimal try/catch with proper preservation of $@ .SH "VERSION" .IX Header "VERSION" version 0.31 .SH "SYNOPSIS" .IX Header "SYNOPSIS" You can use Try::Tiny's \f(CW\*(C`try\*(C'\fR and \f(CW\*(C`catch\*(C'\fR to expect and handle exceptional conditions, avoiding quirks in Perl and common mistakes: .PP .Vb 6 \& # handle errors with a catch handler \& try { \& die "foo"; \& } catch { \& warn "caught error: $_"; # not $@ \& }; .Ve .PP You can also use it like a standalone \f(CW\*(C`eval\*(C'\fR to catch and ignore any error conditions. Obviously, this is an extreme measure not to be undertaken lightly: .PP .Vb 4 \& # just silence errors \& try { \& die "foo"; \& }; .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" This module provides bare bones \f(CW\*(C`try\*(C'\fR/\f(CW\*(C`catch\*(C'\fR/\f(CW\*(C`finally\*(C'\fR statements that are designed to minimize common mistakes with eval blocks, and \s-1NOTHING\s0 else. .PP This is unlike TryCatch which provides a nice syntax and avoids adding another call stack layer, and supports calling \f(CW\*(C`return\*(C'\fR from the \f(CW\*(C`try\*(C'\fR block to return from the parent subroutine. These extra features come at a cost of a few dependencies, namely Devel::Declare and Scope::Upper which are occasionally problematic, and the additional catch filtering uses Moose type constraints which may not be desirable either. .PP The main focus of this module is to provide simple and reliable error handling for those having a hard time installing TryCatch, but who still want to write correct \f(CW\*(C`eval\*(C'\fR blocks without 5 lines of boilerplate each time. .PP It's designed to work as correctly as possible in light of the various pathological edge cases (see \*(L"\s-1BACKGROUND\*(R"\s0) and to be compatible with any style of error values (simple strings, references, objects, overloaded objects, etc). .PP If the \f(CW\*(C`try\*(C'\fR block dies, it returns the value of the last statement executed in the \f(CW\*(C`catch\*(C'\fR block, if there is one. Otherwise, it returns \f(CW\*(C`undef\*(C'\fR in scalar context or the empty list in list context. The following examples all assign \f(CW"bar"\fR to \f(CW$x\fR: .PP .Vb 3 \& my $x = try { die "foo" } catch { "bar" }; \& my $x = try { die "foo" } || "bar"; \& my $x = (try { die "foo" }) // "bar"; \& \& my $x = eval { die "foo" } || "bar"; .Ve .PP You can add \f(CW\*(C`finally\*(C'\fR blocks, yielding the following: .PP .Vb 3 \& my $x; \& try { die \*(Aqfoo\*(Aq } finally { $x = \*(Aqbar\*(Aq }; \& try { die \*(Aqfoo\*(Aq } catch { warn "Got a die: $_" } finally { $x = \*(Aqbar\*(Aq }; .Ve .PP \&\f(CW\*(C`finally\*(C'\fR blocks are always executed making them suitable for cleanup code which cannot be handled using local. You can add as many \f(CW\*(C`finally\*(C'\fR blocks to a given \f(CW\*(C`try\*(C'\fR block as you like. .PP Note that adding a \f(CW\*(C`finally\*(C'\fR block without a preceding \f(CW\*(C`catch\*(C'\fR block suppresses any errors. This behaviour is consistent with using a standalone \&\f(CW\*(C`eval\*(C'\fR, but it is not consistent with \f(CW\*(C`try\*(C'\fR/\f(CW\*(C`finally\*(C'\fR patterns found in other programming languages, such as Java, Python, Javascript or C#. If you learned the \f(CW\*(C`try\*(C'\fR/\f(CW\*(C`finally\*(C'\fR pattern from one of these languages, watch out for this. .SH "EXPORTS" .IX Header "EXPORTS" All functions are exported by default using Exporter. .PP If you need to rename the \f(CW\*(C`try\*(C'\fR, \f(CW\*(C`catch\*(C'\fR or \f(CW\*(C`finally\*(C'\fR keyword consider using Sub::Import to get Sub::Exporter's flexibility. .IP "try (&;@)" 4 .IX Item "try (&;@)" Takes one mandatory \f(CW\*(C`try\*(C'\fR subroutine, an optional \f(CW\*(C`catch\*(C'\fR subroutine and \f(CW\*(C`finally\*(C'\fR subroutine. .Sp The mandatory subroutine is evaluated in the context of an \f(CW\*(C`eval\*(C'\fR block. .Sp If no error occurred the value from the first block is returned, preserving list/scalar context. .Sp If there was an error and the second subroutine was given it will be invoked with the error in \f(CW$_\fR (localized) and as that block's first and only argument. .Sp \&\f(CW$@\fR does \fBnot\fR contain the error. Inside the \f(CW\*(C`catch\*(C'\fR block it has the same value it had before the \f(CW\*(C`try\*(C'\fR block was executed. .Sp Note that the error may be false, but if that happens the \f(CW\*(C`catch\*(C'\fR block will still be invoked. .Sp Once all execution is finished then the \f(CW\*(C`finally\*(C'\fR block, if given, will execute. .IP "catch (&;@)" 4 .IX Item "catch (&;@)" Intended to be used in the second argument position of \f(CW\*(C`try\*(C'\fR. .Sp Returns a reference to the subroutine it was given but blessed as \&\f(CW\*(C`Try::Tiny::Catch\*(C'\fR which allows try to decode correctly what to do with this code reference. .Sp .Vb 1 \& catch { ... } .Ve .Sp Inside the \f(CW\*(C`catch\*(C'\fR block the caught error is stored in \f(CW$_\fR, while previous value of \f(CW$@\fR is still available for use. This value may or may not be meaningful depending on what happened before the \f(CW\*(C`try\*(C'\fR, but it might be a good idea to preserve it in an error stack. .Sp For code that captures \f(CW$@\fR when throwing new errors (i.e. Class::Throwable), you'll need to do: .Sp .Vb 1 \& local $@ = $_; .Ve .IP "finally (&;@)" 4 .IX Item "finally (&;@)" .Vb 3 \& try { ... } \& catch { ... } \& finally { ... }; .Ve .Sp Or .Sp .Vb 2 \& try { ... } \& finally { ... }; .Ve .Sp Or even .Sp .Vb 3 \& try { ... } \& finally { ... } \& catch { ... }; .Ve .Sp Intended to be the second or third element of \f(CW\*(C`try\*(C'\fR. \f(CW\*(C`finally\*(C'\fR blocks are always executed in the event of a successful \f(CW\*(C`try\*(C'\fR or if \f(CW\*(C`catch\*(C'\fR is run. This allows you to locate cleanup code which cannot be done via \f(CW\*(C`local()\*(C'\fR e.g. closing a file handle. .Sp When invoked, the \f(CW\*(C`finally\*(C'\fR block is passed the error that was caught. If no error was caught, it is passed nothing. (Note that the \f(CW\*(C`finally\*(C'\fR block does not localize \f(CW$_\fR with the error, since unlike in a \f(CW\*(C`catch\*(C'\fR block, there is no way to know if \f(CW\*(C`$_ == undef\*(C'\fR implies that there were no errors.) In other words, the following code does just what you would expect: .Sp .Vb 11 \& try { \& die_sometimes(); \& } catch { \& # ...code run in case of error \& } finally { \& if (@_) { \& print "The try block died with: @_\en"; \& } else { \& print "The try block ran without error.\en"; \& } \& }; .Ve .Sp \&\fBYou must always do your own error handling in the \f(CB\*(C`finally\*(C'\fB block\fR. \f(CW\*(C`Try::Tiny\*(C'\fR will not do anything about handling possible errors coming from code located in these blocks. .Sp Furthermore \fBexceptions in \f(CB\*(C`finally\*(C'\fB blocks are not trappable and are unable to influence the execution of your program\fR. This is due to limitation of \&\f(CW\*(C`DESTROY\*(C'\fR\-based scope guards, which \f(CW\*(C`finally\*(C'\fR is implemented on top of. This may change in a future version of Try::Tiny. .Sp In the same way \f(CW\*(C`catch()\*(C'\fR blesses the code reference this subroutine does the same except it bless them as \f(CW\*(C`Try::Tiny::Finally\*(C'\fR. .SH "BACKGROUND" .IX Header "BACKGROUND" There are a number of issues with \f(CW\*(C`eval\*(C'\fR. .SS "Clobbering $@" .IX Subsection "Clobbering $@" When you run an \f(CW\*(C`eval\*(C'\fR block and it succeeds, \f(CW$@\fR will be cleared, potentially clobbering an error that is currently being caught. .PP This causes action at a distance, clearing previous errors your caller may have not yet handled. .PP \&\f(CW$@\fR must be properly localized before invoking \f(CW\*(C`eval\*(C'\fR in order to avoid this issue. .PP More specifically, before Perl version 5.14.0 \&\f(CW$@\fR was clobbered at the beginning of the \f(CW\*(C`eval\*(C'\fR, which also made it impossible to capture the previous error before you die (for instance when making exception objects with error stacks). .PP For this reason \f(CW\*(C`try\*(C'\fR will actually set \f(CW$@\fR to its previous value (the one available before entering the \f(CW\*(C`try\*(C'\fR block) in the beginning of the \f(CW\*(C`eval\*(C'\fR block. .SS "Localizing $@ silently masks errors" .IX Subsection "Localizing $@ silently masks errors" Inside an \f(CW\*(C`eval\*(C'\fR block, \f(CW\*(C`die\*(C'\fR behaves sort of like: .PP .Vb 4 \& sub die { \& $@ = $_[0]; \& return_undef_from_eval(); \& } .Ve .PP This means that if you were polite and localized \f(CW$@\fR you can't die in that scope, or your error will be discarded (printing \*(L"Something's wrong\*(R" instead). .PP The workaround is very ugly: .PP .Vb 5 \& my $error = do { \& local $@; \& eval { ... }; \& $@; \& }; \& \& ... \& die $error; .Ve .SS "$@ might not be a true value" .IX Subsection "$@ might not be a true value" This code is wrong: .PP .Vb 3 \& if ( $@ ) { \& ... \& } .Ve .PP because due to the previous caveats it may have been unset. .PP \&\f(CW$@\fR could also be an overloaded error object that evaluates to false, but that's asking for trouble anyway. .PP The classic failure mode (fixed in Perl 5.14.0) is: .PP .Vb 3 \& sub Object::DESTROY { \& eval { ... } \& } \& \& eval { \& my $obj = Object\->new; \& \& die "foo"; \& }; \& \& if ( $@ ) { \& \& } .Ve .PP In this case since \f(CW\*(C`Object::DESTROY\*(C'\fR is not localizing \f(CW$@\fR but still uses \&\f(CW\*(C`eval\*(C'\fR, it will set \f(CW$@\fR to \f(CW""\fR. .PP The destructor is called when the stack is unwound, after \f(CW\*(C`die\*(C'\fR sets \f(CW$@\fR to \&\f(CW"foo at Foo.pm line 42\en"\fR, so by the time \f(CW\*(C`if ( $@ )\*(C'\fR is evaluated it has been cleared by \f(CW\*(C`eval\*(C'\fR in the destructor. .PP The workaround for this is even uglier than the previous ones. Even though we can't save the value of \f(CW$@\fR from code that doesn't localize, we can at least be sure the \f(CW\*(C`eval\*(C'\fR was aborted due to an error: .PP .Vb 2 \& my $failed = not eval { \& ... \& \& return 1; \& }; .Ve .PP This is because an \f(CW\*(C`eval\*(C'\fR that caught a \f(CW\*(C`die\*(C'\fR will always return a false value. .SH "ALTERNATE SYNTAX" .IX Header "ALTERNATE SYNTAX" Using Perl 5.10 you can use \*(L"Switch statements\*(R" in perlsyn (but please don't, because that syntax has since been deprecated because there was too much unexpected magical behaviour). .PP The \f(CW\*(C`catch\*(C'\fR block is invoked in a topicalizer context (like a \f(CW\*(C`given\*(C'\fR block), but note that you can't return a useful value from \f(CW\*(C`catch\*(C'\fR using the \f(CW\*(C`when\*(C'\fR blocks without an explicit \f(CW\*(C`return\*(C'\fR. .PP This is somewhat similar to Perl 6's \f(CW\*(C`CATCH\*(C'\fR blocks. You can use it to concisely match errors: .PP .Vb 6 \& try { \& require Foo; \& } catch { \& when (/^Can\*(Aqt locate .*?\e.pm in \e@INC/) { } # ignore \& default { die $_ } \& }; .Ve .SH "CAVEATS" .IX Header "CAVEATS" .IP "\(bu" 4 \&\f(CW@_\fR is not available within the \f(CW\*(C`try\*(C'\fR block, so you need to copy your argument list. In case you want to work with argument values directly via \f(CW@_\fR aliasing (i.e. allow \f(CW\*(C`$_[1] = "foo"\*(C'\fR), you need to pass \f(CW@_\fR by reference: .Sp .Vb 4 \& sub foo { \& my ( $self, @args ) = @_; \& try { $self\->bar(@args) } \& } .Ve .Sp or .Sp .Vb 5 \& sub bar_in_place { \& my $self = shift; \& my $args = \e@_; \& try { $_ = $self\->bar($_) for @$args } \& } .Ve .IP "\(bu" 4 \&\f(CW\*(C`return\*(C'\fR returns from the \f(CW\*(C`try\*(C'\fR block, not from the parent sub (note that this is also how \f(CW\*(C`eval\*(C'\fR works, but not how TryCatch works): .Sp .Vb 7 \& sub parent_sub { \& try { \& die; \& } \& catch { \& return; \& }; \& \& say "this text WILL be displayed, even though an exception is thrown"; \& } .Ve .Sp Instead, you should capture the return value: .Sp .Vb 6 \& sub parent_sub { \& my $success = try { \& die; \& 1; \& }; \& return unless $success; \& \& say "This text WILL NEVER appear!"; \& } \& # OR \& sub parent_sub_with_catch { \& my $success = try { \& die; \& 1; \& } \& catch { \& # do something with $_ \& return undef; #see note \& }; \& return unless $success; \& \& say "This text WILL NEVER appear!"; \& } .Ve .Sp Note that if you have a \f(CW\*(C`catch\*(C'\fR block, it must return \f(CW\*(C`undef\*(C'\fR for this to work, since if a \f(CW\*(C`catch\*(C'\fR block exists, its return value is returned in place of \f(CW\*(C`undef\*(C'\fR when an exception is thrown. .IP "\(bu" 4 \&\f(CW\*(C`try\*(C'\fR introduces another caller stack frame. Sub::Uplevel is not used. Carp will not report this when using full stack traces, though, because \&\f(CW%Carp::Internal\fR is used. This lack of magic is considered a feature. .IP "\(bu" 4 The value of \f(CW$_\fR in the \f(CW\*(C`catch\*(C'\fR block is not guaranteed to be the value of the exception thrown (\f(CW$@\fR) in the \f(CW\*(C`try\*(C'\fR block. There is no safe way to ensure this, since \f(CW\*(C`eval\*(C'\fR may be used unhygienically in destructors. The only guarantee is that the \f(CW\*(C`catch\*(C'\fR will be called if an exception is thrown. .IP "\(bu" 4 The return value of the \f(CW\*(C`catch\*(C'\fR block is not ignored, so if testing the result of the expression for truth on success, be sure to return a false value from the \f(CW\*(C`catch\*(C'\fR block: .Sp .Vb 4 \& my $obj = try { \& MightFail\->new; \& } catch { \& ... \& \& return; # avoid returning a true value; \& }; \& \& return unless $obj; .Ve .IP "\(bu" 4 \&\f(CW$SIG{_\|_DIE_\|_}\fR is still in effect. .Sp Though it can be argued that \f(CW$SIG{_\|_DIE_\|_}\fR should be disabled inside of \&\f(CW\*(C`eval\*(C'\fR blocks, since it isn't people have grown to rely on it. Therefore in the interests of compatibility, \f(CW\*(C`try\*(C'\fR does not disable \f(CW$SIG{_\|_DIE_\|_}\fR for the scope of the error throwing code. .IP "\(bu" 4 Lexical \f(CW$_\fR may override the one set by \f(CW\*(C`catch\*(C'\fR. .Sp For example Perl 5.10's \f(CW\*(C`given\*(C'\fR form uses a lexical \f(CW$_\fR, creating some confusing behavior: .Sp .Vb 10 \& given ($foo) { \& when (...) { \& try { \& ... \& } catch { \& warn $_; # will print $foo, not the error \& warn $_[0]; # instead, get the error like this \& } \& } \& } .Ve .Sp Note that this behavior was changed once again in Perl5 version 18 <https://metacpan.org/module/perldelta#given-now-aliases-the-global-_>. However, since the entirety of lexical \f(CW$_\fR is now considered experimental <https://metacpan.org/module/perldelta#Lexical-_-is-now-experimental>, it is unclear whether the new version 18 behavior is final. .SH "SEE ALSO" .IX Header "SEE ALSO" .IP "Syntax::Keyword::Try" 4 .IX Item "Syntax::Keyword::Try" Only available on perls >= 5.14, with a slightly different syntax (e.g. no trailing \f(CW\*(C`;\*(C'\fR because it's actually a keyword, not a sub, but this means you can \f(CW\*(C`return\*(C'\fR and \f(CW\*(C`next\*(C'\fR within it). Use Feature::Compat::Try to automatically switch to the native \f(CW\*(C`try\*(C'\fR syntax in newer perls (when available). See also Try Catch Exception Handling. .IP "TryCatch" 4 .IX Item "TryCatch" Much more feature complete, more convenient semantics, but at the cost of implementation complexity. .IP "autodie" 4 .IX Item "autodie" Automatic error throwing for builtin functions and more. Also designed to work well with \f(CW\*(C`given\*(C'\fR/\f(CW\*(C`when\*(C'\fR. .IP "Throwable" 4 .IX Item "Throwable" A lightweight role for rolling your own exception classes. .IP "Error" 4 .IX Item "Error" Exception object implementation with a \f(CW\*(C`try\*(C'\fR statement. Does not localize \&\f(CW$@\fR. .IP "Exception::Class::TryCatch" 4 .IX Item "Exception::Class::TryCatch" Provides a \f(CW\*(C`catch\*(C'\fR statement, but properly calling \f(CW\*(C`eval\*(C'\fR is your responsibility. .Sp The \f(CW\*(C`try\*(C'\fR keyword pushes \f(CW$@\fR onto an error stack, avoiding some of the issues with \f(CW$@\fR, but you still need to localize to prevent clobbering. .SH "LIGHTNING TALK" .IX Header "LIGHTNING TALK" I gave a lightning talk about this module, you can see the slides (Firefox only): .PP <http://web.archive.org/web/20100628040134/http://nothingmuch.woobling.org/talks/takahashi.xul> .PP Or read the source: .PP <http://web.archive.org/web/20100305133605/http://nothingmuch.woobling.org/talks/yapc_asia_2009/try_tiny.yml> .SH "SUPPORT" .IX Header "SUPPORT" Bugs may be submitted through the \s-1RT\s0 bug tracker <https://rt.cpan.org/Public/Dist/Display.html?Name=Try-Tiny> (or bug\-Try\-Tiny@rt.cpan.org <mailto:bug-Try-Tiny@rt.cpan.org>). .SH "AUTHORS" .IX Header "AUTHORS" .IP "\(bu" 4 יובל קוג'מן (Yuval Kogman) <nothingmuch@woobling.org> .IP "\(bu" 4 Jesse Luehrs <doy@tozt.net> .SH "CONTRIBUTORS" .IX Header "CONTRIBUTORS" .IP "\(bu" 4 Karen Etheridge <ether@cpan.org> .IP "\(bu" 4 Peter Rabbitson <ribasushi@cpan.org> .IP "\(bu" 4 Ricardo Signes <rjbs@cpan.org> .IP "\(bu" 4 Mark Fowler <mark@twoshortplanks.com> .IP "\(bu" 4 Graham Knop <haarg@haarg.org> .IP "\(bu" 4 Aristotle Pagaltzis <pagaltzis@gmx.de> .IP "\(bu" 4 Dagfinn Ilmari Mannsåker <ilmari@ilmari.org> .IP "\(bu" 4 Lukas Mai <l.mai@web.de> .IP "\(bu" 4 Alex <alex@koban.(none)> .IP "\(bu" 4 anaxagoras <walkeraj@gmail.com> .IP "\(bu" 4 Andrew Yates <ayates@haddock.local> .IP "\(bu" 4 awalker <awalker@sourcefire.com> .IP "\(bu" 4 chromatic <chromatic@wgz.org> .IP "\(bu" 4 cm-perl <cm\-perl@users.noreply.github.com> .IP "\(bu" 4 David Lowe <davidl@lokku.com> .IP "\(bu" 4 Glenn Fowler <cebjyre@cpan.org> .IP "\(bu" 4 Hans Dieter Pearcey <hdp@weftsoar.net> .IP "\(bu" 4 Jens Berthold <jens@jebecs.de> .IP "\(bu" 4 Jonathan Yu <JAWNSY@cpan.org> .IP "\(bu" 4 Marc Mims <marc@questright.com> .IP "\(bu" 4 Mark Stosberg <mark@stosberg.com> .IP "\(bu" 4 Pali <pali@cpan.org> .IP "\(bu" 4 Paul Howarth <paul@city\-fan.org> .IP "\(bu" 4 Rudolf Leermakers <rudolf@hatsuseno.org> .SH "COPYRIGHT AND LICENCE" .IX Header "COPYRIGHT AND LICENCE" This software is Copyright (c) 2009 by יובל קוג'מן (Yuval Kogman). .PP This is free software, licensed under: .PP .Vb 1 \& The MIT (X11) License .Ve PK�����A0]>DKv��Kv����man/man3/version::Internals.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "version::Internals 3" .TH version::Internals 3 "2022-07-15" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" version::Internals \- Perl extension for Version Objects .SH "DESCRIPTION" .IX Header "DESCRIPTION" Overloaded version objects for all modern versions of Perl. This documents the internal data representation and underlying code for version.pm. See \&\fIversion.pod\fR for daily usage. This document is only useful for users interested in the gory details. .SH "WHAT IS A VERSION?" .IX Header "WHAT IS A VERSION?" For the purposes of this module, a version \*(L"number\*(R" is a sequence of positive integer values separated by one or more decimal points and optionally a single underscore. This corresponds to what Perl itself uses for a version, as well as extending the \*(L"version as number\*(R" that is discussed in the various editions of the Camel book. .PP There are actually two distinct kinds of version objects: .IP "Decimal versions" 4 .IX Item "Decimal versions" Any version which \*(L"looks like a number\*(R", see \*(L"Decimal Versions\*(R". This also includes versions with a single decimal point and a single embedded underscore, see \*(L"Alpha Versions\*(R", even though these must be quoted to preserve the underscore formatting. .IP "Dotted-Decimal versions" 4 .IX Item "Dotted-Decimal versions" Also referred to as \*(L"Dotted-Integer\*(R", these contains more than one decimal point and may have an optional embedded underscore, see Dotted-Decimal Versions. This is what is commonly used in most open source software as the \*(L"external\*(R" version (the one used as part of the tag or tarfile name). A leading 'v' character is now required and will warn if it missing. .PP Both of these methods will produce similar version objects, in that the default stringification will yield the version \*(L"Normal Form\*(R" only if required: .PP .Vb 3 \& $v = version\->new(1.002); # 1.002, but compares like 1.2.0 \& $v = version\->new(1.002003); # 1.002003 \& $v2 = version\->new("v1.2.3"); # v1.2.3 .Ve .PP In specific, version numbers initialized as \*(L"Decimal Versions\*(R" will stringify as they were originally created (i.e. the same string that was passed to \f(CW\*(C`new()\*(C'\fR. Version numbers initialized as \*(L"Dotted-Decimal Versions\*(R" will be stringified as \*(L"Normal Form\*(R". .SS "Decimal Versions" .IX Subsection "Decimal Versions" These correspond to historical versions of Perl itself prior to 5.6.0, as well as all other modules which follow the Camel rules for the \&\f(CW$VERSION\fR scalar. A Decimal version is initialized with what looks like a floating point number. Leading zeros \fBare\fR significant and trailing zeros are implied so that a minimum of three places is maintained between subversions. What this means is that any subversion (digits to the right of the decimal place) that contains less than three digits will have trailing zeros added to make up the difference, but only for purposes of comparison with other version objects. For example: .PP .Vb 7 \& # Prints Equivalent to \& $v = version\->new( 1.2); # 1.2 v1.200.0 \& $v = version\->new( 1.02); # 1.02 v1.20.0 \& $v = version\->new( 1.002); # 1.002 v1.2.0 \& $v = version\->new( 1.0023); # 1.0023 v1.2.300 \& $v = version\->new( 1.00203); # 1.00203 v1.2.30 \& $v = version\->new( 1.002003); # 1.002003 v1.2.3 .Ve .PP All of the preceding examples are true whether or not the input value is quoted. The important feature is that the input value contains only a single decimal. See also \*(L"Alpha Versions\*(R". .PP \&\s-1IMPORTANT NOTE:\s0 As shown above, if your Decimal version contains more than 3 significant digits after the decimal place, it will be split on each multiple of 3, so 1.0003 is equivalent to v1.0.300, due to the need to remain compatible with Perl's own 5.005_03 == 5.5.30 interpretation. Any trailing zeros are ignored for mathematical comparison purposes. .SS "Dotted-Decimal Versions" .IX Subsection "Dotted-Decimal Versions" These are the newest form of versions, and correspond to Perl's own version style beginning with 5.6.0. Starting with Perl 5.10.0, and most likely Perl 6, this is likely to be the preferred form. This method normally requires that the input parameter be quoted, although Perl's after 5.8.1 can use v\-strings as a special form of quoting, but this is highly discouraged. .PP Unlike \*(L"Decimal Versions\*(R", Dotted-Decimal Versions have more than a single decimal point, e.g.: .PP .Vb 6 \& # Prints \& $v = version\->new( "v1.200"); # v1.200.0 \& $v = version\->new("v1.20.0"); # v1.20.0 \& $v = qv("v1.2.3"); # v1.2.3 \& $v = qv("1.2.3"); # v1.2.3 \& $v = qv("1.20"); # v1.20.0 .Ve .PP In general, Dotted-Decimal Versions permit the greatest amount of freedom to specify a version, whereas Decimal Versions enforce a certain uniformity. .PP Just like \*(L"Decimal Versions\*(R", Dotted-Decimal Versions can be used as \&\*(L"Alpha Versions\*(R". .SS "Alpha Versions" .IX Subsection "Alpha Versions" For module authors using \s-1CPAN,\s0 the convention has been to note unstable releases with an underscore in the version string. (See \s-1CPAN\s0.) version.pm follows this convention and alpha releases will test as being newer than the more recent stable release, and less than the next stable release. Only the last element may be separated by an underscore: .PP .Vb 2 \& # Declaring \& use version 0.77; our $VERSION = version\->declare("v1.2_3"); \& \& # Parsing \& $v1 = version\->parse("v1.2_3"); \& $v1 = version\->parse("1.002_003"); .Ve .PP Note that you \fBmust\fR quote the version when writing an alpha Decimal version. The stringified form of Decimal versions will always be the same string that was used to initialize the version object. .SS "Regular Expressions for Version Parsing" .IX Subsection "Regular Expressions for Version Parsing" A formalized definition of the legal forms for version strings is included in the \f(CW\*(C`version::regex\*(C'\fR class. Primitives are included for common elements, although they are scoped to the file so they are useful for reference purposes only. There are two publicly accessible scalars that can be used in other code (not exported): .ie n .IP "$version::LAX" 4 .el .IP "\f(CW$version::LAX\fR" 4 .IX Item "$version::LAX" This regexp covers all of the legal forms allowed under the current version string parser. This is not to say that all of these forms are recommended, and some of them can only be used when quoted. .Sp For dotted decimals: .Sp .Vb 3 \& v1.2 \& 1.2345.6 \& v1.23_4 .Ve .Sp The leading 'v' is optional if two or more decimals appear. If only a single decimal is included, then the leading 'v' is required to trigger the dotted-decimal parsing. A leading zero is permitted, though not recommended except when quoted, because of the risk that Perl will treat the number as octal. A trailing underscore plus one or more digits denotes an alpha or development release (and must be quoted to be parsed properly). .Sp For decimal versions: .Sp .Vb 3 \& 1 \& 1.2345 \& 1.2345_01 .Ve .Sp an integer portion, an optional decimal point, and optionally one or more digits to the right of the decimal are all required. A trailing underscore is permitted and a leading zero is permitted. Just like the lax dotted-decimal version, quoting the values is required for alpha/development forms to be parsed correctly. .ie n .IP "$version::STRICT" 4 .el .IP "\f(CW$version::STRICT\fR" 4 .IX Item "$version::STRICT" This regexp covers a much more limited set of formats and constitutes the best practices for initializing version objects. Whether you choose to employ decimal or dotted-decimal for is a personal preference however. .RS 4 .IP "v1.234.5" 4 .IX Item "v1.234.5" For dotted-decimal versions, a leading 'v' is required, with three or more sub-versions of no more than three digits. A leading 0 (zero) before the first sub-version (in the above example, '1') is also prohibited. .IP "2.3456" 4 .IX Item "2.3456" For decimal versions, an integer portion (no leading 0), a decimal point, and one or more digits to the right of the decimal are all required. .RE .RS 4 .RE .PP Both of the provided scalars are already compiled as regular expressions and do not contain either anchors or implicit groupings, so they can be included in your own regular expressions freely. For example, consider the following code: .PP .Vb 6 \& ($pkg, $ver) =~ / \& ^[ \et]* \& use [ \et]+($PKGNAME) \& (?:[ \et]+($version::STRICT))? \& [ \et]*; \& /x; .Ve .PP This would match a line of the form: .PP .Vb 1 \& use Foo::Bar::Baz v1.2.3; # legal only in Perl 5.8.1+ .Ve .PP where \f(CW$PKGNAME\fR is another regular expression that defines the legal forms for package names. .SH "IMPLEMENTATION DETAILS" .IX Header "IMPLEMENTATION DETAILS" .SS "Equivalence between Decimal and Dotted-Decimal Versions" .IX Subsection "Equivalence between Decimal and Dotted-Decimal Versions" When Perl 5.6.0 was released, the decision was made to provide a transformation between the old-style decimal versions and new-style dotted-decimal versions: .PP .Vb 2 \& 5.6.0 == 5.006000 \& 5.005_04 == 5.5.40 .Ve .PP The floating point number is taken and split first on the single decimal place, then each group of three digits to the right of the decimal makes up the next digit, and so on until the number of significant digits is exhausted, \&\fBplus\fR enough trailing zeros to reach the next multiple of three. .PP This was the method that version.pm adopted as well. Some examples may be helpful: .PP .Vb 9 \& equivalent \& decimal zero\-padded dotted\-decimal \& \-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\-\-\-\- \& 1.2 1.200 v1.200.0 \& 1.02 1.020 v1.20.0 \& 1.002 1.002 v1.2.0 \& 1.0023 1.002300 v1.2.300 \& 1.00203 1.002030 v1.2.30 \& 1.002003 1.002003 v1.2.3 .Ve .SS "Quoting Rules" .IX Subsection "Quoting Rules" Because of the nature of the Perl parsing and tokenizing routines, certain initialization values \fBmust\fR be quoted in order to correctly parse as the intended version, especially when using the \f(CW\*(C`declare\*(C'\fR or \&\*(L"\fBqv()\fR\*(R" methods. While you do not have to quote decimal numbers when creating version objects, it is always safe to quote \fBall\fR initial values when using version.pm methods, as this will ensure that what you type is what is used. .PP Additionally, if you quote your initializer, then the quoted value that goes \&\fBin\fR will be exactly what comes \fBout\fR when your \f(CW$VERSION\fR is printed (stringified). If you do not quote your value, Perl's normal numeric handling comes into play and you may not get back what you were expecting. .PP If you use a mathematic formula that resolves to a floating point number, you are dependent on Perl's conversion routines to yield the version you expect. You are pretty safe by dividing by a power of 10, for example, but other operations are not likely to be what you intend. For example: .PP .Vb 4 \& $VERSION = version\->new((qw$Revision: 1.4)[1]/10); \& print $VERSION; # yields 0.14 \& $V2 = version\->new(100/9); # Integer overflow in decimal number \& print $V2; # yields something like 11.111.111.100 .Ve .PP Perl 5.8.1 and beyond are able to automatically quote v\-strings but that is not possible in earlier versions of Perl. In other words: .PP .Vb 2 \& $version = version\->new("v2.5.4"); # legal in all versions of Perl \& $newvers = version\->new(v2.5.4); # legal only in Perl >= 5.8.1 .Ve .SS "What about v\-strings?" .IX Subsection "What about v-strings?" There are two ways to enter v\-strings: a bare number with two or more decimal points, or a bare number with one or more decimal points and a leading 'v' character (also bare). For example: .PP .Vb 2 \& $vs1 = 1.2.3; # encoded as \e1\e2\e3 \& $vs2 = v1.2; # encoded as \e1\e2 .Ve .PP However, the use of bare v\-strings to initialize version objects is \&\fBstrongly\fR discouraged in all circumstances. Also, bare v\-strings are not completely supported in any version of Perl prior to 5.8.1. .PP If you insist on using bare v\-strings with Perl > 5.6.0, be aware of the following limitations: .PP 1) For Perl releases 5.6.0 through 5.8.0, the v\-string code merely guesses, based on some characteristics of v\-strings. You \fBmust\fR use a three part version, e.g. 1.2.3 or v1.2.3 in order for this heuristic to be successful. .PP 2) For Perl releases 5.8.1 and later, v\-strings have changed in the Perl core to be magical, which means that the version.pm code can automatically determine whether the v\-string encoding was used. .PP 3) In all cases, a version created using v\-strings will have a stringified form that has a leading 'v' character, for the simple reason that sometimes it is impossible to tell whether one was present initially. .SS "Version Object Internals" .IX Subsection "Version Object Internals" version.pm provides an overloaded version object that is designed to both encapsulate the author's intended \f(CW$VERSION\fR assignment as well as make it completely natural to use those objects as if they were numbers (e.g. for comparisons). To do this, a version object contains both the original representation as typed by the author, as well as a parsed representation to ease comparisons. Version objects employ overload methods to simplify code that needs to compare, print, etc the objects. .PP The internal structure of version objects is a blessed hash with several components: .PP .Vb 11 \& bless( { \& \*(Aqoriginal\*(Aq => \*(Aqv1.2.3_4\*(Aq, \& \*(Aqalpha\*(Aq => 1, \& \*(Aqqv\*(Aq => 1, \& \*(Aqversion\*(Aq => [ \& 1, \& 2, \& 3, \& 4 \& ] \& }, \*(Aqversion\*(Aq ); .Ve .IP "original" 4 .IX Item "original" A faithful representation of the value used to initialize this version object. The only time this will not be precisely the same characters that exist in the source file is if a short dotted-decimal version like v1.2 was used (in which case it will contain 'v1.2'). This form is \&\fB\s-1STRONGLY\s0\fR discouraged, in that it will confuse you and your users. .IP "qv" 4 .IX Item "qv" A boolean that denotes whether this is a decimal or dotted-decimal version. See \*(L"\fBis_qv()\fR\*(R" in version. .IP "alpha" 4 .IX Item "alpha" A boolean that denotes whether this is an alpha version. \s-1NOTE:\s0 that the underscore can only appear in the last position. See \*(L"\fBis_alpha()\fR\*(R" in version. .IP "version" 4 .IX Item "version" An array of non-negative integers that is used for comparison purposes with other version objects. .SS "Replacement \s-1UNIVERSAL::VERSION\s0" .IX Subsection "Replacement UNIVERSAL::VERSION" In addition to the version objects, this modules also replaces the core \&\s-1UNIVERSAL::VERSION\s0 function with one that uses version objects for its comparisons. The return from this operator is always the stringified form as a simple scalar (i.e. not an object), but the warning message generated includes either the stringified form or the normal form, depending on how it was called. .PP For example: .PP .Vb 2 \& package Foo; \& $VERSION = 1.2; \& \& package Bar; \& $VERSION = "v1.3.5"; # works with all Perl\*(Aqs (since it is quoted) \& \& package main; \& use version; \& \& print $Foo::VERSION; # prints 1.2 \& \& print $Bar::VERSION; # prints 1.003005 \& \& eval "use foo 10"; \& print $@; # prints "foo version 10 required..." \& eval "use foo 1.3.5; # work in Perl 5.6.1 or better \& print $@; # prints "foo version 1.3.5 required..." \& \& eval "use bar 1.3.6"; \& print $@; # prints "bar version 1.3.6 required..." \& eval "use bar 1.004"; # note Decimal version \& print $@; # prints "bar version 1.004 required..." .Ve .PP \&\s-1IMPORTANT NOTE:\s0 This may mean that code which searches for a specific string (to determine whether a given module is available) may need to be changed. It is always better to use the built-in comparison implicit in \&\f(CW\*(C`use\*(C'\fR or \f(CW\*(C`require\*(C'\fR, rather than manually poking at \f(CW\*(C`class\->VERSION\*(C'\fR and then doing a comparison yourself. .PP The replacement \s-1UNIVERSAL::VERSION,\s0 when used as a function, like this: .PP .Vb 1 \& print $module\->VERSION; .Ve .PP will also exclusively return the stringified form. See \*(L"Stringification\*(R" for more details. .SH "USAGE DETAILS" .IX Header "USAGE DETAILS" .SS "Using modules that use version.pm" .IX Subsection "Using modules that use version.pm" As much as possible, the version.pm module remains compatible with all current code. However, if your module is using a module that has defined \&\f(CW$VERSION\fR using the version class, there are a couple of things to be aware of. For purposes of discussion, we will assume that we have the following module installed: .PP .Vb 4 \& package Example; \& use version; $VERSION = qv(\*(Aq1.2.2\*(Aq); \& ...module code here... \& 1; .Ve .IP "Decimal versions always work" 4 .IX Item "Decimal versions always work" Code of the form: .Sp .Vb 1 \& use Example 1.002003; .Ve .Sp will always work correctly. The \f(CW\*(C`use\*(C'\fR will perform an automatic \&\f(CW$VERSION\fR comparison using the floating point number given as the first term after the module name (e.g. above 1.002.003). In this case, the installed module is too old for the requested line, so you would see an error like: .Sp .Vb 1 \& Example version 1.002003 (v1.2.3) required\-\-this is only version 1.002002 (v1.2.2)... .Ve .IP "Dotted-Decimal version work sometimes" 4 .IX Item "Dotted-Decimal version work sometimes" With Perl >= 5.6.2, you can also use a line like this: .Sp .Vb 1 \& use Example 1.2.3; .Ve .Sp and it will again work (i.e. give the error message as above), even with releases of Perl which do not normally support v\-strings (see \*(L"What about v\-strings?\*(R" above). This has to do with that fact that \f(CW\*(C`use\*(C'\fR only checks to see if the second term \fIlooks like a number\fR and passes that to the replacement \s-1UNIVERSAL::VERSION\s0. This is not true in Perl 5.005_04, however, so you are \fBstrongly encouraged\fR to always use a Decimal version in your code, even for those versions of Perl which support the Dotted-Decimal version. .SS "Object Methods" .IX Subsection "Object Methods" .IP "\fBnew()\fR" 4 .IX Item "new()" Like many \s-1OO\s0 interfaces, the \fBnew()\fR method is used to initialize version objects. If two arguments are passed to \f(CW\*(C`new()\*(C'\fR, the \fBsecond\fR one will be used as if it were prefixed with \*(L"v\*(R". This is to support historical use of the \&\f(CW\*(C`qw\*(C'\fR operator with the \s-1CVS\s0 variable \f(CW$Revision\fR, which is automatically incremented by \s-1CVS\s0 every time the file is committed to the repository. .Sp In order to facilitate this feature, the following code can be employed: .Sp .Vb 1 \& $VERSION = version\->new(qw$Revision: 2.7 $); .Ve .Sp and the version object will be created as if the following code were used: .Sp .Vb 1 \& $VERSION = version\->new("v2.7"); .Ve .Sp In other words, the version will be automatically parsed out of the string, and it will be quoted to preserve the meaning \s-1CVS\s0 normally carries for versions. The \s-1CVS\s0 \f(CW$Revision\fR$ increments differently from Decimal versions (i.e. 1.10 follows 1.9), so it must be handled as if it were a Dotted-Decimal Version. .Sp A new version object can be created as a copy of an existing version object, either as a class method: .Sp .Vb 2 \& $v1 = version\->new(12.3); \& $v2 = version\->new($v1); .Ve .Sp or as an object method: .Sp .Vb 2 \& $v1 = version\->new(12.3); \& $v2 = $v1\->new(12.3); .Ve .Sp and in each case, \f(CW$v1\fR and \f(CW$v2\fR will be identical. \s-1NOTE:\s0 if you create a new object using an existing object like this: .Sp .Vb 1 \& $v2 = $v1\->new(); .Ve .Sp the new object \fBwill not\fR be a clone of the existing object. In the example case, \f(CW$v2\fR will be an empty object of the same type as \f(CW$v1\fR. .IP "\fBqv()\fR" 4 .IX Item "qv()" An alternate way to create a new version object is through the exported \&\fBqv()\fR sub. This is not strictly like other q? operators (like qq, qw), in that the only delimiters supported are parentheses (or spaces). It is the best way to initialize a short version without triggering the floating point interpretation. For example: .Sp .Vb 2 \& $v1 = qv(1.2); # v1.2.0 \& $v2 = qv("1.2"); # also v1.2.0 .Ve .Sp As you can see, either a bare number or a quoted string can usually be used interchangeably, except in the case of a trailing zero, which must be quoted to be converted properly. For this reason, it is strongly recommended that all initializers to \fBqv()\fR be quoted strings instead of bare numbers. .Sp To prevent the \f(CW\*(C`qv()\*(C'\fR function from being exported to the caller's namespace, either use version with a null parameter: .Sp .Vb 1 \& use version (); .Ve .Sp or just require version, like this: .Sp .Vb 1 \& require version; .Ve .Sp Both methods will prevent the \fBimport()\fR method from firing and exporting the \&\f(CW\*(C`qv()\*(C'\fR sub. .PP For the subsequent examples, the following three objects will be used: .PP .Vb 3 \& $ver = version\->new("1.2.3.4"); # see "Quoting Rules" \& $alpha = version\->new("1.2.3_4"); # see "Alpha Versions" \& $nver = version\->new(1.002); # see "Decimal Versions" .Ve .IP "Normal Form" 4 .IX Item "Normal Form" For any version object which is initialized with multiple decimal places (either quoted or if possible v\-string), or initialized using the \fBqv()\fR operator, the stringified representation is returned in a normalized or reduced form (no extraneous zeros), and with a leading 'v': .Sp .Vb 6 \& print $ver\->normal; # prints as v1.2.3.4 \& print $ver\->stringify; # ditto \& print $ver; # ditto \& print $nver\->normal; # prints as v1.2.0 \& print $nver\->stringify; # prints as 1.002, \& # see "Stringification" .Ve .Sp In order to preserve the meaning of the processed version, the normalized representation will always contain at least three sub terms. In other words, the following is guaranteed to always be true: .Sp .Vb 3 \& my $newver = version\->new($ver\->stringify); \& if ($newver eq $ver ) # always true \& {...} .Ve .IP "Numification" 4 .IX Item "Numification" Although all mathematical operations on version objects are forbidden by default, it is possible to retrieve a number which corresponds to the version object through the use of the \f(CW$obj\fR\->numify method. For formatting purposes, when displaying a number which corresponds a version object, all sub versions are assumed to have three decimal places. So for example: .Sp .Vb 2 \& print $ver\->numify; # prints 1.002003004 \& print $nver\->numify; # prints 1.002 .Ve .Sp Unlike the stringification operator, there is never any need to append trailing zeros to preserve the correct version value. .IP "Stringification" 4 .IX Item "Stringification" The default stringification for version objects returns exactly the same string as was used to create it, whether you used \f(CW\*(C`new()\*(C'\fR or \f(CW\*(C`qv()\*(C'\fR, with one exception. The sole exception is if the object was created using \&\f(CW\*(C`qv()\*(C'\fR and the initializer did not have two decimal places or a leading \&'v' (both optional), then the stringified form will have a leading 'v' prepended, in order to support round-trip processing. .Sp For example: .Sp .Vb 7 \& Initialized as Stringifies to \& ============== ============== \& version\->new("1.2") 1.2 \& version\->new("v1.2") v1.2 \& qv("1.2.3") 1.2.3 \& qv("v1.3.5") v1.3.5 \& qv("1.2") v1.2 ### exceptional case .Ve .Sp See also \s-1UNIVERSAL::VERSION\s0, as this also returns the stringified form when used as a class method. .Sp \&\s-1IMPORTANT NOTE:\s0 There is one exceptional cases shown in the above table where the \*(L"initializer\*(R" is not stringwise equivalent to the stringified representation. If you use the \f(CW\*(C`qv\*(C'\fR() operator on a version without a leading 'v' \fBand\fR with only a single decimal place, the stringified output will have a leading 'v', to preserve the sense. See the \*(L"\fBqv()\fR\*(R" operator for more details. .Sp \&\s-1IMPORTANT NOTE 2:\s0 Attempting to bypass the normal stringification rules by manually applying \fBnumify()\fR and \fBnormal()\fR will sometimes yield surprising results: .Sp .Vb 1 \& print version\->new(version\->new("v1.0")\->numify)\->normal; # v1.0.0 .Ve .Sp The reason for this is that the \fBnumify()\fR operator will turn \*(L"v1.0\*(R" into the equivalent string \*(L"1.000000\*(R". Forcing the outer version object to \fBnormal()\fR form will display the mathematically equivalent \*(L"v1.0.0\*(R". .Sp As the example in \*(L"\fBnew()\fR\*(R" shows, you can always create a copy of an existing version object with the same value by the very compact: .Sp .Vb 1 \& $v2 = $v1\->new($v1); .Ve .Sp and be assured that both \f(CW$v1\fR and \f(CW$v2\fR will be completely equivalent, down to the same internal representation as well as stringification. .IP "Comparison operators" 4 .IX Item "Comparison operators" Both \f(CW\*(C`cmp\*(C'\fR and \f(CW\*(C`<=>\*(C'\fR operators perform the same comparison between terms (upgrading to a version object automatically). Perl automatically generates all of the other comparison operators based on those two. In addition to the obvious equalities listed below, appending a single trailing 0 term does not change the value of a version for comparison purposes. In other words \*(L"v1.2\*(R" and \*(L"1.2.0\*(R" will compare as identical. .Sp For example, the following relations hold: .Sp .Vb 7 \& As Number As String Truth Value \& \-\-\-\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\- \& $ver > 1.0 $ver gt "1.0" true \& $ver < 2.5 $ver lt true \& $ver != 1.3 $ver ne "1.3" true \& $ver == 1.2 $ver eq "1.2" false \& $ver == 1.2.3.4 $ver eq "1.2.3.4" see discussion below .Ve .Sp It is probably best to chose either the Decimal notation or the string notation and stick with it, to reduce confusion. Perl6 version objects \&\fBmay\fR only support Decimal comparisons. See also \*(L"Quoting Rules\*(R". .Sp \&\s-1WARNING:\s0 Comparing version with unequal numbers of decimal points (whether explicitly or implicitly initialized), may yield unexpected results at first glance. For example, the following inequalities hold: .Sp .Vb 2 \& version\->new(0.96) > version\->new(0.95); # 0.960.0 > 0.950.0 \& version\->new("0.96.1") < version\->new(0.95); # 0.096.1 < 0.950.0 .Ve .Sp For this reason, it is best to use either exclusively \*(L"Decimal Versions\*(R" or \&\*(L"Dotted-Decimal Versions\*(R" with multiple decimal points. .IP "Logical Operators" 4 .IX Item "Logical Operators" If you need to test whether a version object has been initialized, you can simply test it directly: .Sp .Vb 2 \& $vobj = version\->new($something); \& if ( $vobj ) # true only if $something was non\-blank .Ve .Sp You can also test whether a version object is an alpha version, for example to prevent the use of some feature not present in the main release: .Sp .Vb 3 \& $vobj = version\->new("1.2_3"); # MUST QUOTE \& ...later... \& if ( $vobj\->is_alpha ) # True .Ve .SH "AUTHOR" .IX Header "AUTHOR" John Peacock <jpeacock@cpan.org> .SH "SEE ALSO" .IX Header "SEE ALSO" perl. PK�����A0]?&��&����man/man3/CPAN::Admin.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "CPAN::Admin 3" .TH CPAN::Admin 3 "2018-11-27" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" .Vb 1 \& CPAN::Admin \- A CPAN Shell for CPAN admins .Ve .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& perl \-MCPAN::Admin \-e shell .Ve .SH "STATUS" .IX Header "STATUS" Note: this module is currently not maintained. If you need it and fix it for your needs, please submit patches. .SH "DESCRIPTION" .IX Header "DESCRIPTION" CPAN::Admin is a subclass of \s-1CPAN\s0 that adds the commands \f(CW\*(C`register\*(C'\fR and \f(CW\*(C`modsearch\*(C'\fR to the \s-1CPAN\s0 shell. .PP \&\f(CW\*(C`register\*(C'\fR calls \f(CW\*(C`get\*(C'\fR on the named module, assembles a couple of informations (description, language), and calls Netscape with the \&\-remote argument so that a form is filled with all the assembled informations and the registration can be performed with a single click. If the command line has more than one argument, register does not run a \f(CW\*(C`get\*(C'\fR, instead it interprets the rest of the line as \s-1DSLI\s0 status, description, and userid and sends them to netscape such that the form is again mostly filled and can be edited or confirmed with a single click. CPAN::Admin never performs the submission click for you, it is only intended to fill in the form on \s-1PAUSE\s0 and leave the confirmation to you. .PP \&\f(CW\*(C`modsearch\*(C'\fR simply passes the arguments to the search engine for the modules@perl.org mailing list at <http://www.xray.mpe.mpg.de> where all registration requests are stored. It does so in the same way as register, namely with the \f(CW\*(C`netscape \-remote\*(C'\fR command. .PP An experimental feature has also been added, namely to color already registered modules in listings. If you have Term::ANSIColor installed, the u, r, and m commands will show already registered modules in green. .SH "PREREQUISITES" .IX Header "PREREQUISITES" URI::Escape, a browser available in the path, the browser must understand the \-remote switch (as far as I know, this is only available on \s-1UNIX\s0); coloring of registered modules is only available if Term::ANSIColor is installed. .SH "LICENSE" .IX Header "LICENSE" This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. PK�����A0] m�� m����man/man3/CPAN::FirstTime.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "CPAN::FirstTime 3" .TH CPAN::FirstTime 3 "2022-04-03" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" CPAN::FirstTime \- Utility for CPAN::Config file Initialization .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBCPAN::FirstTime::init()\fR .SH "DESCRIPTION" .IX Header "DESCRIPTION" The init routine asks a few questions and writes a CPAN/Config.pm or CPAN/MyConfig.pm file (depending on what it is currently using). .PP In the following all questions and explanations regarding config variables are collected. .IP "allow_installing_module_downgrades" 2 .IX Item "allow_installing_module_downgrades" The \s-1CPAN\s0 shell can watch the \f(CW\*(C`blib/\*(C'\fR directories that are built up before running \f(CW\*(C`make test\*(C'\fR to determine whether the current distribution will end up with modules being overwritten with decreasing module version numbers. It can then let the build of this distro fail when it discovers a downgrade. .Sp Do you want to allow installing distros with decreasing module versions compared to what you have installed (yes, no, ask/yes, ask/no)? .IP "allow_installing_outdated_dists" 2 .IX Item "allow_installing_outdated_dists" The \s-1CPAN\s0 shell can watch the \f(CW\*(C`blib/\*(C'\fR directories that are built up before running \f(CW\*(C`make test\*(C'\fR to determine whether the current distribution contains modules that are indexed with a distro with a higher distro-version number than the current one. It can then let the build of this distro fail when it would not represent the most up-to-date version of the distro. .Sp Note: choosing anything but 'yes' for this option will need CPAN::DistnameInfo being installed for taking effect. .Sp Do you want to allow installing distros that are not indexed as the highest distro-version for all contained modules (yes, no, ask/yes, ask/no)? .IP "auto_commit" 2 .IX Item "auto_commit" Normally \s-1CPAN\s0.pm keeps config variables in memory and changes need to be saved in a separate 'o conf commit' command to make them permanent between sessions. If you set the 'auto_commit' option to true, changes to a config variable are always automatically committed to disk. .Sp Always commit changes to config variables to disk? .IP "build_cache" 2 .IX Item "build_cache" \&\s-1CPAN\s0.pm can limit the size of the disk area for keeping the build directories with all the intermediate files. .Sp Cache size for build directory (in \s-1MB\s0)? .IP "build_dir" 2 .IX Item "build_dir" Directory where the build process takes place? .IP "build_dir_reuse" 2 .IX Item "build_dir_reuse" Until version 1.88 \s-1CPAN\s0.pm never trusted the contents of the build_dir directory between sessions. Since 1.88_58 \s-1CPAN\s0.pm has a YAML-based mechanism that makes it possible to share the contents of the build_dir/ directory between different sessions with the same version of perl. People who prefer to test things several days before installing will like this feature because it saves a lot of time. .Sp If you say yes to the following question, \s-1CPAN\s0 will try to store enough information about the build process so that it can pick up in future sessions at the same state of affairs as it left a previous session. .Sp Store and re-use state information about distributions between \&\s-1CPAN\s0.pm sessions? .IP "build_requires_install_policy" 2 .IX Item "build_requires_install_policy" When a module declares another one as a 'build_requires' prerequisite this means that the other module is only needed for building or testing the module but need not be installed permanently. In this case you may wish to install that other module nonetheless or just keep it in the 'build_dir' directory to have it available only temporarily. Installing saves time on future installations but makes the perl installation bigger. .Sp You can choose if you want to always install (yes), never install (no) or be always asked. In the latter case you can set the default answer for the question to yes (ask/yes) or no (ask/no). .Sp Policy on installing 'build_requires' modules (yes, no, ask/yes, ask/no)? .IP "cache_metadata" 2 .IX Item "cache_metadata" To considerably speed up the initial \s-1CPAN\s0 shell startup, it is possible to use Storable to create a cache of metadata. If Storable is not available, the normal index mechanism will be used. .Sp Note: this mechanism is not used when use_sqlite is on and SQLite is running. .Sp Cache metadata (yes/no)? .IP "check_sigs" 2 .IX Item "check_sigs" \&\s-1CPAN\s0 packages can be digitally signed by authors and thus verified with the security provided by strong cryptography. The exact mechanism is defined in the Module::Signature module. While this is generally considered a good thing, it is not always convenient to the end user to install modules that are signed incorrectly or where the key of the author is not available or where some prerequisite for Module::Signature has a bug and so on. .Sp With the check_sigs parameter you can turn signature checking on and off. The default is off for now because the whole tool chain for the functionality is not yet considered mature by some. The author of \&\s-1CPAN\s0.pm would recommend setting it to true most of the time and turning it off only if it turns out to be annoying. .Sp Note that if you do not have Module::Signature installed, no signature checks will be performed at all. .Sp Always try to check and verify signatures if a \s-1SIGNATURE\s0 file is in the package and Module::Signature is installed (yes/no)? .IP "cleanup_after_install" 2 .IX Item "cleanup_after_install" Users who install modules and do not intend to look back, can free occupied disk space quickly by letting \s-1CPAN\s0.pm cleanup each build directory immediately after a successful install. .Sp Remove build directory after a successful install? (yes/no)? .IP "colorize_output" 2 .IX Item "colorize_output" When you have Term::ANSIColor installed, you can turn on colorized output to have some visual differences between normal \s-1CPAN\s0.pm output, warnings, debugging output, and the output of the modules being installed. Set your favorite colors after some experimenting with the Term::ANSIColor module. .Sp Please note that on Windows platforms colorized output also requires the Win32::Console::ANSI module. .Sp Do you want to turn on colored output? .IP "colorize_print" 2 .IX Item "colorize_print" Color for normal output? .IP "colorize_warn" 2 .IX Item "colorize_warn" Color for warnings? .IP "colorize_debug" 2 .IX Item "colorize_debug" Color for debugging messages? .IP "commandnumber_in_prompt" 2 .IX Item "commandnumber_in_prompt" The prompt of the cpan shell can contain the current command number for easier tracking of the session or be a plain string. .Sp Do you want the command number in the prompt (yes/no)? .IP "connect_to_internet_ok" 2 .IX Item "connect_to_internet_ok" If you have never defined your own \f(CW\*(C`urllist\*(C'\fR in your configuration then \f(CW\*(C`CPAN.pm\*(C'\fR will be hesitant to use the built in default sites for downloading. It will ask you once per session if a connection to the internet is \s-1OK\s0 and only if you say yes, it will try to connect. But to avoid this question, you can choose your favorite download sites once and get away with it. Or, if you have no favorite download sites answer yes to the following question. .Sp If no urllist has been chosen yet, would you prefer \s-1CPAN\s0.pm to connect to the built-in default sites without asking? (yes/no)? .IP "ftp_passive" 2 .IX Item "ftp_passive" Shall we always set the \s-1FTP_PASSIVE\s0 environment variable when dealing with ftp download (yes/no)? .IP "ftpstats_period" 2 .IX Item "ftpstats_period" Statistics about downloads are truncated by size and period simultaneously. .Sp How many days shall we keep statistics about downloads? .IP "ftpstats_size" 2 .IX Item "ftpstats_size" Statistics about downloads are truncated by size and period simultaneously. Setting this to zero or negative disables download statistics. .Sp How many items shall we keep in the statistics about downloads? .IP "getcwd" 2 .IX Item "getcwd" \&\s-1CPAN\s0.pm changes the current working directory often and needs to determine its own current working directory. Per default it uses Cwd::cwd but if this doesn't work on your system for some reason, alternatives can be configured according to the following table: .Sp .Vb 5 \& cwd Cwd::cwd \& getcwd Cwd::getcwd \& fastcwd Cwd::fastcwd \& getdcwd Cwd::getdcwd \& backtickcwd external command cwd .Ve .Sp Preferred method for determining the current working directory? .IP "halt_on_failure" 2 .IX Item "halt_on_failure" Normally, \s-1CPAN\s0.pm continues processing the full list of targets and dependencies, even if one of them fails. However, you can specify that \s-1CPAN\s0 should halt after the first failure. (Note that optional recommended or suggested modules that fail will not cause a halt.) .Sp Do you want to halt on failure (yes/no)? .IP "histfile" 2 .IX Item "histfile" If you have one of the readline packages (Term::ReadLine::Perl, Term::ReadLine::Gnu, possibly others) installed, the interactive \s-1CPAN\s0 shell will have history support. The next two questions deal with the filename of the history file and with its size. If you do not want to set this variable, please hit \s-1SPACE ENTER\s0 to the following question. .Sp File to save your history? .IP "histsize" 2 .IX Item "histsize" Number of lines to save? .IP "inactivity_timeout" 2 .IX Item "inactivity_timeout" Sometimes you may wish to leave the processes run by \s-1CPAN\s0 alone without caring about them. Because the Makefile.PL or the Build.PL sometimes contains question you're expected to answer, you can set a timer that will kill a 'perl Makefile.PL' process after the specified time in seconds. .Sp If you set this value to 0, these processes will wait forever. This is the default and recommended setting. .Sp Timeout for inactivity during {Makefile,Build}.PL? .IP "index_expire" 2 .IX Item "index_expire" The \s-1CPAN\s0 indexes are usually rebuilt once or twice per hour, but the typical \s-1CPAN\s0 mirror mirrors only once or twice per day. Depending on the quality of your mirror and your desire to be on the bleeding edge, you may want to set the following value to more or less than one day (which is the default). It determines after how many days \s-1CPAN\s0.pm downloads new indexes. .Sp Let the index expire after how many days? .IP "inhibit_startup_message" 2 .IX Item "inhibit_startup_message" When the \s-1CPAN\s0 shell is started it normally displays a greeting message that contains the running version and the status of readline support. .Sp Do you want to turn this message off? .IP "keep_source_where" 2 .IX Item "keep_source_where" Unless you are accessing the \s-1CPAN\s0 on your filesystem via a file: \s-1URL, CPAN\s0.pm needs to keep the source files it downloads somewhere. Please supply a directory where the downloaded files are to be kept. .Sp Download target directory? .IP "load_module_verbosity" 2 .IX Item "load_module_verbosity" When \s-1CPAN\s0.pm loads a module it needs for some optional feature, it usually reports about module name and version. Choose 'v' to get this message, 'none' to suppress it. .Sp Verbosity level for loading modules (none or v)? .IP "makepl_arg" 2 .IX Item "makepl_arg" Every Makefile.PL is run by perl in a separate process. Likewise we run 'make' and 'make install' in separate processes. If you have any parameters (e.g. \s-1PREFIX, UNINST\s0 or the like) you want to pass to the calls, please specify them here. .Sp If you don't understand this question, just press \s-1ENTER.\s0 .Sp Typical frequently used settings: .Sp .Vb 1 \& PREFIX=~/perl # non\-root users (please see manual for more hints) .Ve .Sp Parameters for the 'perl Makefile.PL' command? .IP "make_arg" 2 .IX Item "make_arg" Parameters for the 'make' command? Typical frequently used setting: .Sp .Vb 1 \& \-j3 # dual processor system (on GNU make) .Ve .Sp Your choice: .IP "make_install_arg" 2 .IX Item "make_install_arg" Parameters for the 'make install' command? Typical frequently used setting: .Sp .Vb 2 \& UNINST=1 # to always uninstall potentially conflicting files \& # (but do NOT use with local::lib or INSTALL_BASE) .Ve .Sp Your choice: .IP "make_install_make_command" 2 .IX Item "make_install_make_command" Do you want to use a different make command for 'make install'? Cautious people will probably prefer: .Sp .Vb 5 \& su root \-c make \& or \& sudo make \& or \& /path1/to/sudo \-u admin_account /path2/to/make .Ve .Sp or some such. Your choice: .IP "mbuildpl_arg" 2 .IX Item "mbuildpl_arg" A Build.PL is run by perl in a separate process. Likewise we run \&'./Build' and './Build install' in separate processes. If you have any parameters you want to pass to the calls, please specify them here. .Sp Typical frequently used settings: .Sp .Vb 1 \& \-\-install_base /home/xxx # different installation directory .Ve .Sp Parameters for the 'perl Build.PL' command? .IP "mbuild_arg" 2 .IX Item "mbuild_arg" Parameters for the './Build' command? Setting might be: .Sp .Vb 1 \& \-\-extra_linker_flags \-L/usr/foo/lib # non\-standard library location .Ve .Sp Your choice: .IP "mbuild_install_arg" 2 .IX Item "mbuild_install_arg" Parameters for the './Build install' command? Typical frequently used setting: .Sp .Vb 2 \& \-\-uninst 1 # uninstall conflicting files \& # (but do NOT use with local::lib or INSTALL_BASE) .Ve .Sp Your choice: .IP "mbuild_install_build_command" 2 .IX Item "mbuild_install_build_command" Do you want to use a different command for './Build install'? Sudo users will probably prefer: .Sp .Vb 5 \& su root \-c ./Build \& or \& sudo ./Build \& or \& /path1/to/sudo \-u admin_account ./Build .Ve .Sp or some such. Your choice: .IP "pager" 2 .IX Item "pager" What is your favorite pager program? .IP "prefer_installer" 2 .IX Item "prefer_installer" When you have Module::Build installed and a module comes with both a Makefile.PL and a Build.PL, which shall have precedence? .Sp The main two standard installer modules are the old and well established ExtUtils::MakeMaker (for short: \s-1EUMM\s0) which uses the Makefile.PL. And the next generation installer Module::Build (\s-1MB\s0) which works with the Build.PL (and often comes with a Makefile.PL too). If a module comes only with one of the two we will use that one but if both are supplied then a decision must be made between \s-1EUMM\s0 and \&\s-1MB.\s0 See also http://rt.cpan.org/Ticket/Display.html?id=29235 for a discussion about the right default. .Sp Or, as a third option you can choose \s-1RAND\s0 which will make a random decision (something regular \s-1CPAN\s0 testers will enjoy). .Sp In case you can choose between running a Makefile.PL or a Build.PL, which installer would you prefer (\s-1EUMM\s0 or \s-1MB\s0 or \s-1RAND\s0)? .IP "prefs_dir" 2 .IX Item "prefs_dir" \&\s-1CPAN\s0.pm can store customized build environments based on regular expressions for distribution names. These are \s-1YAML\s0 files where the default options for \s-1CPAN\s0.pm and the environment can be overridden and dialog sequences can be stored that can later be executed by an Expect.pm object. The \s-1CPAN\s0.pm distribution comes with some prefab \s-1YAML\s0 files that cover sample distributions that can be used as blueprints to store your own prefs. Please check out the distroprefs/ directory of the \s-1CPAN\s0.pm distribution to get a quick start into the prefs system. .Sp Directory where to store default options/environment/dialogs for building modules that need some customization? .IP "prerequisites_policy" 2 .IX Item "prerequisites_policy" The \s-1CPAN\s0 module can detect when a module which you are trying to build depends on prerequisites. If this happens, it can build the prerequisites for you automatically ('follow'), ask you for confirmation ('ask'), or just ignore them ('ignore'). Choosing \&'follow' also sets \s-1PERL_AUTOINSTALL\s0 and \s-1PERL_EXTUTILS_AUTOINSTALL\s0 for \&\*(L"\-\-defaultdeps\*(R" if not already set. .Sp Please set your policy to one of the three values. .Sp Policy on building prerequisites (follow, ask or ignore)? .IP "pushy_https" 2 .IX Item "pushy_https" Boolean. Defaults to true. If this option is true, the cpan shell will use https://cpan.org/ to download stuff from the \s-1CPAN.\s0 It will fall back to http://cpan.org/ if it can't handle https for some reason (missing modules, missing programs). Whenever it falls back to the http protocol, it will issue a warning. .Sp If this option is true, the option \f(CW\*(C`urllist\*(C'\fR will be ignored. Consequently, if you want to work with local mirrors via your own configured list of URLs, you will have to choose no below. .Sp Do you want to turn the pushy_https behaviour on? .IP "randomize_urllist" 2 .IX Item "randomize_urllist" \&\s-1CPAN\s0.pm can introduce some randomness when using hosts for download that are configured in the urllist parameter. Enter a numeric value between 0 and 1 to indicate how often you want to let \s-1CPAN\s0.pm try a random host from the urllist. A value of one specifies to always use a random host as the first try. A value of zero means no randomness at all. Anything in between specifies how often, on average, a random host should be tried first. .Sp Randomize parameter .IP "recommends_policy" 2 .IX Item "recommends_policy" (Experimental feature!) Some \s-1CPAN\s0 modules recommend additional, optional dependencies. These should generally be installed except in resource constrained environments. When this policy is true, recommended modules will be included with required modules. .Sp Include recommended modules? .IP "scan_cache" 2 .IX Item "scan_cache" By default, each time the \s-1CPAN\s0 module is started, cache scanning is performed to keep the cache size in sync ('atstart'). Alternatively, scanning and cleanup can happen when \s-1CPAN\s0 exits ('atexit'). To prevent any cache cleanup, answer 'never'. .Sp Perform cache scanning ('atstart', 'atexit' or 'never')? .IP "shell" 2 .IX Item "shell" What is your favorite shell? .IP "show_unparsable_versions" 2 .IX Item "show_unparsable_versions" During the 'r' command \s-1CPAN\s0.pm finds modules without version number. When the command finishes, it prints a report about this. If you want this report to be very verbose, say yes to the following variable. .Sp Show all individual modules that have no \f(CW$VERSION\fR? .IP "show_upload_date" 2 .IX Item "show_upload_date" The 'd' and the 'm' command normally only show you information they have in their in-memory database and thus will never connect to the internet. If you set the 'show_upload_date' variable to true, 'm' and \&'d' will additionally show you the upload date of the module or distribution. Per default this feature is off because it may require a net connection to get at the upload date. .Sp Always try to show upload date with 'd' and 'm' command (yes/no)? .IP "show_zero_versions" 2 .IX Item "show_zero_versions" During the 'r' command \s-1CPAN\s0.pm finds modules with a version number of zero. When the command finishes, it prints a report about this. If you want this report to be very verbose, say yes to the following variable. .Sp Show all individual modules that have a \f(CW$VERSION\fR of zero? .IP "suggests_policy" 2 .IX Item "suggests_policy" (Experimental feature!) Some \s-1CPAN\s0 modules suggest additional, optional dependencies. These 'suggest' dependencies provide enhanced operation. When this policy is true, suggested modules will be included with required modules. .Sp Include suggested modules? .IP "tar_verbosity" 2 .IX Item "tar_verbosity" When \s-1CPAN\s0.pm uses the tar command, which switch for the verbosity shall be used? Choose 'none' for quiet operation, 'v' for file name listing, 'vv' for full listing. .Sp Tar command verbosity level (none or v or vv)? .IP "term_is_latin" 2 .IX Item "term_is_latin" The next option deals with the charset (a.k.a. character set) your terminal supports. In general, \s-1CPAN\s0 is English speaking territory, so the charset does not matter much but some \s-1CPAN\s0 have names that are outside the \s-1ASCII\s0 range. If your terminal supports \s-1UTF\-8,\s0 you should say no to the next question. If it expects \s-1ISO\-8859\-1\s0 (also known as \&\s-1LATIN1\s0) then you should say yes. If it supports neither, your answer does not matter because you will not be able to read the names of some authors anyway. If you answer no, names will be output in \s-1UTF\-8.\s0 .Sp Your terminal expects \s-1ISO\-8859\-1\s0 (yes/no)? .IP "term_ornaments" 2 .IX Item "term_ornaments" When using Term::ReadLine, you can turn ornaments on so that your input stands out against the output from \s-1CPAN\s0.pm. .Sp Do you want to turn ornaments on? .IP "test_report" 2 .IX Item "test_report" The goal of the \s-1CPAN\s0 Testers project (http://testers.cpan.org/) is to test as many \s-1CPAN\s0 packages as possible on as many platforms as possible. This provides valuable feedback to module authors and potential users to identify bugs or platform compatibility issues and improves the overall quality and value of \s-1CPAN.\s0 .Sp One way you can contribute is to send test results for each module that you install. If you install the CPAN::Reporter module, you have the option to automatically generate and deliver test reports to \s-1CPAN\s0 Testers whenever you run tests on a \s-1CPAN\s0 package. .Sp See the CPAN::Reporter documentation for additional details and configuration settings. If your firewall blocks outgoing traffic, you may need to configure CPAN::Reporter before sending reports. .Sp Generate test reports if CPAN::Reporter is installed (yes/no)? .IP "perl5lib_verbosity" 2 .IX Item "perl5lib_verbosity" When \s-1CPAN\s0.pm extends \f(CW@INC\fR via \s-1PERL5LIB,\s0 it prints a list of directories added (or a summary of how many directories are added). Choose 'v' to get this message, 'none' to suppress it. .Sp Verbosity level for \s-1PERL5LIB\s0 changes (none or v)? .IP "prefer_external_tar" 2 .IX Item "prefer_external_tar" Per default all untar operations are done with the perl module Archive::Tar; by setting this variable to true the external tar command is used if available; on Unix this is usually preferred because they have a reliable and fast gnutar implementation. .Sp Use the external tar program instead of Archive::Tar? .IP "trust_test_report_history" 2 .IX Item "trust_test_report_history" When a distribution has already been tested by CPAN::Reporter on this machine, \s-1CPAN\s0 can skip the test phase and just rely on the test report history instead. .Sp Note that this will not apply to distributions that failed tests because of missing dependencies. Also, tests can be run regardless of the history using \*(L"force\*(R". .Sp Do you want to rely on the test report history (yes/no)? .IP "urllist_ping_external" 2 .IX Item "urllist_ping_external" When automatic selection of the nearest cpan mirrors is performed, turn on the use of the external ping via Net::Ping::External. This is recommended in the case the local network has a transparent proxy. .Sp Do you want to use the external ping command when autoselecting mirrors? .IP "urllist_ping_verbose" 2 .IX Item "urllist_ping_verbose" When automatic selection of the nearest cpan mirrors is performed, this option can be used to turn on verbosity during the selection process. .Sp Do you want to see verbosity turned on when autoselecting mirrors? .IP "use_prompt_default" 2 .IX Item "use_prompt_default" When this is true, \s-1CPAN\s0 will set \s-1PERL_MM_USE_DEFAULT\s0 to a true value. This causes ExtUtils::MakeMaker (and compatible) prompts to use default values instead of stopping to prompt you to answer questions. It also sets \s-1NONINTERACTIVE_TESTING\s0 to a true value to signal more generally that distributions should not try to interact with you. .Sp Do you want to use prompt defaults (yes/no)? .IP "use_sqlite" 2 .IX Item "use_sqlite" CPAN::SQLite is a layer between the index files that are downloaded from the \s-1CPAN\s0 and \s-1CPAN\s0.pm that speeds up metadata queries and reduces memory consumption of \s-1CPAN\s0.pm considerably. .Sp Use CPAN::SQLite if available? (yes/no)? .IP "version_timeout" 2 .IX Item "version_timeout" This timeout prevents \s-1CPAN\s0 from hanging when trying to parse a pathologically coded \f(CW$VERSION\fR from a module. .Sp The default is 15 seconds. If you set this value to 0, no timeout will occur, but this is not recommended. .Sp Timeout for parsing module versions? .IP "yaml_load_code" 2 .IX Item "yaml_load_code" Both \s-1YAML\s0.pm and YAML::Syck are capable of deserialising code. As this requires a string eval, which might be a security risk, you can use this option to enable or disable the deserialisation of code via CPAN::DeferredCode. (Note: This does not work under perl 5.6) .Sp Do you want to enable code deserialisation (yes/no)? .IP "yaml_module" 2 .IX Item "yaml_module" At the time of this writing (2009\-03) there are three \s-1YAML\s0 implementations working: \s-1YAML,\s0 YAML::Syck, and \s-1YAML::XS.\s0 The latter two are faster but need a C compiler installed on your system. There may be more alternative \s-1YAML\s0 conforming modules. When I tried two other players, YAML::Tiny and YAML::Perl, they seemed not powerful enough to work with \s-1CPAN\s0.pm. This may have changed in the meantime. .Sp Which \s-1YAML\s0 implementation would you prefer? .SH "LICENSE" .IX Header "LICENSE" This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. PK�����A0]u{ �� ��%��man/man3/Types::Serialiser::Error.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "Serialiser::Error 3" .TH Serialiser::Error 3 "2013-10-27" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" Types::Serialiser::Error \- dummy module for Types::Serialiser .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& # do not "use" yourself .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" This module exists only to provide overload resolution for Storable and similar modules that assume that class name equals module name. See Types::Serialiser for more info about this class. .SH "AUTHOR" .IX Header "AUTHOR" .Vb 2 \& Marc Lehmann <schmorp@schmorp.de> \& http://home.schmorp.de/ .Ve PK�����A0]*'E"��"����man/man3/CPAN::Mirrors.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "CPAN::Mirrors 3" .TH CPAN::Mirrors 3 "2020-05-23" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" CPAN::Mirrors \- Get CPAN mirror information and select a fast one .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use CPAN::Mirrors; \& \& my $mirrors = CPAN::Mirrors\->new( $mirrored_by_file ); \& \& my $seen = {}; \& \& my $best_continent = $mirrors\->find_best_continents( { seen => $seen } ); \& my @mirrors = $mirrors\->get_mirrors_by_continents( $best_continent ); \& \& my $callback = sub { \& my( $m ) = @_; \& printf "%s = %s\en", $m\->hostname, $m\->rtt \& }; \& $mirrors\->get_mirrors_timings( \e@mirrors, $seen, $callback, %args ); \& \& @mirrors = sort { $a\->rtt <=> $b\->rtt } @mirrors; \& \& print "Best mirrors are ", map( { $_\->rtt } @mirrors[0..3] ), "\en"; .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" .IP "new( \s-1LOCAL_FILE_NAME\s0 )" 4 .IX Item "new( LOCAL_FILE_NAME )" Create a new CPAN::Mirrors object from \s-1LOCAL_FILE_NAME.\s0 This file should look like that in http://www.cpan.org/MIRRORED.BY . .IP "\fBcontinents()\fR" 4 .IX Item "continents()" Return a list of continents based on those defined in \fI\s-1MIRRORED.BY\s0\fR. .IP "countries( [\s-1CONTINENTS\s0] )" 4 .IX Item "countries( [CONTINENTS] )" Return a list of countries based on those defined in \fI\s-1MIRRORED.BY\s0\fR. It only returns countries for the continents you specify (as defined in \f(CW\*(C`continents\*(C'\fR). If you don't specify any continents, it returns all of the countries listed in \fI\s-1MIRRORED.BY\s0\fR. .IP "mirrors( [\s-1COUNTRIES\s0] )" 4 .IX Item "mirrors( [COUNTRIES] )" Return a list of mirrors based on those defined in \fI\s-1MIRRORED.BY\s0\fR. It only returns mirrors for the countries you specify (as defined in \f(CW\*(C`countries\*(C'\fR). If you don't specify any countries, it returns all of the mirrors listed in \fI\s-1MIRRORED.BY\s0\fR. .IP "get_mirrors_by_countries( [\s-1COUNTRIES\s0] )" 4 .IX Item "get_mirrors_by_countries( [COUNTRIES] )" A more sensible synonym for mirrors. .IP "get_mirrors_by_continents( [\s-1CONTINENTS\s0] )" 4 .IX Item "get_mirrors_by_continents( [CONTINENTS] )" Return a list of mirrors for all of continents you specify. If you don't specify any continents, it returns all of the mirrors. .Sp You can specify a single continent or an array reference of continents. .IP "get_countries_by_continents( [\s-1CONTINENTS\s0] )" 4 .IX Item "get_countries_by_continents( [CONTINENTS] )" A more sensible synonym for countries. .IP "default_mirror" 4 .IX Item "default_mirror" Returns the default mirror, http://www.cpan.org/ . This mirror uses dynamic \s-1DNS\s0 to give a close mirror. .IP "best_mirrors" 4 .IX Item "best_mirrors" \&\f(CW\*(C`best_mirrors\*(C'\fR checks for the best mirrors based on the list of continents you pass, or, without that, all continents, as defined by \f(CW\*(C`CPAN::Mirrored::By\*(C'\fR. It pings each mirror, up to the value of \&\f(CW\*(C`how_many\*(C'\fR. In list context, it returns up to \f(CW\*(C`how_many\*(C'\fR mirrors. In scalar context, it returns the single best mirror. .Sp Arguments .Sp .Vb 5 \& how_many \- the number of mirrors to return. Default: 1 \& callback \- a callback for find_best_continents \& verbose \- true or false on all the whining and moaning. Default: false \& continents \- an array ref of the continents to check \& external_ping \- if true, use external ping via Net::Ping::External. Default: false .Ve .Sp If you don't specify the continents, \f(CW\*(C`best_mirrors\*(C'\fR calls \&\f(CW\*(C`find_best_continents\*(C'\fR to get the list of continents to check. .Sp If you don't have Net::Ping v2.13 or later, needed for timings, this returns the default mirror. .Sp \&\f(CW\*(C`external_ping\*(C'\fR should be set and then \f(CW\*(C`Net::Ping::External\*(C'\fR needs to be installed, if the local network has a transparent proxy. .IP "get_n_random_mirrors_by_continents( N, [\s-1CONTINENTS\s0] )" 4 .IX Item "get_n_random_mirrors_by_continents( N, [CONTINENTS] )" Returns up to N random mirrors for the specified continents. Specify the continents as an array reference. .ie n .IP "get_mirrors_timings( \s-1MIRROR_LIST, SEEN, CALLBACK,\s0 %ARGS );" 4 .el .IP "get_mirrors_timings( \s-1MIRROR_LIST, SEEN, CALLBACK,\s0 \f(CW%ARGS\fR );" 4 .IX Item "get_mirrors_timings( MIRROR_LIST, SEEN, CALLBACK, %ARGS );" Pings the listed mirrors and returns a list of mirrors sorted in ascending ping times. .Sp \&\f(CW\*(C`MIRROR_LIST\*(C'\fR is an anonymous array of \f(CW\*(C`CPAN::Mirrored::By\*(C'\fR objects to ping. .Sp The optional argument \f(CW\*(C`SEEN\*(C'\fR is a hash reference used to track the mirrors you've already pinged. .Sp The optional argument \f(CW\*(C`CALLBACK\*(C'\fR is a subroutine reference to call after each ping. It gets the \f(CW\*(C`CPAN::Mirrored::By\*(C'\fR object after each ping. .IP "find_best_continents( \s-1HASH_REF\s0 );" 4 .IX Item "find_best_continents( HASH_REF );" \&\f(CW\*(C`find_best_continents\*(C'\fR goes through each continent and pings \f(CW\*(C`N\*(C'\fR random mirrors on that continent. It then orders the continents by ascending median ping time. In list context, it returns the ordered list of continent. In scalar context, it returns the same list as an anonymous array. .Sp Arguments: .Sp .Vb 6 \& n \- the number of hosts to ping for each continent. Default: 3 \& seen \- a hashref of cached hostname ping times \& verbose \- true or false for noisy or quiet. Default: false \& callback \- a subroutine to run after each ping. \& ping_cache_limit \- how long, in seconds, to reuse previous ping times. \& Default: 1 day .Ve .Sp The \f(CW\*(C`seen\*(C'\fR hash has hostnames as keys and anonymous arrays as values. The anonymous array is a triplet of a \f(CW\*(C`CPAN::Mirrored::By\*(C'\fR object, a ping time, and the epoch time for the measurement. .Sp The callback subroutine gets the \f(CW\*(C`CPAN::Mirrored::By\*(C'\fR object, the ping time, and measurement time (the same things in the \f(CW\*(C`seen\*(C'\fR hashref) as arguments. \f(CW\*(C`find_best_continents\*(C'\fR doesn't care what the callback does and ignores the return value. .Sp With a low value for \f(CW\*(C`N\*(C'\fR, a single mirror might skew the results enough to choose a worse continent. If you have that problem, try a larger value. .SH "AUTHOR" .IX Header "AUTHOR" Andreas Koenig \f(CW\*(C`<andk@cpan.org>\*(C'\fR, David Golden \f(CW\*(C`<dagolden@cpan.org>\*(C'\fR, brian d foy \f(CW\*(C`<bdfoy@cpan.org>\*(C'\fR .SH "LICENSE" .IX Header "LICENSE" This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. .PP See <http://www.perl.com/perl/misc/Artistic.html> PK�����A0]lg �� ����man/man3/CPAN::Kwalify.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "CPAN::Kwalify 3" .TH CPAN::Kwalify 3 "2012-09-08" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" CPAN::Kwalify \- Interface between CPAN.pm and Kwalify.pm .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 2 \& use CPAN::Kwalify; \& validate($schema_name, $data, $file, $doc); .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" .ie n .IP "_validate($schema_name, $data, $file, $doc)" 4 .el .IP "_validate($schema_name, \f(CW$data\fR, \f(CW$file\fR, \f(CW$doc\fR)" 4 .IX Item "_validate($schema_name, $data, $file, $doc)" \&\f(CW$schema_name\fR is the name of a supported schema. Currently only \&\f(CW\*(C`distroprefs\*(C'\fR is supported. \f(CW$data\fR is the data to be validated. \f(CW$file\fR is the absolute path to the file the data are coming from. \f(CW$doc\fR is the index of the document within \f(CW$doc\fR that is to be validated. The last two arguments are only there for better error reporting. .Sp Relies on being called from within \s-1CPAN\s0.pm. .Sp Dies if something fails. Does not return anything useful. .IP "yaml($schema_name)" 4 .IX Item "yaml($schema_name)" Returns the \s-1YAML\s0 text of that schema. Dies if something fails. .SH "AUTHOR" .IX Header "AUTHOR" Andreas Koenig \f(CW\*(C`<andk@cpan.org>\*(C'\fR .SH "LICENSE" .IX Header "LICENSE" This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. .PP See <http://www.perl.com/perl/misc/Artistic.html> PK�����A0]އg! ��! ����man/man3/CPAN::Nox.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "CPAN::Nox 3" .TH CPAN::Nox 3 "2016-08-17" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" CPAN::Nox \- Wrapper around CPAN.pm without using any XS module .SH "SYNOPSIS" .IX Header "SYNOPSIS" Interactive mode: .PP .Vb 1 \& perl \-MCPAN::Nox \-e shell; .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" This package has the same functionality as \s-1CPAN\s0.pm, but tries to prevent the usage of compiled extensions during its own execution. Its primary purpose is a rescue in case you upgraded perl and broke binary compatibility somehow. .SH "LICENSE" .IX Header "LICENSE" This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. .SH "SEE ALSO" .IX Header "SEE ALSO" \&\s-1CPAN\s0 PK�����A0][/�/���man/man3/CPAN.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "CPAN 3" .TH CPAN 3 "2023-05-10" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" CPAN \- query, download and build perl modules from CPAN sites .SH "SYNOPSIS" .IX Header "SYNOPSIS" Interactive mode: .PP .Vb 1 \& perl \-MCPAN \-e shell .Ve .PP \&\-\-or\*(-- .PP .Vb 1 \& cpan .Ve .PP Basic commands: .PP .Vb 1 \& # Modules: \& \& cpan> install Acme::Meta # in the shell \& \& CPAN::Shell\->install("Acme::Meta"); # in perl \& \& # Distributions: \& \& cpan> install NWCLARK/Acme\-Meta\-0.02.tar.gz # in the shell \& \& CPAN::Shell\-> \& install("NWCLARK/Acme\-Meta\-0.02.tar.gz"); # in perl \& \& # module objects: \& \& $mo = CPAN::Shell\->expandany($mod); \& $mo = CPAN::Shell\->expand("Module",$mod); # same thing \& \& # distribution objects: \& \& $do = CPAN::Shell\->expand("Module",$mod)\->distribution; \& $do = CPAN::Shell\->expandany($distro); # same thing \& $do = CPAN::Shell\->expand("Distribution", \& $distro); # same thing .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" The \s-1CPAN\s0 module automates or at least simplifies the make and install of perl modules and extensions. It includes some primitive searching capabilities and knows how to use \s-1LWP,\s0 HTTP::Tiny, Net::FTP and certain external download clients to fetch distributions from the net. .PP These are fetched from one or more mirrored \s-1CPAN\s0 (Comprehensive Perl Archive Network) sites and unpacked in a dedicated directory. .PP The \s-1CPAN\s0 module also supports named and versioned \&\fIbundles\fR of modules. Bundles simplify handling of sets of related modules. See Bundles below. .PP The package contains a session manager and a cache manager. The session manager keeps track of what has been fetched, built, and installed in the current session. The cache manager keeps track of the disk space occupied by the make processes and deletes excess space using a simple \s-1FIFO\s0 mechanism. .PP All methods provided are accessible in a programmer style and in an interactive shell style. .ie n .SS "CPAN::shell([$prompt, $command]) Starting Interactive Mode" .el .SS "CPAN::shell([$prompt, \f(CW$command\fP]) Starting Interactive Mode" .IX Subsection "CPAN::shell([$prompt, $command]) Starting Interactive Mode" Enter interactive mode by running .PP .Vb 1 \& perl \-MCPAN \-e shell .Ve .PP or .PP .Vb 1 \& cpan .Ve .PP which puts you into a readline interface. If \f(CW\*(C`Term::ReadKey\*(C'\fR and either of \f(CW\*(C`Term::ReadLine::Perl\*(C'\fR or \f(CW\*(C`Term::ReadLine::Gnu\*(C'\fR are installed, history and command completion are supported. .PP Once at the command line, type \f(CW\*(C`h\*(C'\fR for one-page help screen; the rest should be self-explanatory. .PP The function call \f(CW\*(C`shell\*(C'\fR takes two optional arguments: one the prompt, the second the default initial command line (the latter only works if a real ReadLine interface module is installed). .PP The most common uses of the interactive modes are .IP "Searching for authors, bundles, distribution files and modules" 2 .IX Item "Searching for authors, bundles, distribution files and modules" There are corresponding one-letter commands \f(CW\*(C`a\*(C'\fR, \f(CW\*(C`b\*(C'\fR, \f(CW\*(C`d\*(C'\fR, and \f(CW\*(C`m\*(C'\fR for each of the four categories and another, \f(CW\*(C`i\*(C'\fR for any of the mentioned four. Each of the four entities is implemented as a class with slightly differing methods for displaying an object. .Sp Arguments to these commands are either strings exactly matching the identification string of an object, or regular expressions matched case-insensitively against various attributes of the objects. The parser only recognizes a regular expression when you enclose it with slashes. .Sp The principle is that the number of objects found influences how an item is displayed. If the search finds one item, the result is displayed with the rather verbose method \f(CW\*(C`as_string\*(C'\fR, but if more than one is found, each object is displayed with the terse method \&\f(CW\*(C`as_glimpse\*(C'\fR. .Sp Examples: .Sp .Vb 10 \& cpan> m Acme::MetaSyntactic \& Module id = Acme::MetaSyntactic \& CPAN_USERID BOOK (Philippe Bruhat (BooK) <[...]>) \& CPAN_VERSION 0.99 \& CPAN_FILE B/BO/BOOK/Acme\-MetaSyntactic\-0.99.tar.gz \& UPLOAD_DATE 2006\-11\-06 \& MANPAGE Acme::MetaSyntactic \- Themed metasyntactic variables names \& INST_FILE /usr/local/lib/perl/5.10.0/Acme/MetaSyntactic.pm \& INST_VERSION 0.99 \& cpan> a BOOK \& Author id = BOOK \& EMAIL [...] \& FULLNAME Philippe Bruhat (BooK) \& cpan> d BOOK/Acme\-MetaSyntactic\-0.99.tar.gz \& Distribution id = B/BO/BOOK/Acme\-MetaSyntactic\-0.99.tar.gz \& CPAN_USERID BOOK (Philippe Bruhat (BooK) <[...]>) \& CONTAINSMODS Acme::MetaSyntactic Acme::MetaSyntactic::Alias [...] \& UPLOAD_DATE 2006\-11\-06 \& cpan> m /lorem/ \& Module = Acme::MetaSyntactic::loremipsum (BOOK/Acme\-MetaSyntactic\-0.99.tar.gz) \& Module Text::Lorem (ADEOLA/Text\-Lorem\-0.3.tar.gz) \& Module Text::Lorem::More (RKRIMEN/Text\-Lorem\-More\-0.12.tar.gz) \& Module Text::Lorem::More::Source (RKRIMEN/Text\-Lorem\-More\-0.12.tar.gz) \& cpan> i /berlin/ \& Distribution BEATNIK/Filter\-NumberLines\-0.02.tar.gz \& Module = DateTime::TimeZone::Europe::Berlin (DROLSKY/DateTime\-TimeZone\-0.7904.tar.gz) \& Module Filter::NumberLines (BEATNIK/Filter\-NumberLines\-0.02.tar.gz) \& Author [...] .Ve .Sp The examples illustrate several aspects: the first three queries target modules, authors, or distros directly and yield exactly one result. The last two use regular expressions and yield several results. The last one targets all of bundles, modules, authors, and distros simultaneously. When more than one result is available, they are printed in one-line format. .ie n .IP """get"", ""make"", ""test"", ""install"", ""clean"" modules or distributions" 2 .el .IP "\f(CWget\fR, \f(CWmake\fR, \f(CWtest\fR, \f(CWinstall\fR, \f(CWclean\fR modules or distributions" 2 .IX Item "get, make, test, install, clean modules or distributions" These commands take any number of arguments and investigate what is necessary to perform the action. Argument processing is as follows: .Sp .Vb 5 \& known module name in format Foo/Bar.pm module \& other embedded slash distribution \& \- with trailing slash dot directory \& enclosing slashes regexp \& known module name in format Foo::Bar module .Ve .Sp If the argument is a distribution file name (recognized by embedded slashes), it is processed. If it is a module, \s-1CPAN\s0 determines the distribution file in which this module is included and processes that, following any dependencies named in the module's \s-1META\s0.yml or Makefile.PL (this behavior is controlled by the configuration parameter \f(CW\*(C`prerequisites_policy\*(C'\fR). If an argument is enclosed in slashes it is treated as a regular expression: it is expanded and if the result is a single object (distribution, bundle or module), this object is processed. .Sp Example: .Sp .Vb 3 \& install Dummy::Perl # installs the module \& install AUXXX/Dummy\-Perl\-3.14.tar.gz # installs that distribution \& install /Dummy\-Perl\-3.14/ # same if the regexp is unambiguous .Ve .Sp \&\f(CW\*(C`get\*(C'\fR downloads a distribution file and untars or unzips it, \f(CW\*(C`make\*(C'\fR builds it, \f(CW\*(C`test\*(C'\fR runs the test suite, and \f(CW\*(C`install\*(C'\fR installs it. .Sp Any \f(CW\*(C`make\*(C'\fR or \f(CW\*(C`test\*(C'\fR is run unconditionally. An .Sp .Vb 1 \& install <distribution_file> .Ve .Sp is also run unconditionally. But for .Sp .Vb 1 \& install <module> .Ve .Sp \&\s-1CPAN\s0 checks whether an install is needed and prints \&\fImodule up to date\fR if the distribution file containing the module doesn't need updating. .Sp \&\s-1CPAN\s0 also keeps track of what it has done within the current session and doesn't try to build a package a second time regardless of whether it succeeded or not. It does not repeat a test run if the test has been run successfully before. Same for install runs. .Sp The \f(CW\*(C`force\*(C'\fR pragma may precede another command (currently: \f(CW\*(C`get\*(C'\fR, \&\f(CW\*(C`make\*(C'\fR, \f(CW\*(C`test\*(C'\fR, or \f(CW\*(C`install\*(C'\fR) to execute the command from scratch and attempt to continue past certain errors. See the section below on the \f(CW\*(C`force\*(C'\fR and the \f(CW\*(C`fforce\*(C'\fR pragma. .Sp The \f(CW\*(C`notest\*(C'\fR pragma skips the test part in the build process. .Sp Example: .Sp .Vb 1 \& cpan> notest install Tk .Ve .Sp A \f(CW\*(C`clean\*(C'\fR command results in a .Sp .Vb 1 \& make clean .Ve .Sp being executed within the distribution file's working directory. .ie n .IP """readme"", ""perldoc"", ""look"" module or distribution" 2 .el .IP "\f(CWreadme\fR, \f(CWperldoc\fR, \f(CWlook\fR module or distribution" 2 .IX Item "readme, perldoc, look module or distribution" \&\f(CW\*(C`readme\*(C'\fR displays the \s-1README\s0 file of the associated distribution. \&\f(CW\*(C`Look\*(C'\fR gets and untars (if not yet done) the distribution file, changes to the appropriate directory and opens a subshell process in that directory. \f(CW\*(C`perldoc\*(C'\fR displays the module's pod documentation in html or plain text format. .ie n .IP """ls"" author" 2 .el .IP "\f(CWls\fR author" 2 .IX Item "ls author" .PD 0 .ie n .IP """ls"" globbing_expression" 2 .el .IP "\f(CWls\fR globbing_expression" 2 .IX Item "ls globbing_expression" .PD The first form lists all distribution files in and below an author's \&\s-1CPAN\s0 directory as stored in the \s-1CHECKSUMS\s0 files distributed on \&\s-1CPAN.\s0 The listing recurses into subdirectories. .Sp The second form limits or expands the output with shell globbing as in the following examples: .Sp .Vb 3 \& ls JV/make* \& ls GSAR/*make* \& ls */*make* .Ve .Sp The last example is very slow and outputs extra progress indicators that break the alignment of the result. .Sp Note that globbing only lists directories explicitly asked for, for example FOO/* will not list FOO/bar/Acme\-Sthg\-n.nn.tar.gz. This may be regarded as a bug that may be changed in some future version. .ie n .IP """failed""" 2 .el .IP "\f(CWfailed\fR" 2 .IX Item "failed" The \f(CW\*(C`failed\*(C'\fR command reports all distributions that failed on one of \&\f(CW\*(C`make\*(C'\fR, \f(CW\*(C`test\*(C'\fR or \f(CW\*(C`install\*(C'\fR for some reason in the currently running shell session. .IP "Persistence between sessions" 2 .IX Item "Persistence between sessions" If the \f(CW\*(C`YAML\*(C'\fR or the \f(CW\*(C`YAML::Syck\*(C'\fR module is installed a record of the internal state of all modules is written to disk after each step. The files contain a signature of the currently running perl version for later perusal. .Sp If the configurations variable \f(CW\*(C`build_dir_reuse\*(C'\fR is set to a true value, then \s-1CPAN\s0.pm reads the collected \s-1YAML\s0 files. If the stored signature matches the currently running perl, the stored state is loaded into memory such that persistence between sessions is effectively established. .ie n .IP "The ""force"" and the ""fforce"" pragma" 2 .el .IP "The \f(CWforce\fR and the \f(CWfforce\fR pragma" 2 .IX Item "The force and the fforce pragma" To speed things up in complex installation scenarios, \s-1CPAN\s0.pm keeps track of what it has already done and refuses to do some things a second time. A \f(CW\*(C`get\*(C'\fR, a \f(CW\*(C`make\*(C'\fR, and an \f(CW\*(C`install\*(C'\fR are not repeated. A \f(CW\*(C`test\*(C'\fR is repeated only if the previous test was unsuccessful. The diagnostic message when \s-1CPAN\s0.pm refuses to do something a second time is one of \fIHas already been \fR\f(CW\*(C`unwrapped|made|tested successfully\*(C'\fR or something similar. Another situation where \s-1CPAN\s0 refuses to act is an \&\f(CW\*(C`install\*(C'\fR if the corresponding \f(CW\*(C`test\*(C'\fR was not successful. .Sp In all these cases, the user can override this stubborn behaviour by prepending the command with the word force, for example: .Sp .Vb 4 \& cpan> force get Foo \& cpan> force make AUTHOR/Bar\-3.14.tar.gz \& cpan> force test Baz \& cpan> force install Acme::Meta .Ve .Sp Each \fIforced\fR command is executed with the corresponding part of its memory erased. .Sp The \f(CW\*(C`fforce\*(C'\fR pragma is a variant that emulates a \f(CW\*(C`force get\*(C'\fR which erases the entire memory followed by the action specified, effectively restarting the whole get/make/test/install procedure from scratch. .IP "Lockfile" 2 .IX Item "Lockfile" Interactive sessions maintain a lockfile, by default \f(CW\*(C`~/.cpan/.lock\*(C'\fR. Batch jobs can run without a lockfile and not disturb each other. .Sp The shell offers to run in \fIdowngraded mode\fR when another process is holding the lockfile. This is an experimental feature that is not yet tested very well. This second shell then does not write the history file, does not use the metadata file, and has a different prompt. .IP "Signals" 2 .IX Item "Signals" \&\s-1CPAN\s0.pm installs signal handlers for \s-1SIGINT\s0 and \s-1SIGTERM.\s0 While you are in the cpan-shell, it is intended that you can press \f(CW\*(C`^C\*(C'\fR anytime and return to the cpan-shell prompt. A \s-1SIGTERM\s0 will cause the cpan-shell to clean up and leave the shell loop. You can emulate the effect of a \&\s-1SIGTERM\s0 by sending two consecutive SIGINTs, which usually means by pressing \f(CW\*(C`^C\*(C'\fR twice. .Sp \&\s-1CPAN\s0.pm ignores \s-1SIGPIPE.\s0 If the user sets \f(CW\*(C`inactivity_timeout\*(C'\fR, a \&\s-1SIGALRM\s0 is used during the run of the \f(CW\*(C`perl Makefile.PL\*(C'\fR or \f(CW\*(C`perl Build.PL\*(C'\fR subprocess. A \s-1SIGALRM\s0 is also used during module version parsing, and is controlled by \f(CW\*(C`version_timeout\*(C'\fR. .SS "CPAN::Shell" .IX Subsection "CPAN::Shell" The commands available in the shell interface are methods in the package CPAN::Shell. If you enter the shell command, your input is split by the \fBText::ParseWords::shellwords()\fR routine, which acts like most shells do. The first word is interpreted as the method to be invoked, and the rest of the words are treated as the method's arguments. Continuation lines are supported by ending a line with a literal backslash. .SS "autobundle" .IX Subsection "autobundle" \&\f(CW\*(C`autobundle\*(C'\fR writes a bundle file into the \&\f(CW\*(C`$CPAN::Config\->{cpan_home}/Bundle\*(C'\fR directory. The file contains a list of all modules that are both available from \s-1CPAN\s0 and currently installed within \f(CW@INC\fR. Duplicates of each distribution are suppressed. The name of the bundle file is based on the current date and a counter, e.g. \fIBundle/Snapshot_2012_05_21_00.pm\fR. This is installed again by running \f(CW\*(C`cpan Bundle::Snapshot_2012_05_21_00\*(C'\fR, or installing \&\f(CW\*(C`Bundle::Snapshot_2012_05_21_00\*(C'\fR from the \s-1CPAN\s0 shell. .PP Return value: path to the written file. .SS "hosts" .IX Subsection "hosts" Note: this feature is still in alpha state and may change in future versions of \s-1CPAN\s0.pm .PP This commands provides a statistical overview over recent download activities. The data for this is collected in the \s-1YAML\s0 file \&\f(CW\*(C`FTPstats.yml\*(C'\fR in your \f(CW\*(C`cpan_home\*(C'\fR directory. If no \s-1YAML\s0 module is configured or \s-1YAML\s0 not installed, no stats are provided. .IP "install_tested" 4 .IX Item "install_tested" Install all distributions that have been tested successfully but have not yet been installed. See also \f(CW\*(C`is_tested\*(C'\fR. .IP "is_tested" 4 .IX Item "is_tested" List all build directories of distributions that have been tested successfully but have not yet been installed. See also \&\f(CW\*(C`install_tested\*(C'\fR. .SS "mkmyconfig" .IX Subsection "mkmyconfig" \&\fBmkmyconfig()\fR writes your own CPAN::MyConfig file into your \f(CW\*(C`~/.cpan/\*(C'\fR directory so that you can save your own preferences instead of the system-wide ones. .SS "r [Module|/Regexp/]..." .IX Subsection "r [Module|/Regexp/]..." scans current perl installation for modules that have a newer version available on \s-1CPAN\s0 and provides a list of them. If called without argument, all potential upgrades are listed; if called with arguments the list is filtered to the modules and regexps given as arguments. .PP The listing looks something like this: .PP .Vb 10 \& Package namespace installed latest in CPAN file \& CPAN 1.94_64 1.9600 ANDK/CPAN\-1.9600.tar.gz \& CPAN::Reporter 1.1801 1.1902 DAGOLDEN/CPAN\-Reporter\-1.1902.tar.gz \& YAML 0.70 0.73 INGY/YAML\-0.73.tar.gz \& YAML::Syck 1.14 1.17 AVAR/YAML\-Syck\-1.17.tar.gz \& YAML::Tiny 1.44 1.50 ADAMK/YAML\-Tiny\-1.50.tar.gz \& CGI 3.43 3.55 MARKSTOS/CGI.pm\-3.55.tar.gz \& Module::Build::YAML 1.40 1.41 DAGOLDEN/Module\-Build\-0.3800.tar.gz \& TAP::Parser::Result::YAML 3.22 3.23 ANDYA/Test\-Harness\-3.23.tar.gz \& YAML::XS 0.34 0.35 INGY/YAML\-LibYAML\-0.35.tar.gz .Ve .PP It suppresses duplicates in the column \f(CW\*(C`in CPAN file\*(C'\fR such that distributions with many upgradeable modules are listed only once. .PP Note that the list is not sorted. .SS "recent ***EXPERIMENTAL COMMAND***" .IX Subsection "recent ***EXPERIMENTAL COMMAND***" The \f(CW\*(C`recent\*(C'\fR command downloads a list of recent uploads to \s-1CPAN\s0 and displays them \fIslowly\fR. While the command is running, a \f(CW$SIG\fR{\s-1INT\s0} exits the loop after displaying the current item. .PP \&\fBNote\fR: This command requires XML::LibXML installed. .PP \&\fBNote\fR: This whole command currently is just a hack and will probably change in future versions of \s-1CPAN\s0.pm, but the general approach will likely remain. .PP \&\fBNote\fR: See also smoke .SS "recompile" .IX Subsection "recompile" \&\fBrecompile()\fR is a special command that takes no argument and runs the make/test/install cycle with brute force over all installed dynamically loadable extensions (a.k.a. \s-1XS\s0 modules) with 'force' in effect. The primary purpose of this command is to finish a network installation. Imagine you have a common source tree for two different architectures. You decide to do a completely independent fresh installation. You start on one architecture with the help of a Bundle file produced earlier. \s-1CPAN\s0 installs the whole Bundle for you, but when you try to repeat the job on the second architecture, \s-1CPAN\s0 responds with a \f(CW"Foo up to date"\fR message for all modules. So you invoke \s-1CPAN\s0's recompile on the second architecture and you're done. .PP Another popular use for \f(CW\*(C`recompile\*(C'\fR is to act as a rescue in case your perl breaks binary compatibility. If one of the modules that \s-1CPAN\s0 uses is in turn depending on binary compatibility (so you cannot run \s-1CPAN\s0 commands), then you should try the CPAN::Nox module for recovery. .SS "report Bundle|Distribution|Module" .IX Subsection "report Bundle|Distribution|Module" The \f(CW\*(C`report\*(C'\fR command temporarily turns on the \f(CW\*(C`test_report\*(C'\fR config variable, then runs the \f(CW\*(C`force test\*(C'\fR command with the given arguments. The \f(CW\*(C`force\*(C'\fR pragma reruns the tests and repeats every step that might have failed before. .SS "smoke ***EXPERIMENTAL COMMAND***" .IX Subsection "smoke ***EXPERIMENTAL COMMAND***" \&\fB*** \s-1WARNING:\s0 this command downloads and executes software from \s-1CPAN\s0 to your computer of completely unknown status. You should never do this with your normal account and better have a dedicated well separated and secured machine to do this. ***\fR .PP The \f(CW\*(C`smoke\*(C'\fR command takes the list of recent uploads to \s-1CPAN\s0 as provided by the \f(CW\*(C`recent\*(C'\fR command and tests them all. While the command is running \f(CW$SIG\fR{\s-1INT\s0} is defined to mean that the current item shall be skipped. .PP \&\fBNote\fR: This whole command currently is just a hack and will probably change in future versions of \s-1CPAN\s0.pm, but the general approach will likely remain. .PP \&\fBNote\fR: See also recent .SS "upgrade [Module|/Regexp/]..." .IX Subsection "upgrade [Module|/Regexp/]..." The \f(CW\*(C`upgrade\*(C'\fR command first runs an \f(CW\*(C`r\*(C'\fR command with the given arguments and then installs the newest versions of all modules that were listed by that. .ie n .SS "The four ""CPAN::*"" Classes: Author, Bundle, Module, Distribution" .el .SS "The four \f(CWCPAN::*\fP Classes: Author, Bundle, Module, Distribution" .IX Subsection "The four CPAN::* Classes: Author, Bundle, Module, Distribution" Although it may be considered internal, the class hierarchy does matter for both users and programmer. \s-1CPAN\s0.pm deals with the four classes mentioned above, and those classes all share a set of methods. Classical single polymorphism is in effect. A metaclass object registers all objects of all kinds and indexes them with a string. The strings referencing objects have a separated namespace (well, not completely separated): .PP .Vb 1 \& Namespace Class \& \& words containing a "/" (slash) Distribution \& words starting with Bundle:: Bundle \& everything else Module or Author .Ve .PP Modules know their associated Distribution objects. They always refer to the most recent official release. Developers may mark their releases as unstable development versions (by inserting an underscore into the module version number which will also be reflected in the distribution name when you run 'make dist'), so the really hottest and newest distribution is not always the default. If a module Foo circulates on \s-1CPAN\s0 in both version 1.23 and 1.23_90, \s-1CPAN\s0.pm offers a convenient way to install version 1.23 by saying .PP .Vb 1 \& install Foo .Ve .PP This would install the complete distribution file (say BAR/Foo\-1.23.tar.gz) with all accompanying material. But if you would like to install version 1.23_90, you need to know where the distribution file resides on \s-1CPAN\s0 relative to the authors/id/ directory. If the author is \s-1BAR,\s0 this might be BAR/Foo\-1.23_90.tar.gz; so you would have to say .PP .Vb 1 \& install BAR/Foo\-1.23_90.tar.gz .Ve .PP The first example will be driven by an object of the class CPAN::Module, the second by an object of class CPAN::Distribution. .SS "Integrating local directories" .IX Subsection "Integrating local directories" Note: this feature is still in alpha state and may change in future versions of \s-1CPAN\s0.pm .PP Distribution objects are normally distributions from the \s-1CPAN,\s0 but there is a slightly degenerate case for Distribution objects, too, of projects held on the local disk. These distribution objects have the same name as the local directory and end with a dot. A dot by itself is also allowed for the current directory at the time \s-1CPAN\s0.pm was used. All actions such as \f(CW\*(C`make\*(C'\fR, \f(CW\*(C`test\*(C'\fR, and \f(CW\*(C`install\*(C'\fR are applied directly to that directory. This gives the command \f(CW\*(C`cpan .\*(C'\fR an interesting touch: while the normal mantra of installing a \s-1CPAN\s0 module without \s-1CPAN\s0.pm is one of .PP .Vb 5 \& perl Makefile.PL perl Build.PL \& ( go and get prerequisites ) \& make ./Build \& make test ./Build test \& make install ./Build install .Ve .PP the command \f(CW\*(C`cpan .\*(C'\fR does all of this at once. It figures out which of the two mantras is appropriate, fetches and installs all prerequisites, takes care of them recursively, and finally finishes the installation of the module in the current directory, be it a \s-1CPAN\s0 module or not. .PP The typical usage case is for private modules or working copies of projects from remote repositories on the local disk. .SS "Redirection" .IX Subsection "Redirection" The usual shell redirection symbols \f(CW\*(C` | \*(C'\fR and \f(CW\*(C`>\*(C'\fR are recognized by the cpan shell \fBonly when surrounded by whitespace\fR. So piping to pager or redirecting output into a file works somewhat as in a normal shell, with the stipulation that you must type extra spaces. .SS "Plugin support ***EXPERIMENTAL***" .IX Subsection "Plugin support ***EXPERIMENTAL***" Plugins are objects that implement any of currently eight methods: .PP .Vb 8 \& pre_get \& post_get \& pre_make \& post_make \& pre_test \& post_test \& pre_install \& post_install .Ve .PP The \f(CW\*(C`plugin_list\*(C'\fR configuration parameter holds a list of strings of the form .PP .Vb 1 \& Modulename=arg0,arg1,arg2,arg3,... .Ve .PP eg: .PP .Vb 1 \& CPAN::Plugin::Flurb=dir,/opt/pkgs/flurb/raw,verbose,1 .Ve .PP At run time, each listed plugin is instantiated as a singleton object by running the equivalent of this pseudo code: .PP .Vb 3 \& my $plugin = <string representation from config>; \& <generate Modulename and arguments from $plugin>; \& my $p = $instance{$plugin} ||= Modulename\->new($arg0,$arg1,...); .Ve .PP The generated singletons are kept around from instantiation until the end of the shell session. <plugin_list> can be reconfigured at any time at run time. While the cpan shell is running, it checks all activated plugins at each of the 8 reference points listed above and runs the respective method if it is implemented for that object. The method is called with the active CPAN::Distribution object passed in as an argument. .SH "CONFIGURATION" .IX Header "CONFIGURATION" When the \s-1CPAN\s0 module is used for the first time, a configuration dialogue tries to determine a couple of site specific options. The result of the dialog is stored in a hash reference \f(CW $CPAN::Config \fR in a file CPAN/Config.pm. .PP Default values defined in the CPAN/Config.pm file can be overridden in a user specific file: CPAN/MyConfig.pm. Such a file is best placed in \f(CW\*(C`$HOME/.cpan/CPAN/MyConfig.pm\*(C'\fR, because \f(CW\*(C`$HOME/.cpan\*(C'\fR is added to the search path of the \s-1CPAN\s0 module before the \fBuse()\fR or \&\fBrequire()\fR statements. The mkmyconfig command writes this file for you. .PP The \f(CW\*(C`o conf\*(C'\fR command has various bells and whistles: .IP "completion support" 4 .IX Item "completion support" If you have a ReadLine module installed, you can hit \s-1TAB\s0 at any point of the commandline and \f(CW\*(C`o conf\*(C'\fR will offer you completion for the built-in subcommands and/or config variable names. .IP "displaying some help: o conf help" 4 .IX Item "displaying some help: o conf help" Displays a short help .IP "displaying current values: o conf [\s-1KEY\s0]" 4 .IX Item "displaying current values: o conf [KEY]" Displays the current value(s) for this config variable. Without \s-1KEY,\s0 displays all subcommands and config variables. .Sp Example: .Sp .Vb 1 \& o conf shell .Ve .Sp If \s-1KEY\s0 starts and ends with a slash, the string in between is treated as a regular expression and only keys matching this regexp are displayed .Sp Example: .Sp .Vb 1 \& o conf /color/ .Ve .IP "changing of scalar values: o conf \s-1KEY VALUE\s0" 4 .IX Item "changing of scalar values: o conf KEY VALUE" Sets the config variable \s-1KEY\s0 to \s-1VALUE.\s0 The empty string can be specified as usual in shells, with \f(CW\*(Aq\*(Aq\fR or \f(CW""\fR .Sp Example: .Sp .Vb 1 \& o conf wget /usr/bin/wget .Ve .IP "changing of list values: o conf \s-1KEY\s0 SHIFT|UNSHIFT|PUSH|POP|SPLICE|LIST" 4 .IX Item "changing of list values: o conf KEY SHIFT|UNSHIFT|PUSH|POP|SPLICE|LIST" If a config variable name ends with \f(CW\*(C`list\*(C'\fR, it is a list. \f(CW\*(C`o conf KEY shift\*(C'\fR removes the first element of the list, \f(CW\*(C`o conf KEY pop\*(C'\fR removes the last element of the list. \f(CW\*(C`o conf KEYS unshift LIST\*(C'\fR prepends a list of values to the list, \f(CW\*(C`o conf KEYS push LIST\*(C'\fR appends a list of valued to the list. .Sp Likewise, \f(CW\*(C`o conf KEY splice LIST\*(C'\fR passes the \s-1LIST\s0 to the corresponding splice command. .Sp Finally, any other list of arguments is taken as a new list value for the \s-1KEY\s0 variable discarding the previous value. .Sp Examples: .Sp .Vb 3 \& o conf urllist unshift http://cpan.dev.local/CPAN \& o conf urllist splice 3 1 \& o conf urllist http://cpan1.local http://cpan2.local ftp://ftp.perl.org .Ve .IP "reverting to saved: o conf defaults" 4 .IX Item "reverting to saved: o conf defaults" Reverts all config variables to the state in the saved config file. .IP "saving the config: o conf commit" 4 .IX Item "saving the config: o conf commit" Saves all config variables to the current config file (CPAN/Config.pm or CPAN/MyConfig.pm that was loaded at start). .PP The configuration dialog can be started any time later again by issuing the command \f(CW\*(C` o conf init \*(C'\fR in the \s-1CPAN\s0 shell. A subset of the configuration dialog can be run by issuing \f(CW\*(C`o conf init WORD\*(C'\fR where \s-1WORD\s0 is any valid config variable or a regular expression. .SS "Config Variables" .IX Subsection "Config Variables" The following keys in the hash reference \f(CW$CPAN::Config\fR are currently defined: .PP .Vb 10 \& allow_installing_module_downgrades \& allow or disallow installing module downgrades \& allow_installing_outdated_dists \& allow or disallow installing modules that are \& indexed in the cpan index pointing to a distro \& with a higher distro\-version number \& applypatch path to external prg \& auto_commit commit all changes to config variables to disk \& build_cache size of cache for directories to build modules \& build_dir locally accessible directory to build modules \& build_dir_reuse boolean if distros in build_dir are persistent \& build_requires_install_policy \& to install or not to install when a module is \& only needed for building. yes|no|ask/yes|ask/no \& bzip2 path to external prg \& cache_metadata use serializer to cache metadata \& check_sigs if signatures should be verified \& cleanup_after_install \& remove build directory immediately after a \& successful install and remember that for the \& duration of the session \& colorize_debug Term::ANSIColor attributes for debugging output \& colorize_output boolean if Term::ANSIColor should colorize output \& colorize_print Term::ANSIColor attributes for normal output \& colorize_warn Term::ANSIColor attributes for warnings \& commandnumber_in_prompt \& boolean if you want to see current command number \& commands_quote preferred character to use for quoting external \& commands when running them. Defaults to double \& quote on Windows, single tick everywhere else; \& can be set to space to disable quoting \& connect_to_internet_ok \& whether to ask if opening a connection is ok before \& urllist is specified \& cpan_home local directory reserved for this package \& curl path to external prg \& dontload_hash DEPRECATED \& dontload_list arrayref: modules in the list will not be \& loaded by the CPAN::has_inst() routine \& ftp path to external prg \& ftp_passive if set, the environment variable FTP_PASSIVE is set \& for downloads \& ftp_proxy proxy host for ftp requests \& ftpstats_period max number of days to keep download statistics \& ftpstats_size max number of items to keep in the download statistics \& getcwd see below \& gpg path to external prg \& gzip location of external program gzip \& halt_on_failure stop processing after the first failure of queued \& items or dependencies \& histfile file to maintain history between sessions \& histsize maximum number of lines to keep in histfile \& http_proxy proxy host for http requests \& inactivity_timeout breaks interactive Makefile.PLs or Build.PLs \& after this many seconds inactivity. Set to 0 to \& disable timeouts. \& index_expire refetch index files after this many days \& inhibit_startup_message \& if true, suppress the startup message \& keep_source_where directory in which to keep the source (if we do) \& load_module_verbosity \& report loading of optional modules used by CPAN.pm \& lynx path to external prg \& make location of external make program \& make_arg arguments that should always be passed to \*(Aqmake\*(Aq \& make_install_make_command \& the make command for running \*(Aqmake install\*(Aq, for \& example \*(Aqsudo make\*(Aq \& make_install_arg same as make_arg for \*(Aqmake install\*(Aq \& makepl_arg arguments passed to \*(Aqperl Makefile.PL\*(Aq \& mbuild_arg arguments passed to \*(Aq./Build\*(Aq \& mbuild_install_arg arguments passed to \*(Aq./Build install\*(Aq \& mbuild_install_build_command \& command to use instead of \*(Aq./Build\*(Aq when we are \& in the install stage, for example \*(Aqsudo ./Build\*(Aq \& mbuildpl_arg arguments passed to \*(Aqperl Build.PL\*(Aq \& ncftp path to external prg \& ncftpget path to external prg \& no_proxy don\*(Aqt proxy to these hosts/domains (comma separated list) \& pager location of external program more (or any pager) \& password your password if you CPAN server wants one \& patch path to external prg \& patches_dir local directory containing patch files \& perl5lib_verbosity verbosity level for PERL5LIB additions \& plugin_list list of active hooks (see Plugin support above \& and the CPAN::Plugin module) \& prefer_external_tar \& per default all untar operations are done with \& Archive::Tar; by setting this variable to true \& the external tar command is used if available \& prefer_installer legal values are MB and EUMM: if a module comes \& with both a Makefile.PL and a Build.PL, use the \& former (EUMM) or the latter (MB); if the module \& comes with only one of the two, that one will be \& used no matter the setting \& prerequisites_policy \& what to do if you are missing module prerequisites \& (\*(Aqfollow\*(Aq automatically, \*(Aqask\*(Aq me, or \*(Aqignore\*(Aq) \& For \*(Aqfollow\*(Aq, also sets PERL_AUTOINSTALL and \& PERL_EXTUTILS_AUTOINSTALL for "\-\-defaultdeps" if \& not already set \& prefs_dir local directory to store per\-distro build options \& proxy_user username for accessing an authenticating proxy \& proxy_pass password for accessing an authenticating proxy \& pushy_https use https to cpan.org when possible, otherwise use http \& to cpan.org and issue a warning \& randomize_urllist add some randomness to the sequence of the urllist \& recommends_policy whether recommended prerequisites should be included \& scan_cache controls scanning of cache (\*(Aqatstart\*(Aq, \*(Aqatexit\*(Aq or \*(Aqnever\*(Aq) \& shell your favorite shell \& show_unparsable_versions \& boolean if r command tells which modules are versionless \& show_upload_date boolean if commands should try to determine upload date \& show_zero_versions boolean if r command tells for which modules $version==0 \& suggests_policy whether suggested prerequisites should be included \& tar location of external program tar \& tar_verbosity verbosity level for the tar command \& term_is_latin deprecated: if true Unicode is translated to ISO\-8859\-1 \& (and nonsense for characters outside latin range) \& term_ornaments boolean to turn ReadLine ornamenting on/off \& test_report email test reports (if CPAN::Reporter is installed) \& trust_test_report_history \& skip testing when previously tested ok (according to \& CPAN::Reporter history) \& unzip location of external program unzip \& urllist arrayref to nearby CPAN sites (or equivalent locations) \& urllist_ping_external \& use external ping command when autoselecting mirrors \& urllist_ping_verbose \& increase verbosity when autoselecting mirrors \& use_prompt_default set PERL_MM_USE_DEFAULT for configure/make/test/install \& use_sqlite use CPAN::SQLite for metadata storage (fast and lean) \& username your username if you CPAN server wants one \& version_timeout stops version parsing after this many seconds. \& Default is 15 secs. Set to 0 to disable. \& wait_list arrayref to a wait server to try (See CPAN::WAIT) \& wget path to external prg \& yaml_load_code enable YAML code deserialisation via CPAN::DeferredCode \& yaml_module which module to use to read/write YAML files .Ve .PP You can set and query each of these options interactively in the cpan shell with the \f(CW\*(C`o conf\*(C'\fR or the \f(CW\*(C`o conf init\*(C'\fR command as specified below. .ie n .IP """o conf <scalar option>""" 2 .el .IP "\f(CWo conf <scalar option>\fR" 2 .IX Item "o conf <scalar option>" prints the current value of the \fIscalar option\fR .ie n .IP """o conf <scalar option> <value>""" 2 .el .IP "\f(CWo conf <scalar option> <value>\fR" 2 .IX Item "o conf <scalar option> <value>" Sets the value of the \fIscalar option\fR to \fIvalue\fR .ie n .IP """o conf <list option>""" 2 .el .IP "\f(CWo conf <list option>\fR" 2 .IX Item "o conf <list option>" prints the current value of the \fIlist option\fR in MakeMaker's neatvalue format. .ie n .IP """o conf <list option> [shift|pop]""" 2 .el .IP "\f(CWo conf <list option> [shift|pop]\fR" 2 .IX Item "o conf <list option> [shift|pop]" shifts or pops the array in the \fIlist option\fR variable .ie n .IP """o conf <list option> [unshift|push|splice] <list>""" 2 .el .IP "\f(CWo conf <list option> [unshift|push|splice] <list>\fR" 2 .IX Item "o conf <list option> [unshift|push|splice] <list>" works like the corresponding perl commands. .IP "interactive editing: o conf init [MATCH|LIST]" 2 .IX Item "interactive editing: o conf init [MATCH|LIST]" Runs an interactive configuration dialog for matching variables. Without argument runs the dialog over all supported config variables. To specify a \s-1MATCH\s0 the argument must be enclosed by slashes. .Sp Examples: .Sp .Vb 2 \& o conf init ftp_passive ftp_proxy \& o conf init /color/ .Ve .Sp Note: this method of setting config variables often provides more explanation about the functioning of a variable than the manpage. .SS "CPAN::anycwd($path): Note on config variable getcwd" .IX Subsection "CPAN::anycwd($path): Note on config variable getcwd" \&\s-1CPAN\s0.pm changes the current working directory often and needs to determine its own current working directory. By default it uses Cwd::cwd, but if for some reason this doesn't work on your system, configure alternatives according to the following table: .IP "cwd" 4 .IX Item "cwd" Calls Cwd::cwd .IP "getcwd" 4 .IX Item "getcwd" Calls Cwd::getcwd .IP "fastcwd" 4 .IX Item "fastcwd" Calls Cwd::fastcwd .IP "getdcwd" 4 .IX Item "getdcwd" Calls Cwd::getdcwd .IP "backtickcwd" 4 .IX Item "backtickcwd" Calls the external command cwd. .SS "Note on the format of the urllist parameter" .IX Subsection "Note on the format of the urllist parameter" urllist parameters are URLs according to \s-1RFC 1738.\s0 We do a little guessing if your \s-1URL\s0 is not compliant, but if you have problems with \&\f(CW\*(C`file\*(C'\fR URLs, please try the correct format. Either: .PP .Vb 1 \& file://localhost/whatever/ftp/pub/CPAN/ .Ve .PP or .PP .Vb 1 \& file:///home/ftp/pub/CPAN/ .Ve .SS "The urllist parameter has CD-ROM support" .IX Subsection "The urllist parameter has CD-ROM support" The \f(CW\*(C`urllist\*(C'\fR parameter of the configuration table contains a list of URLs used for downloading. If the list contains any \&\f(CW\*(C`file\*(C'\fR URLs, \s-1CPAN\s0 always tries there first. This feature is disabled for index files. So the recommendation for the owner of a CD-ROM with \s-1CPAN\s0 contents is: include your local, possibly outdated CD-ROM as a \f(CW\*(C`file\*(C'\fR \s-1URL\s0 at the end of urllist, e.g. .PP .Vb 1 \& o conf urllist push file://localhost/CDROM/CPAN .Ve .PP \&\s-1CPAN\s0.pm will then fetch the index files from one of the \s-1CPAN\s0 sites that come at the beginning of urllist. It will later check for each module to see whether there is a local copy of the most recent version. .PP Another peculiarity of urllist is that the site that we could successfully fetch the last file from automatically gets a preference token and is tried as the first site for the next request. So if you add a new site at runtime it may happen that the previously preferred site will be tried another time. This means that if you want to disallow a site for the next transfer, it must be explicitly removed from urllist. .SS "Maintaining the urllist parameter" .IX Subsection "Maintaining the urllist parameter" If you have \s-1YAML\s0.pm (or some other \s-1YAML\s0 module configured in \&\f(CW\*(C`yaml_module\*(C'\fR) installed, \s-1CPAN\s0.pm collects a few statistical data about recent downloads. You can view the statistics with the \f(CW\*(C`hosts\*(C'\fR command or inspect them directly by looking into the \f(CW\*(C`FTPstats.yml\*(C'\fR file in your \f(CW\*(C`cpan_home\*(C'\fR directory. .PP To get some interesting statistics, it is recommended that \&\f(CW\*(C`randomize_urllist\*(C'\fR be set; this introduces some amount of randomness into the \s-1URL\s0 selection. .ie n .SS "The ""requires"" and ""build_requires"" dependency declarations" .el .SS "The \f(CWrequires\fP and \f(CWbuild_requires\fP dependency declarations" .IX Subsection "The requires and build_requires dependency declarations" Since \s-1CPAN\s0.pm version 1.88_51 modules declared as \f(CW\*(C`build_requires\*(C'\fR by a distribution are treated differently depending on the config variable \f(CW\*(C`build_requires_install_policy\*(C'\fR. By setting \&\f(CW\*(C`build_requires_install_policy\*(C'\fR to \f(CW\*(C`no\*(C'\fR, such a module is not installed. It is only built and tested, and then kept in the list of tested but uninstalled modules. As such, it is available during the build of the dependent module by integrating the path to the \&\f(CW\*(C`blib/arch\*(C'\fR and \f(CW\*(C`blib/lib\*(C'\fR directories in the environment variable \&\s-1PERL5LIB.\s0 If \f(CW\*(C`build_requires_install_policy\*(C'\fR is set to \f(CW\*(C`yes\*(C'\fR, then both modules declared as \f(CW\*(C`requires\*(C'\fR and those declared as \&\f(CW\*(C`build_requires\*(C'\fR are treated alike. By setting to \f(CW\*(C`ask/yes\*(C'\fR or \&\f(CW\*(C`ask/no\*(C'\fR, \s-1CPAN\s0.pm asks the user and sets the default accordingly. .SS "Configuration of the allow_installing_* parameters" .IX Subsection "Configuration of the allow_installing_* parameters" The \f(CW\*(C`allow_installing_*\*(C'\fR parameters are evaluated during the \f(CW\*(C`make\*(C'\fR phase. If set to \f(CW\*(C`yes\*(C'\fR, they allow the testing and the installation of the current distro and otherwise have no effect. If set to \f(CW\*(C`no\*(C'\fR, they may abort the build (preventing testing and installing), depending on the contents of the \&\f(CW\*(C`blib/\*(C'\fR directory. The \f(CW\*(C`blib/\*(C'\fR directory is the directory that holds all the files that would usually be installed in the \f(CW\*(C`install\*(C'\fR phase. .PP \&\f(CW\*(C`allow_installing_outdated_dists\*(C'\fR compares the \f(CW\*(C`blib/\*(C'\fR directory with the \s-1CPAN\s0 index. If it finds something there that belongs, according to the index, to a different dist, it aborts the current build. .PP \&\f(CW\*(C`allow_installing_module_downgrades\*(C'\fR compares the \f(CW\*(C`blib/\*(C'\fR directory with already installed modules, actually their version numbers, as determined by ExtUtils::MakeMaker or equivalent. If a to-be-installed module would downgrade an already installed module, the current build is aborted. .PP An interesting twist occurs when a distroprefs document demands the installation of an outdated dist via goto while \&\f(CW\*(C`allow_installing_outdated_dists\*(C'\fR forbids it. Without additional provisions, this would let the \f(CW\*(C`allow_installing_outdated_dists\*(C'\fR win and the distroprefs lose. So the proper arrangement in such a case is to write a second distroprefs document for the distro that \f(CW\*(C`goto\*(C'\fR points to and overrule the \f(CW\*(C`cpanconfig\*(C'\fR there. E.g.: .PP .Vb 9 \& \-\-\- \& match: \& distribution: "^MAUKE/Keyword\-Simple\-0.04.tar.gz" \& goto: "MAUKE/Keyword\-Simple\-0.03.tar.gz" \& \-\-\- \& match: \& distribution: "^MAUKE/Keyword\-Simple\-0.03.tar.gz" \& cpanconfig: \& allow_installing_outdated_dists: yes .Ve .SS "Configuration for individual distributions (\fIDistroprefs\fP)" .IX Subsection "Configuration for individual distributions (Distroprefs)" (\fBNote:\fR This feature has been introduced in \s-1CPAN\s0.pm 1.8854) .PP Distributions on \s-1CPAN\s0 usually behave according to what we call the \&\s-1CPAN\s0 mantra. Or since the advent of Module::Build we should talk about two mantras: .PP .Vb 4 \& perl Makefile.PL perl Build.PL \& make ./Build \& make test ./Build test \& make install ./Build install .Ve .PP But some modules cannot be built with this mantra. They try to get some extra data from the user via the environment, extra arguments, or interactively\*(--thus disturbing the installation of large bundles like Phalanx100 or modules with many dependencies like Plagger. .PP The distroprefs system of \f(CW\*(C`CPAN.pm\*(C'\fR addresses this problem by allowing the user to specify extra informations and recipes in \s-1YAML\s0 files to either .IP "\(bu" 4 pass additional arguments to one of the four commands, .IP "\(bu" 4 set environment variables .IP "\(bu" 4 instantiate an Expect object that reads from the console, waits for some regular expressions and enters some answers .IP "\(bu" 4 temporarily override assorted \f(CW\*(C`CPAN.pm\*(C'\fR configuration variables .IP "\(bu" 4 specify dependencies the original maintainer forgot .IP "\(bu" 4 disable the installation of an object altogether .PP See the \s-1YAML\s0 and Data::Dumper files that come with the \f(CW\*(C`CPAN.pm\*(C'\fR distribution in the \f(CW\*(C`distroprefs/\*(C'\fR directory for examples. .SS "Filenames" .IX Subsection "Filenames" The \s-1YAML\s0 files themselves must have the \f(CW\*(C`.yml\*(C'\fR extension; all other files are ignored (for two exceptions see \fIFallback Data::Dumper and Storable\fR below). The containing directory can be specified in \&\f(CW\*(C`CPAN.pm\*(C'\fR in the \f(CW\*(C`prefs_dir\*(C'\fR config variable. Try \f(CW\*(C`o conf init prefs_dir\*(C'\fR in the \s-1CPAN\s0 shell to set and activate the distroprefs system. .PP Every \s-1YAML\s0 file may contain arbitrary documents according to the \s-1YAML\s0 specification, and every document is treated as an entity that can specify the treatment of a single distribution. .PP Filenames can be picked arbitrarily; \f(CW\*(C`CPAN.pm\*(C'\fR always reads all files (in alphabetical order) and takes the key \f(CW\*(C`match\*(C'\fR (see below in \fILanguage Specs\fR) as a hashref containing match criteria that determine if the current distribution matches the \s-1YAML\s0 document or not. .SS "Fallback Data::Dumper and Storable" .IX Subsection "Fallback Data::Dumper and Storable" If neither your configured \f(CW\*(C`yaml_module\*(C'\fR nor \s-1YAML\s0.pm is installed, \&\s-1CPAN\s0.pm falls back to using Data::Dumper and Storable and looks for files with the extensions \f(CW\*(C`.dd\*(C'\fR or \f(CW\*(C`.st\*(C'\fR in the \f(CW\*(C`prefs_dir\*(C'\fR directory. These files are expected to contain one or more hashrefs. For Data::Dumper generated files, this is expected to be done with by defining \f(CW$VAR1\fR, \f(CW$VAR2\fR, etc. The \s-1YAML\s0 shell would produce these with the command .PP .Vb 1 \& ysh < somefile.yml > somefile.dd .Ve .PP For Storable files the rule is that they must be constructed such that \&\f(CW\*(C`Storable::retrieve(file)\*(C'\fR returns an array reference and the array elements represent one distropref object each. The conversion from \&\s-1YAML\s0 would look like so: .PP .Vb 3 \& perl \-MYAML=LoadFile \-MStorable=nstore \-e \*(Aq \& @y=LoadFile(shift); \& nstore(\e@y, shift)\*(Aq somefile.yml somefile.st .Ve .PP In bootstrapping situations it is usually sufficient to translate only a few \s-1YAML\s0 files to Data::Dumper for crucial modules like \&\f(CW\*(C`YAML::Syck\*(C'\fR, \f(CW\*(C`YAML.pm\*(C'\fR and \f(CW\*(C`Expect.pm\*(C'\fR. If you prefer Storable over Data::Dumper, remember to pull out a Storable version that writes an older format than all the other Storable versions that will need to read them. .SS "Blueprint" .IX Subsection "Blueprint" The following example contains all supported keywords and structures with the exception of \f(CW\*(C`eexpect\*(C'\fR which can be used instead of \&\f(CW\*(C`expect\*(C'\fR. .PP .Vb 10 \& \-\-\- \& comment: "Demo" \& match: \& module: "Dancing::Queen" \& distribution: "^CHACHACHA/Dancing\-" \& not_distribution: "\e.zip$" \& perl: "/usr/local/cariba\-perl/bin/perl" \& perlconfig: \& archname: "freebsd" \& not_cc: "gcc" \& env: \& DANCING_FLOOR: "Shubiduh" \& disabled: 1 \& cpanconfig: \& make: gmake \& pl: \& args: \& \- "\-\-somearg=specialcase" \& \& env: {} \& \& expect: \& \- "Which is your favorite fruit" \& \- "apple\en" \& \& make: \& args: \& \- all \& \- extra\-all \& \& env: {} \& \& expect: [] \& \& commandline: "echo SKIPPING make" \& \& test: \& args: [] \& \& env: {} \& \& expect: [] \& \& install: \& args: [] \& \& env: \& WANT_TO_INSTALL: YES \& \& expect: \& \- "Do you really want to install" \& \- "y\en" \& \& patches: \& \- "ABCDE/Fedcba\-3.14\-ABCDE\-01.patch" \& \& depends: \& configure_requires: \& LWP: 5.8 \& build_requires: \& Test::Exception: 0.25 \& requires: \& Spiffy: 0.30 .Ve .SS "Language Specs" .IX Subsection "Language Specs" Every \s-1YAML\s0 document represents a single hash reference. The valid keys in this hash are as follows: .IP "comment [scalar]" 4 .IX Item "comment [scalar]" A comment .IP "cpanconfig [hash]" 4 .IX Item "cpanconfig [hash]" Temporarily override assorted \f(CW\*(C`CPAN.pm\*(C'\fR configuration variables. .Sp Supported are: \f(CW\*(C`build_requires_install_policy\*(C'\fR, \f(CW\*(C`check_sigs\*(C'\fR, \&\f(CW\*(C`make\*(C'\fR, \f(CW\*(C`make_install_make_command\*(C'\fR, \f(CW\*(C`prefer_installer\*(C'\fR, \&\f(CW\*(C`test_report\*(C'\fR. Please report as a bug when you need another one supported. .IP "depends [hash] *** \s-1EXPERIMENTAL FEATURE\s0 ***" 4 .IX Item "depends [hash] *** EXPERIMENTAL FEATURE ***" All three types, namely \f(CW\*(C`configure_requires\*(C'\fR, \f(CW\*(C`build_requires\*(C'\fR, and \&\f(CW\*(C`requires\*(C'\fR are supported in the way specified in the \s-1META\s0.yml specification. The current implementation \fImerges\fR the specified dependencies with those declared by the package maintainer. In a future implementation this may be changed to override the original declaration. .IP "disabled [boolean]" 4 .IX Item "disabled [boolean]" Specifies that this distribution shall not be processed at all. .IP "features [array] *** \s-1EXPERIMENTAL FEATURE\s0 ***" 4 .IX Item "features [array] *** EXPERIMENTAL FEATURE ***" Experimental implementation to deal with optional_features from \&\s-1META\s0.yml. Still needs coordination with installer software and currently works only for \s-1META\s0.yml declaring \f(CW\*(C`dynamic_config=0\*(C'\fR. Use with caution. .IP "goto [string]" 4 .IX Item "goto [string]" The canonical name of a delegate distribution to install instead. Useful when a new version, although it tests \s-1OK\s0 itself, breaks something else or a developer release or a fork is already uploaded that is better than the last released version. .IP "install [hash]" 4 .IX Item "install [hash]" Processing instructions for the \f(CW\*(C`make install\*(C'\fR or \f(CW\*(C`./Build install\*(C'\fR phase of the \s-1CPAN\s0 mantra. See below under \fIProcessing Instructions\fR. .IP "make [hash]" 4 .IX Item "make [hash]" Processing instructions for the \f(CW\*(C`make\*(C'\fR or \f(CW\*(C`./Build\*(C'\fR phase of the \&\s-1CPAN\s0 mantra. See below under \fIProcessing Instructions\fR. .IP "match [hash]" 4 .IX Item "match [hash]" A hashref with one or more of the keys \f(CW\*(C`distribution\*(C'\fR, \f(CW\*(C`module\*(C'\fR, \&\f(CW\*(C`perl\*(C'\fR, \f(CW\*(C`perlconfig\*(C'\fR, and \f(CW\*(C`env\*(C'\fR that specify whether a document is targeted at a specific \s-1CPAN\s0 distribution or installation. Keys prefixed with \f(CW\*(C`not_\*(C'\fR negates the corresponding match. .Sp The corresponding values are interpreted as regular expressions. The \&\f(CW\*(C`distribution\*(C'\fR related one will be matched against the canonical distribution name, e.g. \*(L"AUTHOR/Foo\-Bar\-3.14.tar.gz\*(R". .Sp The \f(CW\*(C`module\*(C'\fR related one will be matched against \fIall\fR modules contained in the distribution until one module matches. .Sp The \f(CW\*(C`perl\*(C'\fR related one will be matched against \f(CW$^X\fR (but with the absolute path). .Sp The value associated with \f(CW\*(C`perlconfig\*(C'\fR is itself a hashref that is matched against corresponding values in the \f(CW%Config::Config\fR hash living in the \f(CW\*(C`Config.pm\*(C'\fR module. Keys prefixed with \f(CW\*(C`not_\*(C'\fR negates the corresponding match. .Sp The value associated with \f(CW\*(C`env\*(C'\fR is itself a hashref that is matched against corresponding values in the \f(CW%ENV\fR hash. Keys prefixed with \f(CW\*(C`not_\*(C'\fR negates the corresponding match. .Sp If more than one restriction of \f(CW\*(C`module\*(C'\fR, \f(CW\*(C`distribution\*(C'\fR, etc. is specified, the results of the separately computed match values must all match. If so, the hashref represented by the \&\s-1YAML\s0 document is returned as the preference structure for the current distribution. .IP "patches [array]" 4 .IX Item "patches [array]" An array of patches on \s-1CPAN\s0 or on the local disk to be applied in order via an external patch program. If the value for the \f(CW\*(C`\-p\*(C'\fR parameter is \f(CW0\fR or \f(CW1\fR is determined by reading the patch beforehand. The path to each patch is either an absolute path on the local filesystem or relative to a patch directory specified in the \&\f(CW\*(C`patches_dir\*(C'\fR configuration variable or in the format of a canonical distro name. For examples please consult the distroprefs/ directory in the \s-1CPAN\s0.pm distribution (these examples are not installed by default). .Sp Note: if the \f(CW\*(C`applypatch\*(C'\fR program is installed and \f(CW\*(C`CPAN::Config\*(C'\fR knows about it \fBand\fR a patch is written by the \f(CW\*(C`makepatch\*(C'\fR program, then \f(CW\*(C`CPAN.pm\*(C'\fR lets \f(CW\*(C`applypatch\*(C'\fR apply the patch. Both \f(CW\*(C`makepatch\*(C'\fR and \f(CW\*(C`applypatch\*(C'\fR are available from \s-1CPAN\s0 in the \f(CW\*(C`JV/makepatch\-*\*(C'\fR distribution. .IP "pl [hash]" 4 .IX Item "pl [hash]" Processing instructions for the \f(CW\*(C`perl Makefile.PL\*(C'\fR or \f(CW\*(C`perl Build.PL\*(C'\fR phase of the \s-1CPAN\s0 mantra. See below under \fIProcessing Instructions\fR. .IP "test [hash]" 4 .IX Item "test [hash]" Processing instructions for the \f(CW\*(C`make test\*(C'\fR or \f(CW\*(C`./Build test\*(C'\fR phase of the \s-1CPAN\s0 mantra. See below under \fIProcessing Instructions\fR. .SS "Processing Instructions" .IX Subsection "Processing Instructions" .IP "args [array]" 4 .IX Item "args [array]" Arguments to be added to the command line .IP "commandline" 4 .IX Item "commandline" A full commandline to run via \f(CW\*(C`system()\*(C'\fR. During execution, the environment variable \s-1PERL\s0 is set to $^X (but with an absolute path). If \f(CW\*(C`commandline\*(C'\fR is specified, \&\f(CW\*(C`args\*(C'\fR is not used. .IP "eexpect [hash]" 4 .IX Item "eexpect [hash]" Extended \f(CW\*(C`expect\*(C'\fR. This is a hash reference with four allowed keys, \&\f(CW\*(C`mode\*(C'\fR, \f(CW\*(C`timeout\*(C'\fR, \f(CW\*(C`reuse\*(C'\fR, and \f(CW\*(C`talk\*(C'\fR. .Sp You must install the \f(CW\*(C`Expect\*(C'\fR module to use \f(CW\*(C`eexpect\*(C'\fR. \s-1CPAN\s0.pm does not install it for you. .Sp \&\f(CW\*(C`mode\*(C'\fR may have the values \f(CW\*(C`deterministic\*(C'\fR for the case where all questions come in the order written down and \f(CW\*(C`anyorder\*(C'\fR for the case where the questions may come in any order. The default mode is \&\f(CW\*(C`deterministic\*(C'\fR. .Sp \&\f(CW\*(C`timeout\*(C'\fR denotes a timeout in seconds. Floating-point timeouts are \&\s-1OK.\s0 With \f(CW\*(C`mode=deterministic\*(C'\fR, the timeout denotes the timeout per question; with \f(CW\*(C`mode=anyorder\*(C'\fR it denotes the timeout per byte received from the stream or questions. .Sp \&\f(CW\*(C`talk\*(C'\fR is a reference to an array that contains alternating questions and answers. Questions are regular expressions and answers are literal strings. The Expect module watches the stream from the execution of the external program (\f(CW\*(C`perl Makefile.PL\*(C'\fR, \f(CW\*(C`perl Build.PL\*(C'\fR, \f(CW\*(C`make\*(C'\fR, etc.). .Sp For \f(CW\*(C`mode=deterministic\*(C'\fR, the \s-1CPAN\s0.pm injects the corresponding answer as soon as the stream matches the regular expression. .Sp For \f(CW\*(C`mode=anyorder\*(C'\fR \s-1CPAN\s0.pm answers a question as soon as the timeout is reached for the next byte in the input stream. In this mode you can use the \f(CW\*(C`reuse\*(C'\fR parameter to decide what will happen with a question-answer pair after it has been used. In the default case (reuse=0) it is removed from the array, avoiding being used again accidentally. If you want to answer the question \f(CW\*(C`Do you really want to do that\*(C'\fR several times, then it must be included in the array at least as often as you want this answer to be given. Setting the parameter \f(CW\*(C`reuse\*(C'\fR to 1 makes this repetition unnecessary. .IP "env [hash]" 4 .IX Item "env [hash]" Environment variables to be set during the command .IP "expect [array]" 4 .IX Item "expect [array]" You must install the \f(CW\*(C`Expect\*(C'\fR module to use \f(CW\*(C`expect\*(C'\fR. \s-1CPAN\s0.pm does not install it for you. .Sp \&\f(CW\*(C`expect: <array>\*(C'\fR is a short notation for this \f(CW\*(C`eexpect\*(C'\fR: .Sp .Vb 4 \& eexpect: \& mode: deterministic \& timeout: 15 \& talk: <array> .Ve .ie n .SS "Schema verification with ""Kwalify""" .el .SS "Schema verification with \f(CWKwalify\fP" .IX Subsection "Schema verification with Kwalify" If you have the \f(CW\*(C`Kwalify\*(C'\fR module installed (which is part of the Bundle::CPANxxl), then all your distroprefs files are checked for syntactic correctness. .SS "Example Distroprefs Files" .IX Subsection "Example Distroprefs Files" \&\f(CW\*(C`CPAN.pm\*(C'\fR comes with a collection of example \s-1YAML\s0 files. Note that these are really just examples and should not be used without care because they cannot fit everybody's purpose. After all, the authors of the packages that ask questions had a need to ask, so you should watch their questions and adjust the examples to your environment and your needs. You have been warned:\-) .SH "PROGRAMMER'S INTERFACE" .IX Header "PROGRAMMER'S INTERFACE" If you do not enter the shell, shell commands are available both as methods (\f(CW\*(C`CPAN::Shell\->install(...)\*(C'\fR) and as functions in the calling package (\f(CW\*(C`install(...)\*(C'\fR). Before calling low-level commands, it makes sense to initialize components of \s-1CPAN\s0 you need, e.g.: .PP .Vb 3 \& CPAN::HandleConfig\->load; \& CPAN::Shell::setup_output; \& CPAN::Index\->reload; .Ve .PP High-level commands do such initializations automatically. .PP There's currently only one class that has a stable interface \- CPAN::Shell. All commands that are available in the \s-1CPAN\s0 shell are methods of the class CPAN::Shell. The arguments on the commandline are passed as arguments to the method. .PP So if you take for example the shell command .PP .Vb 1 \& notest install A B C .Ve .PP the actually executed command is .PP .Vb 1 \& CPAN::Shell\->notest("install","A","B","C"); .Ve .PP Each of the commands that produce listings of modules (\f(CW\*(C`r\*(C'\fR, \&\f(CW\*(C`autobundle\*(C'\fR, \f(CW\*(C`u\*(C'\fR) also return a list of the IDs of all modules within the list. .IP "expand($type,@things)" 2 .IX Item "expand($type,@things)" The IDs of all objects available within a program are strings that can be expanded to the corresponding real objects with the \&\f(CW\*(C`CPAN::Shell\->expand("Module",@things)\*(C'\fR method. Expand returns a list of CPAN::Module objects according to the \f(CW@things\fR arguments given. In scalar context, it returns only the first element of the list. .IP "expandany(@things)" 2 .IX Item "expandany(@things)" Like expand, but returns objects of the appropriate type, i.e. CPAN::Bundle objects for bundles, CPAN::Module objects for modules, and CPAN::Distribution objects for distributions. Note: it does not expand to CPAN::Author objects. .IP "Programming Examples" 2 .IX Item "Programming Examples" This enables the programmer to do operations that combine functionalities that are available in the shell. .Sp .Vb 2 \& # install everything that is outdated on my disk: \& perl \-MCPAN \-e \*(AqCPAN::Shell\->install(CPAN::Shell\->r)\*(Aq \& \& # install my favorite programs if necessary: \& for $mod (qw(Net::FTP Digest::SHA Data::Dumper)) { \& CPAN::Shell\->install($mod); \& } \& \& # list all modules on my disk that have no VERSION number \& for $mod (CPAN::Shell\->expand("Module","/./")) { \& next unless $mod\->inst_file; \& # MakeMaker convention for undefined $VERSION: \& next unless $mod\->inst_version eq "undef"; \& print "No VERSION in ", $mod\->id, "\en"; \& } \& \& # find out which distribution on CPAN contains a module: \& print CPAN::Shell\->expand("Module","Apache::Constants")\->cpan_file .Ve .Sp Or if you want to schedule a \fIcron\fR job to watch \s-1CPAN,\s0 you could list all modules that need updating. First a quick and dirty way: .Sp .Vb 1 \& perl \-e \*(Aquse CPAN; CPAN::Shell\->r;\*(Aq .Ve .Sp If you don't want any output should all modules be up to date, parse the output of above command for the regular expression \f(CW\*(C`/modules are up to date/\*(C'\fR and decide to mail the output only if it doesn't match. .Sp If you prefer to do it more in a programmerish style in one single process, something like this may better suit you: .Sp .Vb 7 \& # list all modules on my disk that have newer versions on CPAN \& for $mod (CPAN::Shell\->expand("Module","/./")) { \& next unless $mod\->inst_file; \& next if $mod\->uptodate; \& printf "Module %s is installed as %s, could be updated to %s from CPAN\en", \& $mod\->id, $mod\->inst_version, $mod\->cpan_version; \& } .Ve .Sp If that gives too much output every day, you may want to watch only for three modules. You can write .Sp .Vb 1 \& for $mod (CPAN::Shell\->expand("Module","/Apache|LWP|CGI/")) { .Ve .Sp as the first line instead. Or you can combine some of the above tricks: .Sp .Vb 5 \& # watch only for a new mod_perl module \& $mod = CPAN::Shell\->expand("Module","mod_perl"); \& exit if $mod\->uptodate; \& # new mod_perl arrived, let me know all update recommendations \& CPAN::Shell\->r; .Ve .SS "Methods in the other Classes" .IX Subsection "Methods in the other Classes" .IP "\fBCPAN::Author::as_glimpse()\fR" 4 .IX Item "CPAN::Author::as_glimpse()" Returns a one-line description of the author .IP "\fBCPAN::Author::as_string()\fR" 4 .IX Item "CPAN::Author::as_string()" Returns a multi-line description of the author .IP "\fBCPAN::Author::email()\fR" 4 .IX Item "CPAN::Author::email()" Returns the author's email address .IP "\fBCPAN::Author::fullname()\fR" 4 .IX Item "CPAN::Author::fullname()" Returns the author's name .IP "\fBCPAN::Author::name()\fR" 4 .IX Item "CPAN::Author::name()" An alias for fullname .IP "\fBCPAN::Bundle::as_glimpse()\fR" 4 .IX Item "CPAN::Bundle::as_glimpse()" Returns a one-line description of the bundle .IP "\fBCPAN::Bundle::as_string()\fR" 4 .IX Item "CPAN::Bundle::as_string()" Returns a multi-line description of the bundle .IP "\fBCPAN::Bundle::clean()\fR" 4 .IX Item "CPAN::Bundle::clean()" Recursively runs the \f(CW\*(C`clean\*(C'\fR method on all items contained in the bundle. .IP "\fBCPAN::Bundle::contains()\fR" 4 .IX Item "CPAN::Bundle::contains()" Returns a list of objects' IDs contained in a bundle. The associated objects may be bundles, modules or distributions. .IP "CPAN::Bundle::force($method,@args)" 4 .IX Item "CPAN::Bundle::force($method,@args)" Forces \s-1CPAN\s0 to perform a task that it normally would have refused to do. Force takes as arguments a method name to be called and any number of additional arguments that should be passed to the called method. The internals of the object get the needed changes so that \s-1CPAN\s0.pm does not refuse to take the action. The \f(CW\*(C`force\*(C'\fR is passed recursively to all contained objects. See also the section above on the \f(CW\*(C`force\*(C'\fR and the \f(CW\*(C`fforce\*(C'\fR pragma. .IP "\fBCPAN::Bundle::get()\fR" 4 .IX Item "CPAN::Bundle::get()" Recursively runs the \f(CW\*(C`get\*(C'\fR method on all items contained in the bundle .IP "\fBCPAN::Bundle::inst_file()\fR" 4 .IX Item "CPAN::Bundle::inst_file()" Returns the highest installed version of the bundle in either \f(CW@INC\fR or \&\f(CW\*(C`$CPAN::Config\->{cpan_home}\*(C'\fR. Note that this is different from CPAN::Module::inst_file. .IP "\fBCPAN::Bundle::inst_version()\fR" 4 .IX Item "CPAN::Bundle::inst_version()" Like CPAN::Bundle::inst_file, but returns the \f(CW$VERSION\fR .IP "\fBCPAN::Bundle::uptodate()\fR" 4 .IX Item "CPAN::Bundle::uptodate()" Returns 1 if the bundle itself and all its members are up-to-date. .IP "\fBCPAN::Bundle::install()\fR" 4 .IX Item "CPAN::Bundle::install()" Recursively runs the \f(CW\*(C`install\*(C'\fR method on all items contained in the bundle .IP "\fBCPAN::Bundle::make()\fR" 4 .IX Item "CPAN::Bundle::make()" Recursively runs the \f(CW\*(C`make\*(C'\fR method on all items contained in the bundle .IP "\fBCPAN::Bundle::readme()\fR" 4 .IX Item "CPAN::Bundle::readme()" Recursively runs the \f(CW\*(C`readme\*(C'\fR method on all items contained in the bundle .IP "\fBCPAN::Bundle::test()\fR" 4 .IX Item "CPAN::Bundle::test()" Recursively runs the \f(CW\*(C`test\*(C'\fR method on all items contained in the bundle .IP "\fBCPAN::Distribution::as_glimpse()\fR" 4 .IX Item "CPAN::Distribution::as_glimpse()" Returns a one-line description of the distribution .IP "\fBCPAN::Distribution::as_string()\fR" 4 .IX Item "CPAN::Distribution::as_string()" Returns a multi-line description of the distribution .IP "CPAN::Distribution::author" 4 .IX Item "CPAN::Distribution::author" Returns the CPAN::Author object of the maintainer who uploaded this distribution .IP "\fBCPAN::Distribution::pretty_id()\fR" 4 .IX Item "CPAN::Distribution::pretty_id()" Returns a string of the form \*(L"\s-1AUTHORID/TARBALL\*(R",\s0 where \s-1AUTHORID\s0 is the author's \s-1PAUSE ID\s0 and \s-1TARBALL\s0 is the distribution filename. .IP "\fBCPAN::Distribution::base_id()\fR" 4 .IX Item "CPAN::Distribution::base_id()" Returns the distribution filename without any archive suffix. E.g \&\*(L"Foo\-Bar\-0.01\*(R" .IP "\fBCPAN::Distribution::clean()\fR" 4 .IX Item "CPAN::Distribution::clean()" Changes to the directory where the distribution has been unpacked and runs \f(CW\*(C`make clean\*(C'\fR there. .IP "\fBCPAN::Distribution::containsmods()\fR" 4 .IX Item "CPAN::Distribution::containsmods()" Returns a list of IDs of modules contained in a distribution file. Works only for distributions listed in the 02packages.details.txt.gz file. This typically means that just most recent version of a distribution is covered. .IP "\fBCPAN::Distribution::cvs_import()\fR" 4 .IX Item "CPAN::Distribution::cvs_import()" Changes to the directory where the distribution has been unpacked and runs something like .Sp .Vb 1 \& cvs \-d $cvs_root import \-m $cvs_log $cvs_dir $userid v$version .Ve .Sp there. .IP "\fBCPAN::Distribution::dir()\fR" 4 .IX Item "CPAN::Distribution::dir()" Returns the directory into which this distribution has been unpacked. .IP "CPAN::Distribution::force($method,@args)" 4 .IX Item "CPAN::Distribution::force($method,@args)" Forces \s-1CPAN\s0 to perform a task that it normally would have refused to do. Force takes as arguments a method name to be called and any number of additional arguments that should be passed to the called method. The internals of the object get the needed changes so that \s-1CPAN\s0.pm does not refuse to take the action. See also the section above on the \&\f(CW\*(C`force\*(C'\fR and the \f(CW\*(C`fforce\*(C'\fR pragma. .IP "\fBCPAN::Distribution::get()\fR" 4 .IX Item "CPAN::Distribution::get()" Downloads the distribution from \s-1CPAN\s0 and unpacks it. Does nothing if the distribution has already been downloaded and unpacked within the current session. .IP "\fBCPAN::Distribution::install()\fR" 4 .IX Item "CPAN::Distribution::install()" Changes to the directory where the distribution has been unpacked and runs the external command \f(CW\*(C`make install\*(C'\fR there. If \f(CW\*(C`make\*(C'\fR has not yet been run, it will be run first. A \f(CW\*(C`make test\*(C'\fR is issued in any case and if this fails, the install is cancelled. The cancellation can be avoided by letting \f(CW\*(C`force\*(C'\fR run the \f(CW\*(C`install\*(C'\fR for you. .Sp This install method only has the power to install the distribution if there are no dependencies in the way. To install an object along with all its dependencies, use CPAN::Shell\->install. .Sp Note that \fBinstall()\fR gives no meaningful return value. See \fBuptodate()\fR. .IP "\fBCPAN::Distribution::isa_perl()\fR" 4 .IX Item "CPAN::Distribution::isa_perl()" Returns 1 if this distribution file seems to be a perl distribution. Normally this is derived from the file name only, but the index from \&\s-1CPAN\s0 can contain a hint to achieve a return value of true for other filenames too. .IP "\fBCPAN::Distribution::look()\fR" 4 .IX Item "CPAN::Distribution::look()" Changes to the directory where the distribution has been unpacked and opens a subshell there. Exiting the subshell returns. .IP "\fBCPAN::Distribution::make()\fR" 4 .IX Item "CPAN::Distribution::make()" First runs the \f(CW\*(C`get\*(C'\fR method to make sure the distribution is downloaded and unpacked. Changes to the directory where the distribution has been unpacked and runs the external commands \f(CW\*(C`perl Makefile.PL\*(C'\fR or \f(CW\*(C`perl Build.PL\*(C'\fR and \f(CW\*(C`make\*(C'\fR there. .IP "\fBCPAN::Distribution::perldoc()\fR" 4 .IX Item "CPAN::Distribution::perldoc()" Downloads the pod documentation of the file associated with a distribution (in \s-1HTML\s0 format) and runs it through the external command \fIlynx\fR specified in \f(CW\*(C`$CPAN::Config\->{lynx}\*(C'\fR. If \fIlynx\fR isn't available, it converts it to plain text with the external command \fIhtml2text\fR and runs it through the pager specified in \f(CW\*(C`$CPAN::Config\->{pager}\*(C'\fR. .IP "\fBCPAN::Distribution::prefs()\fR" 4 .IX Item "CPAN::Distribution::prefs()" Returns the hash reference from the first matching \s-1YAML\s0 file that the user has deposited in the \f(CW\*(C`prefs_dir/\*(C'\fR directory. The first succeeding match wins. The files in the \f(CW\*(C`prefs_dir/\*(C'\fR are processed alphabetically, and the canonical distro name (e.g. AUTHOR/Foo\-Bar\-3.14.tar.gz) is matched against the regular expressions stored in the \f(CW$root\fR\->{match}{distribution} attribute value. Additionally all module names contained in a distribution are matched against the regular expressions in the \f(CW$root\fR\->{match}{module} attribute value. The two match values are ANDed together. Each of the two attributes are optional. .IP "\fBCPAN::Distribution::prereq_pm()\fR" 4 .IX Item "CPAN::Distribution::prereq_pm()" Returns the hash reference that has been announced by a distribution as the \f(CW\*(C`requires\*(C'\fR and \f(CW\*(C`build_requires\*(C'\fR elements. These can be declared either by the \f(CW\*(C`META.yml\*(C'\fR (if authoritative) or can be deposited after the run of \f(CW\*(C`Build.PL\*(C'\fR in the file \f(CW\*(C`./_build/prereqs\*(C'\fR or after the run of \f(CW\*(C`Makfile.PL\*(C'\fR written as the \f(CW\*(C`PREREQ_PM\*(C'\fR hash in a comment in the produced \f(CW\*(C`Makefile\*(C'\fR. \fINote\fR: this method only works after an attempt has been made to \f(CW\*(C`make\*(C'\fR the distribution. Returns undef otherwise. .IP "\fBCPAN::Distribution::readme()\fR" 4 .IX Item "CPAN::Distribution::readme()" Downloads the \s-1README\s0 file associated with a distribution and runs it through the pager specified in \f(CW\*(C`$CPAN::Config\->{pager}\*(C'\fR. .IP "\fBCPAN::Distribution::reports()\fR" 4 .IX Item "CPAN::Distribution::reports()" Downloads report data for this distribution from www.cpantesters.org and displays a subset of them. .IP "\fBCPAN::Distribution::read_yaml()\fR" 4 .IX Item "CPAN::Distribution::read_yaml()" Returns the content of the \s-1META\s0.yml of this distro as a hashref. Note: works only after an attempt has been made to \f(CW\*(C`make\*(C'\fR the distribution. Returns undef otherwise. Also returns undef if the content of \s-1META\s0.yml is not authoritative. (The rules about what exactly makes the content authoritative are still in flux.) .IP "\fBCPAN::Distribution::test()\fR" 4 .IX Item "CPAN::Distribution::test()" Changes to the directory where the distribution has been unpacked and runs \f(CW\*(C`make test\*(C'\fR there. .IP "\fBCPAN::Distribution::uptodate()\fR" 4 .IX Item "CPAN::Distribution::uptodate()" Returns 1 if all the modules contained in the distribution are up-to-date. Relies on containsmods. .IP "\fBCPAN::Index::force_reload()\fR" 4 .IX Item "CPAN::Index::force_reload()" Forces a reload of all indices. .IP "\fBCPAN::Index::reload()\fR" 4 .IX Item "CPAN::Index::reload()" Reloads all indices if they have not been read for more than \&\f(CW\*(C`$CPAN::Config\->{index_expire}\*(C'\fR days. .IP "\fBCPAN::InfoObj::dump()\fR" 4 .IX Item "CPAN::InfoObj::dump()" CPAN::Author, CPAN::Bundle, CPAN::Module, and CPAN::Distribution inherit this method. It prints the data structure associated with an object. Useful for debugging. Note: the data structure is considered internal and thus subject to change without notice. .IP "\fBCPAN::Module::as_glimpse()\fR" 4 .IX Item "CPAN::Module::as_glimpse()" Returns a one-line description of the module in four columns: The first column contains the word \f(CW\*(C`Module\*(C'\fR, the second column consists of one character: an equals sign if this module is already installed and up-to-date, a less-than sign if this module is installed but can be upgraded, and a space if the module is not installed. The third column is the name of the module and the fourth column gives maintainer or distribution information. .IP "\fBCPAN::Module::as_string()\fR" 4 .IX Item "CPAN::Module::as_string()" Returns a multi-line description of the module .IP "\fBCPAN::Module::clean()\fR" 4 .IX Item "CPAN::Module::clean()" Runs a clean on the distribution associated with this module. .IP "\fBCPAN::Module::cpan_file()\fR" 4 .IX Item "CPAN::Module::cpan_file()" Returns the filename on \s-1CPAN\s0 that is associated with the module. .IP "\fBCPAN::Module::cpan_version()\fR" 4 .IX Item "CPAN::Module::cpan_version()" Returns the latest version of this module available on \s-1CPAN.\s0 .IP "\fBCPAN::Module::cvs_import()\fR" 4 .IX Item "CPAN::Module::cvs_import()" Runs a cvs_import on the distribution associated with this module. .IP "\fBCPAN::Module::description()\fR" 4 .IX Item "CPAN::Module::description()" Returns a 44 character description of this module. Only available for modules listed in The Module List (CPAN/modules/00modlist.long.html or 00modlist.long.txt.gz) .IP "\fBCPAN::Module::distribution()\fR" 4 .IX Item "CPAN::Module::distribution()" Returns the CPAN::Distribution object that contains the current version of this module. .IP "\fBCPAN::Module::dslip_status()\fR" 4 .IX Item "CPAN::Module::dslip_status()" Returns a hash reference. The keys of the hash are the letters \f(CW\*(C`D\*(C'\fR, \&\f(CW\*(C`S\*(C'\fR, \f(CW\*(C`L\*(C'\fR, \f(CW\*(C`I\*(C'\fR, and <P>, for development status, support level, language, interface and public licence respectively. The data for the \&\s-1DSLIP\s0 status are collected by pause.perl.org when authors register their namespaces. The values of the 5 hash elements are one-character words whose meaning is described in the table below. There are also 5 hash elements \f(CW\*(C`DV\*(C'\fR, \f(CW\*(C`SV\*(C'\fR, \f(CW\*(C`LV\*(C'\fR, \f(CW\*(C`IV\*(C'\fR, and <\s-1PV\s0> that carry a more verbose value of the 5 status variables. .Sp Where the '\s-1DSLIP\s0' characters have the following meanings: .Sp .Vb 7 \& D \- Development Stage (Note: *NO IMPLIED TIMESCALES*): \& i \- Idea, listed to gain consensus or as a placeholder \& c \- under construction but pre\-alpha (not yet released) \& a/b \- Alpha/Beta testing \& R \- Released \& M \- Mature (no rigorous definition) \& S \- Standard, supplied with Perl 5 \& \& S \- Support Level: \& m \- Mailing\-list \& d \- Developer \& u \- Usenet newsgroup comp.lang.perl.modules \& n \- None known, try comp.lang.perl.modules \& a \- abandoned; volunteers welcome to take over maintenance \& \& L \- Language Used: \& p \- Perl\-only, no compiler needed, should be platform independent \& c \- C and perl, a C compiler will be needed \& h \- Hybrid, written in perl with optional C code, no compiler needed \& + \- C++ and perl, a C++ compiler will be needed \& o \- perl and another language other than C or C++ \& \& I \- Interface Style \& f \- plain Functions, no references used \& h \- hybrid, object and function interfaces available \& n \- no interface at all (huh?) \& r \- some use of unblessed References or ties \& O \- Object oriented using blessed references and/or inheritance \& \& P \- Public License \& p \- Standard\-Perl: user may choose between GPL and Artistic \& g \- GPL: GNU General Public License \& l \- LGPL: "GNU Lesser General Public License" (previously known as \& "GNU Library General Public License") \& b \- BSD: The BSD License \& a \- Artistic license alone \& 2 \- Artistic license 2.0 or later \& o \- open source: approved by www.opensource.org \& d \- allows distribution without restrictions \& r \- restricted distribution \& n \- no license at all .Ve .IP "CPAN::Module::force($method,@args)" 4 .IX Item "CPAN::Module::force($method,@args)" Forces \s-1CPAN\s0 to perform a task it would normally refuse to do. Force takes as arguments a method name to be invoked and any number of additional arguments to pass that method. The internals of the object get the needed changes so that \s-1CPAN\s0.pm does not refuse to take the action. See also the section above on the \&\f(CW\*(C`force\*(C'\fR and the \f(CW\*(C`fforce\*(C'\fR pragma. .IP "\fBCPAN::Module::get()\fR" 4 .IX Item "CPAN::Module::get()" Runs a get on the distribution associated with this module. .IP "\fBCPAN::Module::inst_file()\fR" 4 .IX Item "CPAN::Module::inst_file()" Returns the filename of the module found in \f(CW@INC\fR. The first file found is reported, just as perl itself stops searching \f(CW@INC\fR once it finds a module. .IP "\fBCPAN::Module::available_file()\fR" 4 .IX Item "CPAN::Module::available_file()" Returns the filename of the module found in \s-1PERL5LIB\s0 or \f(CW@INC\fR. The first file found is reported. The advantage of this method over \&\f(CW\*(C`inst_file\*(C'\fR is that modules that have been tested but not yet installed are included because \s-1PERL5LIB\s0 keeps track of tested modules. .IP "\fBCPAN::Module::inst_version()\fR" 4 .IX Item "CPAN::Module::inst_version()" Returns the version number of the installed module in readable format. .IP "\fBCPAN::Module::available_version()\fR" 4 .IX Item "CPAN::Module::available_version()" Returns the version number of the available module in readable format. .IP "\fBCPAN::Module::install()\fR" 4 .IX Item "CPAN::Module::install()" Runs an \f(CW\*(C`install\*(C'\fR on the distribution associated with this module. .IP "\fBCPAN::Module::look()\fR" 4 .IX Item "CPAN::Module::look()" Changes to the directory where the distribution associated with this module has been unpacked and opens a subshell there. Exiting the subshell returns. .IP "\fBCPAN::Module::make()\fR" 4 .IX Item "CPAN::Module::make()" Runs a \f(CW\*(C`make\*(C'\fR on the distribution associated with this module. .IP "\fBCPAN::Module::manpage_headline()\fR" 4 .IX Item "CPAN::Module::manpage_headline()" If module is installed, peeks into the module's manpage, reads the headline, and returns it. Moreover, if the module has been downloaded within this session, does the equivalent on the downloaded module even if it hasn't been installed yet. .IP "\fBCPAN::Module::perldoc()\fR" 4 .IX Item "CPAN::Module::perldoc()" Runs a \f(CW\*(C`perldoc\*(C'\fR on this module. .IP "\fBCPAN::Module::readme()\fR" 4 .IX Item "CPAN::Module::readme()" Runs a \f(CW\*(C`readme\*(C'\fR on the distribution associated with this module. .IP "\fBCPAN::Module::reports()\fR" 4 .IX Item "CPAN::Module::reports()" Calls the \fBreports()\fR method on the associated distribution object. .IP "\fBCPAN::Module::test()\fR" 4 .IX Item "CPAN::Module::test()" Runs a \f(CW\*(C`test\*(C'\fR on the distribution associated with this module. .IP "\fBCPAN::Module::uptodate()\fR" 4 .IX Item "CPAN::Module::uptodate()" Returns 1 if the module is installed and up-to-date. .IP "\fBCPAN::Module::userid()\fR" 4 .IX Item "CPAN::Module::userid()" Returns the author's \s-1ID\s0 of the module. .SS "Cache Manager" .IX Subsection "Cache Manager" Currently the cache manager only keeps track of the build directory ($CPAN::Config\->{build_dir}). It is a simple \s-1FIFO\s0 mechanism that deletes complete directories below \f(CW\*(C`build_dir\*(C'\fR as soon as the size of all directories there gets bigger than \f(CW$CPAN::Config\fR\->{build_cache} (in \s-1MB\s0). The contents of this cache may be used for later re-installations that you intend to do manually, but will never be trusted by \s-1CPAN\s0 itself. This is due to the fact that the user might use these directories for building modules on different architectures. .PP There is another directory ($CPAN::Config\->{keep_source_where}) where the original distribution files are kept. This directory is not covered by the cache manager and must be controlled by the user. If you choose to have the same directory as build_dir and as keep_source_where directory, then your sources will be deleted with the same fifo mechanism. .SS "Bundles" .IX Subsection "Bundles" A bundle is just a perl module in the namespace Bundle:: that does not define any functions or methods. It usually only contains documentation. .PP It starts like a perl module with a package declaration and a \f(CW$VERSION\fR variable. After that the pod section looks like any other pod with the only difference being that \fIone special pod section\fR exists starting with (verbatim): .PP .Vb 1 \& =head1 CONTENTS .Ve .PP In this pod section each line obeys the format .PP .Vb 1 \& Module_Name [Version_String] [\- optional text] .Ve .PP The only required part is the first field, the name of a module (e.g. Foo::Bar, i.e. \fInot\fR the name of the distribution file). The rest of the line is optional. The comment part is delimited by a dash just as in the man page header. .PP The distribution of a bundle should follow the same convention as other distributions. .PP Bundles are treated specially in the \s-1CPAN\s0 package. If you say 'install Bundle::Tkkit' (assuming such a bundle exists), \s-1CPAN\s0 will install all the modules in the \s-1CONTENTS\s0 section of the pod. You can install your own Bundles locally by placing a conformant Bundle file somewhere into your \f(CW@INC\fR path. The \fBautobundle()\fR command which is available in the shell interface does that for you by including all currently installed modules in a snapshot bundle file. .SH "PREREQUISITES" .IX Header "PREREQUISITES" The \s-1CPAN\s0 program is trying to depend on as little as possible so the user can use it in hostile environment. It works better the more goodies the environment provides. For example if you try in the \s-1CPAN\s0 shell .PP .Vb 1 \& install Bundle::CPAN .Ve .PP or .PP .Vb 1 \& install Bundle::CPANxxl .Ve .PP you will find the shell more convenient than the bare shell before. .PP If you have a local mirror of \s-1CPAN\s0 and can access all files with \&\*(L"file:\*(R" URLs, then you only need a perl later than perl5.003 to run this module. Otherwise Net::FTP is strongly recommended. \s-1LWP\s0 may be required for non-UNIX systems, or if your nearest \s-1CPAN\s0 site is associated with a \s-1URL\s0 that is not \f(CW\*(C`ftp:\*(C'\fR. .PP If you have neither Net::FTP nor \s-1LWP,\s0 there is a fallback mechanism implemented for an external ftp command or for an external lynx command. .SH "UTILITIES" .IX Header "UTILITIES" .SS "Finding packages and \s-1VERSION\s0" .IX Subsection "Finding packages and VERSION" This module presumes that all packages on \s-1CPAN\s0 .IP "\(bu" 2 declare their \f(CW$VERSION\fR variable in an easy to parse manner. This prerequisite can hardly be relaxed because it consumes far too much memory to load all packages into the running program just to determine the \f(CW$VERSION\fR variable. Currently all programs that are dealing with version use something like this .Sp .Vb 2 \& perl \-MExtUtils::MakeMaker \-le \e \& \*(Aqprint MM\->parse_version(shift)\*(Aq filename .Ve .Sp If you are author of a package and wonder if your \f(CW$VERSION\fR can be parsed, please try the above method. .IP "\(bu" 2 come as compressed or gzipped tarfiles or as zip files and contain a \&\f(CW\*(C`Makefile.PL\*(C'\fR or \f(CW\*(C`Build.PL\*(C'\fR (well, we try to handle a bit more, but with little enthusiasm). .SS "Debugging" .IX Subsection "Debugging" Debugging this module is more than a bit complex due to interference from the software producing the indices on \s-1CPAN,\s0 the mirroring process on \s-1CPAN,\s0 packaging, configuration, synchronicity, and even (gasp!) due to bugs within the \s-1CPAN\s0.pm module itself. .PP For debugging the code of \s-1CPAN\s0.pm itself in interactive mode, some debugging aid can be turned on for most packages within \&\s-1CPAN\s0.pm with one of .IP "o debug package..." 2 .IX Item "o debug package..." sets debug mode for packages. .IP "o debug \-package..." 2 .IX Item "o debug -package..." unsets debug mode for packages. .IP "o debug all" 2 .IX Item "o debug all" turns debugging on for all packages. .IP "o debug number" 2 .IX Item "o debug number" .PP which sets the debugging packages directly. Note that \f(CW\*(C`o debug 0\*(C'\fR turns debugging off. .PP What seems a successful strategy is the combination of \f(CW\*(C`reload cpan\*(C'\fR and the debugging switches. Add a new debug statement while running in the shell and then issue a \f(CW\*(C`reload cpan\*(C'\fR and see the new debugging messages immediately without losing the current context. .PP \&\f(CW\*(C`o debug\*(C'\fR without an argument lists the valid package names and the current set of packages in debugging mode. \f(CW\*(C`o debug\*(C'\fR has built-in completion support. .PP For debugging of \s-1CPAN\s0 data there is the \f(CW\*(C`dump\*(C'\fR command which takes the same arguments as make/test/install and outputs each object's Data::Dumper dump. If an argument looks like a perl variable and contains one of \f(CW\*(C`$\*(C'\fR, \f(CW\*(C`@\*(C'\fR or \f(CW\*(C`%\*(C'\fR, it is \fBeval()\fRed and fed to Data::Dumper directly. .SS "Floppy, Zip, Offline Mode" .IX Subsection "Floppy, Zip, Offline Mode" \&\s-1CPAN\s0.pm works nicely without network access, too. If you maintain machines that are not networked at all, you should consider working with \f(CW\*(C`file:\*(C'\fR URLs. You'll have to collect your modules somewhere first. So you might use \s-1CPAN\s0.pm to put together all you need on a networked machine. Then copy the \f(CW$CPAN::Config\fR\->{keep_source_where} (but not \&\f(CW$CPAN::Config\fR\->{build_dir}) directory on a floppy. This floppy is kind of a personal \s-1CPAN. CPAN\s0.pm on the non-networked machines works nicely with this floppy. See also below the paragraph about CD-ROM support. .SS "Basic Utilities for Programmers" .IX Subsection "Basic Utilities for Programmers" .IP "has_inst($module)" 2 .IX Item "has_inst($module)" Returns true if the module is installed. Used to load all modules into the running \s-1CPAN\s0.pm that are considered optional. The config variable \&\f(CW\*(C`dontload_list\*(C'\fR intercepts the \f(CW\*(C`has_inst()\*(C'\fR call such that an optional module is not loaded despite being available. For example, the following command will prevent \f(CW\*(C`YAML.pm\*(C'\fR from being loaded: .Sp .Vb 1 \& cpan> o conf dontload_list push YAML .Ve .Sp See the source for details. .IP "use_inst($module)" 2 .IX Item "use_inst($module)" Similary to \fBhas_inst()\fR tries to load optional library but also dies if library is not available .IP "has_usable($module)" 2 .IX Item "has_usable($module)" Returns true if the module is installed and in a usable state. Only useful for a handful of modules that are used internally. See the source for details. .IP "instance($module)" 2 .IX Item "instance($module)" The constructor for all the singletons used to represent modules, distributions, authors, and bundles. If the object already exists, this method returns the object; otherwise, it calls the constructor. .IP "\fBfrontend()\fR" 2 .IX Item "frontend()" .PD 0 .IP "frontend($new_frontend)" 2 .IX Item "frontend($new_frontend)" .PD Getter/setter for frontend object. Method just allows to subclass \s-1CPAN\s0.pm. .SH "SECURITY" .IX Header "SECURITY" There's no strong security layer in \s-1CPAN\s0.pm. \s-1CPAN\s0.pm helps you to install foreign, unmasked, unsigned code on your machine. We compare to a checksum that comes from the net just as the distribution file itself. But we try to make it easy to add security on demand: .SS "Cryptographically signed modules" .IX Subsection "Cryptographically signed modules" Since release 1.77, \s-1CPAN\s0.pm has been able to verify cryptographically signed module distributions using Module::Signature. The \s-1CPAN\s0 modules can be signed by their authors, thus giving more security. The simple unsigned \s-1MD5\s0 checksums that were used before by \s-1CPAN\s0 protect mainly against accidental file corruption. .PP You will need to have Module::Signature installed, which in turn requires that you have at least one of Crypt::OpenPGP module or the command-line \fIgpg\fR tool installed. .PP You will also need to be able to connect over the Internet to the public key servers, like pgp.mit.edu, and their port 11731 (the \s-1HKP\s0 protocol). .PP The configuration parameter check_sigs is there to turn signature checking on or off. .SH "EXPORT" .IX Header "EXPORT" Most functions in package \s-1CPAN\s0 are exported by default. The reason for this is that the primary use is intended for the cpan shell or for one-liners. .SH "ENVIRONMENT" .IX Header "ENVIRONMENT" When the \s-1CPAN\s0 shell enters a subshell via the look command, it sets the environment \s-1CPAN_SHELL_LEVEL\s0 to 1, or increments that variable if it is already set. .PP When \s-1CPAN\s0 runs, it sets the environment variable \s-1PERL5_CPAN_IS_RUNNING\s0 to the \s-1ID\s0 of the running process. It also sets \&\s-1PERL5_CPANPLUS_IS_RUNNING\s0 to prevent runaway processes which could happen with older versions of Module::Install. .PP When running \f(CW\*(C`perl Makefile.PL\*(C'\fR, the environment variable \&\f(CW\*(C`PERL5_CPAN_IS_EXECUTING\*(C'\fR is set to the full path of the \&\f(CW\*(C`Makefile.PL\*(C'\fR that is being executed. This prevents runaway processes with newer versions of Module::Install. .PP When the config variable ftp_passive is set, all downloads will be run with the environment variable \s-1FTP_PASSIVE\s0 set to this value. This is in general a good idea as it influences both Net::FTP and \s-1LWP\s0 based connections. The same effect can be achieved by starting the cpan shell with this environment variable set. For Net::FTP alone, one can also always set passive mode by running libnetcfg. .SH "POPULATE AN INSTALLATION WITH LOTS OF MODULES" .IX Header "POPULATE AN INSTALLATION WITH LOTS OF MODULES" Populating a freshly installed perl with one's favorite modules is pretty easy if you maintain a private bundle definition file. To get a useful blueprint of a bundle definition file, the command autobundle can be used on the \s-1CPAN\s0 shell command line. This command writes a bundle definition file for all modules installed for the current perl interpreter. It's recommended to run this command once only, and from then on maintain the file manually under a private name, say Bundle/my_bundle.pm. With a clever bundle file you can then simply say .PP .Vb 1 \& cpan> install Bundle::my_bundle .Ve .PP then answer a few questions and go out for coffee (possibly even in a different city). .PP Maintaining a bundle definition file means keeping track of two things: dependencies and interactivity. \s-1CPAN\s0.pm sometimes fails on calculating dependencies because not all modules define all MakeMaker attributes correctly, so a bundle definition file should specify prerequisites as early as possible. On the other hand, it's annoying that so many distributions need some interactive configuring. So what you can try to accomplish in your private bundle file is to have the packages that need to be configured early in the file and the gentle ones later, so you can go out for coffee after a few minutes and leave \s-1CPAN\s0.pm to churn away unattended. .SH "WORKING WITH CPAN.pm BEHIND FIREWALLS" .IX Header "WORKING WITH CPAN.pm BEHIND FIREWALLS" Thanks to Graham Barr for contributing the following paragraphs about the interaction between perl, and various firewall configurations. For further information on firewalls, it is recommended to consult the documentation that comes with the \fIncftp\fR program. If you are unable to go through the firewall with a simple Perl setup, it is likely that you can configure \fIncftp\fR so that it works through your firewall. .SS "Three basic types of firewalls" .IX Subsection "Three basic types of firewalls" Firewalls can be categorized into three basic types. .IP "http firewall" 4 .IX Item "http firewall" This is when the firewall machine runs a web server, and to access the outside world, you must do so via that web server. If you set environment variables like http_proxy or ftp_proxy to values beginning with http://, or in your web browser you've proxy information set, then you know you are running behind an http firewall. .Sp To access servers outside these types of firewalls with perl (even for ftp), you need \s-1LWP\s0 or HTTP::Tiny. .IP "ftp firewall" 4 .IX Item "ftp firewall" This where the firewall machine runs an ftp server. This kind of firewall will only let you access ftp servers outside the firewall. This is usually done by connecting to the firewall with ftp, then entering a username like \*(L"user@outside.host.com\*(R". .Sp To access servers outside these type of firewalls with perl, you need Net::FTP. .IP "One-way visibility" 4 .IX Item "One-way visibility" One-way visibility means these firewalls try to make themselves invisible to users inside the firewall. An \s-1FTP\s0 data connection is normally created by sending your \s-1IP\s0 address to the remote server and then listening for the return connection. But the remote server will not be able to connect to you because of the firewall. For these types of firewall, \&\s-1FTP\s0 connections need to be done in a passive mode. .Sp There are two that I can think off. .RS 4 .IP "\s-1SOCKS\s0" 4 .IX Item "SOCKS" If you are using a \s-1SOCKS\s0 firewall, you will need to compile perl and link it with the \s-1SOCKS\s0 library. This is what is normally called a 'socksified' perl. With this executable you will be able to connect to servers outside the firewall as if it were not there. .IP "\s-1IP\s0 Masquerade" 4 .IX Item "IP Masquerade" This is when the firewall implemented in the kernel (via \s-1NAT,\s0 or networking address translation), it allows you to hide a complete network behind one \&\s-1IP\s0 address. With this firewall no special compiling is needed as you can access hosts directly. .Sp For accessing ftp servers behind such firewalls you usually need to set the environment variable \f(CW\*(C`FTP_PASSIVE\*(C'\fR or the config variable ftp_passive to a true value. .RE .RS 4 .RE .SS "Configuring lynx or ncftp for going through a firewall" .IX Subsection "Configuring lynx or ncftp for going through a firewall" If you can go through your firewall with e.g. lynx, presumably with a command such as .PP .Vb 1 \& /usr/local/bin/lynx \-pscott:tiger .Ve .PP then you would configure \s-1CPAN\s0.pm with the command .PP .Vb 1 \& o conf lynx "/usr/local/bin/lynx \-pscott:tiger" .Ve .PP That's all. Similarly for ncftp or ftp, you would configure something like .PP .Vb 1 \& o conf ncftp "/usr/bin/ncftp \-f /home/scott/ncftplogin.cfg" .Ve .PP Your mileage may vary... .SH "FAQ" .IX Header "FAQ" .IP "1)" 4 .IX Item "1)" I installed a new version of module X but \s-1CPAN\s0 keeps saying, I have the old version installed .Sp Probably you \fBdo\fR have the old version installed. This can happen if a module installs itself into a different directory in the \&\f(CW@INC\fR path than it was previously installed. This is not really a \&\s-1CPAN\s0.pm problem, you would have the same problem when installing the module manually. The easiest way to prevent this behaviour is to add the argument \f(CW\*(C`UNINST=1\*(C'\fR to the \f(CW\*(C`make install\*(C'\fR call, and that is why many people add this argument permanently by configuring .Sp .Vb 1 \& o conf make_install_arg UNINST=1 .Ve .IP "2)" 4 .IX Item "2)" So why is UNINST=1 not the default? .Sp Because there are people who have their precise expectations about who may install where in the \f(CW@INC\fR path and who uses which \f(CW@INC\fR array. In fine tuned environments \f(CW\*(C`UNINST=1\*(C'\fR can cause damage. .IP "3)" 4 .IX Item "3)" I want to clean up my mess, and install a new perl along with all modules I have. How do I go about it? .Sp Run the autobundle command for your old perl and optionally rename the resulting bundle file (e.g. Bundle/mybundle.pm), install the new perl with the Configure option prefix, e.g. .Sp .Vb 1 \& ./Configure \-Dprefix=/usr/local/perl\-5.6.78.9 .Ve .Sp Install the bundle file you produced in the first step with something like .Sp .Vb 1 \& cpan> install Bundle::mybundle .Ve .Sp and you're done. .IP "4)" 4 .IX Item "4)" When I install bundles or multiple modules with one command there is too much output to keep track of. .Sp You may want to configure something like .Sp .Vb 2 \& o conf make_arg "| tee \-ai /root/.cpan/logs/make.out" \& o conf make_install_arg "| tee \-ai /root/.cpan/logs/make_install.out" .Ve .Sp so that \s-1STDOUT\s0 is captured in a file for later inspection. .IP "5)" 4 .IX Item "5)" I am not root, how can I install a module in a personal directory? .Sp As of \s-1CPAN 1.9463,\s0 if you do not have permission to write the default perl library directories, \s-1CPAN\s0's configuration process will ask you whether you want to bootstrap <local::lib>, which makes keeping a personal perl library directory easy. .Sp Another thing you should bear in mind is that the \s-1UNINST\s0 parameter can be dangerous when you are installing into a private area because you might accidentally remove modules that other people depend on that are not using the private area. .IP "6)" 4 .IX Item "6)" How to get a package, unwrap it, and make a change before building it? .Sp Have a look at the \f(CW\*(C`look\*(C'\fR (!) command. .IP "7)" 4 .IX Item "7)" I installed a Bundle and had a couple of fails. When I retried, everything resolved nicely. Can this be fixed to work on first try? .Sp The reason for this is that \s-1CPAN\s0 does not know the dependencies of all modules when it starts out. To decide about the additional items to install, it just uses data found in the \s-1META\s0.yml file or the generated Makefile. An undetected missing piece breaks the process. But it may well be that your Bundle installs some prerequisite later than some depending item and thus your second try is able to resolve everything. Please note, \s-1CPAN\s0.pm does not know the dependency tree in advance and cannot sort the queue of things to install in a topologically correct order. It resolves perfectly well \fBif\fR all modules declare the prerequisites correctly with the \s-1PREREQ_PM\s0 attribute to MakeMaker or the \f(CW\*(C`requires\*(C'\fR stanza of Module::Build. For bundles which fail and you need to install often, it is recommended to sort the Bundle definition file manually. .IP "8)" 4 .IX Item "8)" In our intranet, we have many modules for internal use. How can I integrate these modules with \s-1CPAN\s0.pm but without uploading the modules to \s-1CPAN\s0? .Sp Have a look at the CPAN::Site module. .IP "9)" 4 .IX Item "9)" When I run \s-1CPAN\s0's shell, I get an error message about things in my \&\f(CW\*(C`/etc/inputrc\*(C'\fR (or \f(CW\*(C`~/.inputrc\*(C'\fR) file. .Sp These are readline issues and can only be fixed by studying readline configuration on your architecture and adjusting the referenced file accordingly. Please make a backup of the \f(CW\*(C`/etc/inputrc\*(C'\fR or \f(CW\*(C`~/.inputrc\*(C'\fR and edit them. Quite often harmless changes like uppercasing or lowercasing some arguments solves the problem. .IP "10)" 4 .IX Item "10)" Some authors have strange characters in their names. .Sp Internally \s-1CPAN\s0.pm uses the \s-1UTF\-8\s0 charset. If your terminal is expecting \s-1ISO\-8859\-1\s0 charset, a converter can be activated by setting term_is_latin to a true value in your config file. One way of doing so would be .Sp .Vb 1 \& cpan> o conf term_is_latin 1 .Ve .Sp If other charset support is needed, please file a bug report against \&\s-1CPAN\s0.pm at rt.cpan.org and describe your needs. Maybe we can extend the support or maybe \s-1UTF\-8\s0 terminals become widely available. .Sp Note: this config variable is deprecated and will be removed in a future version of \s-1CPAN\s0.pm. It will be replaced with the conventions around the family of \f(CW$LANG\fR and \f(CW$LC_\fR* environment variables. .IP "11)" 4 .IX Item "11)" When an install fails for some reason and then I correct the error condition and retry, \s-1CPAN\s0.pm refuses to install the module, saying \&\f(CW\*(C`Already tried without success\*(C'\fR. .Sp Use the force pragma like so .Sp .Vb 1 \& force install Foo::Bar .Ve .Sp Or you can use .Sp .Vb 1 \& look Foo::Bar .Ve .Sp and then \f(CW\*(C`make install\*(C'\fR directly in the subshell. .IP "12)" 4 .IX Item "12)" How do I install a \*(L"\s-1DEVELOPER RELEASE\*(R"\s0 of a module? .Sp By default, \s-1CPAN\s0 will install the latest non-developer release of a module. If you want to install a dev release, you have to specify the partial path starting with the author id to the tarball you wish to install, like so: .Sp .Vb 1 \& cpan> install KWILLIAMS/Module\-Build\-0.27_07.tar.gz .Ve .Sp Note that you can use the \f(CW\*(C`ls\*(C'\fR command to get this path listed. .IP "13)" 4 .IX Item "13)" How do I install a module and all its dependencies from the commandline, without being prompted for anything, despite my \s-1CPAN\s0 configuration (or lack thereof)? .Sp \&\s-1CPAN\s0 uses ExtUtils::MakeMaker's \fBprompt()\fR function to ask its questions, so if you set the \s-1PERL_MM_USE_DEFAULT\s0 environment variable, you shouldn't be asked any questions at all (assuming the modules you are installing are nice about obeying that variable as well): .Sp .Vb 1 \& % PERL_MM_USE_DEFAULT=1 perl \-MCPAN \-e \*(Aqinstall My::Module\*(Aq .Ve .IP "14)" 4 .IX Item "14)" How do I create a Module::Build based Build.PL derived from an ExtUtils::MakeMaker focused Makefile.PL? .Sp http://search.cpan.org/dist/Module\-Build\-Convert/ .IP "15)" 4 .IX Item "15)" I'm frequently irritated with the \s-1CPAN\s0 shell's inability to help me select a good mirror. .Sp \&\s-1CPAN\s0 can now help you select a \*(L"good\*(R" mirror, based on which ones have the lowest 'ping' round-trip times. From the shell, use the command 'o conf init urllist' and allow \s-1CPAN\s0 to automatically select mirrors for you. .Sp Beyond that help, the urllist config parameter is yours. You can add and remove sites at will. You should find out which sites have the best up-to-dateness, bandwidth, reliability, etc. and are topologically close to you. Some people prefer fast downloads, others up-to-dateness, others reliability. You decide which to try in which order. .Sp Henk P. Penning maintains a site that collects data about \s-1CPAN\s0 sites: .Sp .Vb 1 \& http://mirrors.cpan.org/ .Ve .Sp Also, feel free to play with experimental features. Run .Sp .Vb 1 \& o conf init randomize_urllist ftpstats_period ftpstats_size .Ve .Sp and choose your favorite parameters. After a few downloads running the \&\f(CW\*(C`hosts\*(C'\fR command will probably assist you in choosing the best mirror sites. .IP "16)" 4 .IX Item "16)" Why do I get asked the same questions every time I start the shell? .Sp You can make your configuration changes permanent by calling the command \f(CW\*(C`o conf commit\*(C'\fR. Alternatively set the \f(CW\*(C`auto_commit\*(C'\fR variable to true by running \f(CW\*(C`o conf init auto_commit\*(C'\fR and answering the following question with yes. .IP "17)" 4 .IX Item "17)" Older versions of \s-1CPAN\s0.pm had the original root directory of all tarballs in the build directory. Now there are always random characters appended to these directory names. Why was this done? .Sp The random characters are provided by File::Temp and ensure that each module's individual build directory is unique. This makes running \&\s-1CPAN\s0.pm in concurrent processes simultaneously safe. .IP "18)" 4 .IX Item "18)" Speaking of the build directory. Do I have to clean it up myself? .Sp You have the choice to set the config variable \f(CW\*(C`scan_cache\*(C'\fR to \&\f(CW\*(C`never\*(C'\fR. Then you must clean it up yourself. The other possible values, \f(CW\*(C`atstart\*(C'\fR and \f(CW\*(C`atexit\*(C'\fR clean up the build directory when you start (or more precisely, after the first extraction into the build directory) or exit the \s-1CPAN\s0 shell, respectively. If you never start up the \s-1CPAN\s0 shell, you probably also have to clean up the build directory yourself. .IP "19)" 4 .IX Item "19)" How can I switch to sudo instead of local::lib? .Sp The following 5 environment veriables need to be reset to the previous values: \s-1PATH, PERL5LIB, PERL_LOCAL_LIB_ROOT, PERL_MB_OPT, PERL_MM_OPT\s0; and these two \s-1CPAN\s0.pm config variables must be reconfigured: make_install_make_command and mbuild_install_build_command. The five env variables have probably been overwritten in your \f(CW$HOME\fR/.bashrc or some equivalent. You either find them there and delete their traces and logout/login or you override them temporarily, depending on your exact desire. The two cpanpm config variables can be set with: .Sp .Vb 1 \& o conf init /install_.*_command/ .Ve .Sp probably followed by .Sp .Vb 1 \& o conf commit .Ve .SH "COMPATIBILITY" .IX Header "COMPATIBILITY" .SS "\s-1OLD PERL VERSIONS\s0" .IX Subsection "OLD PERL VERSIONS" \&\s-1CPAN\s0.pm is regularly tested to run under 5.005 and assorted newer versions. It is getting more and more difficult to get the minimal prerequisites working on older perls. It is close to impossible to get the whole Bundle::CPAN working there. If you're in the position to have only these old versions, be advised that \s-1CPAN\s0 is designed to work fine without the Bundle::CPAN installed. .PP To get things going, note that GBARR/Scalar\-List\-Utils\-1.18.tar.gz is compatible with ancient perls and that File::Temp is listed as a prerequisite but \s-1CPAN\s0 has reasonable workarounds if it is missing. .SS "\s-1CPANPLUS\s0" .IX Subsection "CPANPLUS" This module and its competitor, the \s-1CPANPLUS\s0 module, are both much cooler than the other. \s-1CPAN\s0.pm is older. \s-1CPANPLUS\s0 was designed to be more modular, but it was never intended to be compatible with \s-1CPAN\s0.pm. .SS "\s-1CPANMINUS\s0" .IX Subsection "CPANMINUS" In the year 2010 App::cpanminus was launched as a new approach to a cpan shell with a considerably smaller footprint. Very cool stuff. .SH "SECURITY ADVICE" .IX Header "SECURITY ADVICE" This software enables you to upgrade software on your computer and so is inherently dangerous because the newly installed software may contain bugs and may alter the way your computer works or even make it unusable. Please consider backing up your data before every upgrade. .SH "BUGS" .IX Header "BUGS" Please report bugs via <http://rt.cpan.org/> .PP Before submitting a bug, please make sure that the traditional method of building a Perl module package from a shell by following the installation instructions of that package still works in your environment. .SH "AUTHOR" .IX Header "AUTHOR" Andreas Koenig \f(CW\*(C`<andk@cpan.org>\*(C'\fR .SH "LICENSE" .IX Header "LICENSE" This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. .PP See <http://www.perl.com/perl/misc/Artistic.html> .SH "TRANSLATIONS" .IX Header "TRANSLATIONS" Kawai,Takanori provides a Japanese translation of a very old version of this manpage at <http://homepage3.nifty.com/hippo2000/perltips/CPAN.htm> .SH "SEE ALSO" .IX Header "SEE ALSO" Many people enter the \s-1CPAN\s0 shell by running the cpan utility program which is installed in the same directory as perl itself. So if you have this directory in your \s-1PATH\s0 variable (or some equivalent in your operating system) then typing \f(CW\*(C`cpan\*(C'\fR in a console window will work for you as well. Above that the utility provides several commandline shortcuts. .PP melezhik (Alexey) sent me a link where he published a chef recipe to work with \s-1CPAN\s0.pm: http://community.opscode.com/cookbooks/cpan. PK�����A0]W������man/man3/JSON::Syck.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "JSON::Syck 3" .TH JSON::Syck 3 "2020-10-26" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" JSON::Syck \- JSON is YAML (but consider using JSON::XS instead!) .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use JSON::Syck; # no exports by default \& \& my $data = JSON::Syck::Load($json); \& my $json = JSON::Syck::Dump($data); \& \& # $file can be an IO object, or a filename \& my $data = JSON::Syck::LoadFile($file); \& JSON::Syck::DumpFile($file, $data); \& \& # Dump into a pre\-existing buffer \& my $json; \& JSON::Syck::DumpInto(\e$json, $data); .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" JSON::Syck is a syck implementation of \s-1JSON\s0 parsing and generation. Because \&\s-1JSON\s0 is \s-1YAML\s0 (<http://redhanded.hobix.com/inspect/yamlIsJson.html>), using syck gives you a fast and memory-efficient parser and dumper for \s-1JSON\s0 data representation. .PP However, a newer module \s-1JSON::XS\s0, has since emerged. It is more flexible, efficient and robust, so please consider using it instead of this module. .SH "DIFFERENCE WITH JSON" .IX Header "DIFFERENCE WITH JSON" You might want to know the difference between the \fI\s-1JSON\s0\fR module and this one. .PP Since \s-1JSON\s0 is a pure-perl module and JSON::Syck is based on libsyck, JSON::Syck is supposed to be very fast and memory efficient. See chansen's benchmark table at <http://idisk.mac.com/christian.hansen/Public/perl/serialize.pl> .PP \&\s-1JSON\s0.pm comes with dozens of ways to do the same thing and lots of options, while JSON::Syck doesn't. There's only \f(CW\*(C`Load\*(C'\fR and \f(CW\*(C`Dump\*(C'\fR. .PP Oh, and JSON::Syck doesn't use camelCase method names :\-) .SH "REFERENCES" .IX Header "REFERENCES" .SS "\s-1SCALAR REFERENCE\s0" .IX Subsection "SCALAR REFERENCE" For now, when you pass a scalar reference to JSON::Syck, it dereferences to get the actual scalar value. .PP JSON::Syck raises an exception when you pass in circular references. .PP If you want to serialize self referencing stuff, you should use \&\s-1YAML\s0 which supports it. .SS "\s-1SUBROUTINE REFERENCE\s0" .IX Subsection "SUBROUTINE REFERENCE" When you pass subroutine reference, JSON::Syck dumps it as null. .SH "UTF\-8 FLAGS" .IX Header "UTF-8 FLAGS" By default this module doesn't touch any of utf\-8 flags set in strings, and assumes \s-1UTF\-8\s0 bytes to be passed and emit. .PP However, when you set \f(CW$JSON::Syck::ImplicitUnicode\fR to 1, this module properly decodes \s-1UTF\-8\s0 binaries and sets \s-1UTF\-8\s0 flag everywhere, as in: .PP .Vb 4 \& JSON (UTF\-8 bytes) => Perl (UTF\-8 flagged) \& JSON (UTF\-8 flagged) => Perl (UTF\-8 flagged) \& Perl (UTF\-8 bytes) => JSON (UTF\-8 flagged) \& Perl (UTF\-8 flagged) => JSON (UTF\-8 flagged) .Ve .PP By default, JSON::Syck::Dump will only transverse up to 512 levels of a datastructure in order to avoid an infinite loop when it is presented with an circular reference. .PP However, you set \f(CW$JSON::Syck::MaxLevels\fR to a larger value if you have very complex structures. .PP Unfortunately, there's no implicit way to dump Perl \s-1UTF\-8\s0 flagged data structure to utf\-8 encoded \s-1JSON.\s0 To do this, simply use Encode module, e.g.: .PP .Vb 2 \& use Encode; \& use JSON::Syck qw(Dump); \& \& my $json = encode_utf8( Dump($data) ); .Ve .PP Alternatively you can use Encode::JavaScript::UCS to encode Unicode strings as in \fI\f(CI%uXXXX\fI\fR form. .PP .Vb 3 \& use Encode; \& use Encode::JavaScript::UCS; \& use JSON::Syck qw(Dump); \& \& my $json_unicode_escaped = encode( \*(AqJavaScript\-UCS\*(Aq, Dump($data) ); .Ve .SH "QUOTING" .IX Header "QUOTING" According to the \s-1JSON\s0 specification, all \s-1JSON\s0 strings are to be double-quoted. However, when embedding JavaScript in \s-1HTML\s0 attributes, it may be more convenient to use single quotes. .PP Set \f(CW$JSON::Syck::SingleQuote\fR to 1 will make both \f(CW\*(C`Dump\*(C'\fR and \f(CW\*(C`Load\*(C'\fR expect single-quoted string literals. .SH "BUGS" .IX Header "BUGS" Dumping into tied (or other magic variables) with \f(CW\*(C`DumpInto\*(C'\fR might not work properly in all cases. .PP When dumping with \f(CW\*(C`DumpFile\*(C'\fR, some spacing might be wrong and \&\f(CW$JSON::Syck::SingleQuote\fR might be handled incorrectly. .SH "SEE ALSO" .IX Header "SEE ALSO" \&\s-1JSON::XS\s0, YAML::Syck .SH "AUTHORS" .IX Header "AUTHORS" Audrey Tang <cpan@audreyt.org> .PP Tatsuhiko Miyagawa <miyagawa@gmail.com> .SH "COPYRIGHT" .IX Header "COPYRIGHT" Copyright 2005\-2009 by Audrey Tang <cpan@audreyt.org>. .PP This software is released under the \s-1MIT\s0 license cited below. .PP The \fIlibsyck\fR code bundled with this library is released by \&\*(L"why the lucky stiff\*(R", under a BSD-style license. See the \fI\s-1COPYING\s0\fR file for details. .ie n .SS "The ""\s-1MIT""\s0 License" .el .SS "The ``\s-1MIT''\s0 License" .IX Subsection "The MIT License" Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \*(L"Software\*(R"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: .PP The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. .PP \&\s-1THE SOFTWARE IS PROVIDED \*(L"AS IS\*(R", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\s0 PK�����A0]qo[�[���man/man3/JSON::XS.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "XS 3" .TH XS 3 "2020-10-27" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" JSON::XS \- JSON serialising/deserialising, done correctly and fast .PP JSON::XS \- 正しくて高速な JSON シリアライザ/デシリアライザ (http://fleur.hio.jp/perldoc/mix/lib/JSON/XS.html) .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use JSON::XS; \& \& # exported functions, they croak on error \& # and expect/generate UTF\-8 \& \& $utf8_encoded_json_text = encode_json $perl_hash_or_arrayref; \& $perl_hash_or_arrayref = decode_json $utf8_encoded_json_text; \& \& # OO\-interface \& \& $coder = JSON::XS\->new\->ascii\->pretty\->allow_nonref; \& $pretty_printed_unencoded = $coder\->encode ($perl_scalar); \& $perl_scalar = $coder\->decode ($unicode_json_text); \& \& # Note that JSON version 2.0 and above will automatically use JSON::XS \& # if available, at virtually no speed overhead either, so you should \& # be able to just: \& \& use JSON; \& \& # and do the same things, except that you have a pure\-perl fallback now. .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" This module converts Perl data structures to \s-1JSON\s0 and vice versa. Its primary goal is to be \fIcorrect\fR and its secondary goal is to be \&\fIfast\fR. To reach the latter goal it was written in C. .PP See \s-1MAPPING,\s0 below, on how \s-1JSON::XS\s0 maps perl values to \s-1JSON\s0 values and vice versa. .SS "\s-1FEATURES\s0" .IX Subsection "FEATURES" .IP "\(bu" 4 correct Unicode handling .Sp This module knows how to handle Unicode, documents how and when it does so, and even documents what \*(L"correct\*(R" means. .IP "\(bu" 4 round-trip integrity .Sp When you serialise a perl data structure using only data types supported by \s-1JSON\s0 and Perl, the deserialised data structure is identical on the Perl level. (e.g. the string \*(L"2.0\*(R" doesn't suddenly become \*(L"2\*(R" just because it looks like a number). There \fIare\fR minor exceptions to this, read the \&\s-1MAPPING\s0 section below to learn about those. .IP "\(bu" 4 strict checking of \s-1JSON\s0 correctness .Sp There is no guessing, no generating of illegal \s-1JSON\s0 texts by default, and only \s-1JSON\s0 is accepted as input by default (the latter is a security feature). .IP "\(bu" 4 fast .Sp Compared to other \s-1JSON\s0 modules and other serialisers such as Storable, this module usually compares favourably in terms of speed, too. .IP "\(bu" 4 simple to use .Sp This module has both a simple functional interface as well as an object oriented interface. .IP "\(bu" 4 reasonably versatile output formats .Sp You can choose between the most compact guaranteed-single-line format possible (nice for simple line-based protocols), a pure-ASCII format (for when your transport is not 8\-bit clean, still supports the whole Unicode range), or a pretty-printed format (for when you want to read that stuff). Or you can combine those features in whatever way you like. .SH "FUNCTIONAL INTERFACE" .IX Header "FUNCTIONAL INTERFACE" The following convenience methods are provided by this module. They are exported by default: .ie n .IP "$json_text = encode_json $perl_scalar" 4 .el .IP "\f(CW$json_text\fR = encode_json \f(CW$perl_scalar\fR" 4 .IX Item "$json_text = encode_json $perl_scalar" Converts the given Perl data structure to a \s-1UTF\-8\s0 encoded, binary string (that is, the string contains octets only). Croaks on error. .Sp This function call is functionally identical to: .Sp .Vb 1 \& $json_text = JSON::XS\->new\->utf8\->encode ($perl_scalar) .Ve .Sp Except being faster. .ie n .IP "$perl_scalar = decode_json $json_text" 4 .el .IP "\f(CW$perl_scalar\fR = decode_json \f(CW$json_text\fR" 4 .IX Item "$perl_scalar = decode_json $json_text" The opposite of \f(CW\*(C`encode_json\*(C'\fR: expects a \s-1UTF\-8\s0 (binary) string and tries to parse that as a \s-1UTF\-8\s0 encoded \s-1JSON\s0 text, returning the resulting reference. Croaks on error. .Sp This function call is functionally identical to: .Sp .Vb 1 \& $perl_scalar = JSON::XS\->new\->utf8\->decode ($json_text) .Ve .Sp Except being faster. .SH "A FEW NOTES ON UNICODE AND PERL" .IX Header "A FEW NOTES ON UNICODE AND PERL" Since this often leads to confusion, here are a few very clear words on how Unicode works in Perl, modulo bugs. .IP "1. Perl strings can store characters with ordinal values > 255." 4 .IX Item "1. Perl strings can store characters with ordinal values > 255." This enables you to store Unicode characters as single characters in a Perl string \- very natural. .IP "2. Perl does \fInot\fR associate an encoding with your strings." 4 .IX Item "2. Perl does not associate an encoding with your strings." \&... until you force it to, e.g. when matching it against a regex, or printing the scalar to a file, in which case Perl either interprets your string as locale-encoded text, octets/binary, or as Unicode, depending on various settings. In no case is an encoding stored together with your data, it is \fIuse\fR that decides encoding, not any magical meta data. .IP "3. The internal utf\-8 flag has no meaning with regards to the encoding of your string." 4 .IX Item "3. The internal utf-8 flag has no meaning with regards to the encoding of your string." Just ignore that flag unless you debug a Perl bug, a module written in \&\s-1XS\s0 or want to dive into the internals of perl. Otherwise it will only confuse you, as, despite the name, it says nothing about how your string is encoded. You can have Unicode strings with that flag set, with that flag clear, and you can have binary data with that flag set and that flag clear. Other possibilities exist, too. .Sp If you didn't know about that flag, just the better, pretend it doesn't exist. .ie n .IP "4. A ""Unicode String"" is simply a string where each character can be validly interpreted as a Unicode code point." 4 .el .IP "4. A ``Unicode String'' is simply a string where each character can be validly interpreted as a Unicode code point." 4 .IX Item "4. A Unicode String is simply a string where each character can be validly interpreted as a Unicode code point." If you have \s-1UTF\-8\s0 encoded data, it is no longer a Unicode string, but a Unicode string encoded in \s-1UTF\-8,\s0 giving you a binary string. .ie n .IP "5. A string containing ""high"" (> 255) character values is \fInot\fR a \s-1UTF\-8\s0 string." 4 .el .IP "5. A string containing ``high'' (> 255) character values is \fInot\fR a \s-1UTF\-8\s0 string." 4 .IX Item "5. A string containing high (> 255) character values is not a UTF-8 string." It's a fact. Learn to live with it. .PP I hope this helps :) .SH "OBJECT-ORIENTED INTERFACE" .IX Header "OBJECT-ORIENTED INTERFACE" The object oriented interface lets you configure your own encoding or decoding style, within the limits of supported formats. .ie n .IP "$json = new \s-1JSON::XS\s0" 4 .el .IP "\f(CW$json\fR = new \s-1JSON::XS\s0" 4 .IX Item "$json = new JSON::XS" Creates a new \s-1JSON::XS\s0 object that can be used to de/encode \s-1JSON\s0 strings. All boolean flags described below are by default \fIdisabled\fR (with the exception of \f(CW\*(C`allow_nonref\*(C'\fR, which defaults to \fIenabled\fR since version \f(CW4.0\fR). .Sp The mutators for flags all return the \s-1JSON\s0 object again and thus calls can be chained: .Sp .Vb 2 \& my $json = JSON::XS\->new\->utf8\->space_after\->encode ({a => [1,2]}) \& => {"a": [1, 2]} .Ve .ie n .IP "$json = $json\->ascii ([$enable])" 4 .el .IP "\f(CW$json\fR = \f(CW$json\fR\->ascii ([$enable])" 4 .IX Item "$json = $json->ascii ([$enable])" .PD 0 .ie n .IP "$enabled = $json\->get_ascii" 4 .el .IP "\f(CW$enabled\fR = \f(CW$json\fR\->get_ascii" 4 .IX Item "$enabled = $json->get_ascii" .PD If \f(CW$enable\fR is true (or missing), then the \f(CW\*(C`encode\*(C'\fR method will not generate characters outside the code range \f(CW0..127\fR (which is \s-1ASCII\s0). Any Unicode characters outside that range will be escaped using either a single \euXXXX (\s-1BMP\s0 characters) or a double \euHHHH\euLLLLL escape sequence, as per \s-1RFC4627.\s0 The resulting encoded \s-1JSON\s0 text can be treated as a native Unicode string, an ascii-encoded, latin1\-encoded or \s-1UTF\-8\s0 encoded string, or any other superset of \s-1ASCII.\s0 .Sp If \f(CW$enable\fR is false, then the \f(CW\*(C`encode\*(C'\fR method will not escape Unicode characters unless required by the \s-1JSON\s0 syntax or other flags. This results in a faster and more compact format. .Sp See also the section \fI\s-1ENCODING/CODESET FLAG NOTES\s0\fR later in this document. .Sp The main use for this flag is to produce \s-1JSON\s0 texts that can be transmitted over a 7\-bit channel, as the encoded \s-1JSON\s0 texts will not contain any 8 bit characters. .Sp .Vb 2 \& JSON::XS\->new\->ascii (1)\->encode ([chr 0x10401]) \& => ["\eud801\eudc01"] .Ve .ie n .IP "$json = $json\->latin1 ([$enable])" 4 .el .IP "\f(CW$json\fR = \f(CW$json\fR\->latin1 ([$enable])" 4 .IX Item "$json = $json->latin1 ([$enable])" .PD 0 .ie n .IP "$enabled = $json\->get_latin1" 4 .el .IP "\f(CW$enabled\fR = \f(CW$json\fR\->get_latin1" 4 .IX Item "$enabled = $json->get_latin1" .PD If \f(CW$enable\fR is true (or missing), then the \f(CW\*(C`encode\*(C'\fR method will encode the resulting \s-1JSON\s0 text as latin1 (or iso\-8859\-1), escaping any characters outside the code range \f(CW0..255\fR. The resulting string can be treated as a latin1\-encoded \s-1JSON\s0 text or a native Unicode string. The \f(CW\*(C`decode\*(C'\fR method will not be affected in any way by this flag, as \f(CW\*(C`decode\*(C'\fR by default expects Unicode, which is a strict superset of latin1. .Sp If \f(CW$enable\fR is false, then the \f(CW\*(C`encode\*(C'\fR method will not escape Unicode characters unless required by the \s-1JSON\s0 syntax or other flags. .Sp See also the section \fI\s-1ENCODING/CODESET FLAG NOTES\s0\fR later in this document. .Sp The main use for this flag is efficiently encoding binary data as \s-1JSON\s0 text, as most octets will not be escaped, resulting in a smaller encoded size. The disadvantage is that the resulting \s-1JSON\s0 text is encoded in latin1 (and must correctly be treated as such when storing and transferring), a rare encoding for \s-1JSON.\s0 It is therefore most useful when you want to store data structures known to contain binary data efficiently in files or databases, not when talking to other \s-1JSON\s0 encoders/decoders. .Sp .Vb 2 \& JSON::XS\->new\->latin1\->encode (["\ex{89}\ex{abc}"] \& => ["\ex{89}\e\eu0abc"] # (perl syntax, U+abc escaped, U+89 not) .Ve .ie n .IP "$json = $json\->utf8 ([$enable])" 4 .el .IP "\f(CW$json\fR = \f(CW$json\fR\->utf8 ([$enable])" 4 .IX Item "$json = $json->utf8 ([$enable])" .PD 0 .ie n .IP "$enabled = $json\->get_utf8" 4 .el .IP "\f(CW$enabled\fR = \f(CW$json\fR\->get_utf8" 4 .IX Item "$enabled = $json->get_utf8" .PD If \f(CW$enable\fR is true (or missing), then the \f(CW\*(C`encode\*(C'\fR method will encode the \s-1JSON\s0 result into \s-1UTF\-8,\s0 as required by many protocols, while the \&\f(CW\*(C`decode\*(C'\fR method expects to be handed a UTF\-8\-encoded string. Please note that UTF\-8\-encoded strings do not contain any characters outside the range \f(CW0..255\fR, they are thus useful for bytewise/binary I/O. In future versions, enabling this option might enable autodetection of the \s-1UTF\-16\s0 and \s-1UTF\-32\s0 encoding families, as described in \s-1RFC4627.\s0 .Sp If \f(CW$enable\fR is false, then the \f(CW\*(C`encode\*(C'\fR method will return the \s-1JSON\s0 string as a (non-encoded) Unicode string, while \f(CW\*(C`decode\*(C'\fR expects thus a Unicode string. Any decoding or encoding (e.g. to \s-1UTF\-8\s0 or \s-1UTF\-16\s0) needs to be done yourself, e.g. using the Encode module. .Sp See also the section \fI\s-1ENCODING/CODESET FLAG NOTES\s0\fR later in this document. .Sp Example, output UTF\-16BE\-encoded \s-1JSON:\s0 .Sp .Vb 2 \& use Encode; \& $jsontext = encode "UTF\-16BE", JSON::XS\->new\->encode ($object); .Ve .Sp Example, decode UTF\-32LE\-encoded \s-1JSON:\s0 .Sp .Vb 2 \& use Encode; \& $object = JSON::XS\->new\->decode (decode "UTF\-32LE", $jsontext); .Ve .ie n .IP "$json = $json\->pretty ([$enable])" 4 .el .IP "\f(CW$json\fR = \f(CW$json\fR\->pretty ([$enable])" 4 .IX Item "$json = $json->pretty ([$enable])" This enables (or disables) all of the \f(CW\*(C`indent\*(C'\fR, \f(CW\*(C`space_before\*(C'\fR and \&\f(CW\*(C`space_after\*(C'\fR (and in the future possibly more) flags in one call to generate the most readable (or most compact) form possible. .Sp Example, pretty-print some simple structure: .Sp .Vb 8 \& my $json = JSON::XS\->new\->pretty(1)\->encode ({a => [1,2]}) \& => \& { \& "a" : [ \& 1, \& 2 \& ] \& } .Ve .ie n .IP "$json = $json\->indent ([$enable])" 4 .el .IP "\f(CW$json\fR = \f(CW$json\fR\->indent ([$enable])" 4 .IX Item "$json = $json->indent ([$enable])" .PD 0 .ie n .IP "$enabled = $json\->get_indent" 4 .el .IP "\f(CW$enabled\fR = \f(CW$json\fR\->get_indent" 4 .IX Item "$enabled = $json->get_indent" .PD If \f(CW$enable\fR is true (or missing), then the \f(CW\*(C`encode\*(C'\fR method will use a multiline format as output, putting every array member or object/hash key-value pair into its own line, indenting them properly. .Sp If \f(CW$enable\fR is false, no newlines or indenting will be produced, and the resulting \s-1JSON\s0 text is guaranteed not to contain any \f(CW\*(C`newlines\*(C'\fR. .Sp This setting has no effect when decoding \s-1JSON\s0 texts. .ie n .IP "$json = $json\->space_before ([$enable])" 4 .el .IP "\f(CW$json\fR = \f(CW$json\fR\->space_before ([$enable])" 4 .IX Item "$json = $json->space_before ([$enable])" .PD 0 .ie n .IP "$enabled = $json\->get_space_before" 4 .el .IP "\f(CW$enabled\fR = \f(CW$json\fR\->get_space_before" 4 .IX Item "$enabled = $json->get_space_before" .PD If \f(CW$enable\fR is true (or missing), then the \f(CW\*(C`encode\*(C'\fR method will add an extra optional space before the \f(CW\*(C`:\*(C'\fR separating keys from values in \s-1JSON\s0 objects. .Sp If \f(CW$enable\fR is false, then the \f(CW\*(C`encode\*(C'\fR method will not add any extra space at those places. .Sp This setting has no effect when decoding \s-1JSON\s0 texts. You will also most likely combine this setting with \f(CW\*(C`space_after\*(C'\fR. .Sp Example, space_before enabled, space_after and indent disabled: .Sp .Vb 1 \& {"key" :"value"} .Ve .ie n .IP "$json = $json\->space_after ([$enable])" 4 .el .IP "\f(CW$json\fR = \f(CW$json\fR\->space_after ([$enable])" 4 .IX Item "$json = $json->space_after ([$enable])" .PD 0 .ie n .IP "$enabled = $json\->get_space_after" 4 .el .IP "\f(CW$enabled\fR = \f(CW$json\fR\->get_space_after" 4 .IX Item "$enabled = $json->get_space_after" .PD If \f(CW$enable\fR is true (or missing), then the \f(CW\*(C`encode\*(C'\fR method will add an extra optional space after the \f(CW\*(C`:\*(C'\fR separating keys from values in \s-1JSON\s0 objects and extra whitespace after the \f(CW\*(C`,\*(C'\fR separating key-value pairs and array members. .Sp If \f(CW$enable\fR is false, then the \f(CW\*(C`encode\*(C'\fR method will not add any extra space at those places. .Sp This setting has no effect when decoding \s-1JSON\s0 texts. .Sp Example, space_before and indent disabled, space_after enabled: .Sp .Vb 1 \& {"key": "value"} .Ve .ie n .IP "$json = $json\->relaxed ([$enable])" 4 .el .IP "\f(CW$json\fR = \f(CW$json\fR\->relaxed ([$enable])" 4 .IX Item "$json = $json->relaxed ([$enable])" .PD 0 .ie n .IP "$enabled = $json\->get_relaxed" 4 .el .IP "\f(CW$enabled\fR = \f(CW$json\fR\->get_relaxed" 4 .IX Item "$enabled = $json->get_relaxed" .PD If \f(CW$enable\fR is true (or missing), then \f(CW\*(C`decode\*(C'\fR will accept some extensions to normal \s-1JSON\s0 syntax (see below). \f(CW\*(C`encode\*(C'\fR will not be affected in any way. \fIBe aware that this option makes you accept invalid \&\s-1JSON\s0 texts as if they were valid!\fR. I suggest only to use this option to parse application-specific files written by humans (configuration files, resource files etc.) .Sp If \f(CW$enable\fR is false (the default), then \f(CW\*(C`decode\*(C'\fR will only accept valid \s-1JSON\s0 texts. .Sp Currently accepted extensions are: .RS 4 .IP "\(bu" 4 list items can have an end-comma .Sp \&\s-1JSON\s0 \fIseparates\fR array elements and key-value pairs with commas. This can be annoying if you write \s-1JSON\s0 texts manually and want to be able to quickly append elements, so this extension accepts comma at the end of such items not just between them: .Sp .Vb 8 \& [ \& 1, \& 2, <\- this comma not normally allowed \& ] \& { \& "k1": "v1", \& "k2": "v2", <\- this comma not normally allowed \& } .Ve .IP "\(bu" 4 shell-style '#'\-comments .Sp Whenever \s-1JSON\s0 allows whitespace, shell-style comments are additionally allowed. They are terminated by the first carriage-return or line-feed character, after which more white-space and comments are allowed. .Sp .Vb 4 \& [ \& 1, # this comment not allowed in JSON \& # neither this one... \& ] .Ve .IP "\(bu" 4 literal \s-1ASCII TAB\s0 characters in strings .Sp Literal \s-1ASCII TAB\s0 characters are now allowed in strings (and treated as \&\f(CW\*(C`\et\*(C'\fR). .Sp .Vb 4 \& [ \& "Hello\etWorld", \& "Hello<TAB>World", # literal <TAB> would not normally be allowed \& ] .Ve .RE .RS 4 .RE .ie n .IP "$json = $json\->canonical ([$enable])" 4 .el .IP "\f(CW$json\fR = \f(CW$json\fR\->canonical ([$enable])" 4 .IX Item "$json = $json->canonical ([$enable])" .PD 0 .ie n .IP "$enabled = $json\->get_canonical" 4 .el .IP "\f(CW$enabled\fR = \f(CW$json\fR\->get_canonical" 4 .IX Item "$enabled = $json->get_canonical" .PD If \f(CW$enable\fR is true (or missing), then the \f(CW\*(C`encode\*(C'\fR method will output \s-1JSON\s0 objects by sorting their keys. This is adding a comparatively high overhead. .Sp If \f(CW$enable\fR is false, then the \f(CW\*(C`encode\*(C'\fR method will output key-value pairs in the order Perl stores them (which will likely change between runs of the same script, and can change even within the same run from 5.18 onwards). .Sp This option is useful if you want the same data structure to be encoded as the same \s-1JSON\s0 text (given the same overall settings). If it is disabled, the same hash might be encoded differently even if contains the same data, as key-value pairs have no inherent ordering in Perl. .Sp This setting has no effect when decoding \s-1JSON\s0 texts. .Sp This setting has currently no effect on tied hashes. .ie n .IP "$json = $json\->allow_nonref ([$enable])" 4 .el .IP "\f(CW$json\fR = \f(CW$json\fR\->allow_nonref ([$enable])" 4 .IX Item "$json = $json->allow_nonref ([$enable])" .PD 0 .ie n .IP "$enabled = $json\->get_allow_nonref" 4 .el .IP "\f(CW$enabled\fR = \f(CW$json\fR\->get_allow_nonref" 4 .IX Item "$enabled = $json->get_allow_nonref" .PD Unlike other boolean options, this opotion is enabled by default beginning with version \f(CW4.0\fR. See \*(L"\s-1SECURITY CONSIDERATIONS\*(R"\s0 for the gory details. .Sp If \f(CW$enable\fR is true (or missing), then the \f(CW\*(C`encode\*(C'\fR method can convert a non-reference into its corresponding string, number or null \s-1JSON\s0 value, which is an extension to \s-1RFC4627.\s0 Likewise, \f(CW\*(C`decode\*(C'\fR will accept those \s-1JSON\s0 values instead of croaking. .Sp If \f(CW$enable\fR is false, then the \f(CW\*(C`encode\*(C'\fR method will croak if it isn't passed an arrayref or hashref, as \s-1JSON\s0 texts must either be an object or array. Likewise, \f(CW\*(C`decode\*(C'\fR will croak if given something that is not a \&\s-1JSON\s0 object or array. .Sp Example, encode a Perl scalar as \s-1JSON\s0 value without enabled \f(CW\*(C`allow_nonref\*(C'\fR, resulting in an error: .Sp .Vb 2 \& JSON::XS\->new\->allow_nonref (0)\->encode ("Hello, World!") \& => hash\- or arrayref expected... .Ve .ie n .IP "$json = $json\->allow_unknown ([$enable])" 4 .el .IP "\f(CW$json\fR = \f(CW$json\fR\->allow_unknown ([$enable])" 4 .IX Item "$json = $json->allow_unknown ([$enable])" .PD 0 .ie n .IP "$enabled = $json\->get_allow_unknown" 4 .el .IP "\f(CW$enabled\fR = \f(CW$json\fR\->get_allow_unknown" 4 .IX Item "$enabled = $json->get_allow_unknown" .PD If \f(CW$enable\fR is true (or missing), then \f(CW\*(C`encode\*(C'\fR will \fInot\fR throw an exception when it encounters values it cannot represent in \s-1JSON\s0 (for example, filehandles) but instead will encode a \s-1JSON\s0 \f(CW\*(C`null\*(C'\fR value. Note that blessed objects are not included here and are handled separately by c<allow_nonref>. .Sp If \f(CW$enable\fR is false (the default), then \f(CW\*(C`encode\*(C'\fR will throw an exception when it encounters anything it cannot encode as \s-1JSON.\s0 .Sp This option does not affect \f(CW\*(C`decode\*(C'\fR in any way, and it is recommended to leave it off unless you know your communications partner. .ie n .IP "$json = $json\->allow_blessed ([$enable])" 4 .el .IP "\f(CW$json\fR = \f(CW$json\fR\->allow_blessed ([$enable])" 4 .IX Item "$json = $json->allow_blessed ([$enable])" .PD 0 .ie n .IP "$enabled = $json\->get_allow_blessed" 4 .el .IP "\f(CW$enabled\fR = \f(CW$json\fR\->get_allow_blessed" 4 .IX Item "$enabled = $json->get_allow_blessed" .PD See \*(L"\s-1OBJECT SERIALISATION\*(R"\s0 for details. .Sp If \f(CW$enable\fR is true (or missing), then the \f(CW\*(C`encode\*(C'\fR method will not barf when it encounters a blessed reference that it cannot convert otherwise. Instead, a \s-1JSON\s0 \f(CW\*(C`null\*(C'\fR value is encoded instead of the object. .Sp If \f(CW$enable\fR is false (the default), then \f(CW\*(C`encode\*(C'\fR will throw an exception when it encounters a blessed object that it cannot convert otherwise. .Sp This setting has no effect on \f(CW\*(C`decode\*(C'\fR. .ie n .IP "$json = $json\->convert_blessed ([$enable])" 4 .el .IP "\f(CW$json\fR = \f(CW$json\fR\->convert_blessed ([$enable])" 4 .IX Item "$json = $json->convert_blessed ([$enable])" .PD 0 .ie n .IP "$enabled = $json\->get_convert_blessed" 4 .el .IP "\f(CW$enabled\fR = \f(CW$json\fR\->get_convert_blessed" 4 .IX Item "$enabled = $json->get_convert_blessed" .PD See \*(L"\s-1OBJECT SERIALISATION\*(R"\s0 for details. .Sp If \f(CW$enable\fR is true (or missing), then \f(CW\*(C`encode\*(C'\fR, upon encountering a blessed object, will check for the availability of the \f(CW\*(C`TO_JSON\*(C'\fR method on the object's class. If found, it will be called in scalar context and the resulting scalar will be encoded instead of the object. .Sp The \f(CW\*(C`TO_JSON\*(C'\fR method may safely call die if it wants. If \f(CW\*(C`TO_JSON\*(C'\fR returns other blessed objects, those will be handled in the same way. \f(CW\*(C`TO_JSON\*(C'\fR must take care of not causing an endless recursion cycle (== crash) in this case. The name of \f(CW\*(C`TO_JSON\*(C'\fR was chosen because other methods called by the Perl core (== not by the user of the object) are usually in upper case letters and to avoid collisions with any \f(CW\*(C`to_json\*(C'\fR function or method. .Sp If \f(CW$enable\fR is false (the default), then \f(CW\*(C`encode\*(C'\fR will not consider this type of conversion. .Sp This setting has no effect on \f(CW\*(C`decode\*(C'\fR. .ie n .IP "$json = $json\->allow_tags ([$enable])" 4 .el .IP "\f(CW$json\fR = \f(CW$json\fR\->allow_tags ([$enable])" 4 .IX Item "$json = $json->allow_tags ([$enable])" .PD 0 .ie n .IP "$enabled = $json\->get_allow_tags" 4 .el .IP "\f(CW$enabled\fR = \f(CW$json\fR\->get_allow_tags" 4 .IX Item "$enabled = $json->get_allow_tags" .PD See \*(L"\s-1OBJECT SERIALISATION\*(R"\s0 for details. .Sp If \f(CW$enable\fR is true (or missing), then \f(CW\*(C`encode\*(C'\fR, upon encountering a blessed object, will check for the availability of the \f(CW\*(C`FREEZE\*(C'\fR method on the object's class. If found, it will be used to serialise the object into a nonstandard tagged \s-1JSON\s0 value (that \s-1JSON\s0 decoders cannot decode). .Sp It also causes \f(CW\*(C`decode\*(C'\fR to parse such tagged \s-1JSON\s0 values and deserialise them via a call to the \f(CW\*(C`THAW\*(C'\fR method. .Sp If \f(CW$enable\fR is false (the default), then \f(CW\*(C`encode\*(C'\fR will not consider this type of conversion, and tagged \s-1JSON\s0 values will cause a parse error in \f(CW\*(C`decode\*(C'\fR, as if tags were not part of the grammar. .ie n .IP "$json\->boolean_values ([$false, $true])" 4 .el .IP "\f(CW$json\fR\->boolean_values ([$false, \f(CW$true\fR])" 4 .IX Item "$json->boolean_values ([$false, $true])" .PD 0 .ie n .IP "($false, $true) = $json\->get_boolean_values" 4 .el .IP "($false, \f(CW$true\fR) = \f(CW$json\fR\->get_boolean_values" 4 .IX Item "($false, $true) = $json->get_boolean_values" .PD By default, \s-1JSON\s0 booleans will be decoded as overloaded \&\f(CW$Types::Serialiser::false\fR and \f(CW$Types::Serialiser::true\fR objects. .Sp With this method you can specify your own boolean values for decoding \- on decode, \s-1JSON\s0 \f(CW\*(C`false\*(C'\fR will be decoded as a copy of \f(CW$false\fR, and \s-1JSON\s0 \&\f(CW\*(C`true\*(C'\fR will be decoded as \f(CW$true\fR (\*(L"copy\*(R" here is the same thing as assigning a value to another variable, i.e. \f(CW\*(C`$copy = $false\*(C'\fR). .Sp Calling this method without any arguments will reset the booleans to their default values. .Sp \&\f(CW\*(C`get_boolean_values\*(C'\fR will return both \f(CW$false\fR and \f(CW$true\fR values, or the empty list when they are set to the default. .ie n .IP "$json = $json\->filter_json_object ([$coderef\->($hashref)])" 4 .el .IP "\f(CW$json\fR = \f(CW$json\fR\->filter_json_object ([$coderef\->($hashref)])" 4 .IX Item "$json = $json->filter_json_object ([$coderef->($hashref)])" When \f(CW$coderef\fR is specified, it will be called from \f(CW\*(C`decode\*(C'\fR each time it decodes a \s-1JSON\s0 object. The only argument is a reference to the newly-created hash. If the code reference returns a single scalar (which need not be a reference), this value (or rather a copy of it) is inserted into the deserialised data structure. If it returns an empty list (\s-1NOTE:\s0 \fInot\fR \f(CW\*(C`undef\*(C'\fR, which is a valid scalar), the original deserialised hash will be inserted. This setting can slow down decoding considerably. .Sp When \f(CW$coderef\fR is omitted or undefined, any existing callback will be removed and \f(CW\*(C`decode\*(C'\fR will not change the deserialised hash in any way. .Sp Example, convert all \s-1JSON\s0 objects into the integer 5: .Sp .Vb 6 \& my $js = JSON::XS\->new\->filter_json_object (sub { 5 }); \& # returns [5] \& $js\->decode (\*(Aq[{}]\*(Aq) \& # throw an exception because allow_nonref is not enabled \& # so a lone 5 is not allowed. \& $js\->decode (\*(Aq{"a":1, "b":2}\*(Aq); .Ve .ie n .IP "$json = $json\->filter_json_single_key_object ($key [=> $coderef\->($value)])" 4 .el .IP "\f(CW$json\fR = \f(CW$json\fR\->filter_json_single_key_object ($key [=> \f(CW$coderef\fR\->($value)])" 4 .IX Item "$json = $json->filter_json_single_key_object ($key [=> $coderef->($value)])" Works remotely similar to \f(CW\*(C`filter_json_object\*(C'\fR, but is only called for \&\s-1JSON\s0 objects having a single key named \f(CW$key\fR. .Sp This \f(CW$coderef\fR is called before the one specified via \&\f(CW\*(C`filter_json_object\*(C'\fR, if any. It gets passed the single value in the \s-1JSON\s0 object. If it returns a single value, it will be inserted into the data structure. If it returns nothing (not even \f(CW\*(C`undef\*(C'\fR but the empty list), the callback from \f(CW\*(C`filter_json_object\*(C'\fR will be called next, as if no single-key callback were specified. .Sp If \f(CW$coderef\fR is omitted or undefined, the corresponding callback will be disabled. There can only ever be one callback for a given key. .Sp As this callback gets called less often then the \f(CW\*(C`filter_json_object\*(C'\fR one, decoding speed will not usually suffer as much. Therefore, single-key objects make excellent targets to serialise Perl objects into, especially as single-key \s-1JSON\s0 objects are as close to the type-tagged value concept as \s-1JSON\s0 gets (it's basically an \s-1ID/VALUE\s0 tuple). Of course, \s-1JSON\s0 does not support this in any way, so you need to make sure your data never looks like a serialised Perl hash. .Sp Typical names for the single object key are \f(CW\*(C`_\|_class_whatever_\|_\*(C'\fR, or \&\f(CW\*(C`$_\|_dollars_are_rarely_used_\|_$\*(C'\fR or \f(CW\*(C`}ugly_brace_placement\*(C'\fR, or even things like \f(CW\*(C`_\|_class_md5sum(classname)_\|_\*(C'\fR, to reduce the risk of clashing with real hashes. .Sp Example, decode \s-1JSON\s0 objects of the form \f(CW\*(C`{ "_\|_widget_\|_" => <id> }\*(C'\fR into the corresponding \f(CW$WIDGET{<id>}\fR object: .Sp .Vb 7 \& # return whatever is in $WIDGET{5}: \& JSON::XS \& \->new \& \->filter_json_single_key_object (_\|_widget_\|_ => sub { \& $WIDGET{ $_[0] } \& }) \& \->decode (\*(Aq{"_\|_widget_\|_": 5\*(Aq) \& \& # this can be used with a TO_JSON method in some "widget" class \& # for serialisation to json: \& sub WidgetBase::TO_JSON { \& my ($self) = @_; \& \& unless ($self\->{id}) { \& $self\->{id} = ..get..some..id..; \& $WIDGET{$self\->{id}} = $self; \& } \& \& { _\|_widget_\|_ => $self\->{id} } \& } .Ve .ie n .IP "$json = $json\->shrink ([$enable])" 4 .el .IP "\f(CW$json\fR = \f(CW$json\fR\->shrink ([$enable])" 4 .IX Item "$json = $json->shrink ([$enable])" .PD 0 .ie n .IP "$enabled = $json\->get_shrink" 4 .el .IP "\f(CW$enabled\fR = \f(CW$json\fR\->get_shrink" 4 .IX Item "$enabled = $json->get_shrink" .PD Perl usually over-allocates memory a bit when allocating space for strings. This flag optionally resizes strings generated by either \&\f(CW\*(C`encode\*(C'\fR or \f(CW\*(C`decode\*(C'\fR to their minimum size possible. This can save memory when your \s-1JSON\s0 texts are either very very long or you have many short strings. It will also try to downgrade any strings to octet-form if possible: perl stores strings internally either in an encoding called UTF-X or in octet-form. The latter cannot store everything but uses less space in general (and some buggy Perl or C code might even rely on that internal representation being used). .Sp The actual definition of what shrink does might change in future versions, but it will always try to save space at the expense of time. .Sp If \f(CW$enable\fR is true (or missing), the string returned by \f(CW\*(C`encode\*(C'\fR will be shrunk-to-fit, while all strings generated by \f(CW\*(C`decode\*(C'\fR will also be shrunk-to-fit. .Sp If \f(CW$enable\fR is false, then the normal perl allocation algorithms are used. If you work with your data, then this is likely to be faster. .Sp In the future, this setting might control other things, such as converting strings that look like integers or floats into integers or floats internally (there is no difference on the Perl level), saving space. .ie n .IP "$json = $json\->max_depth ([$maximum_nesting_depth])" 4 .el .IP "\f(CW$json\fR = \f(CW$json\fR\->max_depth ([$maximum_nesting_depth])" 4 .IX Item "$json = $json->max_depth ([$maximum_nesting_depth])" .PD 0 .ie n .IP "$max_depth = $json\->get_max_depth" 4 .el .IP "\f(CW$max_depth\fR = \f(CW$json\fR\->get_max_depth" 4 .IX Item "$max_depth = $json->get_max_depth" .PD Sets the maximum nesting level (default \f(CW512\fR) accepted while encoding or decoding. If a higher nesting level is detected in \s-1JSON\s0 text or a Perl data structure, then the encoder and decoder will stop and croak at that point. .Sp Nesting level is defined by number of hash\- or arrayrefs that the encoder needs to traverse to reach a given point or the number of \f(CW\*(C`{\*(C'\fR or \f(CW\*(C`[\*(C'\fR characters without their matching closing parenthesis crossed to reach a given character in a string. .Sp Setting the maximum depth to one disallows any nesting, so that ensures that the object is only a single hash/object or array. .Sp If no argument is given, the highest possible setting will be used, which is rarely useful. .Sp Note that nesting is implemented by recursion in C. The default value has been chosen to be as large as typical operating systems allow without crashing. .Sp See \s-1SECURITY CONSIDERATIONS,\s0 below, for more info on why this is useful. .ie n .IP "$json = $json\->max_size ([$maximum_string_size])" 4 .el .IP "\f(CW$json\fR = \f(CW$json\fR\->max_size ([$maximum_string_size])" 4 .IX Item "$json = $json->max_size ([$maximum_string_size])" .PD 0 .ie n .IP "$max_size = $json\->get_max_size" 4 .el .IP "\f(CW$max_size\fR = \f(CW$json\fR\->get_max_size" 4 .IX Item "$max_size = $json->get_max_size" .PD Set the maximum length a \s-1JSON\s0 text may have (in bytes) where decoding is being attempted. The default is \f(CW0\fR, meaning no limit. When \f(CW\*(C`decode\*(C'\fR is called on a string that is longer then this many bytes, it will not attempt to decode the string but throw an exception. This setting has no effect on \f(CW\*(C`encode\*(C'\fR (yet). .Sp If no argument is given, the limit check will be deactivated (same as when \&\f(CW0\fR is specified). .Sp See \s-1SECURITY CONSIDERATIONS,\s0 below, for more info on why this is useful. .ie n .IP "$json_text = $json\->encode ($perl_scalar)" 4 .el .IP "\f(CW$json_text\fR = \f(CW$json\fR\->encode ($perl_scalar)" 4 .IX Item "$json_text = $json->encode ($perl_scalar)" Converts the given Perl value or data structure to its \s-1JSON\s0 representation. Croaks on error. .ie n .IP "$perl_scalar = $json\->decode ($json_text)" 4 .el .IP "\f(CW$perl_scalar\fR = \f(CW$json\fR\->decode ($json_text)" 4 .IX Item "$perl_scalar = $json->decode ($json_text)" The opposite of \f(CW\*(C`encode\*(C'\fR: expects a \s-1JSON\s0 text and tries to parse it, returning the resulting simple scalar or reference. Croaks on error. .ie n .IP "($perl_scalar, $characters) = $json\->decode_prefix ($json_text)" 4 .el .IP "($perl_scalar, \f(CW$characters\fR) = \f(CW$json\fR\->decode_prefix ($json_text)" 4 .IX Item "($perl_scalar, $characters) = $json->decode_prefix ($json_text)" This works like the \f(CW\*(C`decode\*(C'\fR method, but instead of raising an exception when there is trailing garbage after the first \s-1JSON\s0 object, it will silently stop parsing there and return the number of characters consumed so far. .Sp This is useful if your \s-1JSON\s0 texts are not delimited by an outer protocol and you need to know where the \s-1JSON\s0 text ends. .Sp .Vb 2 \& JSON::XS\->new\->decode_prefix ("[1] the tail") \& => ([1], 3) .Ve .SH "INCREMENTAL PARSING" .IX Header "INCREMENTAL PARSING" In some cases, there is the need for incremental parsing of \s-1JSON\s0 texts. While this module always has to keep both \s-1JSON\s0 text and resulting Perl data structure in memory at one time, it does allow you to parse a \&\s-1JSON\s0 stream incrementally. It does so by accumulating text until it has a full \s-1JSON\s0 object, which it then can decode. This process is similar to using \f(CW\*(C`decode_prefix\*(C'\fR to see if a full \s-1JSON\s0 object is available, but is much more efficient (and can be implemented with a minimum of method calls). .PP \&\s-1JSON::XS\s0 will only attempt to parse the \s-1JSON\s0 text once it is sure it has enough text to get a decisive result, using a very simple but truly incremental parser. This means that it sometimes won't stop as early as the full parser, for example, it doesn't detect mismatched parentheses. The only thing it guarantees is that it starts decoding as soon as a syntactically valid \s-1JSON\s0 text has been seen. This means you need to set resource limits (e.g. \f(CW\*(C`max_size\*(C'\fR) to ensure the parser will stop parsing in the presence if syntax errors. .PP The following methods implement this incremental parser. .ie n .IP "[void, scalar or list context] = $json\->incr_parse ([$string])" 4 .el .IP "[void, scalar or list context] = \f(CW$json\fR\->incr_parse ([$string])" 4 .IX Item "[void, scalar or list context] = $json->incr_parse ([$string])" This is the central parsing function. It can both append new text and extract objects from the stream accumulated so far (both of these functions are optional). .Sp If \f(CW$string\fR is given, then this string is appended to the already existing \s-1JSON\s0 fragment stored in the \f(CW$json\fR object. .Sp After that, if the function is called in void context, it will simply return without doing anything further. This can be used to add more text in as many chunks as you want. .Sp If the method is called in scalar context, then it will try to extract exactly \fIone\fR \s-1JSON\s0 object. If that is successful, it will return this object, otherwise it will return \f(CW\*(C`undef\*(C'\fR. If there is a parse error, this method will croak just as \f(CW\*(C`decode\*(C'\fR would do (one can then use \&\f(CW\*(C`incr_skip\*(C'\fR to skip the erroneous part). This is the most common way of using the method. .Sp And finally, in list context, it will try to extract as many objects from the stream as it can find and return them, or the empty list otherwise. For this to work, there must be no separators (other than whitespace) between the \s-1JSON\s0 objects or arrays, instead they must be concatenated back-to-back. If an error occurs, an exception will be raised as in the scalar context case. Note that in this case, any previously-parsed \s-1JSON\s0 texts will be lost. .Sp Example: Parse some \s-1JSON\s0 arrays/objects in a given string and return them. .Sp .Vb 1 \& my @objs = JSON::XS\->new\->incr_parse ("[5][7][1,2]"); .Ve .ie n .IP "$lvalue_string = $json\->incr_text" 4 .el .IP "\f(CW$lvalue_string\fR = \f(CW$json\fR\->incr_text" 4 .IX Item "$lvalue_string = $json->incr_text" This method returns the currently stored \s-1JSON\s0 fragment as an lvalue, that is, you can manipulate it. This \fIonly\fR works when a preceding call to \&\f(CW\*(C`incr_parse\*(C'\fR in \fIscalar context\fR successfully returned an object. Under all other circumstances you must not call this function (I mean it. although in simple tests it might actually work, it \fIwill\fR fail under real world conditions). As a special exception, you can also call this method before having parsed anything. .Sp That means you can only use this function to look at or manipulate text before or after complete \s-1JSON\s0 objects, not while the parser is in the middle of parsing a \s-1JSON\s0 object. .Sp This function is useful in two cases: a) finding the trailing text after a \&\s-1JSON\s0 object or b) parsing multiple \s-1JSON\s0 objects separated by non-JSON text (such as commas). .ie n .IP "$json\->incr_skip" 4 .el .IP "\f(CW$json\fR\->incr_skip" 4 .IX Item "$json->incr_skip" This will reset the state of the incremental parser and will remove the parsed text from the input buffer so far. This is useful after \&\f(CW\*(C`incr_parse\*(C'\fR died, in which case the input buffer and incremental parser state is left unchanged, to skip the text parsed so far and to reset the parse state. .Sp The difference to \f(CW\*(C`incr_reset\*(C'\fR is that only text until the parse error occurred is removed. .ie n .IP "$json\->incr_reset" 4 .el .IP "\f(CW$json\fR\->incr_reset" 4 .IX Item "$json->incr_reset" This completely resets the incremental parser, that is, after this call, it will be as if the parser had never parsed anything. .Sp This is useful if you want to repeatedly parse \s-1JSON\s0 objects and want to ignore any trailing data, which means you have to reset the parser after each successful decode. .SS "\s-1LIMITATIONS\s0" .IX Subsection "LIMITATIONS" The incremental parser is a non-exact parser: it works by gathering as much text as possible that \fIcould\fR be a valid \s-1JSON\s0 text, followed by trying to decode it. .PP That means it sometimes needs to read more data than strictly necessary to diagnose an invalid \s-1JSON\s0 text. For example, after parsing the following fragment, the parser \fIcould\fR stop with an error, as this fragment \&\fIcannot\fR be the beginning of a valid \s-1JSON\s0 text: .PP .Vb 1 \& [, .Ve .PP In reality, hopwever, the parser might continue to read data until a length limit is exceeded or it finds a closing bracket. .SS "\s-1EXAMPLES\s0" .IX Subsection "EXAMPLES" Some examples will make all this clearer. First, a simple example that works similarly to \f(CW\*(C`decode_prefix\*(C'\fR: We want to decode the \s-1JSON\s0 object at the start of a string and identify the portion after the \s-1JSON\s0 object: .PP .Vb 1 \& my $text = "[1,2,3] hello"; \& \& my $json = new JSON::XS; \& \& my $obj = $json\->incr_parse ($text) \& or die "expected JSON object or array at beginning of string"; \& \& my $tail = $json\->incr_text; \& # $tail now contains " hello" .Ve .PP Easy, isn't it? .PP Now for a more complicated example: Imagine a hypothetical protocol where you read some requests from a \s-1TCP\s0 stream, and each request is a \s-1JSON\s0 array, without any separation between them (in fact, it is often useful to use newlines as \*(L"separators\*(R", as these get interpreted as whitespace at the start of the \s-1JSON\s0 text, which makes it possible to test said protocol with \f(CW\*(C`telnet\*(C'\fR...). .PP Here is how you'd do it (it is trivial to write this in an event-based manner): .PP .Vb 1 \& my $json = new JSON::XS; \& \& # read some data from the socket \& while (sysread $socket, my $buf, 4096) { \& \& # split and decode as many requests as possible \& for my $request ($json\->incr_parse ($buf)) { \& # act on the $request \& } \& } .Ve .PP Another complicated example: Assume you have a string with \s-1JSON\s0 objects or arrays, all separated by (optional) comma characters (e.g. \f(CW\*(C`[1],[2], [3]\*(C'\fR). To parse them, we have to skip the commas between the \s-1JSON\s0 texts, and here is where the lvalue-ness of \f(CW\*(C`incr_text\*(C'\fR comes in useful: .PP .Vb 2 \& my $text = "[1],[2], [3]"; \& my $json = new JSON::XS; \& \& # void context, so no parsing done \& $json\->incr_parse ($text); \& \& # now extract as many objects as possible. note the \& # use of scalar context so incr_text can be called. \& while (my $obj = $json\->incr_parse) { \& # do something with $obj \& \& # now skip the optional comma \& $json\->incr_text =~ s/^ \es* , //x; \& } .Ve .PP Now lets go for a very complex example: Assume that you have a gigantic \&\s-1JSON\s0 array-of-objects, many gigabytes in size, and you want to parse it, but you cannot load it into memory fully (this has actually happened in the real world :). .PP Well, you lost, you have to implement your own \s-1JSON\s0 parser. But \s-1JSON::XS\s0 can still help you: You implement a (very simple) array parser and let \&\s-1JSON\s0 decode the array elements, which are all full \s-1JSON\s0 objects on their own (this wouldn't work if the array elements could be \s-1JSON\s0 numbers, for example): .PP .Vb 1 \& my $json = new JSON::XS; \& \& # open the monster \& open my $fh, "<bigfile.json" \& or die "bigfile: $!"; \& \& # first parse the initial "[" \& for (;;) { \& sysread $fh, my $buf, 65536 \& or die "read error: $!"; \& $json\->incr_parse ($buf); # void context, so no parsing \& \& # Exit the loop once we found and removed(!) the initial "[". \& # In essence, we are (ab\-)using the $json object as a simple scalar \& # we append data to. \& last if $json\->incr_text =~ s/^ \es* \e[ //x; \& } \& \& # now we have the skipped the initial "[", so continue \& # parsing all the elements. \& for (;;) { \& # in this loop we read data until we got a single JSON object \& for (;;) { \& if (my $obj = $json\->incr_parse) { \& # do something with $obj \& last; \& } \& \& # add more data \& sysread $fh, my $buf, 65536 \& or die "read error: $!"; \& $json\->incr_parse ($buf); # void context, so no parsing \& } \& \& # in this loop we read data until we either found and parsed the \& # separating "," between elements, or the final "]" \& for (;;) { \& # first skip whitespace \& $json\->incr_text =~ s/^\es*//; \& \& # if we find "]", we are done \& if ($json\->incr_text =~ s/^\e]//) { \& print "finished.\en"; \& exit; \& } \& \& # if we find ",", we can continue with the next element \& if ($json\->incr_text =~ s/^,//) { \& last; \& } \& \& # if we find anything else, we have a parse error! \& if (length $json\->incr_text) { \& die "parse error near ", $json\->incr_text; \& } \& \& # else add more data \& sysread $fh, my $buf, 65536 \& or die "read error: $!"; \& $json\->incr_parse ($buf); # void context, so no parsing \& } .Ve .PP This is a complex example, but most of the complexity comes from the fact that we are trying to be correct (bear with me if I am wrong, I never ran the above example :). .SH "MAPPING" .IX Header "MAPPING" This section describes how \s-1JSON::XS\s0 maps Perl values to \s-1JSON\s0 values and vice versa. These mappings are designed to \*(L"do the right thing\*(R" in most circumstances automatically, preserving round-tripping characteristics (what you put in comes out as something equivalent). .PP For the more enlightened: note that in the following descriptions, lowercase \fIperl\fR refers to the Perl interpreter, while uppercase \fIPerl\fR refers to the abstract Perl language itself. .SS "\s-1JSON\s0 \-> \s-1PERL\s0" .IX Subsection "JSON -> PERL" .IP "object" 4 .IX Item "object" A \s-1JSON\s0 object becomes a reference to a hash in Perl. No ordering of object keys is preserved (\s-1JSON\s0 does not preserve object key ordering itself). .IP "array" 4 .IX Item "array" A \s-1JSON\s0 array becomes a reference to an array in Perl. .IP "string" 4 .IX Item "string" A \s-1JSON\s0 string becomes a string scalar in Perl \- Unicode codepoints in \s-1JSON\s0 are represented by the same codepoints in the Perl string, so no manual decoding is necessary. .IP "number" 4 .IX Item "number" A \s-1JSON\s0 number becomes either an integer, numeric (floating point) or string scalar in perl, depending on its range and any fractional parts. On the Perl level, there is no difference between those as Perl handles all the conversion details, but an integer may take slightly less memory and might represent more values exactly than floating point numbers. .Sp If the number consists of digits only, \s-1JSON::XS\s0 will try to represent it as an integer value. If that fails, it will try to represent it as a numeric (floating point) value if that is possible without loss of precision. Otherwise it will preserve the number as a string value (in which case you lose roundtripping ability, as the \s-1JSON\s0 number will be re-encoded to a \s-1JSON\s0 string). .Sp Numbers containing a fractional or exponential part will always be represented as numeric (floating point) values, possibly at a loss of precision (in which case you might lose perfect roundtripping ability, but the \s-1JSON\s0 number will still be re-encoded as a \s-1JSON\s0 number). .Sp Note that precision is not accuracy \- binary floating point values cannot represent most decimal fractions exactly, and when converting from and to floating point, \s-1JSON::XS\s0 only guarantees precision up to but not including the least significant bit. .IP "true, false" 4 .IX Item "true, false" These \s-1JSON\s0 atoms become \f(CW\*(C`Types::Serialiser::true\*(C'\fR and \&\f(CW\*(C`Types::Serialiser::false\*(C'\fR, respectively. They are overloaded to act almost exactly like the numbers \f(CW1\fR and \f(CW0\fR. You can check whether a scalar is a \s-1JSON\s0 boolean by using the \f(CW\*(C`Types::Serialiser::is_bool\*(C'\fR function (after \f(CW\*(C`use Types::Serialier\*(C'\fR, of course). .IP "null" 4 .IX Item "null" A \s-1JSON\s0 null atom becomes \f(CW\*(C`undef\*(C'\fR in Perl. .ie n .IP "shell-style comments (""# \fItext\fP"")" 4 .el .IP "shell-style comments (\f(CW# \f(CItext\f(CW\fR)" 4 .IX Item "shell-style comments (# text)" As a nonstandard extension to the \s-1JSON\s0 syntax that is enabled by the \&\f(CW\*(C`relaxed\*(C'\fR setting, shell-style comments are allowed. They can start anywhere outside strings and go till the end of the line. .ie n .IP "tagged values (""(\fItag\fP)\fIvalue\fP"")." 4 .el .IP "tagged values (\f(CW(\f(CItag\f(CW)\f(CIvalue\f(CW\fR)." 4 .IX Item "tagged values ((tag)value)." Another nonstandard extension to the \s-1JSON\s0 syntax, enabled with the \&\f(CW\*(C`allow_tags\*(C'\fR setting, are tagged values. In this implementation, the \&\fItag\fR must be a perl package/class name encoded as a \s-1JSON\s0 string, and the \&\fIvalue\fR must be a \s-1JSON\s0 array encoding optional constructor arguments. .Sp See \*(L"\s-1OBJECT SERIALISATION\*(R"\s0, below, for details. .SS "\s-1PERL\s0 \-> \s-1JSON\s0" .IX Subsection "PERL -> JSON" The mapping from Perl to \s-1JSON\s0 is slightly more difficult, as Perl is a truly typeless language, so we can only guess which \s-1JSON\s0 type is meant by a Perl value. .IP "hash references" 4 .IX Item "hash references" Perl hash references become \s-1JSON\s0 objects. As there is no inherent ordering in hash keys (or \s-1JSON\s0 objects), they will usually be encoded in a pseudo-random order. \s-1JSON::XS\s0 can optionally sort the hash keys (determined by the \fIcanonical\fR flag), so the same datastructure will serialise to the same \s-1JSON\s0 text (given same settings and version of \&\s-1JSON::XS\s0), but this incurs a runtime overhead and is only rarely useful, e.g. when you want to compare some \s-1JSON\s0 text against another for equality. .IP "array references" 4 .IX Item "array references" Perl array references become \s-1JSON\s0 arrays. .IP "other references" 4 .IX Item "other references" Other unblessed references are generally not allowed and will cause an exception to be thrown, except for references to the integers \f(CW0\fR and \&\f(CW1\fR, which get turned into \f(CW\*(C`false\*(C'\fR and \f(CW\*(C`true\*(C'\fR atoms in \s-1JSON.\s0 .Sp Since \f(CW\*(C`JSON::XS\*(C'\fR uses the boolean model from Types::Serialiser, you can also \f(CW\*(C`use Types::Serialiser\*(C'\fR and then use \f(CW\*(C`Types::Serialiser::false\*(C'\fR and \f(CW\*(C`Types::Serialiser::true\*(C'\fR to improve readability. .Sp .Vb 2 \& use Types::Serialiser; \& encode_json [\e0, Types::Serialiser::true] # yields [false,true] .Ve .IP "Types::Serialiser::true, Types::Serialiser::false" 4 .IX Item "Types::Serialiser::true, Types::Serialiser::false" These special values from the Types::Serialiser module become \s-1JSON\s0 true and \s-1JSON\s0 false values, respectively. You can also use \f(CW\*(C`\e1\*(C'\fR and \f(CW\*(C`\e0\*(C'\fR directly if you want. .IP "blessed objects" 4 .IX Item "blessed objects" Blessed objects are not directly representable in \s-1JSON,\s0 but \f(CW\*(C`JSON::XS\*(C'\fR allows various ways of handling objects. See \*(L"\s-1OBJECT SERIALISATION\*(R"\s0, below, for details. .IP "simple scalars" 4 .IX Item "simple scalars" Simple Perl scalars (any scalar that is not a reference) are the most difficult objects to encode: \s-1JSON::XS\s0 will encode undefined scalars as \&\s-1JSON\s0 \f(CW\*(C`null\*(C'\fR values, scalars that have last been used in a string context before encoding as \s-1JSON\s0 strings, and anything else as number value: .Sp .Vb 4 \& # dump as number \& encode_json [2] # yields [2] \& encode_json [\-3.0e17] # yields [\-3e+17] \& my $value = 5; encode_json [$value] # yields [5] \& \& # used as string, so dump as string \& print $value; \& encode_json [$value] # yields ["5"] \& \& # undef becomes null \& encode_json [undef] # yields [null] .Ve .Sp You can force the type to be a \s-1JSON\s0 string by stringifying it: .Sp .Vb 4 \& my $x = 3.1; # some variable containing a number \& "$x"; # stringified \& $x .= ""; # another, more awkward way to stringify \& print $x; # perl does it for you, too, quite often .Ve .Sp You can force the type to be a \s-1JSON\s0 number by numifying it: .Sp .Vb 3 \& my $x = "3"; # some variable containing a string \& $x += 0; # numify it, ensuring it will be dumped as a number \& $x *= 1; # same thing, the choice is yours. .Ve .Sp You can not currently force the type in other, less obscure, ways. Tell me if you need this capability (but don't forget to explain why it's needed :). .Sp Note that numerical precision has the same meaning as under Perl (so binary to decimal conversion follows the same rules as in Perl, which can differ to other languages). Also, your perl interpreter might expose extensions to the floating point numbers of your platform, such as infinities or NaN's \- these cannot be represented in \s-1JSON,\s0 and it is an error to pass those in. .SS "\s-1OBJECT SERIALISATION\s0" .IX Subsection "OBJECT SERIALISATION" As \s-1JSON\s0 cannot directly represent Perl objects, you have to choose between a pure \s-1JSON\s0 representation (without the ability to deserialise the object automatically again), and a nonstandard extension to the \s-1JSON\s0 syntax, tagged values. .PP \fI\s-1SERIALISATION\s0\fR .IX Subsection "SERIALISATION" .PP What happens when \f(CW\*(C`JSON::XS\*(C'\fR encounters a Perl object depends on the \&\f(CW\*(C`allow_blessed\*(C'\fR, \f(CW\*(C`convert_blessed\*(C'\fR and \f(CW\*(C`allow_tags\*(C'\fR settings, which are used in this order: .ie n .IP "1. ""allow_tags"" is enabled and the object has a ""FREEZE"" method." 4 .el .IP "1. \f(CWallow_tags\fR is enabled and the object has a \f(CWFREEZE\fR method." 4 .IX Item "1. allow_tags is enabled and the object has a FREEZE method." In this case, \f(CW\*(C`JSON::XS\*(C'\fR uses the Types::Serialiser object serialisation protocol to create a tagged \s-1JSON\s0 value, using a nonstandard extension to the \s-1JSON\s0 syntax. .Sp This works by invoking the \f(CW\*(C`FREEZE\*(C'\fR method on the object, with the first argument being the object to serialise, and the second argument being the constant string \f(CW\*(C`JSON\*(C'\fR to distinguish it from other serialisers. .Sp The \f(CW\*(C`FREEZE\*(C'\fR method can return any number of values (i.e. zero or more). These values and the paclkage/classname of the object will then be encoded as a tagged \s-1JSON\s0 value in the following format: .Sp .Vb 1 \& ("classname")[FREEZE return values...] .Ve .Sp e.g.: .Sp .Vb 3 \& ("URI")["http://www.google.com/"] \& ("MyDate")[2013,10,29] \& ("ImageData::JPEG")["Z3...VlCg=="] .Ve .Sp For example, the hypothetical \f(CW\*(C`My::Object\*(C'\fR \f(CW\*(C`FREEZE\*(C'\fR method might use the objects \f(CW\*(C`type\*(C'\fR and \f(CW\*(C`id\*(C'\fR members to encode the object: .Sp .Vb 2 \& sub My::Object::FREEZE { \& my ($self, $serialiser) = @_; \& \& ($self\->{type}, $self\->{id}) \& } .Ve .ie n .IP "2. ""convert_blessed"" is enabled and the object has a ""TO_JSON"" method." 4 .el .IP "2. \f(CWconvert_blessed\fR is enabled and the object has a \f(CWTO_JSON\fR method." 4 .IX Item "2. convert_blessed is enabled and the object has a TO_JSON method." In this case, the \f(CW\*(C`TO_JSON\*(C'\fR method of the object is invoked in scalar context. It must return a single scalar that can be directly encoded into \&\s-1JSON.\s0 This scalar replaces the object in the \s-1JSON\s0 text. .Sp For example, the following \f(CW\*(C`TO_JSON\*(C'\fR method will convert all \s-1URI\s0 objects to \s-1JSON\s0 strings when serialised. The fatc that these values originally were \s-1URI\s0 objects is lost. .Sp .Vb 4 \& sub URI::TO_JSON { \& my ($uri) = @_; \& $uri\->as_string \& } .Ve .ie n .IP "3. ""allow_blessed"" is enabled." 4 .el .IP "3. \f(CWallow_blessed\fR is enabled." 4 .IX Item "3. allow_blessed is enabled." The object will be serialised as a \s-1JSON\s0 null value. .IP "4. none of the above" 4 .IX Item "4. none of the above" If none of the settings are enabled or the respective methods are missing, \&\f(CW\*(C`JSON::XS\*(C'\fR throws an exception. .PP \fI\s-1DESERIALISATION\s0\fR .IX Subsection "DESERIALISATION" .PP For deserialisation there are only two cases to consider: either nonstandard tagging was used, in which case \f(CW\*(C`allow_tags\*(C'\fR decides, or objects cannot be automatically be deserialised, in which case you can use postprocessing or the \f(CW\*(C`filter_json_object\*(C'\fR or \&\f(CW\*(C`filter_json_single_key_object\*(C'\fR callbacks to get some real objects our of your \s-1JSON.\s0 .PP This section only considers the tagged value case: I a tagged \s-1JSON\s0 object is encountered during decoding and \f(CW\*(C`allow_tags\*(C'\fR is disabled, a parse error will result (as if tagged values were not part of the grammar). .PP If \f(CW\*(C`allow_tags\*(C'\fR is enabled, \f(CW\*(C`JSON::XS\*(C'\fR will look up the \f(CW\*(C`THAW\*(C'\fR method of the package/classname used during serialisation (it will not attempt to load the package as a Perl module). If there is no such method, the decoding will fail with an error. .PP Otherwise, the \f(CW\*(C`THAW\*(C'\fR method is invoked with the classname as first argument, the constant string \f(CW\*(C`JSON\*(C'\fR as second argument, and all the values from the \s-1JSON\s0 array (the values originally returned by the \&\f(CW\*(C`FREEZE\*(C'\fR method) as remaining arguments. .PP The method must then return the object. While technically you can return any Perl scalar, you might have to enable the \f(CW\*(C`enable_nonref\*(C'\fR setting to make that work in all cases, so better return an actual blessed reference. .PP As an example, let's implement a \f(CW\*(C`THAW\*(C'\fR function that regenerates the \&\f(CW\*(C`My::Object\*(C'\fR from the \f(CW\*(C`FREEZE\*(C'\fR example earlier: .PP .Vb 2 \& sub My::Object::THAW { \& my ($class, $serialiser, $type, $id) = @_; \& \& $class\->new (type => $type, id => $id) \& } .Ve .SH "ENCODING/CODESET FLAG NOTES" .IX Header "ENCODING/CODESET FLAG NOTES" The interested reader might have seen a number of flags that signify encodings or codesets \- \f(CW\*(C`utf8\*(C'\fR, \f(CW\*(C`latin1\*(C'\fR and \f(CW\*(C`ascii\*(C'\fR. There seems to be some confusion on what these do, so here is a short comparison: .PP \&\f(CW\*(C`utf8\*(C'\fR controls whether the \s-1JSON\s0 text created by \f(CW\*(C`encode\*(C'\fR (and expected by \f(CW\*(C`decode\*(C'\fR) is \s-1UTF\-8\s0 encoded or not, while \f(CW\*(C`latin1\*(C'\fR and \f(CW\*(C`ascii\*(C'\fR only control whether \f(CW\*(C`encode\*(C'\fR escapes character values outside their respective codeset range. Neither of these flags conflict with each other, although some combinations make less sense than others. .PP Care has been taken to make all flags symmetrical with respect to \&\f(CW\*(C`encode\*(C'\fR and \f(CW\*(C`decode\*(C'\fR, that is, texts encoded with any combination of these flag values will be correctly decoded when the same flags are used \&\- in general, if you use different flag settings while encoding vs. when decoding you likely have a bug somewhere. .PP Below comes a verbose discussion of these flags. Note that a \*(L"codeset\*(R" is simply an abstract set of character-codepoint pairs, while an encoding takes those codepoint numbers and \fIencodes\fR them, in our case into octets. Unicode is (among other things) a codeset, \s-1UTF\-8\s0 is an encoding, and \s-1ISO\-8859\-1\s0 (= latin 1) and \s-1ASCII\s0 are both codesets \fIand\fR encodings at the same time, which can be confusing. .ie n .IP """utf8"" flag disabled" 4 .el .IP "\f(CWutf8\fR flag disabled" 4 .IX Item "utf8 flag disabled" When \f(CW\*(C`utf8\*(C'\fR is disabled (the default), then \f(CW\*(C`encode\*(C'\fR/\f(CW\*(C`decode\*(C'\fR generate and expect Unicode strings, that is, characters with high ordinal Unicode values (> 255) will be encoded as such characters, and likewise such characters are decoded as-is, no changes to them will be done, except \&\*(L"(re\-)interpreting\*(R" them as Unicode codepoints or Unicode characters, respectively (to Perl, these are the same thing in strings unless you do funny/weird/dumb stuff). .Sp This is useful when you want to do the encoding yourself (e.g. when you want to have \s-1UTF\-16\s0 encoded \s-1JSON\s0 texts) or when some other layer does the encoding for you (for example, when printing to a terminal using a filehandle that transparently encodes to \s-1UTF\-8\s0 you certainly do \s-1NOT\s0 want to \s-1UTF\-8\s0 encode your data first and have Perl encode it another time). .ie n .IP """utf8"" flag enabled" 4 .el .IP "\f(CWutf8\fR flag enabled" 4 .IX Item "utf8 flag enabled" If the \f(CW\*(C`utf8\*(C'\fR\-flag is enabled, \f(CW\*(C`encode\*(C'\fR/\f(CW\*(C`decode\*(C'\fR will encode all characters using the corresponding \s-1UTF\-8\s0 multi-byte sequence, and will expect your input strings to be encoded as \s-1UTF\-8,\s0 that is, no \*(L"character\*(R" of the input string must have any value > 255, as \s-1UTF\-8\s0 does not allow that. .Sp The \f(CW\*(C`utf8\*(C'\fR flag therefore switches between two modes: disabled means you will get a Unicode string in Perl, enabled means you get a \s-1UTF\-8\s0 encoded octet/binary string in Perl. .ie n .IP """latin1"" or ""ascii"" flags enabled" 4 .el .IP "\f(CWlatin1\fR or \f(CWascii\fR flags enabled" 4 .IX Item "latin1 or ascii flags enabled" With \f(CW\*(C`latin1\*(C'\fR (or \f(CW\*(C`ascii\*(C'\fR) enabled, \f(CW\*(C`encode\*(C'\fR will escape characters with ordinal values > 255 (> 127 with \f(CW\*(C`ascii\*(C'\fR) and encode the remaining characters as specified by the \f(CW\*(C`utf8\*(C'\fR flag. .Sp If \f(CW\*(C`utf8\*(C'\fR is disabled, then the result is also correctly encoded in those character sets (as both are proper subsets of Unicode, meaning that a Unicode string with all character values < 256 is the same thing as a \&\s-1ISO\-8859\-1\s0 string, and a Unicode string with all character values < 128 is the same thing as an \s-1ASCII\s0 string in Perl). .Sp If \f(CW\*(C`utf8\*(C'\fR is enabled, you still get a correct UTF\-8\-encoded string, regardless of these flags, just some more characters will be escaped using \&\f(CW\*(C`\euXXXX\*(C'\fR then before. .Sp Note that \s-1ISO\-8859\-1\-\s0\fIencoded\fR strings are not compatible with \s-1UTF\-8\s0 encoding, while ASCII-encoded strings are. That is because the \s-1ISO\-8859\-1\s0 encoding is \s-1NOT\s0 a subset of \s-1UTF\-8\s0 (despite the \s-1ISO\-8859\-1\s0 \fIcodeset\fR being a subset of Unicode), while \s-1ASCII\s0 is. .Sp Surprisingly, \f(CW\*(C`decode\*(C'\fR will ignore these flags and so treat all input values as governed by the \f(CW\*(C`utf8\*(C'\fR flag. If it is disabled, this allows you to decode \s-1ISO\-8859\-1\-\s0 and ASCII-encoded strings, as both strict subsets of Unicode. If it is enabled, you can correctly decode \s-1UTF\-8\s0 encoded strings. .Sp So neither \f(CW\*(C`latin1\*(C'\fR nor \f(CW\*(C`ascii\*(C'\fR are incompatible with the \f(CW\*(C`utf8\*(C'\fR flag \- they only govern when the \s-1JSON\s0 output engine escapes a character or not. .Sp The main use for \f(CW\*(C`latin1\*(C'\fR is to relatively efficiently store binary data as \s-1JSON,\s0 at the expense of breaking compatibility with most \s-1JSON\s0 decoders. .Sp The main use for \f(CW\*(C`ascii\*(C'\fR is to force the output to not contain characters with values > 127, which means you can interpret the resulting string as \s-1UTF\-8, ISO\-8859\-1, ASCII, KOI8\-R\s0 or most about any character set and 8\-bit\-encoding, and still get the same data structure back. This is useful when your channel for \s-1JSON\s0 transfer is not 8\-bit clean or the encoding might be mangled in between (e.g. in mail), and works because \s-1ASCII\s0 is a proper subset of most 8\-bit and multibyte encodings in use in the world. .SS "\s-1JSON\s0 and ECMAscript" .IX Subsection "JSON and ECMAscript" \&\s-1JSON\s0 syntax is based on how literals are represented in javascript (the not-standardised predecessor of ECMAscript) which is presumably why it is called \*(L"JavaScript Object Notation\*(R". .PP However, \s-1JSON\s0 is not a subset (and also not a superset of course) of ECMAscript (the standard) or javascript (whatever browsers actually implement). .PP If you want to use javascript's \f(CW\*(C`eval\*(C'\fR function to \*(L"parse\*(R" \s-1JSON,\s0 you might run into parse errors for valid \s-1JSON\s0 texts, or the resulting data structure might not be queryable: .PP One of the problems is that U+2028 and U+2029 are valid characters inside \&\s-1JSON\s0 strings, but are not allowed in ECMAscript string literals, so the following Perl fragment will not output something that can be guaranteed to be parsable by javascript's \f(CW\*(C`eval\*(C'\fR: .PP .Vb 1 \& use JSON::XS; \& \& print encode_json [chr 0x2028]; .Ve .PP The right fix for this is to use a proper \s-1JSON\s0 parser in your javascript programs, and not rely on \f(CW\*(C`eval\*(C'\fR (see for example Douglas Crockford's \&\fIjson2.js\fR parser). .PP If this is not an option, you can, as a stop-gap measure, simply encode to ASCII-only \s-1JSON:\s0 .PP .Vb 1 \& use JSON::XS; \& \& print JSON::XS\->new\->ascii\->encode ([chr 0x2028]); .Ve .PP Note that this will enlarge the resulting \s-1JSON\s0 text quite a bit if you have many non-ASCII characters. You might be tempted to run some regexes to only escape U+2028 and U+2029, e.g.: .PP .Vb 5 \& # DO NOT USE THIS! \& my $json = JSON::XS\->new\->utf8\->encode ([chr 0x2028]); \& $json =~ s/\exe2\ex80\exa8/\e\eu2028/g; # escape U+2028 \& $json =~ s/\exe2\ex80\exa9/\e\eu2029/g; # escape U+2029 \& print $json; .Ve .PP Note that \fIthis is a bad idea\fR: the above only works for U+2028 and U+2029 and thus only for fully ECMAscript-compliant parsers. Many existing javascript implementations, however, have issues with other characters as well \- using \f(CW\*(C`eval\*(C'\fR naively simply \fIwill\fR cause problems. .PP Another problem is that some javascript implementations reserve some property names for their own purposes (which probably makes them non-ECMAscript-compliant). For example, Iceweasel reserves the \&\f(CW\*(C`_\|_proto_\|_\*(C'\fR property name for its own purposes. .PP If that is a problem, you could parse try to filter the resulting \s-1JSON\s0 output for these property strings, e.g.: .PP .Vb 1 \& $json =~ s/"_\|_proto_\|_"\es*:/"_\|_proto_\|_renamed":/g; .Ve .PP This works because \f(CW\*(C`_\|_proto_\|_\*(C'\fR is not valid outside of strings, so every occurrence of \f(CW\*(C`"_\|_proto_\|_"\es*:\*(C'\fR must be a string used as property name. .PP If you know of other incompatibilities, please let me know. .SS "\s-1JSON\s0 and \s-1YAML\s0" .IX Subsection "JSON and YAML" You often hear that \s-1JSON\s0 is a subset of \s-1YAML.\s0 This is, however, a mass hysteria(*) and very far from the truth (as of the time of this writing), so let me state it clearly: \fIin general, there is no way to configure \&\s-1JSON::XS\s0 to output a data structure as valid \s-1YAML\s0\fR that works in all cases. .PP If you really must use \s-1JSON::XS\s0 to generate \s-1YAML,\s0 you should use this algorithm (subject to change in future versions): .PP .Vb 2 \& my $to_yaml = JSON::XS\->new\->utf8\->space_after (1); \& my $yaml = $to_yaml\->encode ($ref) . "\en"; .Ve .PP This will \fIusually\fR generate \s-1JSON\s0 texts that also parse as valid \&\s-1YAML.\s0 Please note that \s-1YAML\s0 has hardcoded limits on (simple) object key lengths that \s-1JSON\s0 doesn't have and also has different and incompatible unicode character escape syntax, so you should make sure that your hash keys are noticeably shorter than the 1024 \*(L"stream characters\*(R" \s-1YAML\s0 allows and that you do not have characters with codepoint values outside the Unicode \s-1BMP\s0 (basic multilingual page). \s-1YAML\s0 also does not allow \f(CW\*(C`\e/\*(C'\fR sequences in strings (which \s-1JSON::XS\s0 does not \fIcurrently\fR generate, but other \s-1JSON\s0 generators might). .PP There might be other incompatibilities that I am not aware of (or the \s-1YAML\s0 specification has been changed yet again \- it does so quite often). In general you should not try to generate \s-1YAML\s0 with a \s-1JSON\s0 generator or vice versa, or try to parse \s-1JSON\s0 with a \s-1YAML\s0 parser or vice versa: chances are high that you will run into severe interoperability problems when you least expect it. .IP "(*)" 4 I have been pressured multiple times by Brian Ingerson (one of the authors of the \s-1YAML\s0 specification) to remove this paragraph, despite him acknowledging that the actual incompatibilities exist. As I was personally bitten by this \*(L"\s-1JSON\s0 is \s-1YAML\*(R"\s0 lie, I refused and said I will continue to educate people about these issues, so others do not run into the same problem again and again. After this, Brian called me a (quote)\fIcomplete and worthless idiot\fR(unquote). .Sp In my opinion, instead of pressuring and insulting people who actually clarify issues with \s-1YAML\s0 and the wrong statements of some of its proponents, I would kindly suggest reading the \s-1JSON\s0 spec (which is not that difficult or long) and finally make \s-1YAML\s0 compatible to it, and educating users about the changes, instead of spreading lies about the real compatibility for many \fIyears\fR and trying to silence people who point out that it isn't true. .Sp Addendum/2009: the \s-1YAML 1.2\s0 spec is still incompatible with \s-1JSON,\s0 even though the incompatibilities have been documented (and are known to Brian) for many years and the spec makes explicit claims that \s-1YAML\s0 is a superset of \s-1JSON.\s0 It would be so easy to fix, but apparently, bullying people and corrupting userdata is so much easier. .SS "\s-1SPEED\s0" .IX Subsection "SPEED" It seems that \s-1JSON::XS\s0 is surprisingly fast, as shown in the following tables. They have been generated with the help of the \f(CW\*(C`eg/bench\*(C'\fR program in the \s-1JSON::XS\s0 distribution, to make it easy to compare on your own system. .PP First comes a comparison between various modules using a very short single-line \s-1JSON\s0 string (also available at <http://dist.schmorp.de/misc/json/short.json>). .PP .Vb 3 \& {"method": "handleMessage", "params": ["user1", \& "we were just talking"], "id": null, "array":[1,11,234,\-5,1e5,1e7, \& 1, 0]} .Ve .PP It shows the number of encodes/decodes per second (\s-1JSON::XS\s0 uses the functional interface, while \s-1JSON::XS/2\s0 uses the \s-1OO\s0 interface with pretty-printing and hashkey sorting enabled, \s-1JSON::XS/3\s0 enables shrink. \s-1JSON::DWIW/DS\s0 uses the deserialise function, while \s-1JSON::DWIW::FJ\s0 uses the from_json method). Higher is better: .PP .Vb 11 \& module | encode | decode | \& \-\-\-\-\-\-\-\-\-\-\-\-\-\-|\-\-\-\-\-\-\-\-\-\-\-\-|\-\-\-\-\-\-\-\-\-\-\-\-| \& JSON::DWIW/DS | 86302.551 | 102300.098 | \& JSON::DWIW/FJ | 86302.551 | 75983.768 | \& JSON::PP | 15827.562 | 6638.658 | \& JSON::Syck | 63358.066 | 47662.545 | \& JSON::XS | 511500.488 | 511500.488 | \& JSON::XS/2 | 291271.111 | 388361.481 | \& JSON::XS/3 | 361577.931 | 361577.931 | \& Storable | 66788.280 | 265462.278 | \& \-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-+ .Ve .PP That is, \s-1JSON::XS\s0 is almost six times faster than \s-1JSON::DWIW\s0 on encoding, about five times faster on decoding, and over thirty to seventy times faster than \s-1JSON\s0's pure perl implementation. It also compares favourably to Storable for small amounts of data. .PP Using a longer test string (roughly 18KB, generated from Yahoo! Locals search \s-1API\s0 (<http://dist.schmorp.de/misc/json/long.json>). .PP .Vb 11 \& module | encode | decode | \& \-\-\-\-\-\-\-\-\-\-\-\-\-\-|\-\-\-\-\-\-\-\-\-\-\-\-|\-\-\-\-\-\-\-\-\-\-\-\-| \& JSON::DWIW/DS | 1647.927 | 2673.916 | \& JSON::DWIW/FJ | 1630.249 | 2596.128 | \& JSON::PP | 400.640 | 62.311 | \& JSON::Syck | 1481.040 | 1524.869 | \& JSON::XS | 20661.596 | 9541.183 | \& JSON::XS/2 | 10683.403 | 9416.938 | \& JSON::XS/3 | 20661.596 | 9400.054 | \& Storable | 19765.806 | 10000.725 | \& \-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-+ .Ve .PP Again, \s-1JSON::XS\s0 leads by far (except for Storable which non-surprisingly decodes a bit faster). .PP On large strings containing lots of high Unicode characters, some modules (such as \s-1JSON::PC\s0) seem to decode faster than \s-1JSON::XS,\s0 but the result will be broken due to missing (or wrong) Unicode handling. Others refuse to decode or encode properly, so it was impossible to prepare a fair comparison table for that case. .SH "SECURITY CONSIDERATIONS" .IX Header "SECURITY CONSIDERATIONS" When you are using \s-1JSON\s0 in a protocol, talking to untrusted potentially hostile creatures requires relatively few measures. .PP First of all, your \s-1JSON\s0 decoder should be secure, that is, should not have any buffer overflows. Obviously, this module should ensure that and I am trying hard on making that true, but you never know. .PP Second, you need to avoid resource-starving attacks. That means you should limit the size of \s-1JSON\s0 texts you accept, or make sure then when your resources run out, that's just fine (e.g. by using a separate process that can crash safely). The size of a \s-1JSON\s0 text in octets or characters is usually a good indication of the size of the resources required to decode it into a Perl structure. While \s-1JSON::XS\s0 can check the size of the \s-1JSON\s0 text, it might be too late when you already have it in memory, so you might want to check the size before you accept the string. .PP Third, \s-1JSON::XS\s0 recurses using the C stack when decoding objects and arrays. The C stack is a limited resource: for instance, on my amd64 machine with 8MB of stack size I can decode around 180k nested arrays but only 14k nested \s-1JSON\s0 objects (due to perl itself recursing deeply on croak to free the temporary). If that is exceeded, the program crashes. To be conservative, the default nesting limit is set to 512. If your process has a smaller stack, you should adjust this setting accordingly with the \&\f(CW\*(C`max_depth\*(C'\fR method. .PP Something else could bomb you, too, that I forgot to think of. In that case, you get to keep the pieces. I am always open for hints, though... .PP Also keep in mind that \s-1JSON::XS\s0 might leak contents of your Perl data structures in its error messages, so when you serialise sensitive information you might want to make sure that exceptions thrown by \s-1JSON::XS\s0 will not end up in front of untrusted eyes. .PP If you are using \s-1JSON::XS\s0 to return packets to consumption by JavaScript scripts in a browser you should have a look at <http://blog.archive.jpsykes.com/47/practical\-csrf\-and\-json\-security/> to see whether you are vulnerable to some common attack vectors (which really are browser design bugs, but it is still you who will have to deal with it, as major browser developers care only for features, not about getting security right). .ie n .SS """\s-1OLD"" VS. ""NEW"" JSON\s0 (\s-1RFC4627 VS. RFC7159\s0)" .el .SS "``\s-1OLD'' VS. ``NEW'' JSON\s0 (\s-1RFC4627 VS. RFC7159\s0)" .IX Subsection "OLD VS. NEW JSON (RFC4627 VS. RFC7159)" \&\s-1JSON\s0 originally required \s-1JSON\s0 texts to represent an array or object \- scalar values were explicitly not allowed. This has changed, and versions of \s-1JSON::XS\s0 beginning with \f(CW4.0\fR reflect this by allowing scalar values by default. .PP One reason why one might not want this is that this removes a fundamental property of \s-1JSON\s0 texts, namely that they are self-delimited and self-contained, or in other words, you could take any number of \*(L"old\*(R" \&\s-1JSON\s0 texts and paste them together, and the result would be unambiguously parseable: .PP .Vb 1 \& [1,3]{"k":5}[][null] # four JSON texts, without doubt .Ve .PP By allowing scalars, this property is lost: in the following example, is this one \s-1JSON\s0 text (the number 12) or two \s-1JSON\s0 texts (the numbers 1 and 2): .PP .Vb 1 \& 12 # could be 12, or 1 and 2 .Ve .PP Another lost property of \*(L"old\*(R" \s-1JSON\s0 is that no lookahead is required to know the end of a \s-1JSON\s0 text, i.e. the \s-1JSON\s0 text definitely ended at the last \f(CW\*(C`]\*(C'\fR or \f(CW\*(C`}\*(C'\fR character, there was no need to read extra characters. .PP For example, a viable network protocol with \*(L"old\*(R" \s-1JSON\s0 was to simply exchange \s-1JSON\s0 texts without delimiter. For \*(L"new\*(R" \s-1JSON,\s0 you have to use a suitable delimiter (such as a newline) after every \s-1JSON\s0 text or ensure you never encode/decode scalar values. .PP Most protocols do work by only transferring arrays or objects, and the easiest way to avoid problems with the \*(L"new\*(R" \s-1JSON\s0 definition is to explicitly disallow scalar values in your encoder and decoder: .PP .Vb 1 \& $json_coder = JSON::XS\->new\->allow_nonref (0) .Ve .PP This is a somewhat unhappy situation, and the blame can fully be put on \&\s-1JSON\s0's inmventor, Douglas Crockford, who unilaterally changed the format in 2006 without consulting the \s-1IETF,\s0 forcing the \s-1IETF\s0 to either fork the format or go with it (as I was told, the \s-1IETF\s0 wasn't amused). .SH "RELATIONSHIP WITH I\-JSON" .IX Header "RELATIONSHIP WITH I-JSON" \&\s-1JSON\s0 is a somewhat sloppily-defined format \- it carries around obvious Javascript baggage, such as not really defining number range, probably because Javascript only has one type of numbers: \s-1IEEE 64\s0 bit floats (\*(L"binary64\*(R"). .PP For this reaosn, \s-1RFC7493\s0 defines \*(L"Internet \s-1JSON\*(R",\s0 which is a restricted subset of \s-1JSON\s0 that is supposedly more interoperable on the internet. .PP While \f(CW\*(C`JSON::XS\*(C'\fR does not offer specific support for I\-JSON, it of course accepts valid I\-JSON and by default implements some of the limitations of I\-JSON, such as parsing numbers as perl numbers, which are usually a superset of binary64 numbers. .PP To generate I\-JSON, follow these rules: .IP "\(bu" 4 always generate \s-1UTF\-8\s0 .Sp I\-JSON must be encoded in \s-1UTF\-8,\s0 the default for \f(CW\*(C`encode_json\*(C'\fR. .IP "\(bu" 4 numbers should be within \s-1IEEE 754\s0 binary64 range .Sp Basically all existing perl installations use binary64 to represent floating point numbers, so all you need to do is to avoid large integers. .IP "\(bu" 4 objects must not have duplicate keys .Sp This is trivially done, as \f(CW\*(C`JSON::XS\*(C'\fR does not allow duplicate keys. .IP "\(bu" 4 do not generate scalar \s-1JSON\s0 texts, use \f(CW\*(C`\->allow_nonref (0)\*(C'\fR .Sp I\-JSON strongly requests you to only encode arrays and objects into \s-1JSON.\s0 .IP "\(bu" 4 times should be strings in \s-1ISO 8601\s0 format .Sp There are a myriad of modules on \s-1CPAN\s0 dealing with \s-1ISO 8601\s0 \- search for \&\f(CW\*(C`ISO8601\*(C'\fR on \s-1CPAN\s0 and use one. .IP "\(bu" 4 encode binary data as base64 .Sp While it's tempting to just dump binary data as a string (and let \&\f(CW\*(C`JSON::XS\*(C'\fR do the escaping), for I\-JSON, it's \fIrecommended\fR to encode binary data as base64. .PP There are some other considerations \- read \s-1RFC7493\s0 for the details if interested. .SH "INTEROPERABILITY WITH OTHER MODULES" .IX Header "INTEROPERABILITY WITH OTHER MODULES" \&\f(CW\*(C`JSON::XS\*(C'\fR uses the Types::Serialiser module to provide boolean constants. That means that the \s-1JSON\s0 true and false values will be comaptible to true and false values of other modules that do the same, such as \s-1JSON::PP\s0 and \s-1CBOR::XS\s0. .SH "INTEROPERABILITY WITH OTHER JSON DECODERS" .IX Header "INTEROPERABILITY WITH OTHER JSON DECODERS" As long as you only serialise data that can be directly expressed in \s-1JSON,\s0 \&\f(CW\*(C`JSON::XS\*(C'\fR is incapable of generating invalid \s-1JSON\s0 output (modulo bugs, but \f(CW\*(C`JSON::XS\*(C'\fR has found more bugs in the official \s-1JSON\s0 testsuite (1) than the official \s-1JSON\s0 testsuite has found in \f(CW\*(C`JSON::XS\*(C'\fR (0)). .PP When you have trouble decoding \s-1JSON\s0 generated by this module using other decoders, then it is very likely that you have an encoding mismatch or the other decoder is broken. .PP When decoding, \f(CW\*(C`JSON::XS\*(C'\fR is strict by default and will likely catch all errors. There are currently two settings that change this: \f(CW\*(C`relaxed\*(C'\fR makes \f(CW\*(C`JSON::XS\*(C'\fR accept (but not generate) some non-standard extensions, and \f(CW\*(C`allow_tags\*(C'\fR will allow you to encode and decode Perl objects, at the cost of not outputting valid \s-1JSON\s0 anymore. .SS "\s-1TAGGED VALUE SYNTAX AND STANDARD JSON EN/DECODERS\s0" .IX Subsection "TAGGED VALUE SYNTAX AND STANDARD JSON EN/DECODERS" When you use \f(CW\*(C`allow_tags\*(C'\fR to use the extended (and also nonstandard and invalid) \s-1JSON\s0 syntax for serialised objects, and you still want to decode the generated When you want to serialise objects, you can run a regex to replace the tagged syntax by standard \s-1JSON\s0 arrays (it only works for \&\*(L"normal\*(R" package names without comma, newlines or single colons). First, the readable Perl version: .PP .Vb 2 \& # if your FREEZE methods return no values, you need this replace first: \& $json =~ s/\e( \es* (" (?: [^\e\e":,]+|\e\e.|::)* ") \es* \e) \es* \e[\es*\e]/[$1]/gx; \& \& # this works for non\-empty constructor arg lists: \& $json =~ s/\e( \es* (" (?: [^\e\e":,]+|\e\e.|::)* ") \es* \e) \es* \e[/[$1,/gx; .Ve .PP And here is a less readable version that is easy to adapt to other languages: .PP .Vb 1 \& $json =~ s/\e(\es*("([^\e\e":,]+|\e\e.|::)*")\es*\e)\es*\e[/[$1,/g; .Ve .PP Here is an ECMAScript version (same regex): .PP .Vb 1 \& json = json.replace (/\e(\es*("([^\e\e":,]+|\e\e.|::)*")\es*\e)\es*\e[/g, "[$1,"); .Ve .PP Since this syntax converts to standard \s-1JSON\s0 arrays, it might be hard to distinguish serialised objects from normal arrays. You can prepend a \&\*(L"magic number\*(R" as first array element to reduce chances of a collision: .PP .Vb 1 \& $json =~ s/\e(\es*("([^\e\e":,]+|\e\e.|::)*")\es*\e)\es*\e[/["XU1peReLzT4ggEllLanBYq4G9VzliwKF",$1,/g; .Ve .PP And after decoding the \s-1JSON\s0 text, you could walk the data structure looking for arrays with a first element of \&\f(CW\*(C`XU1peReLzT4ggEllLanBYq4G9VzliwKF\*(C'\fR. .PP The same approach can be used to create the tagged format with another encoder. First, you create an array with the magic string as first member, the classname as second, and constructor arguments last, encode it as part of your \s-1JSON\s0 structure, and then: .PP .Vb 1 \& $json =~ s/\e[\es*"XU1peReLzT4ggEllLanBYq4G9VzliwKF"\es*,\es*("([^\e\e":,]+|\e\e.|::)*")\es*,/($1)[/g; .Ve .PP Again, this has some limitations \- the magic string must not be encoded with character escapes, and the constructor arguments must be non-empty. .SH "(I\-)THREADS" .IX Header "(I-)THREADS" This module is \fInot\fR guaranteed to be ithread (or \s-1MULTIPLICITY\-\s0) safe and there are no plans to change this. Note that perl's builtin so-called threads/ithreads are officially deprecated and should not be used. .SH "THE PERILS OF SETLOCALE" .IX Header "THE PERILS OF SETLOCALE" Sometimes people avoid the Perl locale support and directly call the system's setlocale function with \f(CW\*(C`LC_ALL\*(C'\fR. .PP This breaks both perl and modules such as \s-1JSON::XS,\s0 as stringification of numbers no longer works correctly (e.g. \f(CW\*(C`$x = 0.1; print "$x"+1\*(C'\fR might print \f(CW1\fR, and \s-1JSON::XS\s0 might output illegal \s-1JSON\s0 as \s-1JSON::XS\s0 relies on perl to stringify numbers). .PP The solution is simple: don't call \f(CW\*(C`setlocale\*(C'\fR, or use it for only those categories you need, such as \f(CW\*(C`LC_MESSAGES\*(C'\fR or \f(CW\*(C`LC_CTYPE\*(C'\fR. .PP If you need \f(CW\*(C`LC_NUMERIC\*(C'\fR, you should enable it only around the code that actually needs it (avoiding stringification of numbers), and restore it afterwards. .SH "SOME HISTORY" .IX Header "SOME HISTORY" At the time this module was created there already were a number of \s-1JSON\s0 modules available on \s-1CPAN,\s0 so what was the reason to write yet another \&\s-1JSON\s0 module? While it seems there are many \s-1JSON\s0 modules, none of them correctly handled all corner cases, and in most cases their maintainers are unresponsive, gone missing, or not listening to bug reports for other reasons. .PP Beginning with version 2.0 of the \s-1JSON\s0 module, when both \s-1JSON\s0 and \&\s-1JSON::XS\s0 are installed, then \s-1JSON\s0 will fall back on \s-1JSON::XS\s0 (this can be overridden) with no overhead due to emulation (by inheriting constructor and methods). If \s-1JSON::XS\s0 is not available, it will fall back to the compatible \s-1JSON::PP\s0 module as backend, so using \s-1JSON\s0 instead of \s-1JSON::XS\s0 gives you a portable \s-1JSON API\s0 that can be fast when you need it and doesn't require a C compiler when that is a problem. .PP Somewhere around version 3, this module was forked into \&\f(CW\*(C`Cpanel::JSON::XS\*(C'\fR, because its maintainer had serious trouble understanding \s-1JSON\s0 and insisted on a fork with many bugs \*(L"fixed\*(R" that weren't actually bugs, while spreading \s-1FUD\s0 about this module without actually giving any details on his accusations. You be the judge, but in my personal opinion, if you want quality, you will stay away from dangerous forks like that. .SH "BUGS" .IX Header "BUGS" While the goal of this module is to be correct, that unfortunately does not mean it's bug-free, only that I think its design is bug-free. If you keep reporting bugs they will be fixed swiftly, though. .PP Please refrain from using rt.cpan.org or any other bug reporting service. I put the contact address into my modules for a reason. .SH "SEE ALSO" .IX Header "SEE ALSO" The \fIjson_xs\fR command line utility for quick experiments. .SH "AUTHOR" .IX Header "AUTHOR" .Vb 2 \& Marc Lehmann <schmorp@schmorp.de> \& http://home.schmorp.de/ .Ve PK�����A0]%��%����man/man3/IO::Tty::Constant.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "Tty::Constant 3" .TH Tty::Constant 3 "2024-03-13" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" IO::Tty::Constant \- Terminal Constants (autogenerated) .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 2 \& use IO::Tty::Constant qw(TIOCNOTTY); \& ... .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" This package defines constants usually found in <termio.h> or <termios.h> (and their #include hierarchy). Find below an autogenerated alphabetic list of all known constants and whether they are defined on your system (prefixed with '+') and have compilation problems ('o'). Undefined or problematic constants are set to 'undef'. .SH "DEFINED CONSTANTS" .IX Header "DEFINED CONSTANTS" .IP "+" 4 B0 .IP "+" 4 B110 .IP "+" 4 B115200 .IP "+" 4 B1200 .IP "+" 4 B134 .IP "+" 4 B150 .IP "\-" 4 B153600 .IP "+" 4 B1800 .IP "+" 4 B19200 .IP "+" 4 B200 .IP "+" 4 B230400 .IP "+" 4 B2400 .IP "+" 4 B300 .IP "\-" 4 B307200 .IP "+" 4 B38400 .IP "+" 4 B460800 .IP "+" 4 B4800 .IP "+" 4 B50 .IP "+" 4 B57600 .IP "+" 4 B600 .IP "+" 4 B75 .IP "\-" 4 B76800 .IP "+" 4 B9600 .IP "+" 4 \&\s-1BRKINT\s0 .IP "+" 4 \&\s-1BS0\s0 .IP "+" 4 \&\s-1BS1\s0 .IP "+" 4 \&\s-1BSDLY\s0 .IP "+" 4 \&\s-1CBAUD\s0 .IP "\-" 4 \&\s-1CBAUDEXT\s0 .IP "+" 4 \&\s-1CBRK\s0 .IP "\-" 4 \&\s-1CCTS_OFLOW\s0 .IP "\-" 4 \&\s-1CDEL\s0 .IP "+" 4 \&\s-1CDSUSP\s0 .IP "+" 4 \&\s-1CEOF\s0 .IP "+" 4 \&\s-1CEOL\s0 .IP "\-" 4 \&\s-1CEOL2\s0 .IP "+" 4 \&\s-1CEOT\s0 .IP "+" 4 \&\s-1CERASE\s0 .IP "\-" 4 \&\s-1CESC\s0 .IP "+" 4 \&\s-1CFLUSH\s0 .IP "+" 4 \&\s-1CIBAUD\s0 .IP "\-" 4 \&\s-1CIBAUDEXT\s0 .IP "+" 4 \&\s-1CINTR\s0 .IP "+" 4 \&\s-1CKILL\s0 .IP "+" 4 \&\s-1CLNEXT\s0 .IP "+" 4 \&\s-1CLOCAL\s0 .IP "\-" 4 \&\s-1CNSWTCH\s0 .IP "\-" 4 \&\s-1CNUL\s0 .IP "+" 4 \&\s-1CQUIT\s0 .IP "+" 4 \&\s-1CR0\s0 .IP "+" 4 \&\s-1CR1\s0 .IP "+" 4 \&\s-1CR2\s0 .IP "+" 4 \&\s-1CR3\s0 .IP "+" 4 \&\s-1CRDLY\s0 .IP "+" 4 \&\s-1CREAD\s0 .IP "+" 4 \&\s-1CRPRNT\s0 .IP "+" 4 \&\s-1CRTSCTS\s0 .IP "\-" 4 \&\s-1CRTSXOFF\s0 .IP "\-" 4 \&\s-1CRTS_IFLOW\s0 .IP "+" 4 \&\s-1CS5\s0 .IP "+" 4 \&\s-1CS6\s0 .IP "+" 4 \&\s-1CS7\s0 .IP "+" 4 \&\s-1CS8\s0 .IP "+" 4 \&\s-1CSIZE\s0 .IP "+" 4 \&\s-1CSTART\s0 .IP "+" 4 \&\s-1CSTOP\s0 .IP "+" 4 \&\s-1CSTOPB\s0 .IP "+" 4 \&\s-1CSUSP\s0 .IP "\-" 4 \&\s-1CSWTCH\s0 .IP "+" 4 \&\s-1CWERASE\s0 .IP "\-" 4 \&\s-1DEFECHO\s0 .IP "\-" 4 \&\s-1DIOC\s0 .IP "\-" 4 \&\s-1DIOCGETP\s0 .IP "\-" 4 \&\s-1DIOCSETP\s0 .IP "\-" 4 \&\s-1DOSMODE\s0 .IP "+" 4 \&\s-1ECHO\s0 .IP "+" 4 \&\s-1ECHOCTL\s0 .IP "+" 4 \&\s-1ECHOE\s0 .IP "+" 4 \&\s-1ECHOK\s0 .IP "+" 4 \&\s-1ECHOKE\s0 .IP "+" 4 \&\s-1ECHONL\s0 .IP "+" 4 \&\s-1ECHOPRT\s0 .IP "+" 4 \&\s-1EXTA\s0 .IP "+" 4 \&\s-1EXTB\s0 .IP "+" 4 \&\s-1FF0\s0 .IP "+" 4 \&\s-1FF1\s0 .IP "+" 4 \&\s-1FFDLY\s0 .IP "\-" 4 \&\s-1FIORDCHK\s0 .IP "+" 4 \&\s-1FLUSHO\s0 .IP "+" 4 \&\s-1HUPCL\s0 .IP "+" 4 \&\s-1ICANON\s0 .IP "+" 4 \&\s-1ICRNL\s0 .IP "+" 4 \&\s-1IEXTEN\s0 .IP "+" 4 \&\s-1IGNBRK\s0 .IP "+" 4 \&\s-1IGNCR\s0 .IP "+" 4 \&\s-1IGNPAR\s0 .IP "+" 4 \&\s-1IMAXBEL\s0 .IP "+" 4 \&\s-1INLCR\s0 .IP "+" 4 \&\s-1INPCK\s0 .IP "+" 4 \&\s-1ISIG\s0 .IP "+" 4 \&\s-1ISTRIP\s0 .IP "+" 4 \&\s-1IUCLC\s0 .IP "+" 4 \&\s-1IXANY\s0 .IP "+" 4 \&\s-1IXOFF\s0 .IP "+" 4 \&\s-1IXON\s0 .IP "\-" 4 \&\s-1KBENABLED\s0 .IP "\-" 4 \&\s-1LDCHG\s0 .IP "\-" 4 \&\s-1LDCLOSE\s0 .IP "\-" 4 \&\s-1LDDMAP\s0 .IP "\-" 4 \&\s-1LDEMAP\s0 .IP "\-" 4 \&\s-1LDGETT\s0 .IP "\-" 4 \&\s-1LDGMAP\s0 .IP "\-" 4 \&\s-1LDIOC\s0 .IP "\-" 4 \&\s-1LDNMAP\s0 .IP "\-" 4 \&\s-1LDOPEN\s0 .IP "\-" 4 \&\s-1LDSETT\s0 .IP "\-" 4 \&\s-1LDSMAP\s0 .IP "\-" 4 \&\s-1LOBLK\s0 .IP "+" 4 \&\s-1NCCS\s0 .IP "+" 4 \&\s-1NL0\s0 .IP "+" 4 \&\s-1NL1\s0 .IP "+" 4 \&\s-1NLDLY\s0 .IP "+" 4 \&\s-1NOFLSH\s0 .IP "+" 4 \&\s-1OCRNL\s0 .IP "+" 4 \&\s-1OFDEL\s0 .IP "+" 4 \&\s-1OFILL\s0 .IP "+" 4 \&\s-1OLCUC\s0 .IP "+" 4 \&\s-1ONLCR\s0 .IP "+" 4 \&\s-1ONLRET\s0 .IP "+" 4 \&\s-1ONOCR\s0 .IP "+" 4 \&\s-1OPOST\s0 .IP "\-" 4 \&\s-1PAGEOUT\s0 .IP "+" 4 \&\s-1PARENB\s0 .IP "\-" 4 \&\s-1PAREXT\s0 .IP "+" 4 \&\s-1PARMRK\s0 .IP "+" 4 \&\s-1PARODD\s0 .IP "+" 4 \&\s-1PENDIN\s0 .IP "\-" 4 \&\s-1RCV1EN\s0 .IP "\-" 4 \&\s-1RTS_TOG\s0 .IP "+" 4 \&\s-1TAB0\s0 .IP "+" 4 \&\s-1TAB1\s0 .IP "+" 4 \&\s-1TAB2\s0 .IP "+" 4 \&\s-1TAB3\s0 .IP "+" 4 \&\s-1TABDLY\s0 .IP "\-" 4 \&\s-1TCDSET\s0 .IP "+" 4 \&\s-1TCFLSH\s0 .IP "+" 4 \&\s-1TCGETA\s0 .IP "+" 4 \&\s-1TCGETS\s0 .IP "+" 4 \&\s-1TCIFLUSH\s0 .IP "+" 4 \&\s-1TCIOFF\s0 .IP "+" 4 \&\s-1TCIOFLUSH\s0 .IP "+" 4 \&\s-1TCION\s0 .IP "+" 4 \&\s-1TCOFLUSH\s0 .IP "+" 4 \&\s-1TCOOFF\s0 .IP "+" 4 \&\s-1TCOON\s0 .IP "+" 4 \&\s-1TCSADRAIN\s0 .IP "+" 4 \&\s-1TCSAFLUSH\s0 .IP "+" 4 \&\s-1TCSANOW\s0 .IP "+" 4 \&\s-1TCSBRK\s0 .IP "+" 4 \&\s-1TCSETA\s0 .IP "+" 4 \&\s-1TCSETAF\s0 .IP "+" 4 \&\s-1TCSETAW\s0 .IP "\-" 4 \&\s-1TCSETCTTY\s0 .IP "+" 4 \&\s-1TCSETS\s0 .IP "+" 4 \&\s-1TCSETSF\s0 .IP "+" 4 \&\s-1TCSETSW\s0 .IP "+" 4 \&\s-1TCXONC\s0 .IP "\-" 4 \&\s-1TERM_D40\s0 .IP "\-" 4 \&\s-1TERM_D42\s0 .IP "\-" 4 \&\s-1TERM_H45\s0 .IP "\-" 4 \&\s-1TERM_NONE\s0 .IP "\-" 4 \&\s-1TERM_TEC\s0 .IP "\-" 4 \&\s-1TERM_TEX\s0 .IP "\-" 4 \&\s-1TERM_V10\s0 .IP "\-" 4 \&\s-1TERM_V61\s0 .IP "+" 4 \&\s-1TIOCCBRK\s0 .IP "\-" 4 \&\s-1TIOCCDTR\s0 .IP "+" 4 \&\s-1TIOCCONS\s0 .IP "+" 4 \&\s-1TIOCEXCL\s0 .IP "\-" 4 \&\s-1TIOCFLUSH\s0 .IP "+" 4 \&\s-1TIOCGETD\s0 .IP "\-" 4 \&\s-1TIOCGETC\s0 .IP "\-" 4 \&\s-1TIOCGETP\s0 .IP "\-" 4 \&\s-1TIOCGLTC\s0 .IP "\-" 4 \&\s-1TIOCSETC\s0 .IP "\-" 4 \&\s-1TIOCSETN\s0 .IP "\-" 4 \&\s-1TIOCSETP\s0 .IP "\-" 4 \&\s-1TIOCSLTC\s0 .IP "+" 4 \&\s-1TIOCGPGRP\s0 .IP "+" 4 \&\s-1TIOCGSID\s0 .IP "+" 4 \&\s-1TIOCGSOFTCAR\s0 .IP "+" 4 \&\s-1TIOCGWINSZ\s0 .IP "\-" 4 \&\s-1TIOCHPCL\s0 .IP "\-" 4 \&\s-1TIOCKBOF\s0 .IP "\-" 4 \&\s-1TIOCKBON\s0 .IP "\-" 4 \&\s-1TIOCLBIC\s0 .IP "\-" 4 \&\s-1TIOCLBIS\s0 .IP "\-" 4 \&\s-1TIOCLGET\s0 .IP "\-" 4 \&\s-1TIOCLSET\s0 .IP "+" 4 \&\s-1TIOCMBIC\s0 .IP "+" 4 \&\s-1TIOCMBIS\s0 .IP "+" 4 \&\s-1TIOCMGET\s0 .IP "+" 4 \&\s-1TIOCMSET\s0 .IP "+" 4 \&\s-1TIOCM_CAR\s0 .IP "+" 4 \&\s-1TIOCM_CD\s0 .IP "+" 4 \&\s-1TIOCM_CTS\s0 .IP "+" 4 \&\s-1TIOCM_DSR\s0 .IP "+" 4 \&\s-1TIOCM_DTR\s0 .IP "+" 4 \&\s-1TIOCM_LE\s0 .IP "+" 4 \&\s-1TIOCM_RI\s0 .IP "+" 4 \&\s-1TIOCM_RNG\s0 .IP "+" 4 \&\s-1TIOCM_RTS\s0 .IP "+" 4 \&\s-1TIOCM_SR\s0 .IP "+" 4 \&\s-1TIOCM_ST\s0 .IP "+" 4 \&\s-1TIOCNOTTY\s0 .IP "+" 4 \&\s-1TIOCNXCL\s0 .IP "+" 4 \&\s-1TIOCOUTQ\s0 .IP "\-" 4 \&\s-1TIOCREMOTE\s0 .IP "+" 4 \&\s-1TIOCSBRK\s0 .IP "+" 4 \&\s-1TIOCSCTTY\s0 .IP "\-" 4 \&\s-1TIOCSDTR\s0 .IP "+" 4 \&\s-1TIOCSETD\s0 .IP "\-" 4 \&\s-1TIOCSIGNAL\s0 .IP "+" 4 \&\s-1TIOCSPGRP\s0 .IP "\-" 4 \&\s-1TIOCSSID\s0 .IP "+" 4 \&\s-1TIOCSSOFTCAR\s0 .IP "\-" 4 \&\s-1TIOCSTART\s0 .IP "+" 4 \&\s-1TIOCSTI\s0 .IP "\-" 4 \&\s-1TIOCSTOP\s0 .IP "+" 4 \&\s-1TIOCSWINSZ\s0 .IP "\-" 4 \&\s-1TM_ANL\s0 .IP "\-" 4 \&\s-1TM_CECHO\s0 .IP "\-" 4 \&\s-1TM_CINVIS\s0 .IP "\-" 4 \&\s-1TM_LCF\s0 .IP "\-" 4 \&\s-1TM_NONE\s0 .IP "\-" 4 \&\s-1TM_SET\s0 .IP "\-" 4 \&\s-1TM_SNL\s0 .IP "+" 4 \&\s-1TOSTOP\s0 .IP "\-" 4 \&\s-1VCEOF\s0 .IP "\-" 4 \&\s-1VCEOL\s0 .IP "+" 4 \&\s-1VDISCARD\s0 .IP "\-" 4 \&\s-1VDSUSP\s0 .IP "+" 4 \&\s-1VEOF\s0 .IP "+" 4 \&\s-1VEOL\s0 .IP "+" 4 \&\s-1VEOL2\s0 .IP "+" 4 \&\s-1VERASE\s0 .IP "+" 4 \&\s-1VINTR\s0 .IP "+" 4 \&\s-1VKILL\s0 .IP "+" 4 \&\s-1VLNEXT\s0 .IP "+" 4 \&\s-1VMIN\s0 .IP "+" 4 \&\s-1VQUIT\s0 .IP "+" 4 \&\s-1VREPRINT\s0 .IP "+" 4 \&\s-1VSTART\s0 .IP "+" 4 \&\s-1VSTOP\s0 .IP "+" 4 \&\s-1VSUSP\s0 .IP "\-" 4 \&\s-1VSWTCH\s0 .IP "+" 4 \&\s-1VT0\s0 .IP "+" 4 \&\s-1VT1\s0 .IP "+" 4 \&\s-1VTDLY\s0 .IP "+" 4 \&\s-1VTIME\s0 .IP "+" 4 \&\s-1VWERASE\s0 .IP "\-" 4 \&\s-1WRAP\s0 .IP "+" 4 \&\s-1XCASE\s0 .IP "\-" 4 \&\s-1XCLUDE\s0 .IP "\-" 4 \&\s-1XMT1EN\s0 .IP "+" 4 \&\s-1XTABS\s0 .SH "FOR MORE INFO SEE" .IX Header "FOR MORE INFO SEE" IO::Tty PK�����A0]j, �� ����man/man3/CPAN::HandleConfig.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "CPAN::HandleConfig 3" .TH CPAN::HandleConfig 3 "2022-04-03" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" CPAN::HandleConfig \- internal configuration handling for CPAN.pm .ie n .SS """CLASS\->safe_quote ITEM""" .el .SS "\f(CWCLASS\->safe_quote ITEM\fP" .IX Subsection "CLASS->safe_quote ITEM" Quotes an item to become safe against spaces in shell interpolation. An item is enclosed in double quotes if: .PP .Vb 2 \& \- the item contains spaces in the middle \& \- the item does not start with a quote .Ve .PP This happens to avoid shell interpolation problems when whitespace is present in directory names. .PP This method uses \f(CW\*(C`commands_quote\*(C'\fR to determine the correct quote. If \f(CW\*(C`commands_quote\*(C'\fR is a space, no quoting will take place. .PP if it starts and ends with the same quote character: leave it as it is .PP if it contains no whitespace: leave it as it is .PP if it contains whitespace, then .PP if it contains quotes: better leave it as it is .PP else: quote it with the correct quote type for the box we're on .SH "LICENSE" .IX Header "LICENSE" This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. PK�����A0]L��L����man/man3/Expect.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "Expect 3" .TH Expect 3 "2024-03-08" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" Expect \- automate interactions with command line programs that expose a text terminal interface. .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use Expect; \& \& # create an Expect object by spawning another process \& my $exp = Expect\->spawn($command, @params) \& or die "Cannot spawn $command: $!\en"; \& \& # or by using an already opened filehandle (e.g. from Net::Telnet) \& my $exp = Expect\->exp_init(\e*FILEHANDLE); \& \& # if you prefer the OO mindset: \& my $exp = Expect\->new; \& $exp\->raw_pty(1); \& $exp\->spawn($command, @parameters) \& or die "Cannot spawn $command: $!\en"; \& \& # send some string there: \& $exp\->send("string\en"); \& \& # or, for the filehandle mindset: \& print $exp "string\en"; \& \& # then do some pattern matching with either the simple interface \& $patidx = $exp\->expect($timeout, @match_patterns); \& \& # or multi\-match on several spawned commands with callbacks, \& # just like the Tcl version \& $exp\->expect($timeout, \& [ qr/regex1/ => sub { my $exp = shift; \& $exp\->send("response\en"); \& exp_continue; } ], \& [ "regexp2" , \e&callback, @cbparms ], \& ); \& \& # if no longer needed, do a soft_close to nicely shut down the command \& $exp\->soft_close(); \& \& # or be less patient with \& $exp\->hard_close(); .Ve .PP Expect.pm is built to either spawn a process or take an existing filehandle and interact with it such that normally interactive tasks can be done without operator assistance. This concept makes more sense if you are already familiar with the versatile Tcl version of Expect. The public functions that make up Expect.pm are: .PP .Vb 10 \& Expect\->new() \& Expect::interconnect(@objects_to_be_read_from) \& Expect::test_handles($timeout, @objects_to_test) \& Expect::version($version_requested | undef); \& $object\->spawn(@command) \& $object\->clear_accum() \& $object\->set_accum($value) \& $object\->debug($debug_level) \& $object\->exp_internal(0 | 1) \& $object\->notransfer(0 | 1) \& $object\->raw_pty(0 | 1) \& $object\->stty(@stty_modes) # See the IO::Stty docs \& $object\->slave() \& $object\->before(); \& $object\->match(); \& $object\->after(); \& $object\->matchlist(); \& $object\->match_number(); \& $object\->error(); \& $object\->command(); \& $object\->exitstatus(); \& $object\->pty_handle(); \& $object\->do_soft_close(); \& $object\->restart_timeout_upon_receive(0 | 1); \& $object\->interact($other_object, $escape_sequence) \& $object\->log_group(0 | 1 | undef) \& $object\->log_user(0 | 1 | undef) \& $object\->log_file("filename" | $filehandle | \e&coderef | undef) \& $object\->manual_stty(0 | 1 | undef) \& $object\->match_max($max_buffersize or undef) \& $object\->pid(); \& $object\->send_slow($delay, @strings_to_send) \& $object\->set_group(@listen_group_objects | undef) \& $object\->set_seq($sequence,\e&function,\e@parameters); .Ve .PP There are several configurable package variables that affect the behavior of Expect. They are: .PP .Vb 8 \& $Expect::Debug; \& $Expect::Exp_Internal; \& $Expect::IgnoreEintr; \& $Expect::Log_Group; \& $Expect::Log_Stdout; \& $Expect::Manual_Stty; \& $Expect::Multiline_Matching; \& $Expect::Do_Soft_Close; .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" See an explanation of What is Expect <http://code-maven.com/expect> .PP The Expect module is a successor of Comm.pl and a descendent of Chat.pl. It more closely resembles the Tcl Expect language than its predecessors. It does not contain any of the networking code found in Comm.pl. I suspect this would be obsolete anyway given the advent of IO::Socket and external tools such as netcat. .PP Expect.pm is an attempt to have more of a \fBswitch()\fR & case feeling to make decision processing more fluid. Three separate types of debugging have been implemented to make code production easier. .PP It is possible to interconnect multiple file handles (and processes) much like Tcl's Expect. An attempt was made to enable all the features of Tcl's Expect without forcing Tcl on the victim programmer :\-) . .PP Please, before you consider using Expect, read the FAQs about \&\*(L"I want to automate password entry for su/ssh/scp/rsh/...\*(R" and \&\*(L"I want to use Expect to automate [anything with a buzzword]...\*(R" .SH "USAGE" .IX Header "USAGE" .IP "new" 4 .IX Item "new" Creates a new Expect object, i.e. a pty. You can change parameters on it before actually spawning a command. This is important if you want to modify the terminal settings for the slave. See \fBslave()\fR below. The object returned is actually a reblessed IO::Pty filehandle, so see there for additional methods. .IP "Expect\->exp_init(\e*FILEHANDLE) \fIor\fR" 4 .IX Item "Expect->exp_init(*FILEHANDLE) or" .PD 0 .IP "Expect\->init(\e*FILEHANDLE)" 4 .IX Item "Expect->init(*FILEHANDLE)" .PD Initializes \f(CW$new_handle_object\fR for use with other Expect functions. It must be passed a \fB_reference_\fR to \s-1FILEHANDLE\s0 if you want it to work properly. IO::File objects are preferable. Returns a reference to the newly created object. .Sp You can use only real filehandles, certain tied filehandles (e.g. Net::SSH2) that lack a \fBfileno()\fR will not work. Net::Telnet objects can be used but have been reported to work only for certain hosts. \s-1YMMV.\s0 .ie n .IP "Expect\->spawn($command, @parameters) \fIor\fR" 4 .el .IP "Expect\->spawn($command, \f(CW@parameters\fR) \fIor\fR" 4 .IX Item "Expect->spawn($command, @parameters) or" .PD 0 .ie n .IP "$object\->spawn($command, @parameters) \fIor\fR" 4 .el .IP "\f(CW$object\fR\->spawn($command, \f(CW@parameters\fR) \fIor\fR" 4 .IX Item "$object->spawn($command, @parameters) or" .ie n .IP "Expect\->new($command, @parameters)" 4 .el .IP "Expect\->new($command, \f(CW@parameters\fR)" 4 .IX Item "Expect->new($command, @parameters)" .PD Forks and execs \f(CW$command\fR. Returns an Expect object upon success or \&\f(CW\*(C`undef\*(C'\fR if the fork was unsuccessful or the command could not be found. \fBspawn()\fR passes its parameters unchanged to Perls \fBexec()\fR, so look there for detailed semantics. .Sp Note that if spawn cannot \fBexec()\fR the given command, the Expect object is still valid and the next \fBexpect()\fR will see \*(L"Cannot exec\*(R", so you can use that for error handling. .Sp Also note that you cannot reuse an object with an already spawned command, even if that command has exited. Sorry, but you have to allocate a new object... .ie n .IP "$object\->debug(0 | 1 | 2 | 3 | undef)" 4 .el .IP "\f(CW$object\fR\->debug(0 | 1 | 2 | 3 | undef)" 4 .IX Item "$object->debug(0 | 1 | 2 | 3 | undef)" Sets debug level for \f(CW$object\fR. 1 refers to general debugging information, 2 refers to verbose debugging and 0 refers to no debugging. If you call \fBdebug()\fR with no parameters it will return the current debugging level. When the object is created the debugging level will match that \f(CW$Expect::Debug\fR, normally 0. .Sp The '3' setting is new with 1.05, and adds the additional functionality of having the _full_ accumulated buffer printed every time data is read from an Expect object. This was implemented by request. I recommend against using this unless you think you need it as it can create quite a quantity of output under some circumstances.. .ie n .IP "$object\->exp_internal(1 | 0)" 4 .el .IP "\f(CW$object\fR\->exp_internal(1 | 0)" 4 .IX Item "$object->exp_internal(1 | 0)" Sets/unsets 'exp_internal' debugging. This is similar in nature to its Tcl counterpart. It is extremely valuable when debugging \fBexpect()\fR sequences. When the object is created the exp_internal setting will match the value of \&\f(CW$Expect::Exp_Internal\fR, normally 0. Returns the current setting if called without parameters. It is highly recommended that you make use of the debugging features lest you have angry code. .ie n .IP "$object\->raw_pty(1 | 0)" 4 .el .IP "\f(CW$object\fR\->raw_pty(1 | 0)" 4 .IX Item "$object->raw_pty(1 | 0)" Set pty to raw mode before spawning. This disables echoing, \s-1CR\-\s0>\s-1LF\s0 translation and an ugly hack for broken Solaris TTYs (which send <space><backspace> to slow things down) and thus gives a more pipe-like behaviour (which is important if you want to transfer binary content). Note that this must be set \fIbefore\fR spawning the program. .ie n .IP "$object\->stty(qw(mode1 mode2...))" 4 .el .IP "\f(CW$object\fR\->stty(qw(mode1 mode2...))" 4 .IX Item "$object->stty(qw(mode1 mode2...))" Sets the tty mode for \f(CW$object\fR's associated terminal to the given modes. Note that on many systems the master side of the pty is not a tty, so you have to modify the slave pty instead, see next item. This needs IO::Stty installed, which is no longer required. .ie n .IP "$object\->\fBslave()\fR" 4 .el .IP "\f(CW$object\fR\->\fBslave()\fR" 4 .IX Item "$object->slave()" Returns a filehandle to the slave part of the pty. Very useful in modifying the terminal settings: .Sp .Vb 1 \& $object\->slave\->stty(qw(raw \-echo)); .Ve .Sp Typical values are 'sane', 'raw', and 'raw \-echo'. Note that I recommend setting the terminal to 'raw' or 'raw \-echo', as this avoids a lot of hassle and gives pipe-like (i.e. transparent) behaviour (without the buffering issue). .ie n .IP "$object\->print(@strings) \fIor\fR" 4 .el .IP "\f(CW$object\fR\->print(@strings) \fIor\fR" 4 .IX Item "$object->print(@strings) or" .PD 0 .ie n .IP "$object\->send(@strings)" 4 .el .IP "\f(CW$object\fR\->send(@strings)" 4 .IX Item "$object->send(@strings)" .PD Sends the given strings to the spawned command. Note that the strings are not logged in the logfile (see print_log_file) but will probably be echoed back by the pty, depending on pty settings (default is echo) and thus end up there anyway. This must also be taken into account when \fBexpect()\fRing for an answer: the next string will be the command just sent. I suggest setting the pty to raw, which disables echo and makes the pty transparently act like a bidirectional pipe. .ie n .IP "$object\->expect($timeout, @match_patterns)" 4 .el .IP "\f(CW$object\fR\->expect($timeout, \f(CW@match_patterns\fR)" 4 .IX Item "$object->expect($timeout, @match_patterns)" .RS 4 .PD 0 .IP "Simple interface" 4 .IX Item "Simple interface" .PD Given \f(CW$timeout\fR in seconds Expect will wait for \f(CW$object\fR's handle to produce one of the match_patterns, which are matched exactly by default. If you want a regexp match, use a regexp object (\f(CW\*(C`qr//\*(C'\fR) or prefix the pattern with '\-re'. .Sp .Vb 2 \& $object\->expect(15, \*(Aqmatch me exactly\*(Aq, qr/match\es+me\es+exactly/); \& $object\->expect(15, \*(Aqmatch me exactly\*(Aq,\*(Aq\-re\*(Aq,\*(Aqmatch\es+me\es+exactly\*(Aq); .Ve .Sp Due to o/s limitations \f(CW$timeout\fR should be a round number. If \f(CW$timeout\fR is 0 Expect will check one time to see if \f(CW$object\fR's handle contains any of the match_patterns. If \f(CW$timeout\fR is undef Expect will wait forever for a pattern to match. If you don't want to explicitly put the timeout on all calls to \f(CW\*(C`expect\*(C'\fR, you can set it via the \f(CW\*(C`timeout\*(C'\fR method . If the first argument of \f(CW\*(C`expect\*(C'\fR doesn't look like a number, that value will be used. .Sp .Vb 2 \& $object\->timeout(15); \& $object\->expect(\*(Aqmatch me exactly\*(Aq,\*(Aq\-re\*(Aq,\*(Aqmatch\es+me\es+exactly\*(Aq); .Ve .Sp If called in a scalar context, \fBexpect()\fR will return the position of the matched pattern within \f(CW@matched_patterns\fR, or undef if no pattern was matched. This is a position starting from 1, so if you want to know which of an array of \f(CW@matched_patterns\fR matched you should subtract one from the return value. .Sp If called in an array context \fBexpect()\fR will return ($matched_pattern_position, \f(CW$error\fR, \f(CW$successfully_matching_string\fR, \&\f(CW$before_match\fR, and \f(CW$after_match\fR). .Sp \&\f(CW$matched_pattern_position\fR will contain the value that would have been returned if \fBexpect()\fR had been called in a scalar context. .Sp \&\f(CW$error\fR is the error that occurred that caused \fBexpect()\fR to return. \f(CW$error\fR will contain a number followed by a string equivalent expressing the nature of the error. Possible values are undef, indicating no error, \&'1:TIMEOUT' indicating that \f(CW$timeout\fR seconds had elapsed without a match, '2:EOF' indicating an eof was read from \f(CW$object\fR, '3: spawn id($fileno) died' indicating that the process exited before matching and '4:$!' indicating whatever error was set in \f(CW$ERRNO\fR during the last read on \f(CW$object\fR's handle or during \fBselect()\fR. All handles indicated by set_group plus \s-1STDOUT\s0 will have all data to come out of \f(CW$object\fR printed to them during \fBexpect()\fR if log_group and log_stdout are set. .Sp \&\f(CW$successfully_matching_string\fR \&\f(CW$before_match\fR \&\f(CW$after_match\fR .Sp Changed from older versions is the regular expression handling. By default now all strings passed to \fBexpect()\fR are treated as literals. To match a regular expression pass '\-re' as a parameter in front of the pattern you want to match as a regexp. .Sp This change makes it possible to match literals and regular expressions in the same \fBexpect()\fR call. .Sp Also new is multiline matching. ^ will now match the beginning of lines. Unfortunately, because perl doesn't use $/ in determining where lines break using $ to find the end of a line frequently doesn't work. This is because your terminal is returning \*(L"\er\en\*(R" at the end of every line. One way to check for a pattern at the end of a line would be to use \er?$ instead of $. .Sp Example: Spawning telnet to a host, you might look for the escape character. telnet would return to you \*(L"\er\enEscape character is \&'^]'.\er\en\*(R". To find this you might use \f(CW$match\fR='^Escape char.*\e.\er?$'; .Sp .Vb 1 \& $telnet\->expect(10,\*(Aq\-re\*(Aq,$match); .Ve .IP "New more Tcl/Expect\-like interface" 4 .IX Item "New more Tcl/Expect-like interface" .Vb 11 \& expect($timeout, \& \*(Aq\-i\*(Aq, [ $obj1, $obj2, ... ], \& [ $re_pattern, sub { ...; exp_continue; }, @subparms, ], \& [ \*(Aqeof\*(Aq, sub { ... } ], \& [ \*(Aqtimeout\*(Aq, sub { ... }, \e$subparm1 ], \& \*(Aq\-i\*(Aq, [ $objn, ...], \& \*(Aq\-ex\*(Aq, $exact_pattern, sub { ... }, \& $exact_pattern, sub { ...; exp_continue_timeout; }, \& \*(Aq\-re\*(Aq, $re_pattern, sub { ... }, \& \*(Aq\-i\*(Aq, \e@object_list, @pattern_list, \& ...); .Ve .Sp It's now possible to expect on more than one connection at a time by specifying '\f(CW\*(C`\-i\*(C'\fR' and a single Expect object or a ref to an array containing Expect objects, e.g. .Sp .Vb 4 \& expect($timeout, \& \*(Aq\-i\*(Aq, $exp1, @patterns_1, \& \*(Aq\-i\*(Aq, [ $exp2, $exp3 ], @patterns_2_3, \& ) .Ve .Sp Furthermore, patterns can now be specified as array refs containing [$regexp, sub { ...}, \f(CW@optional_subprams\fR] . When the pattern matches, the subroutine is called with parameters ($matched_expect_obj, \&\f(CW@optional_subparms\fR). The subroutine can return the symbol `exp_continue' to continue the expect matching with timeout starting anew or return the symbol `exp_continue_timeout' for continuing expect without resetting the timeout count. .Sp .Vb 8 \& $exp\->expect($timeout, \& [ qr/username: /i, sub { my $self = shift; \& $self\->send("$username\en"); \& exp_continue; }], \& [ qr/password: /i, sub { my $self = shift; \& $self\->send("$password\en"); \& exp_continue; }], \& $shell_prompt); .Ve .Sp `expect' is now exported by default. .RE .RS 4 .RE .ie n .IP "$object\->\fBexp_before()\fR \fIor\fR" 4 .el .IP "\f(CW$object\fR\->\fBexp_before()\fR \fIor\fR" 4 .IX Item "$object->exp_before() or" .PD 0 .ie n .IP "$object\->\fBbefore()\fR" 4 .el .IP "\f(CW$object\fR\->\fBbefore()\fR" 4 .IX Item "$object->before()" .PD \&\fBbefore()\fR returns the 'before' part of the last \fBexpect()\fR call. If the last \&\fBexpect()\fR call didn't match anything, \fBexp_before()\fR will return the entire output of the object accumulated before the \fBexpect()\fR call finished. .Sp Note that this is something different than Tcl Expects \fBbefore()\fR!! .ie n .IP "$object\->\fBexp_after()\fR \fIor\fR" 4 .el .IP "\f(CW$object\fR\->\fBexp_after()\fR \fIor\fR" 4 .IX Item "$object->exp_after() or" .PD 0 .ie n .IP "$object\->\fBafter()\fR" 4 .el .IP "\f(CW$object\fR\->\fBafter()\fR" 4 .IX Item "$object->after()" .PD returns the 'after' part of the last \fBexpect()\fR call. If the last \&\fBexpect()\fR call didn't match anything, \fBexp_after()\fR will return \fBundef()\fR. .ie n .IP "$object\->\fBexp_match()\fR \fIor\fR" 4 .el .IP "\f(CW$object\fR\->\fBexp_match()\fR \fIor\fR" 4 .IX Item "$object->exp_match() or" .PD 0 .ie n .IP "$object\->\fBmatch()\fR" 4 .el .IP "\f(CW$object\fR\->\fBmatch()\fR" 4 .IX Item "$object->match()" .PD returns the string matched by the last \fBexpect()\fR call, undef if no string was matched. .ie n .IP "$object\->\fBexp_match_number()\fR \fIor\fR" 4 .el .IP "\f(CW$object\fR\->\fBexp_match_number()\fR \fIor\fR" 4 .IX Item "$object->exp_match_number() or" .PD 0 .ie n .IP "$object\->\fBmatch_number()\fR" 4 .el .IP "\f(CW$object\fR\->\fBmatch_number()\fR" 4 .IX Item "$object->match_number()" .PD \&\fBexp_match_number()\fR returns the number of the pattern matched by the last \&\fBexpect()\fR call. Keep in mind that the first pattern in a list of patterns is 1, not 0. Returns undef if no pattern was matched. .ie n .IP "$object\->\fBexp_matchlist()\fR \fIor\fR" 4 .el .IP "\f(CW$object\fR\->\fBexp_matchlist()\fR \fIor\fR" 4 .IX Item "$object->exp_matchlist() or" .PD 0 .ie n .IP "$object\->\fBmatchlist()\fR" 4 .el .IP "\f(CW$object\fR\->\fBmatchlist()\fR" 4 .IX Item "$object->matchlist()" .PD \&\fBexp_matchlist()\fR returns a list of matched substrings from the brackets () inside the regexp that last matched. ($object\->matchlist)[0] thus corresponds to \f(CW$1\fR, ($object\->matchlist)[1] to \f(CW$2\fR, etc. .ie n .IP "$object\->\fBexp_error()\fR \fIor\fR" 4 .el .IP "\f(CW$object\fR\->\fBexp_error()\fR \fIor\fR" 4 .IX Item "$object->exp_error() or" .PD 0 .ie n .IP "$object\->\fBerror()\fR" 4 .el .IP "\f(CW$object\fR\->\fBerror()\fR" 4 .IX Item "$object->error()" .PD \&\fBexp_error()\fR returns the error generated by the last \fBexpect()\fR call if no pattern was matched. It is typically useful to examine the value returned by \&\fBbefore()\fR to find out what the output of the object was in determining why it didn't match any of the patterns. .ie n .IP "$object\->\fBclear_accum()\fR" 4 .el .IP "\f(CW$object\fR\->\fBclear_accum()\fR" 4 .IX Item "$object->clear_accum()" Clear the contents of the accumulator for \f(CW$object\fR. This gets rid of any residual contents of a handle after \fBexpect()\fR or \fBsend_slow()\fR such that the next \fBexpect()\fR call will only see new data from \f(CW$object\fR. The contents of the accumulator are returned. .ie n .IP "$object\->set_accum($value)" 4 .el .IP "\f(CW$object\fR\->set_accum($value)" 4 .IX Item "$object->set_accum($value)" Sets the content of the accumulator for \f(CW$object\fR to \f(CW$value\fR. The previous content of the accumulator is returned. .ie n .IP "$object\->\fBexp_command()\fR \fIor\fR" 4 .el .IP "\f(CW$object\fR\->\fBexp_command()\fR \fIor\fR" 4 .IX Item "$object->exp_command() or" .PD 0 .ie n .IP "$object\->\fBcommand()\fR" 4 .el .IP "\f(CW$object\fR\->\fBcommand()\fR" 4 .IX Item "$object->command()" .PD \&\fBexp_command()\fR returns the string that was used to spawn the command. Helpful for debugging and for reused patternmatch subroutines. .ie n .IP "$object\->\fBexp_exitstatus()\fR \fIor\fR" 4 .el .IP "\f(CW$object\fR\->\fBexp_exitstatus()\fR \fIor\fR" 4 .IX Item "$object->exp_exitstatus() or" .PD 0 .ie n .IP "$object\->\fBexitstatus()\fR" 4 .el .IP "\f(CW$object\fR\->\fBexitstatus()\fR" 4 .IX Item "$object->exitstatus()" .PD Returns the exit status of \f(CW$object\fR (if it already exited). .ie n .IP "$object\->\fBexp_pty_handle()\fR \fIor\fR" 4 .el .IP "\f(CW$object\fR\->\fBexp_pty_handle()\fR \fIor\fR" 4 .IX Item "$object->exp_pty_handle() or" .PD 0 .ie n .IP "$object\->\fBpty_handle()\fR" 4 .el .IP "\f(CW$object\fR\->\fBpty_handle()\fR" 4 .IX Item "$object->pty_handle()" .PD Returns a string representation of the attached pty, for example: `spawn \fBid\fR\|(5)' (pty has fileno 5), `handle \fBid\fR\|(7)' (pty was initialized from fileno 7) or `\s-1STDIN\s0'. Useful for debugging. .ie n .IP "$object\->restart_timeout_upon_receive(0 | 1)" 4 .el .IP "\f(CW$object\fR\->restart_timeout_upon_receive(0 | 1)" 4 .IX Item "$object->restart_timeout_upon_receive(0 | 1)" If this is set to 1, the expect timeout is retriggered whenever something is received from the spawned command. This allows to perform some aliveness testing and still expect for patterns. .Sp .Vb 5 \& $exp\->restart_timeout_upon_receive(1); \& $exp\->expect($timeout, \& [ timeout => \e&report_timeout ], \& [ qr/pattern/ => \e&handle_pattern], \& ); .Ve .Sp Now the timeout isn't triggered if the command produces any kind of output, i.e. is still alive, but you can act upon patterns in the output. .ie n .IP "$object\->notransfer(1 | 0)" 4 .el .IP "\f(CW$object\fR\->notransfer(1 | 0)" 4 .IX Item "$object->notransfer(1 | 0)" Do not truncate the content of the accumulator after a match. Normally, the accumulator is set to the remains that come after the matched string. Note that this setting is per object and not per pattern, so if you want to have normal acting patterns that truncate the accumulator, you have to add a .Sp .Vb 1 \& $exp\->set_accum($exp\->after); .Ve .Sp to their callback, e.g. .Sp .Vb 12 \& $exp\->notransfer(1); \& $exp\->expect($timeout, \& # accumulator not truncated, pattern1 will match again \& [ "pattern1" => sub { my $self = shift; \& ... \& } ], \& # accumulator truncated, pattern2 will not match again \& [ "pattern2" => sub { my $self = shift; \& ... \& $self\->set_accum($self\->after()); \& } ], \& ); .Ve .Sp This is only a temporary fix until I can rewrite the pattern matching part so it can take that additional \-notransfer argument. .IP "Expect::interconnect(@objects);" 4 .IX Item "Expect::interconnect(@objects);" Read from \f(CW@objects\fR and print to their \f(CW@listen_groups\fR until an escape sequence is matched from one of \f(CW@objects\fR and the associated function returns 0 or undef. The special escape sequence '\s-1EOF\s0' is matched when an object's handle returns an end of file. Note that it is not necessary to include objects that only accept data in \f(CW@objects\fR since the escape sequence is _read_ from an object. Further note that the listen_group for a write-only object is always empty. Why would you want to have objects listening to \s-1STDOUT\s0 (for example)? By default every member of \f(CW@objects\fR _as well as every member of its listen group_ will be set to 'raw \-echo' for the duration of interconnection. Setting \f(CW$object\fR\->\fBmanual_stty()\fR will stop this behavior per object. The original tty settings will be restored as interconnect exits. .Sp For a generic way to interconnect processes, take a look at IPC::Run. .IP "Expect::test_handles(@objects)" 4 .IX Item "Expect::test_handles(@objects)" Given a set of objects determines which objects' handles have data ready to be read. \fBReturns an array\fR who's members are positions in \f(CW@objects\fR that have ready handles. Returns undef if there are no such handles ready. .IP "Expect::version($version_requested or undef);" 4 .IX Item "Expect::version($version_requested or undef);" Returns current version of Expect. As of .99 earlier versions are not supported. Too many things were changed to make versioning possible. .ie n .IP "$object\->interact( ""\e*FILEHANDLE, $escape_sequence"")" 4 .el .IP "\f(CW$object\fR\->interact( \f(CW\e*FILEHANDLE, $escape_sequence\fR)" 4 .IX Item "$object->interact( *FILEHANDLE, $escape_sequence)" \&\fBinteract()\fR is essentially a macro for calling \fBinterconnect()\fR for connecting 2 processes together. \e*FILEHANDLE defaults to \e*STDIN and \&\f(CW$escape_sequence\fR defaults to undef. Interaction ceases when \f(CW$escape_sequence\fR is read from \fB\s-1FILEHANDLE\s0\fR, not \f(CW$object\fR. \f(CW$object\fR's listen group will consist solely of \e*FILEHANDLE for the duration of the interaction. \&\e*FILEHANDLE will not be echoed on \s-1STDOUT.\s0 .ie n .IP "$object\->log_group(0 | 1 | undef)" 4 .el .IP "\f(CW$object\fR\->log_group(0 | 1 | undef)" 4 .IX Item "$object->log_group(0 | 1 | undef)" Set/unset logging of \f(CW$object\fR to its 'listen group'. If set all objects in the listen group will have output from \f(CW$object\fR printed to them during \&\f(CW$object\fR\->\fBexpect()\fR, \f(CW$object\fR\->\fBsend_slow()\fR, and \f(CW\*(C`Expect::interconnect($object , ...)\*(C'\fR. Default value is on. During creation of \f(CW$object\fR the setting will match the value of \f(CW$Expect::Log_Group\fR, normally 1. .ie n .IP "$object\->log_user(0 | 1 | undef) \fIor\fR" 4 .el .IP "\f(CW$object\fR\->log_user(0 | 1 | undef) \fIor\fR" 4 .IX Item "$object->log_user(0 | 1 | undef) or" .PD 0 .ie n .IP "$object\->log_stdout(0 | 1 | undef)" 4 .el .IP "\f(CW$object\fR\->log_stdout(0 | 1 | undef)" 4 .IX Item "$object->log_stdout(0 | 1 | undef)" .PD Set/unset logging of object's handle to \s-1STDOUT.\s0 This corresponds to Tcl's log_user variable. Returns current setting if called without parameters. Default setting is off for initialized handles. When a process object is created (not a filehandle initialized with exp_init) the log_stdout setting will match the value of \f(CW$Expect::Log_Stdout\fR variable, normally 1. If/when you initialize \s-1STDIN\s0 it is usually associated with a tty which will by default echo to \s-1STDOUT\s0 anyway, so be careful or you will have multiple echoes. .ie n .IP "$object\->log_file(""filename"" | $filehandle | \e&coderef | undef)" 4 .el .IP "\f(CW$object\fR\->log_file(``filename'' | \f(CW$filehandle\fR | \e&coderef | undef)" 4 .IX Item "$object->log_file(filename | $filehandle | &coderef | undef)" Log session to a file. All characters send to or received from the spawned process are written to the file. Normally appends to the logfile, but you can pass an additional mode of \*(L"w\*(R" to truncate the file upon \fBopen()\fR: .Sp .Vb 1 \& $object\->log_file("filename", "w"); .Ve .Sp Returns the logfilehandle. .Sp If called with an undef value, stops logging and closes logfile: .Sp .Vb 1 \& $object\->log_file(undef); .Ve .Sp If called without argument, returns the logfilehandle: .Sp .Vb 1 \& $fh = $object\->log_file(); .Ve .Sp Can be set to a code ref, which will be called instead of printing to the logfile: .Sp .Vb 1 \& $object\->log_file(\e&myloggerfunc); .Ve .ie n .IP "$object\->print_log_file(@strings)" 4 .el .IP "\f(CW$object\fR\->print_log_file(@strings)" 4 .IX Item "$object->print_log_file(@strings)" Prints to logfile (if opened) or calls the logfile hook function. This allows the user to add arbitrary text to the logfile. Note that this could also be done as \f(CW$object\fR\->log_file\->\fBprint()\fR but would only work for log files, not code hooks. .ie n .IP "$object\->set_seq($sequence, \e&function, \e@function_parameters)" 4 .el .IP "\f(CW$object\fR\->set_seq($sequence, \e&function, \e@function_parameters)" 4 .IX Item "$object->set_seq($sequence, &function, @function_parameters)" During Expect\->\fBinterconnect()\fR if \f(CW$sequence\fR is read from \f(CW$object\fR &function will be executed with parameters \f(CW@function_parameters\fR. It is \fB_highly recommended_\fR that the escape sequence be a single character since the likelihood is great that the sequence will be broken into to separate reads from the \f(CW$object\fR's handle, making it impossible to strip \f(CW$sequence\fR from getting printed to \f(CW$object\fR's listen group. \e&function should be something like 'main::control_w_function' and \f(CW@function_parameters\fR should be an array defined by the caller, passed by reference to \fBset_seq()\fR. Your function should return a non-zero value if execution of \fBinterconnect()\fR is to resume after the function returns, zero or undefined if \fBinterconnect()\fR should return after your function returns. The special sequence '\s-1EOF\s0' matches the end of file being reached by \f(CW$object\fR. See \fBinterconnect()\fR for details. .ie n .IP "$object\->set_group(@listener_objects)" 4 .el .IP "\f(CW$object\fR\->set_group(@listener_objects)" 4 .IX Item "$object->set_group(@listener_objects)" \&\f(CW@listener_objects\fR is the list of objects that should have their handles printed to by \f(CW$object\fR when Expect::interconnect, \f(CW$object\fR\->\fBexpect()\fR or \&\f(CW$object\fR\->\fBsend_slow()\fR are called. Calling w/out parameters will return the current list of the listener objects. .ie n .IP "$object\->manual_stty(0 | 1 | undef)" 4 .el .IP "\f(CW$object\fR\->manual_stty(0 | 1 | undef)" 4 .IX Item "$object->manual_stty(0 | 1 | undef)" Sets/unsets whether or not Expect should make reasonable guesses as to when and how to set tty parameters for \f(CW$object\fR. Will match \&\f(CW$Expect::Manual_Stty\fR value (normally 0) when \f(CW$object\fR is created. If called without parameters \fBmanual_stty()\fR will return the current manual_stty setting. .ie n .IP "$object\->match_max($maximum_buffer_length | undef) \fIor\fR" 4 .el .IP "\f(CW$object\fR\->match_max($maximum_buffer_length | undef) \fIor\fR" 4 .IX Item "$object->match_max($maximum_buffer_length | undef) or" .PD 0 .ie n .IP "$object\->max_accum($maximum_buffer_length | undef)" 4 .el .IP "\f(CW$object\fR\->max_accum($maximum_buffer_length | undef)" 4 .IX Item "$object->max_accum($maximum_buffer_length | undef)" .PD Set the maximum accumulator size for object. This is useful if you think that the accumulator will grow out of hand during \fBexpect()\fR calls. Since the buffer will be matched by every match_pattern it may get slow if the buffer gets too large. Returns current value if called without parameters. Not defined by default. .ie n .IP "$object\->notransfer(0 | 1)" 4 .el .IP "\f(CW$object\fR\->notransfer(0 | 1)" 4 .IX Item "$object->notransfer(0 | 1)" If set, matched strings will not be deleted from the accumulator. Returns current value if called without parameters. False by default. .ie n .IP "$object\->\fBexp_pid()\fR \fIor\fR" 4 .el .IP "\f(CW$object\fR\->\fBexp_pid()\fR \fIor\fR" 4 .IX Item "$object->exp_pid() or" .PD 0 .ie n .IP "$object\->\fBpid()\fR" 4 .el .IP "\f(CW$object\fR\->\fBpid()\fR" 4 .IX Item "$object->pid()" .PD Return pid of \f(CW$object\fR, if one exists. Initialized filehandles will not have pids (of course). .ie n .IP "$object\->send_slow($delay, @strings);" 4 .el .IP "\f(CW$object\fR\->send_slow($delay, \f(CW@strings\fR);" 4 .IX Item "$object->send_slow($delay, @strings);" print each character from each string of \f(CW@strings\fR one at a time with \f(CW$delay\fR seconds before each character. This is handy for devices such as modems that can be annoying if you send them data too fast. After each character \&\f(CW$object\fR will be checked to determine whether or not it has any new data ready and if so update the accumulator for future \fBexpect()\fR calls and print the output to \s-1STDOUT\s0 and \f(CW@listen_group\fR if log_stdout and log_group are appropriately set. .SS "Configurable Package Variables:" .IX Subsection "Configurable Package Variables:" .ie n .IP "$Expect::Debug" 4 .el .IP "\f(CW$Expect::Debug\fR" 4 .IX Item "$Expect::Debug" Defaults to 0. Newly created objects have a \f(CW$object\fR\->\fBdebug()\fR value of \f(CW$Expect::Debug\fR. See \f(CW$object\fR\->\fBdebug()\fR; .ie n .IP "$Expect::Do_Soft_Close" 4 .el .IP "\f(CW$Expect::Do_Soft_Close\fR" 4 .IX Item "$Expect::Do_Soft_Close" Defaults to 0. When destroying objects, soft_close may take up to half a minute to shut everything down. From now on, only hard_close will be called, which is less polite but still gives the process a chance to terminate properly. Set this to '1' for old behaviour. .ie n .IP "$Expect::Exp_Internal" 4 .el .IP "\f(CW$Expect::Exp_Internal\fR" 4 .IX Item "$Expect::Exp_Internal" Defaults to 0. Newly created objects have a \f(CW$object\fR\->\fBexp_internal()\fR value of \f(CW$Expect::Exp_Internal\fR. See \f(CW$object\fR\->\fBexp_internal()\fR. .ie n .IP "$Expect::IgnoreEintr" 4 .el .IP "\f(CW$Expect::IgnoreEintr\fR" 4 .IX Item "$Expect::IgnoreEintr" Defaults to 0. If set to 1, when waiting for new data, Expect will ignore \s-1EINTR\s0 errors and restart the \fBselect()\fR call instead. .ie n .IP "$Expect::Log_Group" 4 .el .IP "\f(CW$Expect::Log_Group\fR" 4 .IX Item "$Expect::Log_Group" Defaults to 1. Newly created objects have a \f(CW$object\fR\->\fBlog_group()\fR value of \f(CW$Expect::Log_Group\fR. See \f(CW$object\fR\->\fBlog_group()\fR. .ie n .IP "$Expect::Log_Stdout" 4 .el .IP "\f(CW$Expect::Log_Stdout\fR" 4 .IX Item "$Expect::Log_Stdout" Defaults to 1 for spawned commands, 0 for file handles attached with \fBexp_init()\fR. Newly created objects have a \&\f(CW$object\fR\->\fBlog_stdout()\fR value of \f(CW$Expect::Log_Stdout\fR. See \&\f(CW$object\fR\->\fBlog_stdout()\fR. .ie n .IP "$Expect::Manual_Stty" 4 .el .IP "\f(CW$Expect::Manual_Stty\fR" 4 .IX Item "$Expect::Manual_Stty" Defaults to 0. Newly created objects have a \f(CW$object\fR\->\fBmanual_stty()\fR value of \f(CW$Expect::Manual_Stty\fR. See \f(CW$object\fR\->\fBmanual_stty()\fR. .ie n .IP "$Expect::Multiline_Matching" 4 .el .IP "\f(CW$Expect::Multiline_Matching\fR" 4 .IX Item "$Expect::Multiline_Matching" Defaults to 1. Affects whether or not \fBexpect()\fR uses the /m flag for doing regular expression matching. If set to 1 /m is used. .Sp This makes a difference when you are trying to match ^ and $. If you have this on you can match lines in the middle of a page of output using ^ and $ instead of it matching the beginning and end of the entire expression. I think this is handy. .Sp The \f(CW$Expect::Multiline_Matching\fR turns on and off Expect's multi-line matching mode. But this only has an effect if you pass in a string, and then use '\-re' mode. If you pass in a regular expression value (via qr//), then the qr//'s own flags are preserved irrespective of what it gets interpolated into. There was a bug in Perl 5.8.x where interpolating a regex without /m into a match with /m would incorrectly apply the /m to the inner regex too, but this was fixed in Perl 5.10. The correct behavior, as seen in Perl 5.10, is that if you pass in a regex (via qr//), then \f(CW$Expect::Multiline_Matching\fR has no effect. So if you pass in a regex, then you must use the qr's flags to control whether it is multiline (which by default it is not, opposite of the default behavior of Expect). .SH "CONTRIBUTIONS" .IX Header "CONTRIBUTIONS" Lee Eakin <leakin@japh.itg.ti.com> has ported the kibitz script from Tcl/Expect to Perl/Expect. .PP Jeff Carr <jcarr@linuxmachines.com> provided a simple example of how handle terminal window resize events (transmitted via the \s-1WINCH\s0 signal) in a ssh session. .PP You can find both scripts in the examples/ subdir. Thanks to both! .PP Historical notes: .PP There are still a few lines of code dating back to the inspirational Comm.pl and Chat.pl modules without which this would not have been possible. Kudos to Eric Arnold <Eric.Arnold@Sun.com> and Randal 'Nuke your \s-1NT\s0 box with one line of perl code' Schwartz<merlyn@stonehenge.com> for making these available to the perl public. .PP As of .98 I think all the old code is toast. No way could this have been done without it though. Special thanks to Graham Barr for helping make sense of the IO::Handle stuff as well as providing the highly recommended IO::Tty module. .SH "REFERENCES" .IX Header "REFERENCES" Mark Rogaski <rogaski@att.com> wrote: .PP \&\*(L"I figured that you'd like to know that Expect.pm has been very useful to \s-1AT&T\s0 Labs over the past couple of years (since I first talked to Austin about design decisions). We use Expect.pm for managing the switches in our network via the telnet interface, and such automation has significantly increased our reliability. So, you can honestly say that one of the largest digital networks in existence (\s-1AT&T\s0 Frame Relay) uses Expect.pm quite extensively.\*(R" .SH "FAQ \- Frequently Asked Questions" .IX Header "FAQ - Frequently Asked Questions" This is a growing collection of things that might help. Please send you questions that are not answered here to RGiersig@cpan.org .SS "What systems does Expect run on?" .IX Subsection "What systems does Expect run on?" Expect itself doesn't have real system dependencies, but the underlying IO::Tty needs pseudoterminals. IO::Stty uses \s-1POSIX\s0.pm and Fcntl.pm. .PP I have used it on Solaris, Linux and \s-1AIX,\s0 others report *BSD and \s-1OSF\s0 as working. Generally, any modern \s-1POSIX\s0 Unix should do, but there are exceptions to every rule. Feedback is appreciated. .PP See IO::Tty for a list of verified systems. .SS "Can I use this module with ActivePerl on Windows?" .IX Subsection "Can I use this module with ActivePerl on Windows?" Up to now, the answer was 'No', but this has changed. .PP You still cannot use ActivePerl, but if you use the Cygwin environment (http://sources.redhat.com), which brings its own perl, and have the latest IO::Tty (v0.05 or later) installed, it should work (feedback appreciated). .SS "The examples in the tutorial don't work!" .IX Subsection "The examples in the tutorial don't work!" The tutorial is hopelessly out of date and needs a serious overhaul. I apologize for this, I have concentrated my efforts mainly on the functionality. Volunteers welcomed. .SS "How can I find out what Expect is doing?" .IX Subsection "How can I find out what Expect is doing?" If you set .PP .Vb 1 \& $Expect::Exp_Internal = 1; .Ve .PP Expect will tell you very verbosely what it is receiving and sending, what matching it is trying and what it found. You can do this on a per-command base with .PP .Vb 1 \& $exp\->exp_internal(1); .Ve .PP You can also set .PP .Vb 1 \& $Expect::Debug = 1; # or 2, 3 for more verbose output .Ve .PP or .PP .Vb 1 \& $exp\->debug(1); .Ve .PP which gives you even more output. .SS "I am seeing the output of the command I spawned. Can I turn that off?" .IX Subsection "I am seeing the output of the command I spawned. Can I turn that off?" Yes, just set .PP .Vb 1 \& $Expect::Log_Stdout = 0; .Ve .PP to globally disable it or .PP .Vb 1 \& $exp\->log_stdout(0); .Ve .PP for just that command. 'log_user' is provided as an alias so Tcl/Expect user get a \s-1DWIM\s0 experience... :\-) .SS "No, I mean that when I send some text to the spawned process, it gets echoed back and I have to deal with it in the next expect." .IX Subsection "No, I mean that when I send some text to the spawned process, it gets echoed back and I have to deal with it in the next expect." This is caused by the pty, which has probably 'echo' enabled. A solution would be to set the pty to raw mode, which in general is cleaner for communication between two programs (no more unexpected character translations). Unfortunately this would break a lot of old code that sends \*(L"\er\*(R" to the program instead of \*(L"\en\*(R" (translating this is also handled by the pty), so I won't add this to Expect just like that. But feel free to experiment with \f(CW\*(C`$exp\->raw_pty(1)\*(C'\fR. .SS "How do I send control characters to a process?" .IX Subsection "How do I send control characters to a process?" A: You can send any characters to a process with the print command. To represent a control character in Perl, use \ec followed by the letter. For example, control-G can be represented with \*(L"\ecG\*(R" . Note that this will not work if you single-quote your string. So, to send control-C to a process in \&\f(CW$exp\fR, do: .PP .Vb 1 \& print $exp "\ecC"; .Ve .PP Or, if you prefer: .PP .Vb 1 \& $exp\->send("\ecC"); .Ve .PP The ability to include control characters in a string like this is provided by Perl, not by Expect.pm . Trying to learn Expect.pm without a thorough grounding in Perl can be very daunting. We suggest you look into some of the excellent Perl learning material, such as the books _Programming Perl_ and _Learning Perl_ by O'Reilly, as well as the extensive online Perl documentation available through the perldoc command. .SS "My script fails from time to time without any obvious reason. It seems that I am sometimes loosing output from the spawned program." .IX Subsection "My script fails from time to time without any obvious reason. It seems that I am sometimes loosing output from the spawned program." You could be exiting too fast without giving the spawned program enough time to finish. Try adding \f(CW$exp\fR\->\fBsoft_close()\fR to terminate the program gracefully or do an \fBexpect()\fR for 'eof'. .PP Alternatively, try adding a 'sleep 1' after you \fBspawn()\fR the program. It could be that pty creation on your system is just slow (but this is rather improbable if you are using the latest IO-Tty). .SS "I want to automate password entry for su/ssh/scp/rsh/..." .IX Subsection "I want to automate password entry for su/ssh/scp/rsh/..." You shouldn't use Expect for this. Putting passwords, especially root passwords, into scripts in clear text can mean severe security problems. I strongly recommend using other means. For 'su', consider switching to 'sudo', which gives you root access on a per-command and per-user basis without the need to enter passwords. 'ssh'/'scp' can be set up with \s-1RSA\s0 authentication without passwords. 'rsh' can use the .rhost mechanism, but I'd strongly suggest to switch to 'ssh'; to mention 'rsh' and 'security' in the same sentence makes an oxymoron. .PP It will work for 'telnet', though, and there are valid uses for it, but you still might want to consider using 'ssh', as keeping cleartext passwords around is very insecure. .SS "I want to use Expect to automate [anything with a buzzword]..." .IX Subsection "I want to use Expect to automate [anything with a buzzword]..." Are you sure there is no other, easier way? As a rule of thumb, Expect is useful for automating things that expect to talk to a human, where no formal standard applies. For other tasks that do follow a well-defined protocol, there are often better-suited modules that already can handle those protocols. Don't try to do \s-1HTTP\s0 requests by spawning telnet to port 80, use \s-1LWP\s0 instead. To automate \s-1FTP,\s0 take a look at Net::FTP or \f(CW\*(C`ncftp\*(C'\fR (http://www.ncftp.org). You don't use a screwdriver to hammer in your nails either, or do you? .SS "Is it possible to use threads with Expect?" .IX Subsection "Is it possible to use threads with Expect?" Basically yes, with one restriction: you must \fBspawn()\fR your programs in the main thread and then pass the Expect objects to the handling threads. The reason is that \fBspawn()\fR uses \fBfork()\fR, and perlthrtut: .PP .Vb 1 \& "Thinking of mixing fork() and threads? Please lie down and wait until the feeling passes." .Ve .SS "I want to log the whole session to a file." .IX Subsection "I want to log the whole session to a file." Use .PP .Vb 1 \& $exp\->log_file("filename"); .Ve .PP or .PP .Vb 1 \& $exp\->log_file($filehandle); .Ve .PP or even .PP .Vb 1 \& $exp\->log_file(\e&log_procedure); .Ve .PP for maximum flexibility. .PP Note that the logfile is appended to by default, but you can specify an optional mode \*(L"w\*(R" to truncate the logfile: .PP .Vb 1 \& $exp\->log_file("filename", "w"); .Ve .PP To stop logging, just call it with a false argument: .PP .Vb 1 \& $exp\->log_file(undef); .Ve .SS "How can I turn off multi-line matching for my regexps?" .IX Subsection "How can I turn off multi-line matching for my regexps?" To globally unset multi-line matching for all regexps: .PP .Vb 1 \& $Expect::Multiline_Matching = 0; .Ve .PP You can do that on a per-regexp basis by stating \f(CW\*(C`(?\-m)\*(C'\fR inside the regexp (you need perl5.00503 or later for that). .SS "How can I expect on multiple spawned commands?" .IX Subsection "How can I expect on multiple spawned commands?" You can use the \fB\-i\fR parameter to specify a single object or a list of Expect objects. All following patterns will be evaluated against that list. .PP You can specify \fB\-i\fR multiple times to create groups of objects and patterns to match against within the same expect statement. .PP This works just like in Tcl/Expect. .PP See the source example below. .SS "I seem to have problems with ptys!" .IX Subsection "I seem to have problems with ptys!" Well, pty handling is really a black magic, as it is extremely system dependent. I have extensively revised IO-Tty, so these problems should be gone. .PP If your system is listed in the \*(L"verified\*(R" list of IO::Tty, you probably have some non-standard setup, e.g. you compiled your Linux-kernel yourself and disabled ptys. Please ask your friendly sysadmin for help. .PP If your system is not listed, unpack the latest version of IO::Tty, do a 'perl Makefile.PL; make; make test; uname \f(CW\*(C`\-a\*(C'\fR' and send me the results and I'll see what I can deduce from that. .SS "I just want to read the output of a process without \fBexpect()\fPing anything. How can I do this?" .IX Subsection "I just want to read the output of a process without expect()ing anything. How can I do this?" [ Are you sure you need Expect for this? How about \fBqx()\fR or open(\*(L"prog|\*(R")? ] .PP By using expect without any patterns to match. .PP .Vb 3 \& $process\->expect(undef); # Forever until EOF \& $process\->expect($timeout); # For a few seconds \& $process\->expect(0); # Is there anything ready on the handle now? .Ve .SS "Ok, so now how do I get what was read on the handle?" .IX Subsection "Ok, so now how do I get what was read on the handle?" .Vb 1 \& $read = $process\->before(); .Ve .SS "Where's IO::Pty?" .IX Subsection "Where's IO::Pty?" Find it on \s-1CPAN\s0 as IO-Tty, which provides both. .SS "How come when I automate the passwd program to change passwords for me passwd dies before changing the password sometimes/every time?" .IX Subsection "How come when I automate the passwd program to change passwords for me passwd dies before changing the password sometimes/every time?" What's happening is you are closing the handle before passwd exits. When you close the handle to a process, it is sent a signal (\s-1SIGPIPE\s0?) telling it that \s-1STDOUT\s0 has gone away. The default behavior for processes is to die in this circumstance. Two ways you can make this not happen are: .PP .Vb 1 \& $process\->soft_close(); .Ve .PP This will wait 15 seconds for a process to come up with an \s-1EOF\s0 by itself before killing it. .PP .Vb 1 \& $process\->expect(undef); .Ve .PP This will wait forever for the process to match an empty set of patterns. It will return when the process hits an \s-1EOF.\s0 .PP As a rule, you should always \fBexpect()\fR the result of your transaction before you continue with processing. .SS "How come when I try to make a logfile with \fBlog_file()\fP or \fBset_group()\fP it doesn't print anything after the last time I run \fBexpect()\fP?" .IX Subsection "How come when I try to make a logfile with log_file() or set_group() it doesn't print anything after the last time I run expect()?" Output is only printed to the logfile/group when Expect reads from the process, during \fBexpect()\fR, \fBsend_slow()\fR and \fBinterconnect()\fR. One way you can force this is to make use of .PP .Vb 1 \& $process\->expect(undef); .Ve .PP and .PP .Vb 1 \& $process\->expect(0); .Ve .PP which will make \fBexpect()\fR run with an empty pattern set forever or just for an instant to capture the output of \f(CW$process\fR. The output is available in the accumulator, so you can grab it using \&\f(CW$process\fR\->\fBbefore()\fR. .SS "I seem to have problems with terminal settings, double echoing, etc." .IX Subsection "I seem to have problems with terminal settings, double echoing, etc." Tty settings are a major pain to keep track of. If you find unexpected behavior such as double-echoing or a frozen session, doublecheck the documentation for default settings. When in doubt, handle them yourself using \f(CW$exp\fR\->\fBstty()\fR and \fBmanual_stty()\fR functions. As of .98 you shouldn't have to worry about stty settings getting fouled unless you use interconnect or intentionally change them (like doing \-echo to get a password). .PP If you foul up your terminal's tty settings, kill any hung processes and enter 'stty sane' at a shell prompt. This should make your terminal manageable again. .PP Note that IO::Tty returns ptys with your systems default setting regarding echoing, \s-1CRLF\s0 translation etc. and Expect does not change them. I have considered setting the ptys to 'raw' without any translation whatsoever, but this would break a lot of existing things, as '\er' translation would not work anymore. On the other hand, a raw pty works much like a pipe and is more \s-1WYGIWYE\s0 (what you get is what you expect), so I suggest you set it to 'raw' by yourself: .PP .Vb 3 \& $exp = Expect\->new; \& $exp\->raw_pty(1); \& $exp\->spawn(...); .Ve .PP To disable echo: .PP .Vb 1 \& $exp\->slave\->stty(qw(\-echo)); .Ve .SS "I'm spawning a telnet/ssh session and then let the user interact with it. But screen-oriented applications on the other side don't work properly." .IX Subsection "I'm spawning a telnet/ssh session and then let the user interact with it. But screen-oriented applications on the other side don't work properly." You have to set the terminal screen size for that. Luckily, IO::Pty already has a method for that, so modify your code to look like this: .PP .Vb 3 \& my $exp = Expect\->new; \& $exp\->slave\->clone_winsize_from(\e*STDIN); \& $exp\->spawn("telnet somehost); .Ve .PP Also, some applications need the \s-1TERM\s0 shell variable set so they know how to move the cursor across the screen. When logging in, the remote shell sends a query (Ctrl-Z I think) and expects the terminal to answer with a string, e.g. 'xterm'. If you really want to go that way (be aware, madness lies at its end), you can handle that and send back the value in \f(CW$ENV\fR{\s-1TERM\s0}. This is only a hand-waving explanation, please figure out the details by yourself. .SS "I set the terminal size as explained above, but if I resize the window, the application does not notice this." .IX Subsection "I set the terminal size as explained above, but if I resize the window, the application does not notice this." You have to catch the signal \s-1WINCH\s0 (\*(L"window size changed\*(R"), change the terminal size and propagate the signal to the spawned application: .PP .Vb 4 \& my $exp = Expect\->new; \& $exp\->slave\->clone_winsize_from(\e*STDIN); \& $exp\->spawn("ssh somehost); \& $SIG{WINCH} = \e&winch; \& \& sub winch { \& $exp\->slave\->clone_winsize_from(\e*STDIN); \& kill WINCH => $exp\->pid if $exp\->pid; \& $SIG{WINCH} = \e&winch; \& } \& \& $exp\->interact(); .Ve .PP There is an example file ssh.pl in the examples/ subdir that shows how this works with ssh. Please note that I do strongly object against using Expect to automate ssh login, as there are better way to do that (see ssh-keygen). .SS "I noticed that the test uses a string that resembles, but not exactly matches, a well-known sentence that contains every character. What does that mean?" .IX Subsection "I noticed that the test uses a string that resembles, but not exactly matches, a well-known sentence that contains every character. What does that mean?" That means you are anal-retentive. :\-) [Gotcha there!] .ie n .SS "I get a ""Could not assign a pty"" error when running as a non-root user on an \s-1IRIX\s0 box?" .el .SS "I get a ``Could not assign a pty'' error when running as a non-root user on an \s-1IRIX\s0 box?" .IX Subsection "I get a Could not assign a pty error when running as a non-root user on an IRIX box?" The \s-1OS\s0 may not be configured to grant additional pty's (pseudo terminals) to non-root users. /usr/sbin/mkpts should be 4755, not 700 for this to work. I don't know about security implications if you do this. .SS "How come I don't notice when the spawned process closes its stdin/out/err??" .IX Subsection "How come I don't notice when the spawned process closes its stdin/out/err??" You are probably on one of the systems where the master doesn't get an \&\s-1EOF\s0 when the slave closes stdin/out/err. .PP One possible solution is when you spawn a process, follow it with a unique string that would indicate the process is finished. .PP .Vb 1 \& $process = Expect\->spawn(\*(Aqtelnet somehost; echo _\|_\|_\|_END_\|_\|_\|_\*(Aq); .Ve .PP And then \f(CW$process\fR\->expect($timeout,'_\|_\|_\|_END_\|_\|_\|_','other','patterns'); .SH "Source Examples" .IX Header "Source Examples" .SS "How to automate login" .IX Subsection "How to automate login" .Vb 3 \& my $telnet = Net::Telnet\->new("remotehost") # see Net::Telnet \& or die "Cannot telnet to remotehost: $!\en";; \& my $exp = Expect\->exp_init($telnet); \& \& # deprecated use of spawned telnet command \& # my $exp = Expect\->spawn("telnet localhost") \& # or die "Cannot spawn telnet: $!\en";; \& \& my $spawn_ok; \& $exp\->expect($timeout, \& [ \& qr\*(Aqlogin: $\*(Aq, \& sub { \& $spawn_ok = 1; \& my $fh = shift; \& $fh\->send("$username\en"); \& exp_continue; \& } \& ], \& [ \& \*(AqPassword: $\*(Aq, \& sub { \& my $fh = shift; \& print $fh "$password\en"; \& exp_continue; \& } \& ], \& [ \& eof => \& sub { \& if ($spawn_ok) { \& die "ERROR: premature EOF in login.\en"; \& } else { \& die "ERROR: could not spawn telnet.\en"; \& } \& } \& ], \& [ \& timeout => \& sub { \& die "No login.\en"; \& } \& ], \& \*(Aq\-re\*(Aq, qr\*(Aq[#>:] $\*(Aq, #\*(Aq wait for shell prompt, then exit expect \& ); .Ve .SS "How to expect on multiple spawned commands" .IX Subsection "How to expect on multiple spawned commands" .Vb 3 \& foreach my $cmd (@list_of_commands) { \& push @commands, Expect\->spawn($cmd); \& } \& \& expect($timeout, \& \*(Aq\-i\*(Aq, \e@commands, \& [ \& qr"pattern", # find this pattern in output of all commands \& sub { \& my $obj = shift; # object that matched \& print $obj "something\en"; \& exp_continue; # we don\*(Aqt want to terminate the expect call \& } \& ], \& \*(Aq\-i\*(Aq, $some_other_command, \& [ \& "some other pattern", \& sub { \& my ($obj, $parmref) = @_; \& # ... \& \& # now we exit the expect command \& }, \& \e$parm \& ], \& ); .Ve .SS "How to propagate terminal sizes" .IX Subsection "How to propagate terminal sizes" .Vb 4 \& my $exp = Expect\->new; \& $exp\->slave\->clone_winsize_from(\e*STDIN); \& $exp\->spawn("ssh somehost); \& $SIG{WINCH} = \e&winch; \& \& sub winch { \& $exp\->slave\->clone_winsize_from(\e*STDIN); \& kill WINCH => $exp\->pid if $exp\->pid; \& $SIG{WINCH} = \e&winch; \& } \& \& $exp\->interact(); .Ve .SH "HOMEPAGE" .IX Header "HOMEPAGE" <http://sourceforge.net/projects/expectperl/> though the source code is now in GitHub: <https://github.com/jacoby/expect.pm> .SH "MAILING LISTS" .IX Header "MAILING LISTS" There are two mailing lists available, expectperl-announce and expectperl-discuss, at .PP .Vb 1 \& http://lists.sourceforge.net/lists/listinfo/expectperl\-announce .Ve .PP and .PP .Vb 1 \& http://lists.sourceforge.net/lists/listinfo/expectperl\-discuss .Ve .SH "BUG TRACKING" .IX Header "BUG TRACKING" You can use the \s-1CPAN\s0 Request Tracker http://rt.cpan.org/ and submit new bugs under .PP .Vb 1 \& http://rt.cpan.org/Ticket/Create.html?Queue=Expect .Ve .SH "AUTHORS" .IX Header "AUTHORS" (c) 1997 Austin Schutz <\fIASchutz@users.sourceforge.net\fR> (retired) .PP \&\fBexpect()\fR interface & functionality enhancements (c) 1999\-2006 Roland Giersig. .PP This module is now maintained by Dave Jacoby <\fIjacoby@cpan.org\fR> .SH "LICENSE" .IX Header "LICENSE" This module can be used under the same terms as Perl. .SH "DISCLAIMER" .IX Header "DISCLAIMER" \&\s-1THIS SOFTWARE IS PROVIDED\s0 ``\s-1AS IS\s0'' \s-1AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\s0 (\s-1INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES\s0; \s-1LOSS OF USE, DATA, OR PROFITS\s0; \s-1OR BUSINESS INTERRUPTION\s0) \s-1HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\s0 (\s-1INCLUDING NEGLIGENCE OR OTHERWISE\s0) \s-1ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\s0 .PP In other words: Use at your own risk. Provided as is. Your mileage may vary. Read the source, Luke! .PP And finally, just to be sure: .PP Any Use of This Product, in Any Manner Whatsoever, Will Increase the Amount of Disorder in the Universe. Although No Liability Is Implied Herein, the Consumer Is Warned That This Process Will Ultimately Lead to the Heat Death of the Universe. PK�����A0]z!��!����man/man3/IO::Pty.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "Pty 3" .TH Pty 3 "2023-12-28" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" IO::Pty \- Pseudo TTY object class .SH "VERSION" .IX Header "VERSION" 1.20 .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use IO::Pty; \& \& $pty = new IO::Pty; \& \& $slave = $pty\->slave; \& \& foreach $val (1..10) { \& print $pty "$val\en"; \& $_ = <$slave>; \& print "$_"; \& } \& \& close($slave); .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\f(CW\*(C`IO::Pty\*(C'\fR provides an interface to allow the creation of a pseudo tty. .PP \&\f(CW\*(C`IO::Pty\*(C'\fR inherits from \f(CW\*(C`IO::Handle\*(C'\fR and so provide all the methods defined by the \f(CW\*(C`IO::Handle\*(C'\fR package. .PP Please note that pty creation is very system-dependent. If you have problems, see IO::Tty for help. .SH "CONSTRUCTOR" .IX Header "CONSTRUCTOR" .IP "new" 3 .IX Item "new" The \f(CW\*(C`new\*(C'\fR constructor takes no arguments and returns a new file object which is the master side of the pseudo tty. .SH "METHODS" .IX Header "METHODS" .IP "\fBttyname()\fR" 4 .IX Item "ttyname()" Returns the name of the slave pseudo tty. On \s-1UNIX\s0 machines this will be the pathname of the device. Use this name for informational purpose only, to get a slave filehandle, use \fBslave()\fR. .IP "\fBslave()\fR" 4 .IX Item "slave()" The \f(CW\*(C`slave\*(C'\fR method will return the slave filehandle of the given master pty, opening it anew if necessary. If IO::Stty is installed, you can then call \f(CW\*(C`$slave\->stty()\*(C'\fR to modify the terminal settings. .IP "\fBclose_slave()\fR" 4 .IX Item "close_slave()" The slave filehandle will be closed and destroyed. This is necessary in the parent after forking to get rid of the open filehandle, otherwise the parent will not notice if the child exits. Subsequent calls of \f(CW\*(C`slave()\*(C'\fR will return a newly opened slave filehandle. .IP "\fBmake_slave_controlling_terminal()\fR" 4 .IX Item "make_slave_controlling_terminal()" This will set the slave filehandle as the controlling terminal of the current process, which will become a session leader, so this should only be called by a child process after a \fBfork()\fR, e.g. in the callback to \f(CW\*(C`sync_exec()\*(C'\fR (see Proc::SyncExec). See the \f(CW\*(C`try\*(C'\fR script (also \f(CW\*(C`test.pl\*(C'\fR) for an example how to correctly spawn a subprocess. .IP "\fBset_raw()\fR" 4 .IX Item "set_raw()" Will set the pty to raw. Note that this is a one-way operation, you need IO::Stty to set the terminal settings to anything else. .Sp On some systems, the master pty is not a tty. This method checks for that and returns success anyway on such systems. Note that this method must be called on the slave, and probably should be called on the master, just to be sure, i.e. .Sp .Vb 2 \& $pty\->slave\->set_raw(); \& $pty\->set_raw(); .Ve .IP "clone_winsize_from(\e*FH)" 4 .IX Item "clone_winsize_from(*FH)" Gets the terminal size from filehandle \s-1FH\s0 (which must be a terminal) and transfers it to the pty. Returns true on success and undef on failure. Note that this must be called upon the \fIslave\fR, i.e. .Sp .Vb 1 \& $pty\->slave\->clone_winsize_from(\e*STDIN); .Ve .Sp On some systems, the master pty also isatty. I actually have no idea if setting terminal sizes there is passed through to the slave, so if this method is called for a master that is not a tty, it silently returns \s-1OK.\s0 .Sp See the \f(CW\*(C`try\*(C'\fR script for example code how to propagate \s-1SIGWINCH.\s0 .IP "\fBget_winsize()\fR" 4 .IX Item "get_winsize()" Returns the terminal size, in a 4\-element list. .Sp .Vb 1 \& ($row, $col, $xpixel, $ypixel) = $tty\->get_winsize() .Ve .ie n .IP "set_winsize($row, $col, $xpixel, $ypixel)" 4 .el .IP "set_winsize($row, \f(CW$col\fR, \f(CW$xpixel\fR, \f(CW$ypixel\fR)" 4 .IX Item "set_winsize($row, $col, $xpixel, $ypixel)" Sets the terminal size. If not specified, \f(CW$xpixel\fR and \f(CW$ypixel\fR are set to 0. As with \f(CW\*(C`clone_winsize_from\*(C'\fR, this must be called upon the \fIslave\fR. .SH "SEE ALSO" .IX Header "SEE ALSO" IO::Tty, IO::Tty::Constant, IO::Handle, Expect, Proc::SyncExec .SH "MAILING LISTS" .IX Header "MAILING LISTS" As this module is mainly used by Expect, support for it is available via the two Expect mailing lists, expectperl-announce and expectperl-discuss, at .PP .Vb 1 \& http://lists.sourceforge.net/lists/listinfo/expectperl\-announce .Ve .PP and .PP .Vb 1 \& http://lists.sourceforge.net/lists/listinfo/expectperl\-discuss .Ve .SH "AUTHORS" .IX Header "AUTHORS" Originally by Graham Barr <\fIgbarr@pobox.com\fR>, based on the Ptty module by Nick Ing-Simmons <\fInik@tiuk.ti.com\fR>. .PP Now maintained and heavily rewritten by Roland Giersig <\fIRGiersig@cpan.org\fR>. .PP Contains copyrighted stuff from openssh v3.0p1, authored by Tatu Ylonen <ylo@cs.hut.fi>, Markus Friedl and Todd C. Miller <Todd.Miller@courtesan.com>. .SH "COPYRIGHT" .IX Header "COPYRIGHT" Now all code is free software; you can redistribute it and/or modify it under the same terms as Perl itself. .PP Nevertheless the above \s-1AUTHORS\s0 retain their copyrights to the various parts and want to receive credit if their source code is used. See the source for details. .SH "DISCLAIMER" .IX Header "DISCLAIMER" \&\s-1THIS SOFTWARE IS PROVIDED\s0 ``\s-1AS IS\s0'' \s-1AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\s0 (\s-1INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES\s0; \s-1LOSS OF USE, DATA, OR PROFITS\s0; \s-1OR BUSINESS INTERRUPTION\s0) \s-1HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\s0 (\s-1INCLUDING NEGLIGENCE OR OTHERWISE\s0) \s-1ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\s0 .PP In other words: Use at your own risk. Provided as is. Your mileage may vary. Read the source, Luke! .PP And finally, just to be sure: .PP Any Use of This Product, in Any Manner Whatsoever, Will Increase the Amount of Disorder in the Universe. Although No Liability Is Implied Herein, the Consumer Is Warned That This Process Will Ultimately Lead to the Heat Death of the Universe. PK�����A0]EX:2��2����man/man3/App::Cpan.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "App::Cpan 3" .TH App::Cpan 3 "2022-04-03" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" App::Cpan \- easily interact with CPAN from the command line .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 2 \& # with arguments and no switches, installs specified modules \& cpan module_name [ module_name ... ] \& \& # with switches, installs modules with extra behavior \& cpan [\-cfFimtTw] module_name [ module_name ... ] \& \& # use local::lib \& cpan \-I module_name [ module_name ... ] \& \& # one time mirror override for faster mirrors \& cpan \-p ... \& \& # with just the dot, install from the distribution in the \& # current directory \& cpan . \& \& # without arguments, starts CPAN.pm shell \& cpan \& \& # without arguments, but some switches \& cpan [\-ahpruvACDLOPX] .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" This script provides a command interface (not a shell) to \s-1CPAN.\s0 At the moment it uses \s-1CPAN\s0.pm to do the work, but it is not a one-shot command runner for \s-1CPAN\s0.pm. .SS "Options" .IX Subsection "Options" .IP "\-a" 4 .IX Item "-a" Creates a \s-1CPAN\s0.pm autobundle with CPAN::Shell\->autobundle. .IP "\-A module [ module ... ]" 4 .IX Item "-A module [ module ... ]" Shows the primary maintainers for the specified modules. .IP "\-c module" 4 .IX Item "-c module" Runs a `make clean` in the specified module's directories. .IP "\-C module [ module ... ]" 4 .IX Item "-C module [ module ... ]" Show the \fIChanges\fR files for the specified modules .IP "\-D module [ module ... ]" 4 .IX Item "-D module [ module ... ]" Show the module details. This prints one line for each out-of-date module (meaning, modules locally installed but have newer versions on \s-1CPAN\s0). Each line has three columns: module name, local version, and \s-1CPAN\s0 version. .IP "\-f" 4 .IX Item "-f" Force the specified action, when it normally would have failed. Use this to install a module even if its tests fail. When you use this option, \&\-i is not optional for installing a module when you need to force it: .Sp .Vb 1 \& % cpan \-f \-i Module::Foo .Ve .IP "\-F" 4 .IX Item "-F" Turn off \s-1CPAN\s0.pm's attempts to lock anything. You should be careful with this since you might end up with multiple scripts trying to muck in the same directory. This isn't so much of a concern if you're loading a special config with \f(CW\*(C`\-j\*(C'\fR, and that config sets up its own work directories. .IP "\-g module [ module ... ]" 4 .IX Item "-g module [ module ... ]" Downloads to the current directory the latest distribution of the module. .IP "\-G module [ module ... ]" 4 .IX Item "-G module [ module ... ]" \&\s-1UNIMPLEMENTED\s0 .Sp Download to the current directory the latest distribution of the modules, unpack each distribution, and create a git repository for each distribution. .Sp If you want this feature, check out Yanick Champoux's \f(CW\*(C`Git::CPAN::Patch\*(C'\fR distribution. .IP "\-h" 4 .IX Item "-h" Print a help message and exit. When you specify \f(CW\*(C`\-h\*(C'\fR, it ignores all of the other options and arguments. .IP "\-i module [ module ... ]" 4 .IX Item "-i module [ module ... ]" Install the specified modules. With no other switches, this switch is implied. .IP "\-I" 4 .IX Item "-I" Load \f(CW\*(C`local::lib\*(C'\fR (think like \f(CW\*(C`\-I\*(C'\fR for loading lib paths). Too bad \&\f(CW\*(C`\-l\*(C'\fR was already taken. .IP "\-j Config.pm" 4 .IX Item "-j Config.pm" Load the file that has the \s-1CPAN\s0 configuration data. This should have the same format as the standard \fICPAN/Config.pm\fR file, which defines \&\f(CW$CPAN::Config\fR as an anonymous hash. .Sp If the file does not exist, \f(CW\*(C`cpan\*(C'\fR dies. .IP "\-J" 4 .IX Item "-J" Dump the configuration in the same format that \s-1CPAN\s0.pm uses. This is useful for checking the configuration as well as using the dump as a starting point for a new, custom configuration. .IP "\-l" 4 .IX Item "-l" List all installed modules with their versions .IP "\-L author [ author ... ]" 4 .IX Item "-L author [ author ... ]" List the modules by the specified authors. .IP "\-m" 4 .IX Item "-m" Make the specified modules. .IP "\-M mirror1,mirror2,..." 4 .IX Item "-M mirror1,mirror2,..." A comma-separated list of mirrors to use for just this run. The \f(CW\*(C`\-P\*(C'\fR option can find them for you automatically. .IP "\-n" 4 .IX Item "-n" Do a dry run, but don't actually install anything. (unimplemented) .IP "\-O" 4 .IX Item "-O" Show the out-of-date modules. .IP "\-p" 4 .IX Item "-p" Ping the configured mirrors and print a report .IP "\-P" 4 .IX Item "-P" Find the best mirrors you could be using and use them for the current session. .IP "\-r" 4 .IX Item "-r" Recompiles dynamically loaded modules with CPAN::Shell\->recompile. .IP "\-s" 4 .IX Item "-s" Drop in the \s-1CPAN\s0.pm shell. This command does this automatically if you don't specify any arguments. .IP "\-t module [ module ... ]" 4 .IX Item "-t module [ module ... ]" Run a `make test` on the specified modules. .IP "\-T" 4 .IX Item "-T" Do not test modules. Simply install them. .IP "\-u" 4 .IX Item "-u" Upgrade all installed modules. Blindly doing this can really break things, so keep a backup. .IP "\-v" 4 .IX Item "-v" Print the script version and \s-1CPAN\s0.pm version then exit. .IP "\-V" 4 .IX Item "-V" Print detailed information about the cpan client. .IP "\-w" 4 .IX Item "-w" \&\s-1UNIMPLEMENTED\s0 .Sp Turn on cpan warnings. This checks various things, like directory permissions, and tells you about problems you might have. .IP "\-x module [ module ... ]" 4 .IX Item "-x module [ module ... ]" Find close matches to the named modules that you think you might have mistyped. This requires the optional installation of Text::Levenshtein or Text::Levenshtein::Damerau. .IP "\-X" 4 .IX Item "-X" Dump all the namespaces to standard output. .SS "Examples" .IX Subsection "Examples" .Vb 2 \& # print a help message \& cpan \-h \& \& # print the version numbers \& cpan \-v \& \& # create an autobundle \& cpan \-a \& \& # recompile modules \& cpan \-r \& \& # upgrade all installed modules \& cpan \-u \& \& # install modules ( sole \-i is optional ) \& cpan \-i Netscape::Booksmarks Business::ISBN \& \& # force install modules ( must use \-i ) \& cpan \-fi CGI::Minimal URI \& \& # install modules but without testing them \& cpan \-Ti CGI::Minimal URI .Ve .SS "Environment variables" .IX Subsection "Environment variables" There are several components in \s-1CPAN\s0.pm that use environment variables. The build tools, ExtUtils::MakeMaker and Module::Build use some, while others matter to the levels above them. Some of these are specified by the Perl Toolchain Gang: .PP Lancaster Consensus: <https://github.com/Perl\-Toolchain\-Gang/toolchain\-site/blob/master/lancaster\-consensus.md> .PP Oslo Consensus: <https://github.com/Perl\-Toolchain\-Gang/toolchain\-site/blob/master/oslo\-consensus.md> .IP "\s-1NONINTERACTIVE_TESTING\s0" 4 .IX Item "NONINTERACTIVE_TESTING" Assume no one is paying attention and skips prompts for distributions that do that correctly. \f(CWcpan(1)\fR sets this to \f(CW1\fR unless it already has a value (even if that value is false). .IP "\s-1PERL_MM_USE_DEFAULT\s0" 4 .IX Item "PERL_MM_USE_DEFAULT" Use the default answer for a prompted questions. \f(CWcpan(1)\fR sets this to \f(CW1\fR unless it already has a value (even if that value is false). .IP "\s-1CPAN_OPTS\s0" 4 .IX Item "CPAN_OPTS" As with \f(CW\*(C`PERL5OPT\*(C'\fR, a string of additional \f(CWcpan(1)\fR options to add to those you specify on the command line. .IP "\s-1CPANSCRIPT_LOGLEVEL\s0" 4 .IX Item "CPANSCRIPT_LOGLEVEL" The log level to use, with either the embedded, minimal logger or Log::Log4perl if it is installed. Possible values are the same as the \f(CW\*(C`Log::Log4perl\*(C'\fR levels: \f(CW\*(C`TRACE\*(C'\fR, \f(CW\*(C`DEBUG\*(C'\fR, \f(CW\*(C`INFO\*(C'\fR, \f(CW\*(C`WARN\*(C'\fR, \&\f(CW\*(C`ERROR\*(C'\fR, and \f(CW\*(C`FATAL\*(C'\fR. The default is \f(CW\*(C`INFO\*(C'\fR. .IP "\s-1GIT_COMMAND\s0" 4 .IX Item "GIT_COMMAND" The path to the \f(CW\*(C`git\*(C'\fR binary to use for the Git features. The default is \f(CW\*(C`/usr/local/bin/git\*(C'\fR. .SS "Methods" .IX Subsection "Methods" .IP "run( \s-1ARGS\s0 )" 4 .IX Item "run( ARGS )" Just do it. .Sp The \f(CW\*(C`run\*(C'\fR method returns 0 on success and a positive number on failure. See the section on \s-1EXIT CODES\s0 for details on the values. .Sp \&\s-1CPAN\s0.pm sends all the good stuff either to \s-1STDOUT,\s0 or to a temp file if \f(CW$CPAN::Be_Silent\fR is set. I have to intercept that output so I can find out what happened. .Sp Stolen from File::Path::Expand .SH "EXIT VALUES" .IX Header "EXIT VALUES" The script exits with zero if it thinks that everything worked, or a positive number if it thinks that something failed. Note, however, that in some cases it has to divine a failure by the output of things it does not control. For now, the exit codes are vague: .PP .Vb 1 \& 1 An unknown error \& \& 2 The was an external problem \& \& 4 There was an internal problem with the script \& \& 8 A module failed to install .Ve .SH "TO DO" .IX Header "TO DO" * There is initial support for Log4perl if it is available, but I haven't gone through everything to make the NullLogger work out correctly if Log4perl is not installed. .PP * When I capture \s-1CPAN\s0.pm output, I need to check for errors and report them to the user. .PP * Warnings switch .PP * Check then exit .SH "BUGS" .IX Header "BUGS" * none noted .SH "SEE ALSO" .IX Header "SEE ALSO" \&\s-1CPAN\s0, App::cpanminus .SH "SOURCE AVAILABILITY" .IX Header "SOURCE AVAILABILITY" This code is in Github in the \s-1CPAN\s0.pm repository: .PP .Vb 1 \& https://github.com/andk/cpanpm .Ve .PP The source used to be tracked separately in another GitHub repo, but the canonical source is now in the above repo. .SH "CREDITS" .IX Header "CREDITS" Japheth Cleaver added the bits to allow a forced install (\f(CW\*(C`\-f\*(C'\fR). .PP Jim Brandt suggested and provided the initial implementation for the up-to-date and Changes features. .PP Adam Kennedy pointed out that \f(CW\*(C`exit()\*(C'\fR causes problems on Windows where this script ends up with a .bat extension .PP David Golden helps integrate this into the \f(CW\*(C`CPAN.pm\*(C'\fR repos. .PP Jim Keenan fixed up various issues with _download .SH "AUTHOR" .IX Header "AUTHOR" brian d foy, \f(CW\*(C`<bdfoy@cpan.org>\*(C'\fR .SH "COPYRIGHT" .IX Header "COPYRIGHT" Copyright (c) 2001\-2021, brian d foy, All Rights Reserved. .PP You may redistribute this under the same terms as Perl itself. PK�����A0]< �� ����man/man3/CPAN::API::HOWTO.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "CPAN::API::HOWTO 3" .TH CPAN::API::HOWTO 3 "2012-09-08" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" CPAN::API::HOWTO \- a recipe book for programming with CPAN.pm .SH "RECIPES" .IX Header "RECIPES" All of these recipes assume that you have put \*(L"use \s-1CPAN\*(R"\s0 at the top of your program. .SS "What distribution contains a particular module?" .IX Subsection "What distribution contains a particular module?" .Vb 3 \& my $distribution = CPAN::Shell\->expand( \& "Module", "Data::UUID" \& )\->distribution()\->pretty_id(); .Ve .PP This returns a string of the form \*(L"\s-1AUTHORID/TARBALL\*(R".\s0 If you want the full path and filename to this distribution on a \s-1CPAN\s0 mirror, then it is \&\f(CW\*(C`.../authors/id/A/AU/AUTHORID/TARBALL\*(C'\fR. .SS "What modules does a particular distribution contain?" .IX Subsection "What modules does a particular distribution contain?" .Vb 4 \& CPAN::Index\->reload(); \& my @modules = CPAN::Shell\->expand( \& "Distribution", "JHI/Graph\-0.83.tar.gz" \& )\->containsmods(); .Ve .PP You may also refer to a distribution in the form A/AU/AUTHORID/TARBALL. .SH "SEE ALSO" .IX Header "SEE ALSO" the main \s-1CPAN\s0.pm documentation .SH "LICENSE" .IX Header "LICENSE" This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. .PP See <http://www.perl.com/perl/misc/Artistic.html> .SH "AUTHOR" .IX Header "AUTHOR" David Cantrell PK�����A0]+YҚX��X��$��man/man3/POD2::PT_BR::local::lib.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "POD2::PT_BR::local::lib 3" .TH POD2::PT_BR::local::lib 3 "2019-11-14" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" local::lib~[pt_br] \- crie e use um diretório lib/ local para módulos perl com PERL5LIB .SH "SINOPSE" .IX Header "SINOPSE" No código \- .PP .Vb 1 \& use local::lib; # configura um lib local em ~/perl5 \& \& use local::lib \*(Aq~/foo\*(Aq; # idem, mas ~/foo \& \& # Ou... \& use FindBin; \& use local::lib "$FindBin::Bin/../suporte"; # bibliotecas de suporte locais à aplicação .Ve .PP Pela linha de comando (shell) \- .PP .Vb 2 \& # Instala o LWP e suas dependências não encontradas no diretório \*(Aq~/perl5\*(Aq \& perl \-MCPAN \-Mlocal::lib \-e \*(AqCPAN::install(LWP)\*(Aq \& \& # Apenas exibe alguns comandos úteis para a shell \& $ perl \-Mlocal::lib \& export PERL_MB_OPT=\*(Aq\-\-install_base /home/username/perl5\*(Aq \& export PERL_MM_OPT=\*(AqINSTALL_BASE=/home/username/perl5\*(Aq \& export PERL5LIB=\*(Aq/home/username/perl5/lib/perl5/i386\-linux:/home/username/perl5/lib/perl5\*(Aq \& export PATH="/home/username/perl5/bin:$PATH" .Ve .SS "A técnica de 'bootstrapping'" .IX Subsection "A técnica de 'bootstrapping'" Uma forma comum de instalar o local::lib é usando o que é conhecido como técnica de \*(L"bootstrapping\*(R". É uma boa abordagem caso seu administrador de sistemas não tenha instalado o local::lib. Nesse caso, você precisará instalar o local::lib em seu diretório de usuário. .PP Caso você tenha privilégios de administrador, ainda assim deverá configurar suas variáveis de ambiente, como discutido no passo 4, abaixo. Sem elas, você ainda instalará módulos no \s-1CPAN\s0 do sistema e seus scripts Perl não utilizarão o caminho para o lib/ que você definiu com o local::lib. .PP Por padrão, o local::lib instala os módulos do \s-1CPAN\s0 e a si próprio em ~/perl5. .PP Usuários do Windows devem ler \*(L"Diferenças ao usar esse módulo em Win32\*(R". .PP 1. Baixe e descompacte o local::lib do \s-1CPAN\s0 (procure por \*(L"Download\*(R" na página do \s-1CPAN\s0 sobre o local::lib). Faça isso como um usuário comum, não como root ou administrador. Descompacte o arquivo em seu diretório de usuário ou em qualquer outro local conveniente. .PP 2. Execute isso: .PP .Vb 1 \& perl Makefile.PL \-\-bootstrap .Ve .PP Caso o sistema pergunte se deve configurar tudo que puder automaticamente, você provavelmente deve responder que sim (yes). .PP Para instalar o local::lib em um diretório que não o padrão, você precisará especificá\-lo ao chamar o bootstrap, da seguinte forma: .PP .Vb 1 \& perl Makefile.PL \-\-bootstrap=~/foo .Ve .PP 3. Execute isso: (local::lib assume que você possui o comando 'make' instalado em seu sistema) .PP .Vb 1 \& make test && make install .Ve .PP 4. Agora precisamos configurar as variáveis de ambiente apropriadas para que o Perl use nosso recém\-criado diretório lib/. Caso esteja usando bash ou outra shell Bourne, você pode fazer isso adicionando a seguinte linha em seu script de inicialização da shell: .PP .Vb 1 \& echo \*(Aqeval $(perl \-I$HOME/perl5/lib/perl5 \-Mlocal::lib)\*(Aq >>~/.bashrc .Ve .PP Caso esteja usando a shell C, pode fazer da seguinte forma: .PP .Vb 4 \& /bin/csh \& echo $SHELL \& /bin/csh \& perl \-I$HOME/perl5/lib/perl5 \-Mlocal::lib >> ~/.cshrc .Ve .PP Caso tenha passado para o bootstrap um diretório que não o padrão, você precisará indicá\-lo na chamada ao local::lib, dessa forma: .PP .Vb 1 \& echo \*(Aqeval $(perl \-I$HOME/foo/lib/perl5 \-Mlocal::lib=$HOME/foo)\*(Aq >>~/.bashrc .Ve .PP Após atualizar seu arquivo de configuração da shell, certifique-se de processá\-lo novamente para obter as modificações em sua shell atual. Shells Bourne usam \f(CW\*(C`. ~/.bashrc\*(C'\fR para isso, enquanto shells C usam \f(CW\*(C`source ~/.cshrc\*(C'\fR. .PP Se estiver em uma máquina lenta ou operando com grandes limitações de espaço em disco, você pode desativar a geração automática de manpages a partir do \s-1POD\s0 ao instalar módulos. Para isso, basta passar o argumento \&\f(CW\*(C`\-\-no\-manpages\*(C'\fR durante o bootstrap: .PP .Vb 1 \& perl Makefile.PL \-\-bootstrap \-\-no\-manpages .Ve .PP Para evitar ter que fazer vários bootstraps para vários ambientes de módulos Perl na mesma conta de usuário \- por exemplo se você usa o local::lib para desenvolver diferentes aplicativos independentes \- você pode utilizar uma única instalação bootstrap do local::lib para instalar módulos em diretórios diferentes da seguinte forma: .PP .Vb 7 \& cd ~/meudir1 \& perl \-Mlocal::lib=./ \& eval $(perl \-Mlocal::lib=./) ### Para configurar o ambiente apenas nessa shell \& printenv ### Veja que o ~/meudir1 está na PERL5LIB \& perl \-MCPAN \-e install ... ### Os módulos que quiser \& cd ../meudir2 \& ... REPITA ... .Ve .PP Para múltiplos ambientes destinados a múltiplos aplicativos, você pode precisar incluir uma versão modificada das instruções de \f(CW\*(C`use FindBin\*(C'\fR no exemplo \*(L"No código\*(R" acima. Caso tenha feito algo como o que foi descrito acima, terá um conjunto de módulos Perl em \f(CW\*(C`~/meudir1/lib\*(C'\fR. Caso tenha um script em \f(CW\*(C`~/meudir1/scripts/meuscript.pl\*(C'\fR, você precisará indicar a ele onde encontrar os módulos que instalou para ele em \f(CW\*(C`~/meudir1/lib\*(C'\fR. .PP Em \f(CW\*(C`~/meudir1/scripts/meuscript.pl\*(C'\fR: .PP .Vb 4 \& use strict; \& use warnings; \& use local::lib "$FindBin::Bin/.."; ### aponta para ~/meudir1 e o local::lib acha o lib/ \& use lib "$FindBin::Bin/../lib"; ### aponta para ~/meudir1/lib .Ve .PP Coloque isso antes de qualquer bloco \s-1BEGIN\s0 { ... } que precise dos módulos instalados. .SS "Diferenças ao usar esse módulo em Win32" .IX Subsection "Diferenças ao usar esse módulo em Win32" Para configurar as variáveis de ambiente apropriadas para sua sessão atual do \f(CW\*(C`CMD.exe\*(C'\fR, você pode fazer assim: .PP .Vb 5 \& C:\e>perl \-Mlocal::lib \& set PERL_MB_OPT=\-\-install_base C:\eDOCUME~1\eADMINI~1\eperl5 \& set PERL_MM_OPT=INSTALL_BASE=C:\eDOCUME~1\eADMINI~1\eperl5 \& set PERL5LIB=C:\eDOCUME~1\eADMINI~1\eperl5\elib\eperl5;C:\eDOCUME~1\eADMINI~1\eperl5\elib\eperl5\eMSWin32\-x86\-multi\-thread \& set PATH=C:\eDOCUME~1\eADMINI~1\eperl5\ebin;%PATH% \& \& ### Para configurar o ambiente apenas dessa shell \& C:\e>perl \-Mlocal::lib > %TEMP%\etmp.bat && %TEMP%\etmp.bat && del %TEMP%\etemp.bat \& ### em vez de $(perl \-Mlocal::lib=./) .Ve .PP Caso queira que as configurações do ambiente persistam, você precisará adicioná\-las em Painel de Controle \-> Sistema, ou usar o App::local::lib::Win32Helper. .PP O \*(L"~\*(R" é transformado no diretório do perfil do usuário (o diretório com o nome do usuário dentro de \*(L"Documents and Settings\*(R" (Windows \s-1XP\s0 ou anterior) ou \*(L"Usuários\*(R" (Windows Vista e mais recentes)) a menos que \f(CW$ENV\fR{\s-1HOME\s0} exista. Após isso, o nome do diretório é encurtado e os subdiretórios são criados (o que significa que o diretório deve existir). .SH "MOTIVAÇÃO" .IX Header "MOTIVAÇÃO" A versão de um pacote Perl na sua máquina nem sempre é a que você precisa. Obviamente, a melhor coisa a fazer seria atualizá\-la para a versão desejada. No entanto, você pode estar em uma situação que o impede de fazer isso. Talvez você não tenha privilégios de administrador do sistema; ou talvez esteja usando um sistema de gerenciamento de pacotes como o do Debian, e ainda não exista um pacote disponível na versão desejada. .PP local::lib resolve esse problema possibilitando a criação de seu próprio diretório de pacotes Perl obtidos do \s-1CPAN\s0 (em sistemas multi\-usuário, isso normalmente fica dentro do diretório de seu usuário). A instalação do Perl no sistema permanece inalterada; você simplesmente chama o Perl com opções especiais para que ele use os pacotes em seu diretório local em vez dos pacotes do sistema. O local::lib organiza as coisas para que versões dos pacotes Perl instalados localmente tenham precedência sobre as do sistema. .PP Caso esteja usando um sistema de gerenciamento de pacote (como em sistemas Debian), não precisará se preocupar com conflitos entre o Debian e o \s-1CPAN.\s0 Sua versão local dos pacotes será instalada em um diretório completamente diferente das versões instaladas pelo gerenciador de pacotes do sistema. .SH "DESCRIÇÃO" .IX Header "DESCRIÇÃO" Este módulo oferece uma forma rápida e conveniente para criar um repositório de módulos locais ao usuário, dentro do diretório do mesmo. Ele também monta e exibe para o usuário uma lista de variáveis de ambiente utilizando a sintaxe da shell atual do usuário (conforme especificado pela variável de ambiente \f(CW\*(C`SHELL\*(C'\fR), pronta para ser adicionada diretamente no arquivo de configuração da shell. .PP Generalizando, o local::lib permite a criação e uso de um diretório contendo módulos Perl fora do \f(CW@INC\fR do Perl. Isso facilita a produção de aplicações com uma versão específica de determinado módulo, ou coleção de módulos. Também é útil quando o mantenedor de um módulo não aplicou determinado patch que você precisa para seu aplicativo. .PP Durante o \f(CW\*(C`import\*(C'\fR, o local::lib define valores apropriados para as seguintes variáveis de ambiente: .IP "\s-1PERL_MB_OPT\s0" 4 .IX Item "PERL_MB_OPT" .PD 0 .IP "\s-1PERL_MM_OPT\s0" 4 .IX Item "PERL_MM_OPT" .IP "\s-1PERL5LIB\s0" 4 .IX Item "PERL5LIB" .IP "\s-1PATH\s0" 4 .IX Item "PATH" .PD valores serão anexados ao \s-1PATH,\s0 em vez de substituí\-lo. .PP Esses valores são então disponibilizados para referência por qualquer outro código após o \f(CW\*(C`import\*(C'\fR. .SH "CRIANDO UM CONJUNTO AUTO-CONTIDO DE MÓDULOS" .IX Header "CRIANDO UM CONJUNTO AUTO-CONTIDO DE MÓDULOS" Veja lib::core::only para uma maneira de fazer isso \- mas note que há uma série de ressalvas na abordagem, e a melhor forma é sempre fazer o 'build' contra uma versão limpa do perl (i.e. com 'site' e 'vendor' o mais vazios possível). .SH "MÉTODOS" .IX Header "MÉTODOS" .SS "ensure_dir_structure_for" .IX Subsection "ensure_dir_structure_for" .ie n .IP "Argumentos: $caminho_do_diretorio" 4 .el .IP "Argumentos: \f(CW$caminho_do_diretorio\fR" 4 .IX Item "Argumentos: $caminho_do_diretorio" .PD 0 .IP "Valor de Retorno: Nenhum" 4 .IX Item "Valor de Retorno: Nenhum" .PD .PP Tenta criar o caminho fornecido, e todos os diretórios superiores necessários. Gera uma exceção em caso de falha. .SS "print_environment_vars_for" .IX Subsection "print_environment_vars_for" .ie n .IP "Argumentos: $caminho_do_diretorio" 4 .el .IP "Argumentos: \f(CW$caminho_do_diretorio\fR" 4 .IX Item "Argumentos: $caminho_do_diretorio" .PD 0 .IP "Valor de Retorno: Nenhum" 4 .IX Item "Valor de Retorno: Nenhum" .PD .PP Exibe na saída padrão as variáveis listadas acima, devidamente ajustadas para utilizar o caminho fornecido como diretório base. .SS "build_environment_vars_for" .IX Subsection "build_environment_vars_for" .ie n .IP "Argumentos: $caminho_do_diretorio, $interpolar" 4 .el .IP "Argumentos: \f(CW$caminho_do_diretorio\fR, \f(CW$interpolar\fR" 4 .IX Item "Argumentos: $caminho_do_diretorio, $interpolar" .PD 0 .ie n .IP "Valor de Retorno: %variaveis_de_ambiente" 4 .el .IP "Valor de Retorno: \f(CW%variaveis_de_ambiente\fR" 4 .IX Item "Valor de Retorno: %variaveis_de_ambiente" .PD .PP Retorna hash contendo as variáveis de ambiente listadas acima, devidamente ajustadas para utilizar o caminho fornecido como diretório base. .SS "setup_env_hash_for" .IX Subsection "setup_env_hash_for" .ie n .IP "Argumentos: $caminho_do_diretorio" 4 .el .IP "Argumentos: \f(CW$caminho_do_diretorio\fR" 4 .IX Item "Argumentos: $caminho_do_diretorio" .PD 0 .IP "Valor de Retorno: Nenhum" 4 .IX Item "Valor de Retorno: Nenhum" .PD .PP Constrói as chaves no \f(CW%ENV\fR para o caminho fornecido, chamando \&\f(CW\*(C`build_environment_vars_for\*(C'\fR. .SS "install_base_perl_path" .IX Subsection "install_base_perl_path" .ie n .IP "Argumentos: $caminho_do_diretorio" 4 .el .IP "Argumentos: \f(CW$caminho_do_diretorio\fR" 4 .IX Item "Argumentos: $caminho_do_diretorio" .PD 0 .ie n .IP "Valor de Retorno: $caminho_base_de_instalacao" 4 .el .IP "Valor de Retorno: \f(CW$caminho_base_de_instalacao\fR" 4 .IX Item "Valor de Retorno: $caminho_base_de_instalacao" .PD .PP Retorna um caminho de diretório indicando onde instalar os módulos Perl para essa instalação local de bibliotecas. Adiciona os diretórios \f(CW\*(C`lib\*(C'\fR e \f(CW\*(C`perl5\*(C'\fR ao final do caminho fornecido. .SS "install_base_arch_path" .IX Subsection "install_base_arch_path" .ie n .IP "Argumentos: $caminho_do_diretorio" 4 .el .IP "Argumentos: \f(CW$caminho_do_diretorio\fR" 4 .IX Item "Argumentos: $caminho_do_diretorio" .PD 0 .ie n .IP "Valor de Retorno: $caminho_base_de_instalacao_arch" 4 .el .IP "Valor de Retorno: \f(CW$caminho_base_de_instalacao_arch\fR" 4 .IX Item "Valor de Retorno: $caminho_base_de_instalacao_arch" .PD .PP Retorna um caminho de diretório indicando onde instalar os módulos Perl de arquiteturas específicas para essa instalação local de bibliotecas. Baseia-se no valor de retorno do método \*(L"install_base_perl_path\*(R", adicionando o valor de \f(CW$Config{archname}\fR. .SS "install_base_bin_path" .IX Subsection "install_base_bin_path" .ie n .IP "Argumentos: $caminho_do_diretorio" 4 .el .IP "Argumentos: \f(CW$caminho_do_diretorio\fR" 4 .IX Item "Argumentos: $caminho_do_diretorio" .PD 0 .ie n .IP "Valor de Retorno: $caminho_base_de_instalacao_bin" 4 .el .IP "Valor de Retorno: \f(CW$caminho_base_de_instalacao_bin\fR" 4 .IX Item "Valor de Retorno: $caminho_base_de_instalacao_bin" .PD .PP Retorna um caminho de diretório indicando onde instalar programas executáveis para essa instalação local de bibliotecas. Baseia-se no valor de retorno do método \*(L"install_base_perl_path\*(R", adicionando o diretório \f(CW\*(C`bin\*(C'\fR. .SS "resolve_empty_path" .IX Subsection "resolve_empty_path" .ie n .IP "Argumentos: $caminho_do_diretorio" 4 .el .IP "Argumentos: \f(CW$caminho_do_diretorio\fR" 4 .IX Item "Argumentos: $caminho_do_diretorio" .PD 0 .ie n .IP "Valor de Retorno: $caminho_base_de_instalacao" 4 .el .IP "Valor de Retorno: \f(CW$caminho_base_de_instalacao\fR" 4 .IX Item "Valor de Retorno: $caminho_base_de_instalacao" .PD .PP Cria e retorna o caminho de diretório raiz em que a instalação local de módulos deve ser feita. O padrão é \f(CW\*(C`~/perl5\*(C'\fR. .SS "resolve_home_path" .IX Subsection "resolve_home_path" .ie n .IP "Argumentos: $caminho_do_diretorio" 4 .el .IP "Argumentos: \f(CW$caminho_do_diretorio\fR" 4 .IX Item "Argumentos: $caminho_do_diretorio" .PD 0 .ie n .IP "Valor de Retorno: $caminho_para_home" 4 .el .IP "Valor de Retorno: \f(CW$caminho_para_home\fR" 4 .IX Item "Valor de Retorno: $caminho_para_home" .PD .PP Procura pelo diretório padrão (home) do usuário. Gera uma exceção caso não encontre resultado definitivo. .SS "resolve_relative_path" .IX Subsection "resolve_relative_path" .ie n .IP "Argumentos: $caminho_do_diretorio" 4 .el .IP "Argumentos: \f(CW$caminho_do_diretorio\fR" 4 .IX Item "Argumentos: $caminho_do_diretorio" .PD 0 .ie n .IP "Valor de Retorno: $caminho_absoluto" 4 .el .IP "Valor de Retorno: \f(CW$caminho_absoluto\fR" 4 .IX Item "Valor de Retorno: $caminho_absoluto" .PD .PP Transforma o caminho fornecido em um caminho absoluto. .SS "resolve_path" .IX Subsection "resolve_path" .ie n .IP "Argumentos: $caminho_do_diretorio" 4 .el .IP "Argumentos: \f(CW$caminho_do_diretorio\fR" 4 .IX Item "Argumentos: $caminho_do_diretorio" .PD 0 .ie n .IP "Valor de Retorno: $caminho_absoluto" 4 .el .IP "Valor de Retorno: \f(CW$caminho_absoluto\fR" 4 .IX Item "Valor de Retorno: $caminho_absoluto" .PD .PP Invoca os seguintes métodos em sequência, passando o resultado do método anterior para o seguinte, na tentativa de descobrir onde configurar o ambiente para a instalação local de bibliotecas: \*(L"resolve_empty_path\*(R", \&\*(L"resolve_home_path\*(R", \*(L"resolve_relative_path\*(R". Passa o caminho de diretório fornecido para \*(L"resolve_empty_path\*(R" que retorna um resultado que é passado para \*(L"resolve_home_path\*(R", que então tem seu resultado passado para \*(L"resolve_relative_path\*(R". O resultado dessa chamada final é então retornado pelo \*(L"resolve_path\*(R". .SH "UM AVISO SOBRE UNINST=1" .IX Header "UM AVISO SOBRE UNINST=1" Tenha cuidado ao usar o local::lib em conjunto com \*(L"make install UNINST=1\*(R". A idéia dessa opção é desinstalar a versão anterior de um módulo antes de instalar a mais recente. No entanto ela não possui uma verificação de segurança de que a versão antiga e a nova referem-se ao mesmo diretório. Usada em combinação com o local::lib, você pode potencialmente apagar uma versão globalmente acessível de um módulo e instalar a versão mais nova no diretório local. Apenas utilize \*(L"make install UNINST=1\*(R" junto com o local::lib se você entende essas possíveis consequências. .SH "LIMITAÇÕES" .IX Header "LIMITAÇÕES" As ferramentas auxiliares do perl não conseguem lidar com nomes de diretórios contendo espaços, então não é possível fazer seu bootstrap do local::lib em um diretório com espaços. O que você pode fazer é mover seu local::lib para um diretório com espaços \fBapós\fR ter instalado todos os módulos dentro dele. Mas esteja ciente que você não poderá atualizar ou instalar outros módulos do \s-1CPAN\s0 nesse diretório local após a mudança. .PP A detecção da shell é relativamente básica. Neste momento, qualquer coisa com csh no nome será tratada como a C shell ou compatível, e todo o resto será tratado como Bourne, exceto em sistemas Win32. Caso a variável de ambiente \f(CW\*(C`SHELL\*(C'\fR não esteja disponível, assumiremos tratar-se de uma shell compatível com a Bourne. .PP A técnica de bootstrap é um hack e usará o \s-1CPAN\s0.pm para o ExtUtils::MakeMaker mesmo que você tenha o \s-1CPANPLUS\s0 instalado. .PP Destrói qualquer valor pré\-existente nas variáveis de ambiente \s-1PERL5LIB, PERL_MM_OPT\s0 e \s-1PERL_MB_OPT.\s0 .PP Provavelmente deveria auto-configurar o \s-1CPAN\s0 caso isso ainda não tenha sido feito. .PP Correções (patches) são muito bem-vindos para quaisquer dos itens acima. .PP Em sistemas Win32, não há uma forma de escrever no registro as variáveis de ambiente criadas, para que elas persistam a uma reinicialização. .SH "SOLUÇÃO DE PROBLEMAS" .IX Header "SOLUÇÃO DE PROBLEMAS" Se você configurou o local::lib para instalar módulos do \s-1CPAN\s0 em algum lugar do seu 'home', e mais tarde tentou instalar um módulo fazendo \f(CW\*(C`cpan \-i Foo::Bar\*(C'\fR, mas ele falhou com um erro como: \f(CW\*(C`Warning: You do not have permissions to install into /usr/lib64/perl5/site_perl/5.8.8/x86_64\-linux at /usr/lib64/perl5/5.8.8/Foo/Bar.pm\*(C'\fR e em algum lugar no seu log de instalação houver um erro dizendo \f(CW\*(C`\*(AqINSTALL_BASE\*(Aq is not a known MakeMaker parameter name\*(C'\fR, então você de alguma forma perdeu seu ExtUtils::MakeMaker atualizado. .PP Para remediar a situação, execute novamente o procedimento de bootstrap descrito acima. .PP Então, execute \f(CW\*(C`rm \-r ~/.cpan/build/Foo\-Bar*\*(C'\fR .PP Finalmente, execute novamente o \f(CW\*(C`cpan \-i Foo::Bar\*(C'\fR e ele deve instalar sem problemas. .SH "AMBIENTE" .IX Header "AMBIENTE" .IP "\s-1SHELL\s0" 4 .IX Item "SHELL" .PD 0 .IP "\s-1COMSPEC\s0" 4 .IX Item "COMSPEC" .PD O local::lib procura pela variável de ambiente \f(CW\*(C`SHELL\*(C'\fR do usuário ao processar e exibir os comandos a serem adicionados no arquivo de configuração da shell. .Sp Em sistemas Win32, \f(CW\*(C`COMSPEC\*(C'\fR também será examinado. .SH "SUPORTE" .IX Header "SUPORTE" \&\s-1IRC:\s0 .PP .Vb 1 \& Acesse #local\-lib em irc.perl.org. .Ve .SH "AUTOR DA TRADUÇÃO" .IX Header "AUTOR DA TRADUÇÃO" Breno G. de Oliveira, \f(CW\*(C`<garu at cpan.org>\*(C'\fR, após ter perdido uma aposta para o Getty <http://search.cpan.org/~getty/> durante a Copa de 2010. .SH "COPYRIGHT" .IX Header "COPYRIGHT" Copyright (c) 2007 \- 2010 \*(L"\s-1AUTHOR\*(R"\s0 in local::lib e \*(L"\s-1CONTRIBUTORS\*(R"\s0 in local::lib do local::lib como listados em local::lib. .SH "LICENÇA" .IX Header "LICENÇA" Esta biblioteca é software livre e pode ser distribuída sob os mesmo termos do perl. PK�����B0]UeBJ��BJ����man/man3/Switch.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "Switch 3" .TH Switch 3 "2014-03-18" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" Switch \- A switch statement for Perl, do not use if you can use given/when .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use Switch; \& \& switch ($val) { \& case 1 { print "number 1" } \& case "a" { print "string a" } \& case [1..10,42] { print "number in list" } \& case (\e@array) { print "number in list" } \& case /\ew+/ { print "pattern" } \& case qr/\ew+/ { print "pattern" } \& case (\e%hash) { print "entry in hash" } \& case (\e&sub) { print "arg to subroutine" } \& else { print "previous case not true" } \& } .Ve .SH "BACKGROUND" .IX Header "BACKGROUND" [Skip ahead to \*(L"\s-1DESCRIPTION\*(R"\s0 if you don't care about the whys and wherefores of this control structure] .PP In seeking to devise a \*(L"Swiss Army\*(R" case mechanism suitable for Perl, it is useful to generalize this notion of distributed conditional testing as far as possible. Specifically, the concept of \*(L"matching\*(R" between the switch value and the various case values need not be restricted to numeric (or string or referential) equality, as it is in other languages. Indeed, as Table 1 illustrates, Perl offers at least eighteen different ways in which two values could generate a match. .PP .Vb 1 \& Table 1: Matching a switch value ($s) with a case value ($c) \& \& Switch Case Type of Match Implied Matching Code \& Value Value \& ====== ===== ===================== ============= \& \& number same numeric or referential match if $s == $c; \& or ref equality \& \& object method result of method call match if $s\->$c(); \& ref name match if defined $s\->$c(); \& or ref \& \& other other string equality match if $s eq $c; \& non\-ref non\-ref \& scalar scalar \& \& string regexp pattern match match if $s =~ /$c/; \& \& array scalar array entry existence match if 0<=$c && $c<@$s; \& ref array entry definition match if defined $s\->[$c]; \& array entry truth match if $s\->[$c]; \& \& array array array intersection match if intersects(@$s, @$c); \& ref ref (apply this table to \& all pairs of elements \& $s\->[$i] and \& $c\->[$j]) \& \& array regexp array grep match if grep /$c/, @$s; \& ref \& \& hash scalar hash entry existence match if exists $s\->{$c}; \& ref hash entry definition match if defined $s\->{$c}; \& hash entry truth match if $s\->{$c}; \& \& hash regexp hash grep match if grep /$c/, keys %$s; \& ref \& \& sub scalar return value defn match if defined $s\->($c); \& ref return value truth match if $s\->($c); \& \& sub array return value defn match if defined $s\->(@$c); \& ref ref return value truth match if $s\->(@$c); .Ve .PP In reality, Table 1 covers 31 alternatives, because only the equality and intersection tests are commutative; in all other cases, the roles of the \f(CW$s\fR and \f(CW$c\fR variables could be reversed to produce a different test. For example, instead of testing a single hash for the existence of a series of keys (\f(CW\*(C`match if exists $s\->{$c}\*(C'\fR), one could test for the existence of a single key in a series of hashes (\f(CW\*(C`match if exists $c\->{$s}\*(C'\fR). .SH "DESCRIPTION" .IX Header "DESCRIPTION" The Switch.pm module implements a generalized case mechanism that covers most (but not all) of the numerous possible combinations of switch and case values described above. .PP The module augments the standard Perl syntax with two new control statements: \f(CW\*(C`switch\*(C'\fR and \f(CW\*(C`case\*(C'\fR. The \f(CW\*(C`switch\*(C'\fR statement takes a single scalar argument of any type, specified in parentheses. \&\f(CW\*(C`switch\*(C'\fR stores this value as the current switch value in a (localized) control variable. The value is followed by a block which may contain one or more Perl statements (including the \f(CW\*(C`case\*(C'\fR statement described below). The block is unconditionally executed once the switch value has been cached. .PP A \f(CW\*(C`case\*(C'\fR statement takes a single scalar argument (in mandatory parentheses if it's a variable; otherwise the parens are optional) and selects the appropriate type of matching between that argument and the current switch value. The type of matching used is determined by the respective types of the switch value and the \f(CW\*(C`case\*(C'\fR argument, as specified in Table 1. If the match is successful, the mandatory block associated with the \f(CW\*(C`case\*(C'\fR statement is executed. .PP In most other respects, the \f(CW\*(C`case\*(C'\fR statement is semantically identical to an \f(CW\*(C`if\*(C'\fR statement. For example, it can be followed by an \f(CW\*(C`else\*(C'\fR clause, and can be used as a postfix statement qualifier. .PP However, when a \f(CW\*(C`case\*(C'\fR block has been executed control is automatically transferred to the statement after the immediately enclosing \f(CW\*(C`switch\*(C'\fR block, rather than to the next statement within the block. In other words, the success of any \f(CW\*(C`case\*(C'\fR statement prevents other cases in the same scope from executing. But see \*(L"Allowing fall-through\*(R" below. .PP Together these two new statements provide a fully generalized case mechanism: .PP .Vb 1 \& use Switch; \& \& # AND LATER... \& \& %special = ( woohoo => 1, d\*(Aqoh => 1 ); \& \& while (<>) { \& chomp; \& switch ($_) { \& case (%special) { print "homer\en"; } # if $special{$_} \& case /[a\-z]/i { print "alpha\en"; } # if $_ =~ /a\-z/i \& case [1..9] { print "small num\en"; } # if $_ in [1..9] \& case { $_[0] >= 10 } { print "big num\en"; } # if $_ >= 10 \& print "must be punctuation\en" case /\eW/; # if $_ ~= /\eW/ \& } \& } .Ve .PP Note that \f(CW\*(C`switch\*(C'\fRes can be nested within \f(CW\*(C`case\*(C'\fR (or any other) blocks, and a series of \f(CW\*(C`case\*(C'\fR statements can try different types of matches \&\*(-- hash membership, pattern match, array intersection, simple equality, etc. \*(-- against the same switch value. .PP The use of intersection tests against an array reference is particularly useful for aggregating integral cases: .PP .Vb 8 \& sub classify_digit \& { \& switch ($_[0]) { case 0 { return \*(Aqzero\*(Aq } \& case [2,4,6,8] { return \*(Aqeven\*(Aq } \& case [1,3,5,7,9] { return \*(Aqodd\*(Aq } \& case /[A\-F]/i { return \*(Aqhex\*(Aq } \& } \& } .Ve .SS "Allowing fall-through" .IX Subsection "Allowing fall-through" Fall-though (trying another case after one has already succeeded) is usually a Bad Idea in a switch statement. However, this is Perl, not a police state, so there \fIis\fR a way to do it, if you must. .PP If a \f(CW\*(C`case\*(C'\fR block executes an untargeted \f(CW\*(C`next\*(C'\fR, control is immediately transferred to the statement \fIafter\fR the \f(CW\*(C`case\*(C'\fR statement (i.e. usually another case), rather than out of the surrounding \&\f(CW\*(C`switch\*(C'\fR block. .PP For example: .PP .Vb 7 \& switch ($val) { \& case 1 { handle_num_1(); next } # and try next case... \& case "1" { handle_str_1(); next } # and try next case... \& case [0..9] { handle_num_any(); } # and we\*(Aqre done \& case /\ed/ { handle_dig_any(); next } # and try next case... \& case /.*/ { handle_str_any(); next } # and try next case... \& } .Ve .PP If \f(CW$val\fR held the number \f(CW1\fR, the above \f(CW\*(C`switch\*(C'\fR block would call the first three \f(CW\*(C`handle_...\*(C'\fR subroutines, jumping to the next case test each time it encountered a \f(CW\*(C`next\*(C'\fR. After the third \f(CW\*(C`case\*(C'\fR block was executed, control would jump to the end of the enclosing \&\f(CW\*(C`switch\*(C'\fR block. .PP On the other hand, if \f(CW$val\fR held \f(CW10\fR, then only the last two \f(CW\*(C`handle_...\*(C'\fR subroutines would be called. .PP Note that this mechanism allows the notion of \fIconditional fall-through\fR. For example: .PP .Vb 4 \& switch ($val) { \& case [0..9] { handle_num_any(); next if $val < 7; } \& case /\ed/ { handle_dig_any(); } \& } .Ve .PP If an untargeted \f(CW\*(C`last\*(C'\fR statement is executed in a case block, this immediately transfers control out of the enclosing \f(CW\*(C`switch\*(C'\fR block (in other words, there is an implicit \f(CW\*(C`last\*(C'\fR at the end of each normal \f(CW\*(C`case\*(C'\fR block). Thus the previous example could also have been written: .PP .Vb 4 \& switch ($val) { \& case [0..9] { handle_num_any(); last if $val >= 7; next; } \& case /\ed/ { handle_dig_any(); } \& } .Ve .SS "Automating fall-through" .IX Subsection "Automating fall-through" In situations where case fall-through should be the norm, rather than an exception, an endless succession of terminal \f(CW\*(C`next\*(C'\fRs is tedious and ugly. Hence, it is possible to reverse the default behaviour by specifying the string \*(L"fallthrough\*(R" when importing the module. For example, the following code is equivalent to the first example in \*(L"Allowing fall-through\*(R": .PP .Vb 1 \& use Switch \*(Aqfallthrough\*(Aq; \& \& switch ($val) { \& case 1 { handle_num_1(); } \& case "1" { handle_str_1(); } \& case [0..9] { handle_num_any(); last } \& case /\ed/ { handle_dig_any(); } \& case /.*/ { handle_str_any(); } \& } .Ve .PP Note the explicit use of a \f(CW\*(C`last\*(C'\fR to preserve the non-fall-through behaviour of the third case. .SS "Alternative syntax" .IX Subsection "Alternative syntax" Perl 6 will provide a built-in switch statement with essentially the same semantics as those offered by Switch.pm, but with a different pair of keywords. In Perl 6 \f(CW\*(C`switch\*(C'\fR will be spelled \f(CW\*(C`given\*(C'\fR, and \&\f(CW\*(C`case\*(C'\fR will be pronounced \f(CW\*(C`when\*(C'\fR. In addition, the \f(CW\*(C`when\*(C'\fR statement will not require switch or case values to be parenthesized. .PP This future syntax is also (largely) available via the Switch.pm module, by importing it with the argument \f(CW"Perl6"\fR. For example: .PP .Vb 1 \& use Switch \*(AqPerl6\*(Aq; \& \& given ($val) { \& when 1 { handle_num_1(); } \& when ($str1) { handle_str_1(); } \& when [0..9] { handle_num_any(); last } \& when /\ed/ { handle_dig_any(); } \& when /.*/ { handle_str_any(); } \& default { handle anything else; } \& } .Ve .PP Note that scalars still need to be parenthesized, since they would be ambiguous in Perl 5. .PP Note too that you can mix and match both syntaxes by importing the module with: .PP .Vb 1 \& use Switch \*(AqPerl5\*(Aq, \*(AqPerl6\*(Aq; .Ve .SS "Higher-order Operations" .IX Subsection "Higher-order Operations" One situation in which \f(CW\*(C`switch\*(C'\fR and \f(CW\*(C`case\*(C'\fR do not provide a good substitute for a cascaded \f(CW\*(C`if\*(C'\fR, is where a switch value needs to be tested against a series of conditions. For example: .PP .Vb 11 \& sub beverage { \& switch (shift) { \& case { $_[0] < 10 } { return \*(Aqmilk\*(Aq } \& case { $_[0] < 20 } { return \*(Aqcoke\*(Aq } \& case { $_[0] < 30 } { return \*(Aqbeer\*(Aq } \& case { $_[0] < 40 } { return \*(Aqwine\*(Aq } \& case { $_[0] < 50 } { return \*(Aqmalt\*(Aq } \& case { $_[0] < 60 } { return \*(AqMoet\*(Aq } \& else { return \*(Aqmilk\*(Aq } \& } \& } .Ve .PP (This is equivalent to writing \f(CW\*(C`case (sub { $_[0] < 10 })\*(C'\fR, etc.; \f(CW$_[0]\fR is the argument to the anonymous subroutine.) .PP The need to specify each condition as a subroutine block is tiresome. To overcome this, when importing Switch.pm, a special \*(L"placeholder\*(R" subroutine named \f(CW\*(C`_\|_\*(C'\fR [sic] may also be imported. This subroutine converts (almost) any expression in which it appears to a reference to a higher-order function. That is, the expression: .PP .Vb 1 \& use Switch \*(Aq_\|_\*(Aq; \& \& _\|_ < 2 .Ve .PP is equivalent to: .PP .Vb 1 \& sub { $_[0] < 2 } .Ve .PP With \f(CW\*(C`_\|_\*(C'\fR, the previous ugly case statements can be rewritten: .PP .Vb 7 \& case _\|_ < 10 { return \*(Aqmilk\*(Aq } \& case _\|_ < 20 { return \*(Aqcoke\*(Aq } \& case _\|_ < 30 { return \*(Aqbeer\*(Aq } \& case _\|_ < 40 { return \*(Aqwine\*(Aq } \& case _\|_ < 50 { return \*(Aqmalt\*(Aq } \& case _\|_ < 60 { return \*(AqMoet\*(Aq } \& else { return \*(Aqmilk\*(Aq } .Ve .PP The \f(CW\*(C`_\|_\*(C'\fR subroutine makes extensive use of operator overloading to perform its magic. All operations involving _\|_ are overloaded to produce an anonymous subroutine that implements a lazy version of the original operation. .PP The only problem is that operator overloading does not allow the boolean operators \f(CW\*(C`&&\*(C'\fR and \f(CW\*(C`||\*(C'\fR to be overloaded. So a case statement like this: .PP .Vb 1 \& case 0 <= _\|_ && _\|_ < 10 { return \*(Aqdigit\*(Aq } .Ve .PP doesn't act as expected, because when it is executed, it constructs two higher order subroutines and then treats the two resulting references as arguments to \f(CW\*(C`&&\*(C'\fR: .PP .Vb 1 \& sub { 0 <= $_[0] } && sub { $_[0] < 10 } .Ve .PP This boolean expression is inevitably true, since both references are non-false. Fortunately, the overloaded \f(CW\*(Aqbool\*(Aq\fR operator catches this situation and flags it as an error. .SH "DEPENDENCIES" .IX Header "DEPENDENCIES" The module is implemented using Filter::Util::Call and Text::Balanced and requires both these modules to be installed. .SH "AUTHOR" .IX Header "AUTHOR" Damian Conway (damian@conway.org). This module is now maintained by Alexandr Ciornii (alexchorny@gmail.com). Previously was maintained by Rafael Garcia-Suarez and perl5 porters. .SH "BUGS" .IX Header "BUGS" There are undoubtedly serious bugs lurking somewhere in code this funky :\-) Bug reports and other feedback are most welcome. .PP May create syntax errors in other parts of code. .PP On perl 5.10.x may cause syntax error if \*(L"case\*(R" is present inside heredoc. .PP In general, use given/when instead. It were introduced in perl 5.10.0. Perl 5.10.0 was released in 2007. .SH "LIMITATIONS" .IX Header "LIMITATIONS" Due to the heuristic nature of Switch.pm's source parsing, the presence of regexes with embedded newlines that are specified with raw \f(CW\*(C`/.../\*(C'\fR delimiters and don't have a modifier \f(CW\*(C`//x\*(C'\fR are indistinguishable from code chunks beginning with the division operator \f(CW\*(C`/\*(C'\fR. As a workaround you must use \f(CW\*(C`m/.../\*(C'\fR or \f(CW\*(C`m?...?\*(C'\fR for such patterns. Also, the presence of regexes specified with raw \f(CW\*(C`?...?\*(C'\fR delimiters may cause mysterious errors. The workaround is to use \f(CW\*(C`m?...?\*(C'\fR instead. .PP Due to the way source filters work in Perl, you can't use Switch inside an string \f(CW\*(C`eval\*(C'\fR. .PP May not work if sub prototypes are used (RT#33988). .PP Regex captures in when are not available to code. .PP If your source file is longer then 1 million characters and you have a switch statement that crosses the 1 million (or 2 million, etc.) character boundary you will get mysterious errors. The workaround is to use smaller source files. .SH "COPYRIGHT" .IX Header "COPYRIGHT" .Vb 3 \& Copyright (c) 1997\-2008, Damian Conway. All Rights Reserved. \& This module is free software. It may be used, redistributed \& and/or modified under the same terms as Perl itself. .Ve PK�����B0]E4n��n����man/man3/HTTP::Tiny.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "HTTP::Tiny 3" .TH HTTP::Tiny 3 "2023-07-11" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" HTTP::Tiny \- A small, simple, correct HTTP/1.1 client .SH "VERSION" .IX Header "VERSION" version 0.088 .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use HTTP::Tiny; \& \& my $response = HTTP::Tiny\->new\->get(\*(Aqhttp://example.com/\*(Aq); \& \& die "Failed!\en" unless $response\->{success}; \& \& print "$response\->{status} $response\->{reason}\en"; \& \& while (my ($k, $v) = each %{$response\->{headers}}) { \& for (ref $v eq \*(AqARRAY\*(Aq ? @$v : $v) { \& print "$k: $_\en"; \& } \& } \& \& print $response\->{content} if length $response\->{content}; .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" This is a very simple \s-1HTTP/1.1\s0 client, designed for doing simple requests without the overhead of a large framework like LWP::UserAgent. .PP It is more correct and more complete than HTTP::Lite. It supports proxies and redirection. It also correctly resumes after \s-1EINTR.\s0 .PP If IO::Socket::IP 0.25 or later is installed, HTTP::Tiny will use it instead of IO::Socket::INET for transparent support for both IPv4 and IPv6. .PP Cookie support requires HTTP::CookieJar or an equivalent class. .SH "METHODS" .IX Header "METHODS" .SS "new" .IX Subsection "new" .Vb 1 \& $http = HTTP::Tiny\->new( %attributes ); .Ve .PP This constructor returns a new HTTP::Tiny object. Valid attributes include: .IP "\(bu" 4 \&\f(CW\*(C`agent\*(C'\fR — A user-agent string (defaults to 'HTTP\-Tiny/$VERSION'). If \f(CW\*(C`agent\*(C'\fR — ends in a space character, the default user-agent string is appended. .IP "\(bu" 4 \&\f(CW\*(C`cookie_jar\*(C'\fR — An instance of HTTP::CookieJar — or equivalent class that supports the \f(CW\*(C`add\*(C'\fR and \f(CW\*(C`cookie_header\*(C'\fR methods .IP "\(bu" 4 \&\f(CW\*(C`default_headers\*(C'\fR — A hashref of default headers to apply to requests .IP "\(bu" 4 \&\f(CW\*(C`local_address\*(C'\fR — The local \s-1IP\s0 address to bind to .IP "\(bu" 4 \&\f(CW\*(C`keep_alive\*(C'\fR — Whether to reuse the last connection (if for the same scheme, host and port) (defaults to 1) .IP "\(bu" 4 \&\f(CW\*(C`max_redirect\*(C'\fR — Maximum number of redirects allowed (defaults to 5) .IP "\(bu" 4 \&\f(CW\*(C`max_size\*(C'\fR — Maximum response size in bytes (only when not using a data callback). If defined, requests with responses larger than this will return a 599 status code. .IP "\(bu" 4 \&\f(CW\*(C`http_proxy\*(C'\fR — \s-1URL\s0 of a proxy server to use for \s-1HTTP\s0 connections (default is \f(CW$ENV{http_proxy}\fR — if set) .IP "\(bu" 4 \&\f(CW\*(C`https_proxy\*(C'\fR — \s-1URL\s0 of a proxy server to use for \s-1HTTPS\s0 connections (default is \f(CW$ENV{https_proxy}\fR — if set) .IP "\(bu" 4 \&\f(CW\*(C`proxy\*(C'\fR — \s-1URL\s0 of a generic proxy server for both \s-1HTTP\s0 and \s-1HTTPS\s0 connections (default is \f(CW$ENV{all_proxy}\fR — if set) .IP "\(bu" 4 \&\f(CW\*(C`no_proxy\*(C'\fR — List of domain suffixes that should not be proxied. Must be a comma-separated string or an array reference. (default is \f(CW$ENV{no_proxy}\fR —) .IP "\(bu" 4 \&\f(CW\*(C`timeout\*(C'\fR — Request timeout in seconds (default is 60) If a socket open, read or write takes longer than the timeout, the request response status code will be 599. .IP "\(bu" 4 \&\f(CW\*(C`verify_SSL\*(C'\fR — A boolean that indicates whether to validate the \s-1TLS/SSL\s0 certificate of an \f(CW\*(C`https\*(C'\fR — connection (default is true). Changed from false to true in version 0.083. .IP "\(bu" 4 \&\f(CW\*(C`SSL_options\*(C'\fR — A hashref of \f(CW\*(C`SSL_*\*(C'\fR — options to pass through to IO::Socket::SSL .IP "\(bu" 4 \&\f(CW$ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT}\fR \- Changes the default certificate verification behavior to not check server identity if set to 1. Only effective if \f(CW\*(C`verify_SSL\*(C'\fR is not set. Added in version 0.083. .PP An accessor/mutator method exists for each attribute. .PP Passing an explicit \f(CW\*(C`undef\*(C'\fR for \f(CW\*(C`proxy\*(C'\fR, \f(CW\*(C`http_proxy\*(C'\fR or \f(CW\*(C`https_proxy\*(C'\fR will prevent getting the corresponding proxies from the environment. .PP Errors during request execution will result in a pseudo-HTTP status code of 599 and a reason of \*(L"Internal Exception\*(R". The content field in the response will contain the text of the error. .PP The \f(CW\*(C`keep_alive\*(C'\fR parameter enables a persistent connection, but only to a single destination scheme, host and port. If any connection-relevant attributes are modified via accessor, or if the process \s-1ID\s0 or thread \s-1ID\s0 change, the persistent connection will be dropped. If you want persistent connections across multiple destinations, use multiple HTTP::Tiny objects. .PP See \*(L"\s-1SSL SUPPORT\*(R"\s0 for more on the \f(CW\*(C`verify_SSL\*(C'\fR and \f(CW\*(C`SSL_options\*(C'\fR attributes. .SS "get|head|put|post|patch|delete" .IX Subsection "get|head|put|post|patch|delete" .Vb 3 \& $response = $http\->get($url); \& $response = $http\->get($url, \e%options); \& $response = $http\->head($url); .Ve .PP These methods are shorthand for calling \f(CW\*(C`request()\*(C'\fR for the given method. The \&\s-1URL\s0 must have unsafe characters escaped and international domain names encoded. See \f(CW\*(C`request()\*(C'\fR for valid options and a description of the response. .PP The \f(CW\*(C`success\*(C'\fR field of the response will be true if the status code is 2XX. .SS "post_form" .IX Subsection "post_form" .Vb 2 \& $response = $http\->post_form($url, $form_data); \& $response = $http\->post_form($url, $form_data, \e%options); .Ve .PP This method executes a \f(CW\*(C`POST\*(C'\fR request and sends the key/value pairs from a form data hash or array reference to the given \s-1URL\s0 with a \f(CW\*(C`content\-type\*(C'\fR of \&\f(CW\*(C`application/x\-www\-form\-urlencoded\*(C'\fR. If data is provided as an array reference, the order is preserved; if provided as a hash reference, the terms are sorted on key and value for consistency. See documentation for the \&\f(CW\*(C`www_form_urlencode\*(C'\fR method for details on the encoding. .PP The \s-1URL\s0 must have unsafe characters escaped and international domain names encoded. See \f(CW\*(C`request()\*(C'\fR for valid options and a description of the response. Any \f(CW\*(C`content\-type\*(C'\fR header or content in the options hashref will be ignored. .PP The \f(CW\*(C`success\*(C'\fR field of the response will be true if the status code is 2XX. .SS "mirror" .IX Subsection "mirror" .Vb 4 \& $response = $http\->mirror($url, $file, \e%options) \& if ( $response\->{success} ) { \& print "$file is up to date\en"; \& } .Ve .PP Executes a \f(CW\*(C`GET\*(C'\fR request for the \s-1URL\s0 and saves the response body to the file name provided. The \s-1URL\s0 must have unsafe characters escaped and international domain names encoded. If the file already exists, the request will include an \&\f(CW\*(C`If\-Modified\-Since\*(C'\fR header with the modification timestamp of the file. You may specify a different \f(CW\*(C`If\-Modified\-Since\*(C'\fR header yourself in the \f(CW\*(C`$options\->{headers}\*(C'\fR hash. .PP The \f(CW\*(C`success\*(C'\fR field of the response will be true if the status code is 2XX or if the status code is 304 (unmodified). .PP If the file was modified and the server response includes a properly formatted \f(CW\*(C`Last\-Modified\*(C'\fR header, the file modification time will be updated accordingly. .SS "request" .IX Subsection "request" .Vb 2 \& $response = $http\->request($method, $url); \& $response = $http\->request($method, $url, \e%options); .Ve .PP Executes an \s-1HTTP\s0 request of the given method type ('\s-1GET\s0', '\s-1HEAD\s0', '\s-1POST\s0', \&'\s-1PUT\s0', etc.) on the given \s-1URL.\s0 The \s-1URL\s0 must have unsafe characters escaped and international domain names encoded. .PP \&\fB\s-1NOTE\s0\fR: Method names are \fBcase-sensitive\fR per the \s-1HTTP/1.1\s0 specification. Don't use \f(CW\*(C`get\*(C'\fR when you really want \f(CW\*(C`GET\*(C'\fR. See \s-1LIMITATIONS\s0 for how this applies to redirection. .PP If the \s-1URL\s0 includes a \*(L"user:password\*(R" stanza, they will be used for Basic-style authorization headers. (Authorization headers will not be included in a redirected request.) For example: .PP .Vb 1 \& $http\->request(\*(AqGET\*(Aq, \*(Aqhttp://Aladdin:open sesame@example.com/\*(Aq); .Ve .PP If the \*(L"user:password\*(R" stanza contains reserved characters, they must be percent-escaped: .PP .Vb 1 \& $http\->request(\*(AqGET\*(Aq, \*(Aqhttp://john%40example.com:password@example.com/\*(Aq); .Ve .PP A hashref of options may be appended to modify the request. .PP Valid options are: .IP "\(bu" 4 \&\f(CW\*(C`headers\*(C'\fR — A hashref containing headers to include with the request. If the value for a header is an array reference, the header will be output multiple times with each value in the array. These headers over-write any default headers. .IP "\(bu" 4 \&\f(CW\*(C`content\*(C'\fR — A scalar to include as the body of the request \s-1OR\s0 a code reference that will be called iteratively to produce the body of the request .IP "\(bu" 4 \&\f(CW\*(C`trailer_callback\*(C'\fR — A code reference that will be called if it exists to provide a hashref of trailing headers (only used with chunked transfer-encoding) .IP "\(bu" 4 \&\f(CW\*(C`data_callback\*(C'\fR — A code reference that will be called for each chunks of the response body received. .IP "\(bu" 4 \&\f(CW\*(C`peer\*(C'\fR — Override host resolution and force all connections to go only to a specific peer address, regardless of the \s-1URL\s0 of the request. This will include any redirections! This options should be used with extreme caution (e.g. debugging or very special circumstances). It can be given as either a scalar or a code reference that will receive the hostname and whose response will be taken as the address. .PP The \f(CW\*(C`Host\*(C'\fR header is generated from the \s-1URL\s0 in accordance with \s-1RFC 2616.\s0 It is a fatal error to specify \f(CW\*(C`Host\*(C'\fR in the \f(CW\*(C`headers\*(C'\fR option. Other headers may be ignored or overwritten if necessary for transport compliance. .PP If the \f(CW\*(C`content\*(C'\fR option is a code reference, it will be called iteratively to provide the content body of the request. It should return the empty string or undef when the iterator is exhausted. .PP If the \f(CW\*(C`content\*(C'\fR option is the empty string, no \f(CW\*(C`content\-type\*(C'\fR or \&\f(CW\*(C`content\-length\*(C'\fR headers will be generated. .PP If the \f(CW\*(C`data_callback\*(C'\fR option is provided, it will be called iteratively until the entire response body is received. The first argument will be a string containing a chunk of the response body, the second argument will be the in-progress response hash reference, as described below. (This allows customizing the action of the callback based on the \f(CW\*(C`status\*(C'\fR or \f(CW\*(C`headers\*(C'\fR received prior to the content body.) .PP Content data in the request/response is handled as \*(L"raw bytes\*(R". Any encoding/decoding (with associated headers) are the responsibility of the caller. .PP The \f(CW\*(C`request\*(C'\fR method returns a hashref containing the response. The hashref will have the following keys: .IP "\(bu" 4 \&\f(CW\*(C`success\*(C'\fR — Boolean indicating whether the operation returned a 2XX status code .IP "\(bu" 4 \&\f(CW\*(C`url\*(C'\fR — \s-1URL\s0 that provided the response. This is the \s-1URL\s0 of the request unless there were redirections, in which case it is the last \s-1URL\s0 queried in a redirection chain .IP "\(bu" 4 \&\f(CW\*(C`status\*(C'\fR — The \s-1HTTP\s0 status code of the response .IP "\(bu" 4 \&\f(CW\*(C`reason\*(C'\fR — The response phrase returned by the server .IP "\(bu" 4 \&\f(CW\*(C`content\*(C'\fR — The body of the response. If the response does not have any content or if a data callback is provided to consume the response body, this will be the empty string .IP "\(bu" 4 \&\f(CW\*(C`headers\*(C'\fR — A hashref of header fields. All header field names will be normalized to be lower case. If a header is repeated, the value will be an arrayref; it will otherwise be a scalar string containing the value .IP "\(bu" 4 \&\f(CW\*(C`protocol\*(C'\fR \- If this field exists, it is the protocol of the response such as \s-1HTTP/1.0\s0 or \s-1HTTP/1.1\s0 .IP "\(bu" 4 \&\f(CW\*(C`redirects\*(C'\fR If this field exists, it is an arrayref of response hash references from redirects in the same order that redirections occurred. If it does not exist, then no redirections occurred. .PP On an error during the execution of the request, the \f(CW\*(C`status\*(C'\fR field will contain 599, and the \f(CW\*(C`content\*(C'\fR field will contain the text of the error. .SS "www_form_urlencode" .IX Subsection "www_form_urlencode" .Vb 2 \& $params = $http\->www_form_urlencode( $data ); \& $response = $http\->get("http://example.com/query?$params"); .Ve .PP This method converts the key/value pairs from a data hash or array reference into a \f(CW\*(C`x\-www\-form\-urlencoded\*(C'\fR string. The keys and values from the data reference will be \s-1UTF\-8\s0 encoded and escaped per \s-1RFC 3986.\s0 If a value is an array reference, the key will be repeated with each of the values of the array reference. If data is provided as a hash reference, the key/value pairs in the resulting string will be sorted by key and value for consistent ordering. .SS "can_ssl" .IX Subsection "can_ssl" .Vb 3 \& $ok = HTTP::Tiny\->can_ssl; \& ($ok, $why) = HTTP::Tiny\->can_ssl; \& ($ok, $why) = $http\->can_ssl; .Ve .PP Indicates if \s-1SSL\s0 support is available. When called as a class object, it checks for the correct version of Net::SSLeay and IO::Socket::SSL. When called as an object methods, if \f(CW\*(C`SSL_verify\*(C'\fR is true or if \f(CW\*(C`SSL_verify_mode\*(C'\fR is set in \f(CW\*(C`SSL_options\*(C'\fR, it checks that a \s-1CA\s0 file is available. .PP In scalar context, returns a boolean indicating if \s-1SSL\s0 is available. In list context, returns the boolean and a (possibly multi-line) string of errors indicating why \s-1SSL\s0 isn't available. .SS "connected" .IX Subsection "connected" .Vb 2 \& $host = $http\->connected; \& ($host, $port) = $http\->connected; .Ve .PP Indicates if a connection to a peer is being kept alive, per the \f(CW\*(C`keep_alive\*(C'\fR option. .PP In scalar context, returns the peer host and port, joined with a colon, or \&\f(CW\*(C`undef\*(C'\fR (if no peer is connected). In list context, returns the peer host and port or an empty list (if no peer is connected). .PP \&\fBNote\fR: This method cannot reliably be used to discover whether the remote host has closed its end of the socket. .SH "TLS/SSL SUPPORT" .IX Header "TLS/SSL SUPPORT" Direct \f(CW\*(C`https\*(C'\fR connections are supported only if IO::Socket::SSL 1.56 or greater and Net::SSLeay 1.49 or greater are installed. An error will occur if new enough versions of these modules are not installed or if the \s-1TLS\s0 encryption fails. You can also use \f(CW\*(C`HTTP::Tiny::can_ssl()\*(C'\fR utility function that returns boolean to see if the required modules are installed. .PP An \f(CW\*(C`https\*(C'\fR connection may be made via an \f(CW\*(C`http\*(C'\fR proxy that supports the \s-1CONNECT\s0 command (i.e. \s-1RFC 2817\s0). You may not proxy \f(CW\*(C`https\*(C'\fR via a proxy that itself requires \f(CW\*(C`https\*(C'\fR to communicate. .PP \&\s-1TLS/SSL\s0 provides two distinct capabilities: .IP "\(bu" 4 Encrypted communication channel .IP "\(bu" 4 Verification of server identity .PP \&\fBBy default, HTTP::Tiny verifies server identity\fR. .PP This was changed in version 0.083 due to security concerns. The previous default behavior can be enabled by setting \f(CW$ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT}\fR to 1. .PP Verification is done by checking that that the \s-1TLS/SSL\s0 connection has a valid certificate corresponding to the host name of the connection and that the certificate has been verified by a \s-1CA.\s0 Assuming you trust the \s-1CA,\s0 this will protect against machine-in-the-middle attacks <http://en.wikipedia.org/wiki/Machine-in-the-middle_attack>. .PP Certificate verification requires a file containing trusted \s-1CA\s0 certificates. .PP If the environment variable \f(CW\*(C`SSL_CERT_FILE\*(C'\fR is present, HTTP::Tiny will try to find a \s-1CA\s0 certificate file in that location. .PP If the Mozilla::CA module is installed, HTTP::Tiny will use the \s-1CA\s0 file included with it as a source of trusted \s-1CA\s0's. .PP If that module is not available, then HTTP::Tiny will search several system-specific default locations for a \s-1CA\s0 certificate file: .IP "\(bu" 4 /etc/ssl/certs/ca\-certificates.crt .IP "\(bu" 4 /etc/pki/tls/certs/ca\-bundle.crt .IP "\(bu" 4 /etc/ssl/ca\-bundle.pem .IP "\(bu" 4 /etc/openssl/certs/ca\-certificates.crt .IP "\(bu" 4 /etc/ssl/cert.pem .IP "\(bu" 4 /usr/local/share/certs/ca\-root\-nss.crt .IP "\(bu" 4 /etc/pki/tls/cacert.pem .IP "\(bu" 4 /etc/certs/ca\-certificates.crt .PP An error will be occur if \f(CW\*(C`verify_SSL\*(C'\fR is true and no \s-1CA\s0 certificate file is available. .PP If you desire complete control over \s-1TLS/SSL\s0 connections, the \f(CW\*(C`SSL_options\*(C'\fR attribute lets you provide a hash reference that will be passed through to \&\f(CW\*(C`IO::Socket::SSL::start_SSL()\*(C'\fR, overriding any options set by HTTP::Tiny. For example, to provide your own trusted \s-1CA\s0 file: .PP .Vb 3 \& SSL_options => { \& SSL_ca_file => $file_path, \& } .Ve .PP The \f(CW\*(C`SSL_options\*(C'\fR attribute could also be used for such things as providing a client certificate for authentication to a server or controlling the choice of cipher used for the \s-1TLS/SSL\s0 connection. See IO::Socket::SSL documentation for details. .SH "PROXY SUPPORT" .IX Header "PROXY SUPPORT" HTTP::Tiny can proxy both \f(CW\*(C`http\*(C'\fR and \f(CW\*(C`https\*(C'\fR requests. Only Basic proxy authorization is supported and it must be provided as part of the proxy \s-1URL:\s0 \&\f(CW\*(C`http://user:pass@proxy.example.com/\*(C'\fR. .PP HTTP::Tiny supports the following proxy environment variables: .IP "\(bu" 4 http_proxy or \s-1HTTP_PROXY\s0 .IP "\(bu" 4 https_proxy or \s-1HTTPS_PROXY\s0 .IP "\(bu" 4 all_proxy or \s-1ALL_PROXY\s0 .PP If the \f(CW\*(C`REQUEST_METHOD\*(C'\fR environment variable is set, then this might be a \s-1CGI\s0 process and \f(CW\*(C`HTTP_PROXY\*(C'\fR would be set from the \f(CW\*(C`Proxy:\*(C'\fR header, which is a security risk. If \f(CW\*(C`REQUEST_METHOD\*(C'\fR is set, \f(CW\*(C`HTTP_PROXY\*(C'\fR (the upper case variant only) is ignored, but \f(CW\*(C`CGI_HTTP_PROXY\*(C'\fR is considered instead. .PP Tunnelling \f(CW\*(C`https\*(C'\fR over an \f(CW\*(C`http\*(C'\fR proxy using the \s-1CONNECT\s0 method is supported. If your proxy uses \f(CW\*(C`https\*(C'\fR itself, you can not tunnel \f(CW\*(C`https\*(C'\fR over it. .PP Be warned that proxying an \f(CW\*(C`https\*(C'\fR connection opens you to the risk of a man-in-the-middle attack by the proxy server. .PP The \f(CW\*(C`no_proxy\*(C'\fR environment variable is supported in the format of a comma-separated list of domain extensions proxy should not be used for. .PP Proxy arguments passed to \f(CW\*(C`new\*(C'\fR will override their corresponding environment variables. .SH "LIMITATIONS" .IX Header "LIMITATIONS" HTTP::Tiny is \fIconditionally compliant\fR with the \&\s-1HTTP/1.1\s0 specifications <http://www.w3.org/Protocols/>: .IP "\(bu" 4 \&\*(L"Message Syntax and Routing\*(R" [\s-1RFC7230\s0] .IP "\(bu" 4 \&\*(L"Semantics and Content\*(R" [\s-1RFC7231\s0] .IP "\(bu" 4 \&\*(L"Conditional Requests\*(R" [\s-1RFC7232\s0] .IP "\(bu" 4 \&\*(L"Range Requests\*(R" [\s-1RFC7233\s0] .IP "\(bu" 4 \&\*(L"Caching\*(R" [\s-1RFC7234\s0] .IP "\(bu" 4 \&\*(L"Authentication\*(R" [\s-1RFC7235\s0] .PP It attempts to meet all \*(L"\s-1MUST\*(R"\s0 requirements of the specification, but does not implement all \*(L"\s-1SHOULD\*(R"\s0 requirements. (Note: it was developed against the earlier \s-1RFC 2616\s0 specification and may not yet meet the revised \s-1RFC 7230\-7235\s0 spec.) Additionally, HTTP::Tiny supports the \f(CW\*(C`PATCH\*(C'\fR method of \s-1RFC 5789.\s0 .PP Some particular limitations of note include: .IP "\(bu" 4 HTTP::Tiny focuses on correct transport. Users are responsible for ensuring that user-defined headers and content are compliant with the \s-1HTTP/1.1\s0 specification. .IP "\(bu" 4 Users must ensure that URLs are properly escaped for unsafe characters and that international domain names are properly encoded to \s-1ASCII.\s0 See URI::Escape, URI::_punycode and Net::IDN::Encode. .IP "\(bu" 4 Redirection is very strict against the specification. Redirection is only automatic for response codes 301, 302, 307 and 308 if the request method is \&'\s-1GET\s0' or '\s-1HEAD\s0'. Response code 303 is always converted into a '\s-1GET\s0' redirection, as mandated by the specification. There is no automatic support for status 305 (\*(L"Use proxy\*(R") redirections. .IP "\(bu" 4 There is no provision for delaying a request body using an \f(CW\*(C`Expect\*(C'\fR header. Unexpected \f(CW\*(C`1XX\*(C'\fR responses are silently ignored as per the specification. .IP "\(bu" 4 Only 'chunked' \f(CW\*(C`Transfer\-Encoding\*(C'\fR is supported. .IP "\(bu" 4 There is no support for a Request-URI of '*' for the '\s-1OPTIONS\s0' request. .IP "\(bu" 4 Headers mentioned in the RFCs and some other, well-known headers are generated with their canonical case. Other headers are sent in the case provided by the user. Except for control headers (which are sent first), headers are sent in arbitrary order. .PP Despite the limitations listed above, HTTP::Tiny is considered feature-complete. New feature requests should be directed to HTTP::Tiny::UA. .SH "SEE ALSO" .IX Header "SEE ALSO" .IP "\(bu" 4 HTTP::Tiny::UA \- Higher level \s-1UA\s0 features for HTTP::Tiny .IP "\(bu" 4 HTTP::Thin \- HTTP::Tiny wrapper with HTTP::Request/HTTP::Response compatibility .IP "\(bu" 4 HTTP::Tiny::Mech \- Wrap WWW::Mechanize instance in HTTP::Tiny compatible interface .IP "\(bu" 4 IO::Socket::IP \- Required for IPv6 support .IP "\(bu" 4 IO::Socket::SSL \- Required for \s-1SSL\s0 support .IP "\(bu" 4 LWP::UserAgent \- If HTTP::Tiny isn't enough for you, this is the \*(L"standard\*(R" way to do things .IP "\(bu" 4 Mozilla::CA \- Required if you want to validate \s-1SSL\s0 certificates .IP "\(bu" 4 Net::SSLeay \- Required for \s-1SSL\s0 support .SH "SUPPORT" .IX Header "SUPPORT" .SS "Bugs / Feature Requests" .IX Subsection "Bugs / Feature Requests" Please report any bugs or feature requests through the issue tracker at <https://github.com/Perl\-Toolchain\-Gang/HTTP\-Tiny/issues>. You will be notified automatically of any progress on your issue. .SS "Source Code" .IX Subsection "Source Code" This is open source software. The code repository is available for public review and contribution under the terms of the license. .PP <https://github.com/Perl\-Toolchain\-Gang/HTTP\-Tiny> .PP .Vb 1 \& git clone https://github.com/Perl\-Toolchain\-Gang/HTTP\-Tiny.git .Ve .SH "AUTHORS" .IX Header "AUTHORS" .IP "\(bu" 4 Christian Hansen <chansen@cpan.org> .IP "\(bu" 4 David Golden <dagolden@cpan.org> .SH "CONTRIBUTORS" .IX Header "CONTRIBUTORS" .IP "\(bu" 4 Alan Gardner <gardner@pythian.com> .IP "\(bu" 4 Alessandro Ghedini <al3xbio@gmail.com> .IP "\(bu" 4 A. Sinan Unur <nanis@cpan.org> .IP "\(bu" 4 Brad Gilbert <bgills@cpan.org> .IP "\(bu" 4 brian m. carlson <sandals@crustytoothpaste.net> .IP "\(bu" 4 Chris Nehren <apeiron@cpan.org> .IP "\(bu" 4 Chris Weyl <cweyl@alumni.drew.edu> .IP "\(bu" 4 Claes Jakobsson <claes@surfar.nu> .IP "\(bu" 4 Clinton Gormley <clint@traveljury.com> .IP "\(bu" 4 Craig A. Berry <craigberry@mac.com> .IP "\(bu" 4 Craig Berry <cberry@cpan.org> .IP "\(bu" 4 David Golden <xdg@xdg.me> .IP "\(bu" 4 David Mitchell <davem@iabyn.com> .IP "\(bu" 4 Dean Pearce <pearce@pythian.com> .IP "\(bu" 4 Edward Zborowski <ed@rubensteintech.com> .IP "\(bu" 4 Felipe Gasper <felipe@felipegasper.com> .IP "\(bu" 4 Graham Knop <haarg@haarg.org> .IP "\(bu" 4 Greg Kennedy <kennedy.greg@gmail.com> .IP "\(bu" 4 James E Keenan <jkeenan@cpan.org> .IP "\(bu" 4 James Raspass <jraspass@gmail.com> .IP "\(bu" 4 Jeremy Mates <jmates@cpan.org> .IP "\(bu" 4 Jess Robinson <castaway@desert\-island.me.uk> .IP "\(bu" 4 Karen Etheridge <ether@cpan.org> .IP "\(bu" 4 Lukas Eklund <leklund@gmail.com> .IP "\(bu" 4 Martin J. Evans <mjegh@ntlworld.com> .IP "\(bu" 4 Martin-Louis Bright <mlbright@gmail.com> .IP "\(bu" 4 Matthew Horsfall <wolfsage@gmail.com> .IP "\(bu" 4 Michael R. Davis <mrdvt92@users.noreply.github.com> .IP "\(bu" 4 Mike Doherty <doherty@cpan.org> .IP "\(bu" 4 Nicolas Rochelemagne <rochelemagne@cpanel.net> .IP "\(bu" 4 Olaf Alders <olaf@wundersolutions.com> .IP "\(bu" 4 Olivier Mengué <dolmen@cpan.org> .IP "\(bu" 4 Petr Písař <ppisar@redhat.com> .IP "\(bu" 4 sanjay-cpu <snjkmr32@gmail.com> .IP "\(bu" 4 Serguei Trouchelle <stro@cpan.org> .IP "\(bu" 4 Shoichi Kaji <skaji@cpan.org> .IP "\(bu" 4 SkyMarshal <skymarshal1729@gmail.com> .IP "\(bu" 4 Sören Kornetzki <soeren.kornetzki@delti.com> .IP "\(bu" 4 Steve Grazzini <steve.grazzini@grantstreet.com> .IP "\(bu" 4 Stig Palmquist <git@stig.io> .IP "\(bu" 4 Syohei \s-1YOSHIDA\s0 <syohex@gmail.com> .IP "\(bu" 4 Tatsuhiko Miyagawa <miyagawa@bulknews.net> .IP "\(bu" 4 Tom Hukins <tom@eborcom.com> .IP "\(bu" 4 Tony Cook <tony@develop\-help.com> .IP "\(bu" 4 Xavier Guimard <yadd@debian.org> .SH "COPYRIGHT AND LICENSE" .IX Header "COPYRIGHT AND LICENSE" This software is copyright (c) 2023 by Christian Hansen. .PP This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. PK�����B0]O.��.����man/man3/Types::Serialiser.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "Serialiser 3" .TH Serialiser 3 "2020-12-01" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" Types::Serialiser \- simple data types for common serialisation formats .SH "SYNOPSIS" .IX Header "SYNOPSIS" .SH "DESCRIPTION" .IX Header "DESCRIPTION" This module provides some extra datatypes that are used by common serialisation formats such as \s-1JSON\s0 or \s-1CBOR.\s0 The idea is to have a repository of simple/small constants and containers that can be shared by different implementations so they become interoperable between each other. .SH "SIMPLE SCALAR CONSTANTS" .IX Header "SIMPLE SCALAR CONSTANTS" Simple scalar constants are values that are overloaded to act like simple Perl values, but have (class) type to differentiate them from normal Perl scalars. This is necessary because these have different representations in the serialisation formats. .PP In the following, functions with zero or one arguments have a prototype of \&\f(CW\*(C`()\*(C'\fR and \f(CW\*(C`($)\*(C'\fR, respectively, so act as constants and unary operators. .SS "\s-1BOOLEANS\s0 (Types::Serialiser::Boolean class)" .IX Subsection "BOOLEANS (Types::Serialiser::Boolean class)" This type has only two instances, true and false. A natural representation for these in Perl is \f(CW1\fR and \f(CW0\fR, but serialisation formats need to be able to differentiate between them and mere numbers. .ie n .IP "$Types::Serialiser::true, Types::Serialiser::true" 4 .el .IP "\f(CW$Types::Serialiser::true\fR, Types::Serialiser::true" 4 .IX Item "$Types::Serialiser::true, Types::Serialiser::true" This value represents the \*(L"true\*(R" value. In most contexts is acts like the number \f(CW1\fR. It is up to you whether you use the variable form (\f(CW$Types::Serialiser::true\fR) or the constant form (\f(CW\*(C`Types::Serialiser::true\*(C'\fR). .Sp The constant is represented as a reference to a scalar containing \f(CW1\fR \- implementations are allowed to directly test for this. .ie n .IP "$Types::Serialiser::false, Types::Serialiser::false" 4 .el .IP "\f(CW$Types::Serialiser::false\fR, Types::Serialiser::false" 4 .IX Item "$Types::Serialiser::false, Types::Serialiser::false" This value represents the \*(L"false\*(R" value. In most contexts is acts like the number \f(CW0\fR. It is up to you whether you use the variable form (\f(CW$Types::Serialiser::false\fR) or the constant form (\f(CW\*(C`Types::Serialiser::false\*(C'\fR). .Sp The constant is represented as a reference to a scalar containing \f(CW0\fR \- implementations are allowed to directly test for this. .ie n .IP "Types::Serialiser::as_bool $value" 4 .el .IP "Types::Serialiser::as_bool \f(CW$value\fR" 4 .IX Item "Types::Serialiser::as_bool $value" Converts a Perl scalar into a boolean, which is useful syntactic sugar. Strictly equivalent to: .Sp .Vb 1 \& $value ? $Types::Serialiser::true : $Types::Serialiser::false .Ve .ie n .IP "$is_bool = Types::Serialiser::is_bool $value" 4 .el .IP "\f(CW$is_bool\fR = Types::Serialiser::is_bool \f(CW$value\fR" 4 .IX Item "$is_bool = Types::Serialiser::is_bool $value" Returns true iff the \f(CW$value\fR is either \f(CW$Types::Serialiser::true\fR or \&\f(CW$Types::Serialiser::false\fR. .Sp For example, you could differentiate between a perl true value and a \&\f(CW\*(C`Types::Serialiser::true\*(C'\fR by using this: .Sp .Vb 1 \& $value && Types::Serialiser::is_bool $value .Ve .ie n .IP "$is_true = Types::Serialiser::is_true $value" 4 .el .IP "\f(CW$is_true\fR = Types::Serialiser::is_true \f(CW$value\fR" 4 .IX Item "$is_true = Types::Serialiser::is_true $value" Returns true iff \f(CW$value\fR is \f(CW$Types::Serialiser::true\fR. .ie n .IP "$is_false = Types::Serialiser::is_false $value" 4 .el .IP "\f(CW$is_false\fR = Types::Serialiser::is_false \f(CW$value\fR" 4 .IX Item "$is_false = Types::Serialiser::is_false $value" Returns false iff \f(CW$value\fR is \f(CW$Types::Serialiser::false\fR. .SS "\s-1ERROR\s0 (Types::Serialiser::Error class)" .IX Subsection "ERROR (Types::Serialiser::Error class)" This class has only a single instance, \f(CW\*(C`error\*(C'\fR. It is used to signal an encoding or decoding error. In \s-1CBOR\s0 for example, and object that couldn't be encoded will be represented by a \s-1CBOR\s0 undefined value, which is represented by the error value in Perl. .ie n .IP "$Types::Serialiser::error, Types::Serialiser::error" 4 .el .IP "\f(CW$Types::Serialiser::error\fR, Types::Serialiser::error" 4 .IX Item "$Types::Serialiser::error, Types::Serialiser::error" This value represents the \*(L"error\*(R" value. Accessing values of this type will throw an exception. .Sp The constant is represented as a reference to a scalar containing \f(CW\*(C`undef\*(C'\fR \&\- implementations are allowed to directly test for this. .ie n .IP "$is_error = Types::Serialiser::is_error $value" 4 .el .IP "\f(CW$is_error\fR = Types::Serialiser::is_error \f(CW$value\fR" 4 .IX Item "$is_error = Types::Serialiser::is_error $value" Returns false iff \f(CW$value\fR is \f(CW$Types::Serialiser::error\fR. .SH "NOTES FOR XS USERS" .IX Header "NOTES FOR XS USERS" The recommended way to detect whether a scalar is one of these objects is to check whether the stash is the \f(CW\*(C`Types::Serialiser::Boolean\*(C'\fR or \&\f(CW\*(C`Types::Serialiser::Error\*(C'\fR stash, and then follow the scalar reference to see if it's \f(CW1\fR (true), \f(CW0\fR (false) or \f(CW\*(C`undef\*(C'\fR (error). .PP While it is possible to use an isa test, directly comparing stash pointers is faster and guaranteed to work. .PP For historical reasons, the \f(CW\*(C`Types::Serialiser::Boolean\*(C'\fR stash is just an alias for \f(CW\*(C`JSON::PP::Boolean\*(C'\fR. When printed, the classname with usually be \f(CW\*(C`JSON::PP::Boolean\*(C'\fR, but isa tests and stash pointer comparison will normally work correctly (i.e. Types::Serialiser::true \s-1ISA\s0 JSON::PP::Boolean, but also \s-1ISA\s0 Types::Serialiser::Boolean). .SH "A GENERIC OBJECT SERIALIATION PROTOCOL" .IX Header "A GENERIC OBJECT SERIALIATION PROTOCOL" This section explains the object serialisation protocol used by \&\s-1CBOR::XS\s0. It is meant to be generic enough to support any kind of generic object serialiser. .PP This protocol is called \*(L"the Types::Serialiser object serialisation protocol\*(R". .SS "\s-1ENCODING\s0" .IX Subsection "ENCODING" When the encoder encounters an object that it cannot otherwise encode (for example, \s-1CBOR::XS\s0 can encode a few special types itself, and will first attempt to use the special \f(CW\*(C`TO_CBOR\*(C'\fR serialisation protocol), it will look up the \f(CW\*(C`FREEZE\*(C'\fR method on the object. .PP Note that the \f(CW\*(C`FREEZE\*(C'\fR method will normally be called \fIduring\fR encoding, and \fI\s-1MUST NOT\s0\fR change the data structure that is being encoded in any way, or it might cause memory corruption or worse. .PP If it exists, it will call it with two arguments: the object to serialise, and a constant string that indicates the name of the data model. For example \s-1CBOR::XS\s0 uses \f(CW\*(C`CBOR\*(C'\fR, and the \s-1JSON\s0 and \s-1JSON::XS\s0 modules (or any other \s-1JSON\s0 serialiser), would use \f(CW\*(C`JSON\*(C'\fR as second argument. .PP The \f(CW\*(C`FREEZE\*(C'\fR method can then return zero or more values to identify the object instance. The serialiser is then supposed to encode the class name and all of these return values (which must be encodable in the format) using the relevant form for Perl objects. In \s-1CBOR\s0 for example, there is a registered tag number for encoded perl objects. .PP The values that \f(CW\*(C`FREEZE\*(C'\fR returns must be serialisable with the serialiser that calls it. Therefore, it is recommended to use simple types such as strings and numbers, and maybe array references and hashes (basically, the \&\s-1JSON\s0 data model). You can always use a more complex format for a specific data model by checking the second argument, the data model. .PP The \*(L"data model\*(R" is not the same as the \*(L"data format\*(R" \- the data model indicates what types and kinds of return values can be returned from \&\f(CW\*(C`FREEZE\*(C'\fR. For example, in \f(CW\*(C`CBOR\*(C'\fR it is permissible to return tagged \s-1CBOR\s0 values, while \s-1JSON\s0 does not support these at all, so \f(CW\*(C`JSON\*(C'\fR would be a valid (but too limited) data model name for \f(CW\*(C`CBOR::XS\*(C'\fR. similarly, a serialising format that supports more or less the same data model as \s-1JSON\s0 could use \f(CW\*(C`JSON\*(C'\fR as data model without losing anything. .SS "\s-1DECODING\s0" .IX Subsection "DECODING" When the decoder then encounters such an encoded perl object, it should look up the \f(CW\*(C`THAW\*(C'\fR method on the stored classname, and invoke it with the classname, the constant string to identify the data model/data format, and all the return values returned by \f(CW\*(C`FREEZE\*(C'\fR. .SS "\s-1EXAMPLES\s0" .IX Subsection "EXAMPLES" See the \f(CW\*(C`OBJECT SERIALISATION\*(C'\fR section in the \s-1CBOR::XS\s0 manpage for more details, an example implementation, and code examples. .PP Here is an example \f(CW\*(C`FREEZE\*(C'\fR/\f(CW\*(C`THAW\*(C'\fR method pair: .PP .Vb 2 \& sub My::Object::FREEZE { \& my ($self, $model) = @_; \& \& ($self\->{type}, $self\->{id}, $self\->{variant}) \& } \& \& sub My::Object::THAW { \& my ($class, $model, $type, $id, $variant) = @_; \& \& $class\->new (type => $type, id => $id, variant => $variant) \& } .Ve .SH "BUGS" .IX Header "BUGS" The use of overload makes this module much heavier than it should be (on my system, this module: 4kB \s-1RSS,\s0 overload: 260kB \s-1RSS\s0). .SH "SEE ALSO" .IX Header "SEE ALSO" Currently, \s-1JSON::XS\s0 and \s-1CBOR::XS\s0 use these types. .SH "AUTHOR" .IX Header "AUTHOR" .Vb 2 \& Marc Lehmann <schmorp@schmorp.de> \& http://home.schmorp.de/ .Ve PK�����B0]D% ��% ����man/man3/JSON::XS::Boolean.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "XS::Boolean 3" .TH XS::Boolean 3 "2013-10-29" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" JSON::XS::Boolean \- dummy module providing JSON::XS::Boolean .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& # do not "use" yourself .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" This module exists only to provide overload resolution for Storable and similar modules. It's only needed for compatibility with data serialised (by other modules such as Storable) that was decoded by \s-1JSON::XS\s0 versions before 3.0. .PP Since 3.0, JSON::PP::Boolean has replaced it. Support for JSON::XS::Boolean will be removed in a future release. .SH "AUTHOR" .IX Header "AUTHOR" .Vb 2 \& Marc Lehmann <schmorp@schmorp.de> \& http://home.schmorp.de/ .Ve PK�����B0]��������������man/man3/common::sense.3pmnu�6$��������PK�����B0]Jt ��t ����man/man3/CPAN::Tarzip.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "CPAN::Tarzip 3" .TH CPAN::Tarzip 3 "2020-05-19" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" CPAN::Tarzip \- internal handling of tar archives for CPAN.pm .SH "LICENSE" .IX Header "LICENSE" This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. PK�����B0] �� ����man/man3/CPAN::Plugin.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "CPAN::Plugin 3" .TH CPAN::Plugin 3 "2020-05-19" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" CPAN::Plugin \- Base class for CPAN shell extensions .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 2 \& package CPAN::Plugin::Flurb; \& use parent \*(AqCPAN::Plugin\*(Aq; \& \& sub post_test { \& my ($self, $distribution_object) = @_; \& $self = $self\->new (distribution_object => $distribution_object); \& ...; \& } .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" .SS "Alpha Status" .IX Subsection "Alpha Status" The plugin system in the \s-1CPAN\s0 shell was introduced in version 2.07 and is still considered experimental. .SS "How Plugins work?" .IX Subsection "How Plugins work?" See \*(L"Plugin support\*(R" in \s-1CPAN\s0. .SH "METHODS" .IX Header "METHODS" .SS "plugin_requires" .IX Subsection "plugin_requires" returns list of packages given plugin requires for functionality. This list is evaluated using \f(CW\*(C`CPAN\->use_inst\*(C'\fR method. .SS "distribution_object" .IX Subsection "distribution_object" Get current distribution object. .SS "distribution" .IX Subsection "distribution" .SS "distribution_info" .IX Subsection "distribution_info" .SS "build_dir" .IX Subsection "build_dir" Simple delegatees for misc parameters derived from distribution .SS "is_xs" .IX Subsection "is_xs" Predicate to detect whether package contains \s-1XS.\s0 .SH "AUTHOR" .IX Header "AUTHOR" Branislav Zahradnik <barney@cpan.org> PK�����B0]a!������man/man3/CPAN::Distroprefs.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "CPAN::Distroprefs 3" .TH CPAN::Distroprefs 3 "2020-03-01" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" CPAN::Distroprefs \-\- read and match distroprefs .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use CPAN::Distroprefs; \& \& my %info = (... distribution/environment info ...); \& \& my $finder = CPAN::Distroprefs\->find($prefs_dir, \e%ext_map); \& \& while (my $result = $finder\->next) { \& \& die $result\->as_string if $result\->is_fatal; \& \& warn($result\->as_string), next if $result\->is_warning; \& \& for my $pref (@{ $result\->prefs }) { \& if ($pref\->matches(\e%info)) { \& return $pref; \& } \& } \& } .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" This module encapsulates reading Distroprefs and matching them against \s-1CPAN\s0 distributions. .SH "INTERFACE" .IX Header "INTERFACE" .Vb 1 \& my $finder = CPAN::Distroprefs\->find($dir, \e%ext_map); \& \& while (my $result = $finder\->next) { ... } .Ve .PP Build an iterator which finds distroprefs files in the tree below the given directory. Within the tree directories matching \f(CW\*(C`m/^[._]/\*(C'\fR are pruned. .PP \&\f(CW%ext_map\fR is a hashref whose keys are file extensions and whose values are modules used to load matching files: .PP .Vb 5 \& { \& \*(Aqyml\*(Aq => \*(AqYAML::Syck\*(Aq, \& \*(Aqdd\*(Aq => \*(AqData::Dumper\*(Aq, \& ... \& } .Ve .PP Each time \f(CW\*(C`$finder\->next\*(C'\fR is called, the iterator returns one of two possible values: .IP "\(bu" 4 a CPAN::Distroprefs::Result object .IP "\(bu" 4 \&\f(CW\*(C`undef\*(C'\fR, indicating that no prefs files remain to be found .SH "RESULTS" .IX Header "RESULTS" \&\f(CW\*(C`find()\*(C'\fR returns CPAN::Distroprefs::Result objects to indicate success or failure when reading a prefs file. .SS "Common" .IX Subsection "Common" All results share some common attributes: .PP \fItype\fR .IX Subsection "type" .PP \&\f(CW\*(C`success\*(C'\fR, \f(CW\*(C`warning\*(C'\fR, or \f(CW\*(C`fatal\*(C'\fR .PP \fIfile\fR .IX Subsection "file" .PP the file from which these prefs were read, or to which this error refers (relative filename) .PP \fIext\fR .IX Subsection "ext" .PP the file's extension, which determines how to load it .PP \fIdir\fR .IX Subsection "dir" .PP the directory the file was read from .PP \fIabs\fR .IX Subsection "abs" .PP the absolute path to the file .SS "Errors" .IX Subsection "Errors" Error results (warning and fatal) contain: .PP \fImsg\fR .IX Subsection "msg" .PP the error message (usually either \f(CW$!\fR or a \s-1YAML\s0 error) .SS "Successes" .IX Subsection "Successes" Success results contain: .PP \fIprefs\fR .IX Subsection "prefs" .PP an arrayref of CPAN::Distroprefs::Pref objects .SH "PREFS" .IX Header "PREFS" CPAN::Distroprefs::Pref objects represent individual distroprefs documents. They are constructed automatically as part of \f(CW\*(C`success\*(C'\fR results from \f(CW\*(C`find()\*(C'\fR. .PP \fIdata\fR .IX Subsection "data" .PP the pref information as a hashref, suitable for e.g. passing to Kwalify .PP \fImatch_attributes\fR .IX Subsection "match_attributes" .PP returns a list of the valid match attributes (see the Distroprefs section in \s-1CPAN\s0) .PP currently: \f(CW\*(C`env perl perlconfig distribution module\*(C'\fR .PP \fIhas_any_match\fR .IX Subsection "has_any_match" .PP true if this pref has a 'match' attribute at all .PP \fIhas_valid_subkeys\fR .IX Subsection "has_valid_subkeys" .PP true if this pref has a 'match' attribute and at least one valid match attribute .PP \fImatches\fR .IX Subsection "matches" .PP .Vb 1 \& if ($pref\->matches(\e%arg)) { ... } .Ve .PP true if this pref matches the passed-in hashref, which must have a value for each of the \f(CW\*(C`match_attributes\*(C'\fR (above) .SH "LICENSE" .IX Header "LICENSE" This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. PK�����B0]&D����#��man/man3/CPAN::Plugin::Specfile.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "CPAN::Plugin::Specfile 3" .TH CPAN::Plugin::Specfile 3 "2020-05-19" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" CPAN::Plugin::Specfile \- Proof of concept implementation of a trivial CPAN::Plugin .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 2 \& # once in the cpan shell \& o conf plugin_list push CPAN::Plugin::Specfile \& \& # make permanent \& o conf commit \& \& # any time in the cpan shell to write a spec file \& test Acme::Meta \& \& # disable \& # if it is the last in plugin_list: \& o conf plugin_list pop \& # otherwise, determine the index to splice: \& o conf plugin_list \& # and then use splice, e.g. to splice position 3: \& o conf plugin_list splice 3 1 .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" Implemented as a post-test hook, this plugin writes a specfile after every successful test run. The content is also written to the terminal. .PP As a side effect, the timestamps of the written specfiles reflect the linear order of all dependencies. .PP \&\fB\s-1WARNING:\s0\fR This code is just a small demo how to use the plugin system of the \s-1CPAN\s0 shell, not a full fledged spec file writer. Do not expect new features in this plugin. .SS "\s-1OPTIONS\s0" .IX Subsection "OPTIONS" The target directory to store the spec files in can be set using \f(CW\*(C`dir\*(C'\fR as in .PP .Vb 1 \& o conf plugin_list push CPAN::Plugin::Specfile=dir,/tmp/specfiles\-000042 .Ve .PP The default directory for this is the \&\f(CW\*(C`plugins/CPAN::Plugin::Specfile\*(C'\fR directory in the \fIcpan_home\fR directory. .SH "AUTHOR" .IX Header "AUTHOR" Andreas Koenig <andk@cpan.org>, Branislav Zahradnik <barney@cpan.org> PK�����B0] , �� ����man/man3/lib::core::only.3pmnu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "lib::core::only 3" .TH lib::core::only 3 "2017-11-08" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" lib::core::only \- Remove all non\-core paths from @INC to avoid site/vendor dirs .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use lib::core::only; # now @INC contains only the two core directories .Ve .PP To get only the core directories plus the ones for the local::lib in scope: .PP .Vb 1 \& $ perl \-mlocal::lib \-Mlib::core::only \-Mlocal::lib=~/perl5 myscript.pl .Ve .PP To attempt to do a self-contained build (but note this will not reliably propagate into subprocesses, see the \s-1CAVEATS\s0 below): .PP .Vb 1 \& $ PERL5OPT=\*(Aq\-mlocal::lib \-Mlib::core::only \-Mlocal::lib=~/perl5\*(Aq cpan .Ve .PP Please note that it is necessary to use \f(CW\*(C`local::lib\*(C'\fR twice for this to work. First so that \f(CW\*(C`lib::core::only\*(C'\fR doesn't prevent \f(CW\*(C`local::lib\*(C'\fR from loading (it's not currently in core) and then again after \f(CW\*(C`lib::core::only\*(C'\fR so that the local paths are not removed. .SH "DESCRIPTION" .IX Header "DESCRIPTION" lib::core::only is simply a shortcut to say \*(L"please reduce my \f(CW@INC\fR to only the core lib and archlib (architecture-specific lib) directories of this perl\*(R". .PP You might want to do this to ensure a local::lib contains only the code you need, or to test an App::FatPacker tree, or to avoid known bad vendor packages. .PP You might want to use this to try and install a self-contained tree of perl modules. Be warned that that probably won't work (see \*(L"\s-1CAVEATS\*(R"\s0). .PP This module was extracted from local::lib's \-\-self\-contained feature, and contains the only part that ever worked. I apologise to anybody who thought anything else did. .SH "CAVEATS" .IX Header "CAVEATS" This does \fBnot\fR propagate properly across perl invocations like local::lib's stuff does. It can't. It's only a module import, so it \fBonly affects the specific perl \s-1VM\s0 instance in which you load and \fBimport()\fB it\fR. .PP If you want to cascade it across invocations, you can set the \s-1PERL5OPT\s0 environment variable to '\-Mlib::core::only' and it'll sort of work. But be aware that taint mode ignores this, so some modules' build and test code probably will as well. .PP You also need to be aware that perl's command line options are not processed in order \- \-I options take effect before \-M options, so .PP .Vb 1 \& perl \-Mlib::core::only \-Ilib .Ve .PP is unlike to do what you want \- it's exactly equivalent to: .PP .Vb 1 \& perl \-Mlib::core::only .Ve .PP If you want to combine a core-only \f(CW@INC\fR with additional paths, you need to add the additional paths using \-M options and the lib module: .PP .Vb 1 \& perl \-Mlib::core::only \-Mlib=lib \& \& # or if you\*(Aqre trying to test compiled code: \& \& perl \-Mlib::core::only \-Mblib .Ve .PP For more information on the impossibility of sanely propagating this across module builds without help from the build program, see <http://www.shadowcat.co.uk/blog/matt\-s\-trout/tainted\-love> \- and for ways to achieve the old \-\-self\-contained feature's results, look at App::FatPacker's tree function, and at App::cpanminus's \-\-local\-lib\-contained feature. .SH "AUTHOR" .IX Header "AUTHOR" Matt S. Trout <mst@shadowcat.co.uk> .SH "LICENSE" .IX Header "LICENSE" This library is free software under the same terms as perl itself. .SH "COPYRIGHT" .IX Header "COPYRIGHT" (c) 2010 the lib::core::only \*(L"\s-1AUTHOR\*(R"\s0 as specified above. PK�����B0]ɋ8/��8/����man/man1/cpan.1nu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "CPAN 1" .TH CPAN 1 "2022-04-03" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" cpan \- easily interact with CPAN from the command line .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 2 \& # with arguments and no switches, installs specified modules \& cpan module_name [ module_name ... ] \& \& # with switches, installs modules with extra behavior \& cpan [\-cfFimtTw] module_name [ module_name ... ] \& \& # use local::lib \& cpan \-I module_name [ module_name ... ] \& \& # one time mirror override for faster mirrors \& cpan \-p ... \& \& # with just the dot, install from the distribution in the \& # current directory \& cpan . \& \& # without arguments, starts CPAN.pm shell \& cpan \& \& # without arguments, but some switches \& cpan [\-ahpruvACDLOPX] .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" This script provides a command interface (not a shell) to \s-1CPAN.\s0 At the moment it uses \s-1CPAN\s0.pm to do the work, but it is not a one-shot command runner for \s-1CPAN\s0.pm. .SS "Options" .IX Subsection "Options" .IP "\-a" 4 .IX Item "-a" Creates a \s-1CPAN\s0.pm autobundle with CPAN::Shell\->autobundle. .IP "\-A module [ module ... ]" 4 .IX Item "-A module [ module ... ]" Shows the primary maintainers for the specified modules. .IP "\-c module" 4 .IX Item "-c module" Runs a `make clean` in the specified module's directories. .IP "\-C module [ module ... ]" 4 .IX Item "-C module [ module ... ]" Show the \fIChanges\fR files for the specified modules .IP "\-D module [ module ... ]" 4 .IX Item "-D module [ module ... ]" Show the module details. This prints one line for each out-of-date module (meaning, modules locally installed but have newer versions on \s-1CPAN\s0). Each line has three columns: module name, local version, and \s-1CPAN\s0 version. .IP "\-f" 4 .IX Item "-f" Force the specified action, when it normally would have failed. Use this to install a module even if its tests fail. When you use this option, \&\-i is not optional for installing a module when you need to force it: .Sp .Vb 1 \& % cpan \-f \-i Module::Foo .Ve .IP "\-F" 4 .IX Item "-F" Turn off \s-1CPAN\s0.pm's attempts to lock anything. You should be careful with this since you might end up with multiple scripts trying to muck in the same directory. This isn't so much of a concern if you're loading a special config with \f(CW\*(C`\-j\*(C'\fR, and that config sets up its own work directories. .IP "\-g module [ module ... ]" 4 .IX Item "-g module [ module ... ]" Downloads to the current directory the latest distribution of the module. .IP "\-G module [ module ... ]" 4 .IX Item "-G module [ module ... ]" \&\s-1UNIMPLEMENTED\s0 .Sp Download to the current directory the latest distribution of the modules, unpack each distribution, and create a git repository for each distribution. .Sp If you want this feature, check out Yanick Champoux's \f(CW\*(C`Git::CPAN::Patch\*(C'\fR distribution. .IP "\-h" 4 .IX Item "-h" Print a help message and exit. When you specify \f(CW\*(C`\-h\*(C'\fR, it ignores all of the other options and arguments. .IP "\-i module [ module ... ]" 4 .IX Item "-i module [ module ... ]" Install the specified modules. With no other switches, this switch is implied. .IP "\-I" 4 .IX Item "-I" Load \f(CW\*(C`local::lib\*(C'\fR (think like \f(CW\*(C`\-I\*(C'\fR for loading lib paths). Too bad \&\f(CW\*(C`\-l\*(C'\fR was already taken. .IP "\-j Config.pm" 4 .IX Item "-j Config.pm" Load the file that has the \s-1CPAN\s0 configuration data. This should have the same format as the standard \fICPAN/Config.pm\fR file, which defines \&\f(CW$CPAN::Config\fR as an anonymous hash. .IP "\-J" 4 .IX Item "-J" Dump the configuration in the same format that \s-1CPAN\s0.pm uses. This is useful for checking the configuration as well as using the dump as a starting point for a new, custom configuration. .IP "\-l" 4 .IX Item "-l" List all installed modules with their versions .IP "\-L author [ author ... ]" 4 .IX Item "-L author [ author ... ]" List the modules by the specified authors. .IP "\-m" 4 .IX Item "-m" Make the specified modules. .IP "\-M mirror1,mirror2,..." 4 .IX Item "-M mirror1,mirror2,..." A comma-separated list of mirrors to use for just this run. The \f(CW\*(C`\-P\*(C'\fR option can find them for you automatically. .IP "\-n" 4 .IX Item "-n" Do a dry run, but don't actually install anything. (unimplemented) .IP "\-O" 4 .IX Item "-O" Show the out-of-date modules. .IP "\-p" 4 .IX Item "-p" Ping the configured mirrors and print a report .IP "\-P" 4 .IX Item "-P" Find the best mirrors you could be using and use them for the current session. .IP "\-r" 4 .IX Item "-r" Recompiles dynamically loaded modules with CPAN::Shell\->recompile. .IP "\-s" 4 .IX Item "-s" Drop in the \s-1CPAN\s0.pm shell. This command does this automatically if you don't specify any arguments. .IP "\-t module [ module ... ]" 4 .IX Item "-t module [ module ... ]" Run a `make test` on the specified modules. .IP "\-T" 4 .IX Item "-T" Do not test modules. Simply install them. .IP "\-u" 4 .IX Item "-u" Upgrade all installed modules. Blindly doing this can really break things, so keep a backup. .IP "\-v" 4 .IX Item "-v" Print the script version and \s-1CPAN\s0.pm version then exit. .IP "\-V" 4 .IX Item "-V" Print detailed information about the cpan client. .IP "\-w" 4 .IX Item "-w" \&\s-1UNIMPLEMENTED\s0 .Sp Turn on cpan warnings. This checks various things, like directory permissions, and tells you about problems you might have. .IP "\-x module [ module ... ]" 4 .IX Item "-x module [ module ... ]" Find close matches to the named modules that you think you might have mistyped. This requires the optional installation of Text::Levenshtein or Text::Levenshtein::Damerau. .IP "\-X" 4 .IX Item "-X" Dump all the namespaces to standard output. .SS "Examples" .IX Subsection "Examples" .Vb 2 \& # print a help message \& cpan \-h \& \& # print the version numbers \& cpan \-v \& \& # create an autobundle \& cpan \-a \& \& # recompile modules \& cpan \-r \& \& # upgrade all installed modules \& cpan \-u \& \& # install modules ( sole \-i is optional ) \& cpan \-i Netscape::Booksmarks Business::ISBN \& \& # force install modules ( must use \-i ) \& cpan \-fi CGI::Minimal URI \& \& # install modules but without testing them \& cpan \-Ti CGI::Minimal URI .Ve .SS "Environment variables" .IX Subsection "Environment variables" There are several components in \s-1CPAN\s0.pm that use environment variables. The build tools, ExtUtils::MakeMaker and Module::Build use some, while others matter to the levels above them. Some of these are specified by the Perl Toolchain Gang: .PP Lancaster Consensus: <https://github.com/Perl\-Toolchain\-Gang/toolchain\-site/blob/master/lancaster\-consensus.md> .PP Oslo Consensus: <https://github.com/Perl\-Toolchain\-Gang/toolchain\-site/blob/master/oslo\-consensus.md> .IP "\s-1NONINTERACTIVE_TESTING\s0" 4 .IX Item "NONINTERACTIVE_TESTING" Assume no one is paying attention and skips prompts for distributions that do that correctly. \f(CWcpan(1)\fR sets this to \f(CW1\fR unless it already has a value (even if that value is false). .IP "\s-1PERL_MM_USE_DEFAULT\s0" 4 .IX Item "PERL_MM_USE_DEFAULT" Use the default answer for a prompted questions. \f(CWcpan(1)\fR sets this to \f(CW1\fR unless it already has a value (even if that value is false). .IP "\s-1CPAN_OPTS\s0" 4 .IX Item "CPAN_OPTS" As with \f(CW\*(C`PERL5OPT\*(C'\fR, a string of additional \f(CWcpan(1)\fR options to add to those you specify on the command line. .IP "\s-1CPANSCRIPT_LOGLEVEL\s0" 4 .IX Item "CPANSCRIPT_LOGLEVEL" The log level to use, with either the embedded, minimal logger or Log::Log4perl if it is installed. Possible values are the same as the \f(CW\*(C`Log::Log4perl\*(C'\fR levels: \f(CW\*(C`TRACE\*(C'\fR, \f(CW\*(C`DEBUG\*(C'\fR, \f(CW\*(C`INFO\*(C'\fR, \f(CW\*(C`WARN\*(C'\fR, \&\f(CW\*(C`ERROR\*(C'\fR, and \f(CW\*(C`FATAL\*(C'\fR. The default is \f(CW\*(C`INFO\*(C'\fR. .IP "\s-1GIT_COMMAND\s0" 4 .IX Item "GIT_COMMAND" The path to the \f(CW\*(C`git\*(C'\fR binary to use for the Git features. The default is \f(CW\*(C`/usr/local/bin/git\*(C'\fR. .SH "EXIT VALUES" .IX Header "EXIT VALUES" The script exits with zero if it thinks that everything worked, or a positive number if it thinks that something failed. Note, however, that in some cases it has to divine a failure by the output of things it does not control. For now, the exit codes are vague: .PP .Vb 1 \& 1 An unknown error \& \& 2 The was an external problem \& \& 4 There was an internal problem with the script \& \& 8 A module failed to install .Ve .SH "TO DO" .IX Header "TO DO" * one shot configuration values from the command line .SH "BUGS" .IX Header "BUGS" * none noted .SH "SEE ALSO" .IX Header "SEE ALSO" Most behaviour, including environment variables and configuration, comes directly from \s-1CPAN\s0.pm. .SH "SOURCE AVAILABILITY" .IX Header "SOURCE AVAILABILITY" This code is in Github in the \s-1CPAN\s0.pm repository: .PP .Vb 1 \& https://github.com/andk/cpanpm .Ve .PP The source used to be tracked separately in another GitHub repo, but the canonical source is now in the above repo. .SH "CREDITS" .IX Header "CREDITS" Japheth Cleaver added the bits to allow a forced install (\-f). .PP Jim Brandt suggest and provided the initial implementation for the up-to-date and Changes features. .PP Adam Kennedy pointed out that \fBexit()\fR causes problems on Windows where this script ends up with a .bat extension .SH "AUTHOR" .IX Header "AUTHOR" brian d foy, \f(CW\*(C`<bdfoy@cpan.org>\*(C'\fR .SH "COPYRIGHT" .IX Header "COPYRIGHT" Copyright (c) 2001\-2015, brian d foy, All Rights Reserved. .PP You may redistribute this under the same terms as Perl itself. PK�����B0]y¬������man/man1/cpan-mirrors.1nu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "CPAN-MIRRORS 1" .TH CPAN-MIRRORS 1 "2018-09-22" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" cpan\-mirrors \- Find the fatest CPAN mirrors nearby .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& % cpan\-mirrors \& \& Testing Africa \& is.co.za \-> 733.72 ms \& wa.co.za \-> 601.56 ms \& ucu.ac.ug \-> 407.99 ms \& mirror.ac.za \-> 614.05 ms \& zol.co.zw \-> 612.57 ms \& \-\->median time: 612.57 ms \& Testing Oceania \& serversaustralia.com.au \-> 38.62 ms \& cpan.inspire.net.nz \-> 67.81 ms \& digitalpacific.com.au \-> 37.89 ms \& uber.com.au \-> 44.47 ms \& lagoon.nc \-> 67.49 ms \& \-\->median time: 44.47 ms \& Testing North America \& httpupdate40.cpanel.net \-> 254.00 ms \& httpupdate35.cpanel.net \-> 409.75 ms \& mirrors.sonic.net \-> 368.56 ms \& httpupdate8.cpanel.net \-> 409.10 ms \& cpan.cs.utah.edu \-> 408.00 ms \& \-\->median time: 408.00 ms \& Testing Asia \& vinahost.vn \-> 146.10 ms \& yazd.ac.ir \-> 469.29 ms \& mirrors\-ru.go\-parts.com \-> 367.09 ms \& sohu.com \-> 399.76 ms \& neolabs.kz \-> 610.92 ms \& \-\->median time: 399.76 ms \& Testing South America \& mmgdesigns.com.ar \-> 407.96 ms \& nbtelecom.com.br \-> 401.96 ms \& sunsite.dcc.uchile.cl \-> 415.69 ms \& linorg.usp.br \-> 424.65 ms \& unal.edu.co \-> 407.60 ms \& \-\->median time: 407.96 ms \& Testing Europe \& uk2.net \-> 409.37 ms \& ambiweb.de \-> 710.65 ms \& bibleonline.ru \-> 471.49 ms \& rol.ru \-> 469.70 ms \& rub.de \-> 346.57 ms \& \-\->median time: 469.70 ms \& Median result by continent: \& 44 ms Oceania \& 400 ms Asia \& 408 ms South America \& 408 ms North America \& 470 ms Europe \& 613 ms Africa \& Best continent is Oceania \& cpan.catalyst.net.nz = 78.8369178771973 ms \& cpan.inspire.net.nz = 69.4258213043213 ms \& cpan.mirrors.ilisys.com.au = 50.2209663391113 ms \& digitalpacific.com.au = 40.1251316070557 ms \& lagoon.nc = 88.8760089874268 ms \& nautile.nc = 65.7081604003906 ms \& optusnet.com.au = 41.4729118347168 ms \& serversaustralia.com.au = 46.1521148681641 ms \& uber.com.au = 48.2730865478516 ms \& waia.asn.au = 86.2929821014404 ms \& webtastix.net = 70.5790519714355 ms \& Best mirrors are digitalpacific.com.au \& optusnet.com.au \& serversaustralia.com.au \& uber.com.au \& cpan.mirrors.ilisys.com.au \& \& Best urls are \& http://cpan.mirror.digitalpacific.com.au/ \& http://mirror.optusnet.com.au/CPAN/ \& http://cpan.mirror.serversaustralia.com.au/ \& http://cpan.mirror.uber.com.au/ \& http://cpan.mirrors.ilisys.com.au/ .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\s-1CPAN\s0 Mirrors come and go, and maybe you do too. The mirror that was fast then might not be fast now, especially if you changed hemispheres. .PP This program goes through the \fI\s-1MIRRORED.BY\s0\fR file and pings a selection of mirrors on each continent to find the fastest mirrors. From the best continent, it samples several mirrors to find the \s-1URL\s0 you should add to your configuration. .PP If you don't have a \fI\s-1MIRRORED.BY\s0\fR file, this program will download one for you. It looks through your existing \f(CW\*(C`urllist\*(C'\fR configuration and then default locations. It stores it in \f(CW\*(C`~/.cpan\*(C'\fR (or your configured location to store files). .SH "TO DO" .IX Header "TO DO" .IP "\(bu" 4 Add an option to update the \s-1CPAN\s0 configuration .IP "\(bu" 4 Add an option to give \s-1JSON\s0 output .SH "AUTHOR" .IX Header "AUTHOR" brian d foy \f(CW\*(C`<bdfoy@cpan.org>\*(C'\fR .SH "LICENSE" .IX Header "LICENSE" This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. .PP See <http://www.perl.com/perl/misc/Artistic.html> PK�����B0]=؝!��!����man/man1/json_xs.1nu�6$��������.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "JSON_XS 1" .TH JSON_XS 1 "2018-11-15" "perl v5.26.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" json_xs \- JSON::XS commandline utility .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& json_xs [\-v] [\-f inputformat] [\-t outputformat] .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fIjson_xs\fR converts between some input and output formats (one of them is \&\s-1JSON\s0). .PP The default input format is \f(CW\*(C`json\*(C'\fR and the default output format is \&\f(CW\*(C`json\-pretty\*(C'\fR. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\-v" 4 .IX Item "-v" Be slightly more verbose. .IP "\-f fromformat" 4 .IX Item "-f fromformat" Read a file in the given format from \s-1STDIN.\s0 .Sp \&\f(CW\*(C`fromformat\*(C'\fR can be one of: .RS 4 .IP "json \- a json text encoded, either utf\-8, utf16\-be/le, utf32\-be/le" 4 .IX Item "json - a json text encoded, either utf-8, utf16-be/le, utf32-be/le" .PD 0 .IP "cbor \- \s-1CBOR\s0 (\s-1RFC 7049,\s0 \s-1CBOR::XS\s0), a kind of binary \s-1JSON\s0" 4 .IX Item "cbor - CBOR (RFC 7049, CBOR::XS), a kind of binary JSON" .IP "storable \- a Storable frozen value" 4 .IX Item "storable - a Storable frozen value" .IP "storable-file \- a Storable file (Storable has two incompatible formats)" 4 .IX Item "storable-file - a Storable file (Storable has two incompatible formats)" .IP "bencode \- use Convert::Bencode, if available (used by torrent files, among others)" 4 .IX Item "bencode - use Convert::Bencode, if available (used by torrent files, among others)" .IP "clzf \- Compress::LZF format (requires that module to be installed)" 4 .IX Item "clzf - Compress::LZF format (requires that module to be installed)" .ie n .IP "eval \- evaluate the given code as (non\-utf\-8) Perl, basically the reverse of ""\-t dump""" 4 .el .IP "eval \- evaluate the given code as (non\-utf\-8) Perl, basically the reverse of ``\-t dump''" 4 .IX Item "eval - evaluate the given code as (non-utf-8) Perl, basically the reverse of -t dump" .IP "yaml \- \s-1YAML\s0 format (requires that module to be installed)" 4 .IX Item "yaml - YAML format (requires that module to be installed)" .IP "string \- do not attempt to decode the file data" 4 .IX Item "string - do not attempt to decode the file data" .ie n .IP "none \- nothing is read, creates an ""undef"" scalar \- mainly useful with ""\-e""" 4 .el .IP "none \- nothing is read, creates an \f(CWundef\fR scalar \- mainly useful with \f(CW\-e\fR" 4 .IX Item "none - nothing is read, creates an undef scalar - mainly useful with -e" .RE .RS 4 .RE .IP "\-t toformat" 4 .IX Item "-t toformat" .PD Write the file in the given format to \s-1STDOUT.\s0 .Sp \&\f(CW\*(C`toformat\*(C'\fR can be one of: .RS 4 .IP "json, json\-utf\-8 \- json, utf\-8 encoded" 4 .IX Item "json, json-utf-8 - json, utf-8 encoded" .PD 0 .IP "json-pretty \- as above, but pretty-printed" 4 .IX Item "json-pretty - as above, but pretty-printed" .IP "json\-utf\-16le, json\-utf\-16be \- little endian/big endian utf\-16" 4 .IX Item "json-utf-16le, json-utf-16be - little endian/big endian utf-16" .IP "json\-utf\-32le, json\-utf\-32be \- little endian/big endian utf\-32" 4 .IX Item "json-utf-32le, json-utf-32be - little endian/big endian utf-32" .IP "cbor \- \s-1CBOR\s0 (\s-1RFC 7049,\s0 \s-1CBOR::XS\s0), a kind of binary \s-1JSON\s0" 4 .IX Item "cbor - CBOR (RFC 7049, CBOR::XS), a kind of binary JSON" .IP "cbor-packed \- \s-1CBOR\s0 using extensions to make it smaller" 4 .IX Item "cbor-packed - CBOR using extensions to make it smaller" .IP "storable \- a Storable frozen value in network format" 4 .IX Item "storable - a Storable frozen value in network format" .IP "storable-file \- a Storable file in network format (Storable has two incompatible formats)" 4 .IX Item "storable-file - a Storable file in network format (Storable has two incompatible formats)" .IP "bencode \- use Convert::Bencode, if available (used by torrent files, among others)" 4 .IX Item "bencode - use Convert::Bencode, if available (used by torrent files, among others)" .IP "clzf \- Compress::LZF format" 4 .IX Item "clzf - Compress::LZF format" .IP "yaml \- \s-1YAML::XS\s0 format" 4 .IX Item "yaml - YAML::XS format" .IP "dump \- Data::Dump" 4 .IX Item "dump - Data::Dump" .IP "dumper \- Data::Dumper" 4 .IX Item "dumper - Data::Dumper" .IP "string \- writes the data out as if it were a string" 4 .IX Item "string - writes the data out as if it were a string" .ie n .IP "none \- nothing gets written, mainly useful together with ""\-e""" 4 .el .IP "none \- nothing gets written, mainly useful together with \f(CW\-e\fR" 4 .IX Item "none - nothing gets written, mainly useful together with -e" .PD Note that Data::Dumper doesn't handle self-referential data structures correctly \- use \*(L"dump\*(R" instead. .RE .RS 4 .RE .IP "\-e code" 4 .IX Item "-e code" Evaluate perl code after reading the data and before writing it out again \&\- can be used to filter, create or extract data. The data that has been written is in \f(CW$_\fR, and whatever is in there is written out afterwards. .SH "EXAMPLES" .IX Header "EXAMPLES" .Vb 1 \& json_xs \-t none <isitreally.json .Ve .PP \&\*(L"\s-1JSON\s0 Lint\*(R" \- tries to parse the file \fIisitreally.json\fR as \s-1JSON\s0 \- if it is valid \s-1JSON,\s0 the command outputs nothing, otherwise it will print an error message and exit with non-zero exit status. .PP .Vb 1 \& <src.json json_xs >pretty.json .Ve .PP Prettify the \s-1JSON\s0 file \fIsrc.json\fR to \fIdst.json\fR. .PP .Vb 1 \& json_xs \-f storable\-file <file .Ve .PP Read the serialised Storable file \fIfile\fR and print a human-readable \s-1JSON\s0 version of it to \s-1STDOUT.\s0 .PP .Vb 1 \& json_xs \-f storable\-file \-t yaml <file .Ve .PP Same as above, but write \s-1YAML\s0 instead (not using \s-1JSON\s0 at all :) .PP .Vb 1 \& json_xs \-f none \-e \*(Aq$_ = [1, 2, 3]\*(Aq .Ve .PP Dump the perl array as \s-1UTF\-8\s0 encoded \s-1JSON\s0 text. .PP .Vb 1 \& <torrentfile json_xs \-f bencode \-e \*(Aq$_ = join "\en", map @$_, @{$_\->{"announce\-list"}}\*(Aq \-t string .Ve .PP Print the tracker list inside a torrent file. .PP .Vb 1 \& lwp\-request http://cpantesters.perl.org/show/JSON\-XS.json | json_xs .Ve .PP Fetch the cpan-testers result summary \f(CW\*(C`JSON::XS\*(C'\fR and pretty-print it. .SH "AUTHOR" .IX Header "AUTHOR" Copyright (C) 2008 Marc Lehmann <json@schmorp.de> PK�����,1]A������doc/ea-libmemcached/AUTHORSnu�[��������Brian Aker, brian@tangent.org -- Client Library, Tools Mark Atwood, -- Tools, Docs Patrick Galbraith, -- C++ Interface Padraig O'Sullivan, -- C++ Interface (current one) Tim Bunce, -- Docs Trond Norbye, trond.norbye@gmail.com -- Binary protocol, Misc Yin Chen, -- Ketama Work Toru Maesaka, dev@torum.net -- Stats analysis Eric Lambert, -- UDP work Monty Taylor, -- Build Releated (Pandora) Mingqiang Zhuang, Schooner Information Technology, Inc. -- Rewrite of memslap PK�����,1]v��v����doc/ea-libmemcached/THANKSnu�[��������(This is not complete, please send email if I have missed someone.) Eirik A. Nygaard - IO Patch Cal Heldenbrand - Awesome feedback on performance Dustin Sallings - Insight into protocol Tobias Luetke - Performance Feedback Andre Cruz - Help with getting the CRC Hash function to match other connectors Brian Pontz - Hsieh hash Tim Bunce - Perl Driver work and feedback on API Kevin Dalley - Bug Fixes Patrick Galbraith - work on C++ interface Ross McFarland - Idea for sorting servers. Marcelo Fernandez - TCP/IP timeout pieces Yin Chen - Ketama support/weighted support Monty Taylor - Debian Packages, Cleanup work for configure PK�����,1]\������doc/ea-libmemcached/COPYINGnu�[��������Software License Agreement (BSD License) Copyright (c) 2012, Data Differential (http://datadifferential.com/) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of TangentOrg nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PK�����,1]B;������doc/ea-libmemcached/READMEnu�[��������Hi! So what is this? It is a library in C for connecting to memcached servers (http://danga.com/memcached/). It is also a collection of utilities designed to work with memcached. memcat - Copy the value of a key to standard output memflush - Flush the contents of your servers. memrm - Remove a key(s) from the server. memstat - Dump the stats of your servers to standard output memslap - Load generation utility (benchmark!) The library is in production in many locations (upwards to 1000+ nodes). The project has information here: http://libmemcached.org/ Documentation can be found here: http://docs.libmemcached.org/ Want to contribute? If you write more then three good patches, then I will just ask you to commit directly to the tree. I am happy to have additional people work on this project. Please checkout README.FIRST. You will need a C99 compiler in order to compile this library. Currently we actively support Linux, MacOSX, FreeBSD, and Solaris. A Windows port is currently not supported, but one is listed on the homepage that is supported by someone in the community. If you are looking for answers to your questions please check out the mailing list at: http://lists.libmemcached.org/ Subscription information for the libmemcached mailing list can be found there. Please report bugs here: https://bugs.launchpad.net/libmemcached/ You can purchase support from: http://datadifferential.com/ Cheers, -Brian Aker Seattle, WA. PK�����,1]Π������doc/ea-libmemcached/TODOnu�[��������- Write a shell application (?) - Fix version in command line tools - Write (more) test cases for all command line tools (!!!) - Write some sort of "default" options bit for tools - More examples using libraries - Doxygen? - implement more connection/hash algo - implement compression - Revisit get() code (look for performance enhancements) - Add support for managing servers for clusters. - Build embedded version PK�����,1]uF��������doc/ea-libmemcached/NEWSnu�[��������See Changelog PK�����,1]|L ��L ����man/man1/memstat.1nu�[��������.\" Man page generated from reStructuredText. . .TH "MEMSTAT" "1" "February 09, 2014" "1.0.18" "libmemcached" .SH NAME memstat \- libmemcached Documentation . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp memstat \- Display the operating status of a single or group of memcached servers .SH SYNOPSIS .sp memstat [options] .SH DESCRIPTION .sp \fBmemstat\fP dumps the state of memcached(1) servers. It displays all data to stdout. .SH OPTIONS .sp You can specify servers via the option: .INDENT 0.0 .TP .B \-\-servers .UNINDENT .sp or via the environment variable: .INDENT 0.0 .TP .B \(aqMEMCACHED_SERVERS, \-\-args\(aq .UNINDENT .sp which can be used to specify the "argument" sent to the stats command (ie slab, size, items, etc..). .sp For a full list of operations run the tool with: .INDENT 0.0 .TP .B \-\-help .UNINDENT .INDENT 0.0 .TP .B \-\-analyze .UNINDENT .SH HOME .sp To find out more information please check: \fI\%http://libmemcached.org/\fP .SH SEE ALSO .sp \fImemcached(1)\fP \fIlibmemcached(3)\fP .SH AUTHOR Brian Aker .SH COPYRIGHT 2011-2013, Brian Aker DataDifferential, http://datadifferential.com/ .\" Generated by docutils manpage writer. . PK�����,1]kI������man/man1/memexist.1nu�[��������.\" Man page generated from reStructuredText. . .TH "MEMEXIST" "1" "February 09, 2014" "1.0.18" "libmemcached" .SH NAME memexist \- libmemcached Documentation . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp memexist [options] [key] .SH DESCRIPTION .sp \fBmemexist\fP checks for the existance of a key within a cluster. .SH OPTIONS .sp You can specify servers via the option: .INDENT 0.0 .TP .B \-\-servers .UNINDENT .sp or via the environment variable: .INDENT 0.0 .TP .B \(gaMEMCACHED_SERVERS\(ga .UNINDENT .sp If you do not specify either these, the final value in the command line list is the name of a server(s). .sp For a full list of operations run the tool with the option: .INDENT 0.0 .TP .B \-\-help .UNINDENT .SH HOME .sp To find out more information please check: \fI\%http://libmemcached.org/\fP .SH AUTHOR .sp Brian Aker, <\fI\%brian@tangent.org\fP> .SH SEE ALSO .sp \fImemcached(1)\fP \fIlibmemcached(3)\fP .SH AUTHOR Brian Aker .SH COPYRIGHT 2011-2013, Brian Aker DataDifferential, http://datadifferential.com/ .\" Generated by docutils manpage writer. . PK�����,1]< �� ����man/man1/memflush.1nu�[��������.\" Man page generated from reStructuredText. . .TH "MEMFLUSH" "1" "February 09, 2014" "1.0.18" "libmemcached" .SH NAME memflush \- libmemcached Documentation . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp Reset a server or list of servers .SH SYNOPSIS .sp memflush [options] .SH DESCRIPTION .sp \fBmemflush\fP resets the contents of memcached(1) servers. This means that all data in the specified servers will be deleted. .SH OPTIONS .sp You can specify servers via the option: .INDENT 0.0 .TP .B \-\-servers .UNINDENT .sp or via the environment variable: .INDENT 0.0 .TP .B \(gaMEMCACHED_SERVERS\(ga .UNINDENT .sp For a full list of operations run the tool with option: .INDENT 0.0 .TP .B \-\-help .UNINDENT .SH HOME .sp To find out more information please check: \fI\%http://libmemcached.org/\fP .SH AUTHOR .sp Brian Aker, <\fI\%brian@tangent.org\fP> .sp Mark Atwood <\fI\%mark@fallenpegasus.com\fP> .SH SEE ALSO .sp \fImemcached(1)\fP \fIlibmemcached(3)\fP .SH AUTHOR Brian Aker .SH COPYRIGHT 2011-2013, Brian Aker DataDifferential, http://datadifferential.com/ .\" Generated by docutils manpage writer. . PK�����,1]{,V"������man/man1/memtouch.1nu�[��������.\" Man page generated from reStructuredText. . .TH "MEMTOUCH" "1" "February 09, 2014" "1.0.18" "libmemcached" .SH NAME memtouch \- libmemcached Documentation . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp memtouch [options] key .SH DESCRIPTION .sp \fBmemtouch\fP does a "touch" on the specified key. .SH OPTIONS .sp You can specify servers via the option: .INDENT 0.0 .TP .B \-\-servers .UNINDENT .sp or via the environment variable: .INDENT 0.0 .TP .B \(gaMEMCACHED_SERVERS\(ga .UNINDENT .sp If you do not specify either these, the final value in the command line list is the name of a server(s). .sp For a full list of operations run the tool with the option: .INDENT 0.0 .TP .B \-\-help .UNINDENT .SH HOME .sp To find out more information please check: \fI\%http://libmemcached.org/\fP .SH AUTHOR .sp Brian Aker, <\fI\%brian@tangent.org\fP> .SH SEE ALSO .sp \fImemcached(1)\fP \fIlibmemcached(3)\fP .SH AUTHOR Brian Aker .SH COPYRIGHT 2011-2013, Brian Aker DataDifferential, http://datadifferential.com/ .\" Generated by docutils manpage writer. . PK�����,1]/d������man/man1/memrm.1nu�[��������.\" Man page generated from reStructuredText. . .TH "MEMRM" "1" "February 09, 2014" "1.0.18" "libmemcached" .SH NAME memrm \- libmemcached Documentation . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp memrm \- Remove a key(s) from a collection of memcached servers .SH SYNOPSIS .sp memrm [options] [key] .SH DESCRIPTION .sp \fBmemrm\fP removes items, specified by key, from memcached(1) servers. .SH OPTIONS .sp You can specify servers via the option: .INDENT 0.0 .TP .B \-\-servers .UNINDENT .sp or via the environment variable: .INDENT 0.0 .TP .B \(gaMEMCACHED_SERVERS\(ga .UNINDENT .sp For a full list of operations run the tool with the .INDENT 0.0 .TP .B \-\-help .UNINDENT .SH HOME .sp To find out more information please check: \fI\%http://libmemcached.org/\fP .SH AUTHOR .sp Brian Aker, <\fI\%brian@tangent.org\fP> .sp Mark Atwood, <\fI\%mark@fallenpegasus.com\fP> .SH SEE ALSO .sp \fImemcached(1)\fP \fIlibmemcached(3)\fP .SH AUTHOR Brian Aker .SH COPYRIGHT 2011-2013, Brian Aker DataDifferential, http://datadifferential.com/ .\" Generated by docutils manpage writer. . PK�����,1]_@tB �� ����man/man1/memcp.1nu�[��������.\" Man page generated from reStructuredText. . .TH "MEMCP" "1" "February 09, 2014" "1.0.18" "libmemcached" .SH NAME memcp \- libmemcached Documentation . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp Copies files to a collection of memcached servers .SH SYNOPSIS .sp memcp [options] [file] [server] .SH DESCRIPTION .sp \fBmemcp\fP copies one or more files into memcached(1) servers. It is similar to the standard UNIX cp(1) command. .sp The key names will be the names of the files, without any directory path. .SH OPTIONS .sp You can specify servers via the option: .INDENT 0.0 .TP .B \-\-servers .UNINDENT .sp or via the environment variable: .INDENT 0.0 .TP .B \(gaMEMCACHED_SERVERS\(ga .UNINDENT .sp If you do not specify either these, the final value in the command line list is the name of a server(s). .sp For a full list of operations run the tool with the option: .INDENT 0.0 .TP .B \-\-help .UNINDENT .SH HOME .sp To find out more information please check: \fI\%http://libmemcached.org/\fP .SH AUTHOR .sp Brian Aker, <\fI\%brian@tangent.org\fP> .sp Mark Atwood, <\fI\%mark@fallenpegasus.com\fP> .SH SEE ALSO .sp \fImemcached(1)\fP \fIlibmemcached(3)\fP .SH AUTHOR Brian Aker .SH COPYRIGHT 2011-2013, Brian Aker DataDifferential, http://datadifferential.com/ .\" Generated by docutils manpage writer. . PK�����,1]/ �� ����man/man1/memping.1nu�[��������.\" Man page generated from reStructuredText. . .TH "MEMPING" "1" "February 09, 2014" "1.0.18" "libmemcached" .SH NAME memping \- libmemcached Documentation . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp memping [options] [server] .SH DESCRIPTION .sp \fBmemping\fP can be used to ping a memcached server to see if it is taking connections. .SH OPTIONS .sp You can specify servers via the option: .INDENT 0.0 .TP .B \-\-servers .UNINDENT .sp or via the environment variable: .INDENT 0.0 .TP .B \(gaMEMCACHED_SERVERS\(ga .UNINDENT .sp If you do not specify either these, the final value in the command line list is the name of a server(s). .sp For a full list of operations run the tool with the option: .INDENT 0.0 .TP .B \-\-help .UNINDENT .SH HOME .sp To find out more information please check: \fI\%http://libmemcached.org/\fP .SH AUTHOR .sp Brian Aker, <\fI\%brian@tangent.org\fP> .SH SEE ALSO .sp \fImemcached(1)\fP \fIlibmemcached(3)\fP .SH AUTHOR Brian Aker .SH COPYRIGHT 2011-2013, Brian Aker DataDifferential, http://datadifferential.com/ .\" Generated by docutils manpage writer. . PK�����,1]/1��1����man/man1/memerror.1nu�[��������.\" Man page generated from reStructuredText. . .TH "MEMERROR" "1" "February 09, 2014" "1.0.18" "libmemcached" .SH NAME memerror \- libmemcached Documentation . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp Translates a memcached error code into a string .SH SYNOPSIS .sp memerror [options] [error code] .SH DESCRIPTION .sp \fBmemerror\fP translates an error code from libmemcached(3) into a human readable string. .SH OPTIONS .sp For a full list of operations run the tool with option: .INDENT 0.0 .TP .B \-\-help .UNINDENT .SH HOME .sp To find out more infoerroration please check: \fI\%http://libmemcached.org/\fP .SH AUTHOR .sp Brian Aker, <\fI\%brian@tangent.org\fP> .SH SEE ALSO .sp \fImemcached(1)\fP \fIlibmemcached(3)\fP .SH AUTHOR Brian Aker .SH COPYRIGHT 2011-2013, Brian Aker DataDifferential, http://datadifferential.com/ .\" Generated by docutils manpage writer. . PK�����,1][]"#��#����man/man1/memaslap.1nu�[��������.\" Man page generated from reStructuredText. . .TH "MEMASLAP" "1" "February 09, 2014" "1.0.18" "libmemcached" .SH NAME memaslap \- libmemcached Documentation . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp memaslap [options] .INDENT 0.0 .TP .B \-\-help .UNINDENT .INDENT 0.0 .TP .B MEMCACHED_SERVERS .UNINDENT .SH DESCRIPTION .sp \fBmemaslap\fP is a load generation and benchmark tool for memcached servers. It generates configurable workload such as threads, concurrencies, connections, run time, overwrite, miss rate, key size, value size, get/set proportion, expected throughput, and so on. Furthermore, it also testss data verification, expire\-time verification, UDP, binary protocol, facebook test, replication test, multi\-get and reconnection, etc. .sp Memaslap manages network connections like memcached with libevent. Each thread of memaslap is bound with a CPU core, all the threads don\(aqt communicate with each other, and there are several socket connections in each thread. Each connection keeps key size distribution, value size distribution, and command distribution by itself. .sp You can specify servers via the \fImemslap \-\-servers\fP option or via the environment variable \fI\%MEMCACHED_SERVERS\fP\&. .SH FEATURES .sp Memslap is developed to for the following purposes: .sp Manages network connections with libevent asynchronously. .sp Set both TCP and UDP up to use non\-blocking IO. .sp Improves parallelism: higher performance in multi\-threads environments. .sp Improves time efficiency: faster processing speed. .sp Generates key and value more efficiently; key size distribution and value size distribution are configurable. .sp Supports get, multi\-get, and set commands; command distribution is configurable. .sp Supports controllable miss rate and overwrite rate. .sp Supports data and expire\-time verification. .sp Supports dumping statistic information periodically. .sp Supports thousands of TCP connections. .sp Supports binary protocol. .sp Supports facebook test (set with TCP and multi\-get with UDP) and replication test. .SH DETAILS .SS Effective implementation of network. .sp For memaslap, both TCP and UDP use non\-blocking network IO. All the network events are managed by libevent as memcached. The network module of memaslap is similar to memcached. Libevent can ensure memaslap can handle network very efficiently. .SS Effective implementation of multi\-threads and concurrency .sp Memslap has the similar implementation of multi\-threads to memcached. Memslap creates one or more self\-governed threads; each thread is bound with one CPU core if the system testss setting CPU core affinity. .sp In addition, each thread has a libevent to manage the events of the network; each thread has one or more self\-governed concurrencies; and each concurrency has one or more socket connections. All the concurrencies don’t communicate with each other even though they are in the same thread. .sp Memslap can create thousands of socket connections, and each concurrency has tens of socket connections. Each concurrency randomly or sequentially selects one socket connection from its socket connection pool to run, so memaslap can ensure each concurrency handles one socket connection at any given time. Users can specify the number of concurrency and socket connections of each concurrency according to their expected workload. .SS Effective implementation of generating key and value .sp In order to improve time efficiency and space efficiency, memaslap creates a random characters table with 10M characters. All the suffixes of keys and values are generated from this random characters table. .sp Memslap uses the offset in the character table and the length of the string to identify a string. It can save much memory. Each key contains two parts, a prefix and a suffix. The prefix is an uint64_t, 8 bytes. In order to verify the data set before, memaslap need to ensure each key is unique, so it uses the prefix to identify a key. The prefix cannot include illegal characters, such as ‘r’, ‘n’, ‘0’ and ‘ ‘. And memaslap has an algorithm to ensure that. .sp Memslap doesn’t generate all the objects (key\-value pairs) at the beginning. It only generates enough objects to fill the task window (default 10K objects) of each concurrency. Each object has the following basic information, key prefix, key suffix offset in the character table, key length, value offset in the character table, and value length. .sp In the work process, each concurrency sequentially or randomly selects an object from the window to do set operation or get operation. At the same time, each concurrency kicks objects out of its window and adds new object into it. .SS Simple but useful task scheduling .sp Memslap uses libevent to schedule all the concurrencies of threads, and each concurrency schedules tasks based on the local task window. Memslap assumes that if each concurrency keeps the same key distribution, value distribution and commands distribution, from outside, memaslap keeps all the distribution as a whole. Each task window includes a lot of objects, each object stores its basic information, such as key, value, expire time, and so on. At any time, all the objects in the window keep the same and fixed key and value distribution. If an object is overwritten, the value of the object will be updated. Memslap verifies the data or expire\-time according to the object information stored in the task window. .sp Libevent selects which concurrency to handle based on a specific network event. Then the concurrency selects which command (get or set) to operate based on the command distribution. If it needs to kick out an old object and add a new object, in order to keep the same key and value distribution, the new object must have the same key length and value length. .sp If memcached server has two cache layers (memory and SSD), running memaslap with different window sizes can get different cache miss rates. If memaslap adds enough objects into the windows at the beginning, and the cache of memcached cannot store all the objects initialized, then memaslap will get some objects from the second cache layer. It causes the first cache layer to miss. So the user can specify the window size to get the expected miss rate of the first cache layer. .SS Useful implementation of multi\-servers , UDP, TCP, multi\-get and binary protocol .sp Because each thread is self\-governed, memaslap can assign different threads to handle different memcached servers. This is just one of the ways in which memaslap tests multiple servers. The only limitation is that the number of servers cannot be greater than the number of threads. The other way to test multiple servers is for replication test. Each concurrency has one socket connection to each memcached server. For the implementation, memaslap can set some objects to one memcached server, and get these objects from the other servers. .sp By default, Memslap does single get. If the user specifies multi\-get option, memaslap will collect enough get commands and pack and send the commands together. .sp Memslap testss both the ASCII protocol and binary protocol, but it runs on the ASCII protocol by default. Memslap by default runs on the TCP protocol, but it also tests UDP. Because UDP is unreliable, dropped packages and out\-of\-order packages may occur. Memslap creates a memory buffer to handle these problems. Memslap tries to read all the response data of one command from the server and reorders the response data. If some packages get lost, the waiting timeout mechanism can ensure half\-baked packages will be discarded and the next command will be sent. .SH USAGE .sp Below are some usage samples: .sp memaslap \-s 127.0.0.1:11211 \-S 5s .sp memaslap \-s 127.0.0.1:11211 \-t 2m \-v 0.2 \-e 0.05 \-b .sp memaslap \-s 127.0.0.1:11211 \-F config \-t 2m \-w 40k \-S 20s \-o 0.2 .sp memaslap \-s 127.0.0.1:11211 \-F config \-t 2m \-T 4 \-c 128 \-d 20 \-P 40k .sp memaslap \-s 127.0.0.1:11211 \-F config \-t 2m \-d 50 \-a \-n 40 .sp memaslap \-s 127.0.0.1:11211,127.0.0.1:11212 \-F config \-t 2m .sp memaslap \-s 127.0.0.1:11211,127.0.0.1:11212 \-F config \-t 2m \-p 2 .sp The user must specify one server at least to run memaslap. The rest of the parameters have default values, as shown below: .sp Thread number = 1 Concurrency = 16 .sp Run time = 600 seconds Configuration file = NULL .sp Key size = 64 Value size = 1024 .sp Get/set = 9:1 Window size = 10k .sp Execute number = 0 Single get = true .sp Multi\-get = false Number of sockets of each concurrency = 1 .sp Reconnect = false Data verification = false .sp Expire\-time verification = false ASCII protocol = true .sp Binary protocol = false Dumping statistic information .sp periodically = false .sp Overwrite proportion = 0% UDP = false .sp TCP = true Limit throughput = false .sp Facebook test = false Replication test = false .SS Key size, value size and command distribution. .sp All the distributions are read from the configuration file specified by user with “—cfg_cmd” option. If the user does not specify a configuration file, memaslap will run with the default distribution (key size = 64, value size = 1024, get/set = 9:1). For information on how to edit the configuration file, refer to the “Configuration File” section. .sp The minimum key size is 16 bytes; the maximum key size is 250 bytes. The precision of proportion is 0.001. The proportion of distribution will be rounded to 3 decimal places. .sp The minimum value size is 1 bytes; the maximum value size is 1M bytes. The precision of proportion is 0.001. The proportion of distribution will be rounded to 3 decimal places. Currently, memaslap only testss set and get commands. And it testss 100% set and 100% get. For 100% get, it will preset some objects to the server. .SS Multi\-thread and concurrency .sp The high performance of memaslap benefits from the special schedule of thread and concurrency. It’s important to specify the proper number of them. The default number of threads is 1; the default number of concurrency is 16. The user can use “—threads” and “\-\-concurrency” to specify these variables. .sp If the system tests setting CPU affinity and the number of threads specified by the user is greater than 1, memaslap will try to bind each thread to a different CPU core. So if you want to get the best performance memaslap, it is better to specify the number of thread equal to the number of CPU cores. The number of threads specified by the user can also be less or greater than the number of CPU cores. Because of the limitation of implementation, the number of concurrencies could be the multiple of the number of threads. .INDENT 0.0 .IP 1. 3 For 8 CPU cores system .UNINDENT .sp For example: .sp \-\-threads=2 \-\-concurrency=128 .sp \-\-threads=8 \-\-concurrency=128 .sp \-\-threads=8 \-\-concurrency=256 .sp \-\-threads=12 \-\-concurrency=144 .INDENT 0.0 .IP 2. 3 For 16 CPU cores system .UNINDENT .sp For example: .sp \-\-threads=8 \-\-concurrency=128 .sp \-\-threads=16 \-\-concurrency=256 .sp \-\-threads=16 \-\-concurrency=512 .sp \-\-threads=24 \-\-concurrency=288 .sp The memaslap performs very well, when used to test the performance of memcached servers. Most of the time, the bottleneck is the network or the server. If for some reason the user wants to limit the performance of memaslap, there are two ways to do this: .sp Decrease the number of threads and concurrencies. Use the option “\-\-tps” that memaslap provides to limit the throughput. This option allows the user to get the expected throughput. For example, assume that the maximum throughput is 50 kops/s for a specific configuration, you can specify the throughput equal to or less than the maximum throughput using “\-\-tps” option. .SS Window size .sp Most of the time, the user does not need to specify the window size. The default window size is 10k. For Schooner Memcached, the user can specify different window sizes to get different cache miss rates based on the test case. Memslap testss cache miss rate between 0% and 100%. If you use this utility to test the performance of Schooner Memcached, you can specify a proper window size to get the expected cache miss rate. The formula for calculating window size is as follows: .sp Assume that the key size is 128 bytes, and the value size is 2048 bytes, and concurrency=128. .sp 1. Small cache cache_size=1M, 100% cache miss (all data get from SSD). win_size=10k .INDENT 0.0 .IP 2. 3 cache_size=4G .UNINDENT .sp (1). cache miss rate 0% .sp win_size=8k .sp (2). cache miss rate 5% .sp win_size=11k .INDENT 0.0 .IP 3. 3 cache_size=16G .UNINDENT .sp (1). cache miss rate 0% .sp win_size=32k .sp (2). cache miss .sp rate 5% .sp win_size=46k .sp The formula for calculating window size for cache miss rate 0%: .sp cache_size / concurrency / (key_size + value_size) * 0.5 .sp The formula for calculating window size for cache miss rate 5%: .sp cache_size / concurrency / (key_size + value_size) * 0.7 .SS Verification .sp Memslap testss both data verification and expire\-time verification. The user can use "\-\-verify=" or "\-v" to specify the proportion of data verification. In theory, it testss 100% data verification. The user can use "\-\-exp_verify=" or "\-e" to specify the proportion of expire\-time verification. In theory, it testss 100% expire\-time verification. Specify the "\-\-verbose" options to get more detailed error information. .sp For example: \-\-exp_verify=0.01 –verify=0.1 , it means that 1% of the objects set with expire\-time, 10% of the objects gotten will be verified. If the objects are gotten, memaslap will verify the expire\-time and value. .SS multi\-servers and multi\-config .sp Memslap testss multi\-servers based on self\-governed thread. There is a limitation that the number of servers cannot be greater than the number of threads. Memslap assigns one thread to handle one server at least. The user can use the "\-\-servers=" or "\-s" option to specify multi\-servers. .sp For example: .sp \-\-servers=10.1.1.1:11211,10.1.1.2:11212,10.1.1.3:11213 \-\-threads=6 \-\-concurrency=36 .sp The above command means that there are 6 threads, with each thread having 6 concurrencies and that threads 0 and 3 handle server 0 (10.1.1.1); threads 1 and 4 handle server 1 (10.1.1.2); and thread 2 and 5 handle server 2 (10.1.1.3). .sp All the threads and concurrencies in memaslap are self\-governed. .sp So is memaslap. The user can start up several memaslap instances. The user can run memaslap on different client machines to communicate with the same memcached server at the same. It is recommended that the user start different memaslap on different machines using the same configuration. .SS Run with execute number mode or time mode .sp The default memaslap runs with time mode. The default run time is 10 minutes. If it times out, memaslap will exit. Do not specify both execute number mode and time mode at the same time; just specify one instead. .sp For example: .sp \-\-time=30s (It means the test will run 30 seconds.) .sp \-\-execute_number=100000 (It means that after running 100000 commands, the test will exit.) .SS Dump statistic information periodically. .sp The user can use "\-\-stat_freq=" or "\-S" to specify the frequency. .sp For example: .sp \-\-stat_freq=20s .sp Memslap will dump the statistics of the commands (get and set) at the frequency of every 20 seconds. .sp For more information on the format of dumping statistic information, refer to “Format of Output” section. .SS Multi\-get .sp The user can use "\-\-division=" or "\-d" to specify multi\-get keys count. Memslap by default does single get with TCP. Memslap also testss data verification and expire\-time verification for multi\-get. .sp Memslap testss multi\-get with both TCP and UDP. Because of the different implementation of the ASCII protocol and binary protocol, there are some differences between the two. For the ASCII protocol, memaslap sends one “multi\-get” to the server once. For the binary protocol, memaslap sends several single get commands together as “multi\-get” to the server. .SS UDP and TCP .sp Memslap testss both UDP and TCP. For TCP, memaslap does not reconnect the memcached server if socket connections are lost. If all the socket connections are lost or memcached server crashes, memaslap will exit. If the user specifies the “\-\-reconnect” option when socket connections are lost, it will reconnect them. .sp User can use “\-\-udp” to enable the UDP feature, but UDP comes with some limitations: .sp UDP cannot set data more than 1400 bytes. .sp UDP is not testsed by the binary protocol because the binary protocol of memcached does not tests that. .sp UDP doesn’t tests reconnection. .SS Facebook test .sp Set data with TCP and multi\-get with UDP. Specify the following options: .sp "\-\-facebook \-\-division=50" .sp If you want to create thousands of TCP connections, specify the .sp "\-\-conn_sock=" option. .sp For example: \-\-facebook \-\-division=50 \-\-conn_sock=200 .sp The above command means that memaslap will do facebook test, each concurrency has 200 socket TCP connections and one UDP socket. .sp Memslap sets objects with the TCP socket, and multi\-gets 50 objects once with the UDP socket. .sp If you specify "\-\-division=50", the key size must be less that 25 bytes because the UDP packet size is 1400 bytes. .SS Replication test .sp For replication test, the user must specify at least two memcached servers. The user can use “—rep_write=” option to enable feature. .sp For example: .sp \-\-servers=10.1.1.1:11211,10.1.1.2:11212 –rep_write=2 .sp The above command means that there are 2 replication memcached servers, memaslap will set objects to both server 0 and server 1, get objects which are set to server 0 before from server 1, and also get objects which are set to server 1 before from server 0. If server 0 crashes, memaslap will only get objects from server 1. If server 0 comes back to life again, memaslap will reconnect server 0. If both server 0 and server 1 crash, memaslap will exit. .SS Supports thousands of TCP connections .sp Start memaslap with "\-\-conn_sock=" or "\-n" to enable this feature. Make sure that your system can tests opening thousands of files and creating thousands of sockets. However, this feature does not tests reconnection if sockets disconnect. .sp For example: .sp \-\-threads=8 \-\-concurrency=128 \-\-conn_sock=128 .sp The above command means that memaslap starts up 8 threads, each thread has 16 concurrencies, each concurrency has 128 TCP socket connections, and the total number of TCP socket connections is 128 * 128 = 16384. .SS Supports binary protocol .sp Start memaslap with "\-\-binary" or "\-B" options to enable this feature. It testss all the above features except UDP, because the latest memcached 1.3.3 does not implement binary UDP protocol. .sp For example: .sp \-\-binary .sp Since memcached 1.3.3 doesn\(aqt implement binary UDP protocol, memaslap does not tests UDP. In addition, memcached 1.3.3 does not tests multi\-get. If you specify "\-\-division=50" option, it just sends 50 get commands together as “mulit\-get” to the server. .SH CONFIGURATION FILE .sp This section describes the format of the configuration file. By default when no configuration file is specified memaslap reads the default one located at ~/.memaslap.cnf. .sp Below is a sample configuration file: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- #comments should start with \(aq#\(aq #key #start_len end_len proportion # #key length range from start_len to end_len #start_len must be equal to or greater than 16 #end_len must be equal to or less than 250 #start_len must be equal to or greater than end_len #memaslap will generate keys according to the key range #proportion: indicates keys generated from one range accounts for the total generated keys # #example1: key range 16~100 accounts for 80% # key range 101~200 accounts for 10% # key range 201~250 accounts for 10% # total should be 1 (0.8+0.1+0.1 = 1) # # 16 100 0.8 # 101 200 0.1 # 201 249 0.1 # #example2: all keys length are 128 bytes # # 128 128 1 key 128 128 1 #value #start_len end_len proportion # #value length range from start_len to end_len #start_len must be equal to or greater than 1 #end_len must be equal to or less than 1M #start_len must be equal to or greater than end_len #memaslap will generate values according to the value range #proportion: indicates values generated from one range accounts for the total generated values # #example1: value range 1~1000 accounts for 80% # value range 1001~10000 accounts for 10% # value range 10001~100000 accounts for 10% # total should be 1 (0.8+0.1+0.1 = 1) # # 1 1000 0.8 # 1001 10000 0.1 # 10001 100000 0.1 # #example2: all value length are 128 bytes # # 128 128 1 value 2048 2048 1 #cmd #cmd_type cmd_proportion # #currently memaslap only testss get and set command. # #cmd_type #set 0 #get 1 # #example: set command accounts for 50% # get command accounts for 50% # total should be 1 (0.5+0.5 = 1) # # cmd # 0 0.5 # 1 0.5 cmd 0 0.1 1.0 0.9 .ft P .fi .UNINDENT .UNINDENT .SH FORMAT OF OUTPUT .sp At the beginning, memaslap displays some configuration information as follows: .sp servers : 127.0.0.1:11211 .sp threads count: 1 .sp concurrency: 16 .sp run time: 20s .sp windows size: 10k .sp set proportion: set_prop=0.10 .sp get proportion: get_prop=0.90 .SS Where .sp servers : "servers" .INDENT 0.0 .INDENT 3.5 The servers used by memaslap. .UNINDENT .UNINDENT .sp threads count .INDENT 0.0 .INDENT 3.5 The number of threads memaslap runs with. .UNINDENT .UNINDENT .sp concurrency .INDENT 0.0 .INDENT 3.5 The number of concurrencies memaslap runs with. .UNINDENT .UNINDENT .sp run time .INDENT 0.0 .INDENT 3.5 How long to run memaslap. .UNINDENT .UNINDENT .sp windows size .INDENT 0.0 .INDENT 3.5 The task window size of each concurrency. .UNINDENT .UNINDENT .sp set proportion .INDENT 0.0 .INDENT 3.5 The proportion of set command. .UNINDENT .UNINDENT .sp get proportion .INDENT 0.0 .INDENT 3.5 The proportion of get command. .UNINDENT .UNINDENT .sp The output of dynamic statistics is something like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- Get Statistics Type Time(s) Ops TPS(ops/s) Net(M/s) Get_miss Min(us) Max(us) Avg(us) Std_dev Geo_dist Period 5 345826 69165 65.3 0 27 2198 203 95.43 177.29 Global 20 1257935 62896 71.8 0 26 3791 224 117.79 192.60 Set Statistics Type Time(s) Ops TPS(ops/s) Net(M/s) Get_miss Min(us) Max(us) Avg(us) Std_dev Geo_dist Period 5 38425 7685 7.3 0 42 628 240 88.05 220.21 Global 20 139780 6989 8.0 0 37 3790 253 117.93 224.83 Total Statistics Type Time(s) Ops TPS(ops/s) Net(M/s) Get_miss Min(us) Max(us) Avg(us) Std_dev Geo_dist Period 5 384252 76850 72.5 0 27 2198 207 94.72 181.18 Global 20 1397720 69886 79.7 0 26 3791 227 117.93 195.60 \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- .ft P .fi .UNINDENT .UNINDENT .SS Where .sp Get Statistics .INDENT 0.0 .INDENT 3.5 Statistics information of get command .UNINDENT .UNINDENT .sp Set Statistics .INDENT 0.0 .INDENT 3.5 Statistics information of set command .UNINDENT .UNINDENT .sp Total Statistics .INDENT 0.0 .INDENT 3.5 Statistics information of both get and set command .UNINDENT .UNINDENT .sp Period .INDENT 0.0 .INDENT 3.5 Result within a period .UNINDENT .UNINDENT .sp Global .INDENT 0.0 .INDENT 3.5 Accumulated results .UNINDENT .UNINDENT .sp Ops .INDENT 0.0 .INDENT 3.5 Total operations .UNINDENT .UNINDENT .sp TPS .INDENT 0.0 .INDENT 3.5 Throughput, operations/second .UNINDENT .UNINDENT .sp Net .INDENT 0.0 .INDENT 3.5 The rate of network .UNINDENT .UNINDENT .sp Get_miss .INDENT 0.0 .INDENT 3.5 How many objects can’t be gotten .UNINDENT .UNINDENT .sp Min .INDENT 0.0 .INDENT 3.5 The minimum response time .UNINDENT .UNINDENT .sp Max .INDENT 0.0 .INDENT 3.5 The maximum response time .UNINDENT .UNINDENT .sp Avg: .INDENT 0.0 .INDENT 3.5 The average response time .UNINDENT .UNINDENT .sp Std_dev .INDENT 0.0 .INDENT 3.5 Standard deviation of response time .UNINDENT .UNINDENT .sp Geo_dist .INDENT 0.0 .INDENT 3.5 Geometric distribution based on natural exponential function .UNINDENT .UNINDENT .sp At the end, memaslap will output something like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- Get Statistics (1257956 events) Min: 26 Max: 3791 Avg: 224 Geo: 192.60 Std: 116.23 Log2 Dist: 4: 0 10 84490 215345 8: 484890 459823 12543 824 12: 31 Set Statistics (139782 events) Min: 37 Max: 3790 Avg: 253 Geo: 224.84 Std: 116.83 Log2 Dist: 4: 0 0 4200 16988 8: 50784 65574 2064 167 12: 5 Total Statistics (1397738 events) Min: 26 Max: 3791 Avg: 227 Geo: 195.60 Std: 116.60 Log2 Dist: 4: 0 10 88690 232333 8: 535674 525397 14607 991 12: 36 cmd_get: 1257969 cmd_set: 139785 get_misses: 0 verify_misses: 0 verify_failed: 0 expired_get: 0 unexpired_unget: 0 written_bytes: 242516030 read_bytes: 1003702556 object_bytes: 152086080 packet_disorder: 0 packet_drop: 0 udp_timeout: 0 Run time: 20.0s Ops: 1397754 TPS: 69817 Net_rate: 59.4M/s \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- .ft P .fi .UNINDENT .UNINDENT .SS Where .sp Get Statistics .INDENT 0.0 .INDENT 3.5 Get statistics of response time .UNINDENT .UNINDENT .sp Set Statistics .INDENT 0.0 .INDENT 3.5 Set statistics of response time .UNINDENT .UNINDENT .sp Total Statistics .INDENT 0.0 .INDENT 3.5 Both get and set statistics of response time .UNINDENT .UNINDENT .sp Min .INDENT 0.0 .INDENT 3.5 The accumulated and minimum response time .UNINDENT .UNINDENT .sp Max .INDENT 0.0 .INDENT 3.5 The accumulated and maximum response time .UNINDENT .UNINDENT .sp Avg .INDENT 0.0 .INDENT 3.5 The accumulated and average response time .UNINDENT .UNINDENT .sp Std .INDENT 0.0 .INDENT 3.5 Standard deviation of response time .UNINDENT .UNINDENT .sp Log2 Dist .INDENT 0.0 .INDENT 3.5 Geometric distribution based on logarithm 2 .UNINDENT .UNINDENT .sp cmd_get .INDENT 0.0 .INDENT 3.5 Total get commands done .UNINDENT .UNINDENT .sp cmd_set .INDENT 0.0 .INDENT 3.5 Total set commands done .UNINDENT .UNINDENT .sp get_misses .INDENT 0.0 .INDENT 3.5 How many objects can’t be gotten from server .UNINDENT .UNINDENT .sp verify_misses .INDENT 0.0 .INDENT 3.5 How many objects need to verify but can’t get them .UNINDENT .UNINDENT .sp verify_failed .INDENT 0.0 .INDENT 3.5 How many objects with insistent value .UNINDENT .UNINDENT .sp expired_get .INDENT 0.0 .INDENT 3.5 How many objects are expired but we get them .UNINDENT .UNINDENT .sp unexpired_unget .INDENT 0.0 .INDENT 3.5 How many objects are unexpired but we can’t get them .UNINDENT .UNINDENT .sp written_bytes .INDENT 0.0 .INDENT 3.5 Total written bytes .UNINDENT .UNINDENT .sp read_bytes .INDENT 0.0 .INDENT 3.5 Total read bytes .UNINDENT .UNINDENT .sp object_bytes .INDENT 0.0 .INDENT 3.5 Total object bytes .UNINDENT .UNINDENT .sp packet_disorder .INDENT 0.0 .INDENT 3.5 How many UDP packages are disorder .UNINDENT .UNINDENT .sp packet_drop .INDENT 0.0 .INDENT 3.5 How many UDP packages are lost .UNINDENT .UNINDENT .sp udp_timeout .INDENT 0.0 .INDENT 3.5 How many times UDP time out happen .UNINDENT .UNINDENT .sp Run time .INDENT 0.0 .INDENT 3.5 Total run time .UNINDENT .UNINDENT .sp Ops .INDENT 0.0 .INDENT 3.5 Total operations .UNINDENT .UNINDENT .sp TPS .INDENT 0.0 .INDENT 3.5 Throughput, operations/second .UNINDENT .UNINDENT .sp Net_rate .INDENT 0.0 .INDENT 3.5 The average rate of network .UNINDENT .UNINDENT .SH OPTIONS .INDENT 0.0 .TP .B \-s, \-\-servers= List one or more servers to connect. Servers count must be less than threads count. e.g.: \-\-servers=localhost:1234,localhost:11211 .TP .B \-T, \-\-threads= Number of threads to startup, better equal to CPU numbers. Default 8. .TP .B \-c, \-\-concurrency= Number of concurrency to simulate with load. Default 128. .TP .B \-n, \-\-conn_sock= Number of TCP socks per concurrency. Default 1. .TP .B \-x, \-\-execute_number= Number of operations(get and set) to execute for the given test. Default 1000000. .TP .B \-t, \-\-time= How long the test to run, suffix: s\-seconds, m\-minutes, h\-hours, d\-days e.g.: \-\-time=2h. .TP .B \-F, \-\-cfg_cmd= Load the configure file to get command,key and value distribution list. .TP .B \-w, \-\-win_size= Task window size of each concurrency, suffix: K, M e.g.: \-\-win_size=10k. Default 10k. .TP .B \-X, \-\-fixed_size= Fixed length of value. .TP .B \-v, \-\-verify= The proportion of date verification, e.g.: \-\-verify=0.01 .TP .B \-d, \-\-division= Number of keys to multi\-get once. Default 1, means single get. .TP .B \-S, \-\-stat_freq= Frequency of dumping statistic information. suffix: s\-seconds, m\-minutes, e.g.: \-\-resp_freq=10s. .TP .B \-e, \-\-exp_verify= The proportion of objects with expire time, e.g.: \-\-exp_verify=0.01. Default no object with expire time .TP .B \-o, \-\-overwrite= The proportion of objects need overwrite, e.g.: \-\-overwrite=0.01. Default never overwrite object. .UNINDENT .INDENT 0.0 .TP .B \-R\fP,\fB \-\-reconnect Reconnect tests, when connection is closed it will be reconnected. .TP .B \-U\fP,\fB \-\-udp UDP tests, default memaslap uses TCP, TCP port and UDP port of server must be same. .TP .B \-a\fP,\fB \-\-facebook Whether it enables facebook test feature, set with TCP and multi\-get with UDP. .TP .B \-B\fP,\fB \-\-binary Whether it enables binary protocol. Default with ASCII protocol. .UNINDENT .INDENT 0.0 .TP .B \-P, \-\-tps= Expected throughput, suffix: K, e.g.: \-\-tps=10k. .TP .B \-p, \-\-rep_write= The first nth servers can write data, e.g.: \-\-rep_write=2. .UNINDENT .INDENT 0.0 .TP .B \-b\fP,\fB \-\-verbose Whether it outputs detailed information when verification fails. .TP .B \-h\fP,\fB \-\-help Display this message and then exit. .TP .B \-V\fP,\fB \-\-version Display the version of the application and then exit. .UNINDENT .SH EXAMPLES .sp memaslap \-s 127.0.0.1:11211 \-S 5s .sp memaslap \-s 127.0.0.1:11211 \-t 2m \-v 0.2 \-e 0.05 \-b .sp memaslap \-s 127.0.0.1:11211 \-F config \-t 2m \-w 40k \-S 20s \-o 0.2 .sp memaslap \-s 127.0.0.1:11211 \-F config \-t 2m \-T 4 \-c 128 \-d 20 \-P 40k .sp memaslap \-s 127.0.0.1:11211 \-F config \-t 2m \-d 50 \-a \-n 40 .sp memaslap \-s 127.0.0.1:11211,127.0.0.1:11212 \-F config \-t 2m .sp memaslap \-s 127.0.0.1:11211,127.0.0.1:11212 \-F config \-t 2m \-p 2 .SH HOME .sp To find out more information please check: \fI\%http://libmemcached.org/\fP .SH AUTHORS .sp Mingqiang Zhuang <\fI\%mingqiangzhuang@hengtiansoft.com\fP> (Schooner Technolgy) Brian Aker, <\fI\%brian@tangent.org\fP> .SH SEE ALSO .sp \fImemcached(1)\fP \fIlibmemcached(3)\fP .SH AUTHOR Brian Aker .SH COPYRIGHT 2011-2013, Brian Aker DataDifferential, http://datadifferential.com/ .\" Generated by docutils manpage writer. . PK�����,1]KAg ��g ����man/man1/memcat.1nu�[��������.\" Man page generated from reStructuredText. . .TH "MEMCAT" "1" "February 09, 2014" "1.0.18" "libmemcached" .SH NAME memcat \- libmemcached Documentation . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp memcat [options] key .sp Copy a set of keys to stdout .SH DESCRIPTION .sp \fBmemcat\fP outputs to stdout the value a single or multiple set of keys stored in a memcached(1) server. If any key is not found an error is returned. .sp It is similar to the standard UNIX cat(1) utility. .SH OPTIONS .sp You can specify servers via the option: .INDENT 0.0 .TP .B \-\-servers .UNINDENT .sp or via the environment variable: .INDENT 0.0 .TP .B \(gaMEMCACHED_SERVERS\(ga .UNINDENT .sp For a full list of operations run the tool with the option: .INDENT 0.0 .TP .B \-\-help .UNINDENT .SH HOME .sp To find out more information please check: \fI\%http://libmemcached.org/\fP .SH AUTHOR .sp Brian Aker, <\fI\%brian@tangent.org\fP> .sp Mark Atwood <\fI\%mark@fallenpegasus.com\fP> .SH SEE ALSO .sp \fImemcached(1)\fP \fIlibmemcached(3)\fP .SH AUTHOR Brian Aker .SH COPYRIGHT 2011-2013, Brian Aker DataDifferential, http://datadifferential.com/ .\" Generated by docutils manpage writer. . PK�����,1]>YT ��T ����man/man1/memcapable.1nu�[��������.\" Man page generated from reStructuredText. . .TH "MEMCAPABLE" "1" "February 09, 2014" "1.0.18" "libmemcached" .SH NAME memcapable \- libmemcached Documentation . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp memcapable [options] .SH DESCRIPTION .sp \fBmemcapable\fP connects to the specified memcached server and tries to determine its capabilities by running the various commands and verifying the response. .SH LIMITATIONS .sp The current version of memcapable will only verify the binary protocol. .SH OPTIONS .INDENT 0.0 .TP .B \-h hostname .UNINDENT .sp Specify the hostname to connect to. The default is \fIlocalhost\fP .INDENT 0.0 .TP .B \-p port .UNINDENT .sp Specify the port number to connect to. The default is \fI11211\fP .INDENT 0.0 .TP .B \-c .UNINDENT .sp Generate a coredump when it detects an error from the server. .INDENT 0.0 .TP .B \-v .UNINDENT .sp Print out the comparison when it detects an error from the server. .INDENT 0.0 .TP .B \-t n .UNINDENT .sp Set the timeout from an IO operation to/from the server to \fIn\fP seconds. .SH HOME .sp To find out more information please check: \fI\%http://libmemcached.org/\fP .SH AUTHOR .sp Trond Norbye, <\fI\%trond.norbye@gmail.com\fP> .SH SEE ALSO .sp \fImemcached(1)\fP \fIlibmemcached(3)\fP .SH AUTHOR Brian Aker .SH COPYRIGHT 2011-2013, Brian Aker DataDifferential, http://datadifferential.com/ .\" Generated by docutils manpage writer. . PK�����,1]/r��r����man/man1/memdump.1nu�[��������.\" Man page generated from reStructuredText. . .TH "MEMDUMP" "1" "February 09, 2014" "1.0.18" "libmemcached" .SH NAME memdump \- libmemcached Documentation . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp Dump a list of keys from a server. .SH SYNOPSIS .sp memdump [options] .SH DESCRIPTION .sp \fBmemdump\fP dumps a list of "keys" from all servers that it is told to fetch from. Because memcached does not guarentee to provide all keys it is not possible to get a complete "dump". .SH OPTIONS .sp For a full list of operations run the tool with option: .INDENT 0.0 .TP .B \-\-help .UNINDENT .SH HOME .sp To find out more information please check: \fI\%http://libmemcached.org/\fP .SH AUTHOR .sp Brian Aker, <\fI\%brian@tangent.org\fP> .SH SEE ALSO .sp \fImemcached(1)\fP \fIlibmemcached(3)\fP .SH AUTHOR Brian Aker .SH COPYRIGHT 2011-2013, Brian Aker DataDifferential, http://datadifferential.com/ .\" Generated by docutils manpage writer. . PK�����,1]u([������man/man1/memparse.1nu�[��������.\" Man page generated from reStructuredText. . .TH "MEMPARSE" "1" "February 09, 2014" "1.0.18" "libmemcached" .SH NAME memparse \- libmemcached Documentation . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp Copies files to a collection of memcached servers .SH SYNOPSIS .sp memparse [options] "option string" .SH DESCRIPTION .sp \fBmemparse\fP can be used to validate an option string. .SH OPTIONS .sp For a full list of operations run the tool with the option: .INDENT 0.0 .TP .B \-\-help .UNINDENT .SH HOME .sp To find out more information please check: \fI\%http://libmemcached.org/\fP .SH AUTHOR .sp Brian Aker, <\fI\%brian@tangent.org\fP> .SH SEE ALSO .sp \fImemcached(1)\fP \fIlibmemcached(3)\fP .SH AUTHOR Brian Aker .SH COPYRIGHT 2011-2013, Brian Aker DataDifferential, http://datadifferential.com/ .\" Generated by docutils manpage writer. . PK�����,1]No ��o ����man/man1/memslap.1nu�[��������.\" Man page generated from reStructuredText. . .TH "MEMSLAP" "1" "February 09, 2014" "1.0.18" "libmemcached" .SH NAME memslap \- libmemcached Documentation . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp memslap [options] .SH DESCRIPTION .sp \fBmemslap\fP is a load generation and benchmark tool for memcached(1) servers. It generates configurable workload such as threads, concurrencies, connections, run time, overwrite, miss rate, key size, value size, get/set proportion, expected throughput, and so on. .SH OPTIONS .sp You can specify servers via the option: .INDENT 0.0 .TP .B \-\-servers .UNINDENT .sp or via the environment variable: .INDENT 0.0 .TP .B \(gaMEMCACHED_SERVERS\(ga .UNINDENT .sp For a full list of operations run the tool with: .INDENT 0.0 .TP .B \-\-help .UNINDENT .SH HOME .sp To find out more information please check: \(aq\fI\%http://libmemcached.org/\fP <\fI\%http://libmemcached.org/\fP>\(aq_ .SH AUTHOR .sp Brian Aker, <\fI\%brian@tangent.org\fP> .SH SEE ALSO .sp \fImemcached(1)\fP \fIlibmemcached(3)\fP .SH AUTHOR Brian Aker .SH COPYRIGHT 2011-2013, Brian Aker DataDifferential, http://datadifferential.com/ .\" Generated by docutils manpage writer. . PK�����=1]9Ύ*M��*M����fpm/status.htmlnu�[��������<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <!-- (c) 2011 Jerome Loyet The PHP License, version 3.01 This is sample real-time status page for FPM. You can change it to better fit your needs. --> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> <style type="text/css"> body {background-color: #ffffff; color: #000000;} body, td, th, h1, h2 {font-family: sans-serif;} pre {margin: 0px; font-family: monospace;} a:link {color: #000099; text-decoration: none; background-color: #ffffff;} a:hover {text-decoration: underline;} table {border-collapse: collapse;} .center {text-align: center;} .center table { margin-left: auto; margin-right: auto; text-align: left;} .center th { text-align: center !important; } td, th { border: 1px solid #000000; font-size: 75%; vertical-align: baseline;} h1 {font-size: 150%;} h2 {font-size: 125%;} .p {text-align: left;} .e {background-color: #ccccff; font-weight: bold; color: #000000;} .h {background-color: #9999cc; font-weight: bold; color: #000000;} .v {background-color: #cccccc; color: #000000;} .w {background-color: #ccccff; color: #000000;} .h th { cursor: pointer; } img {float: right; border: 0px;} hr {width: 600px; background-color: #cccccc; border: 0px; height: 1px; color: #000000;} </style> <title>PHP-FPM status page
PHP Logo

PHP-FPM real-time status page


Status URL
Ajax status
Refresh Rate
Actions

Pool Status

 

Active Processes status

PID↓Start TimeStart SinceRequests ServedRequest DurationRequest methodRequest URIContent LengthUserScript

Idle Processes status

PID↓Start TimeStart SinceRequests ServedRequest DurationRequest methodRequest URIContent LengthUserScriptLast Request %CPULast Request Memory

Valid XHTML 1.0 Transitional

PK=1]a``&doc/ea-php83-php-sourceguardian/READMEnu[You need to read the included SourceGuardian Loader License before using SourceGuardian loaders.PK=1].q``Adoc/ea-php83-php-sourceguardian/SourceGuardian_Loader_License.pdfnu[%PDF-1.3 % 4 0 obj << /Length 5 0 R /Filter /FlateDecode >> stream x͝$q߿S \]JIaEPLo~!̬[fv )6vuUVV+~7t|9õC;u7_o~~wwR)e]O9wtxo~w}pyͻw۾>|S}xO}?Ov}x~/7jk޾zw6ծ?0uCu΃#wHGu#4p㑿f 12}l }$>2?%Oae8xl v+Dnv?f}x_&]םwF7kEe]wݝNd$qU8hv {T@6`VDdAmr #fEt1EoOa.IOOYGwf4j́ru?w;mZU#ڟǮ I~6U}<7X_[_x^^Okܖ%9OL@v#MG=\s'"%U?nQ͌9R6w=$ΚON|cxu滦lѢ4ME}Rw;_I~wχc77GLFGO Oe~W!? SqѓOdK@Um7فGqC zZ?9Uu§ )(A~R$ǻW2 Dx% w8FnlτScY4 qՓ a3C\rg؅K<05u<K3ra;e)dYʇLjxPx UˍZk ikNݵdqC,m  z sX,_T :R܂%k2O6r)nxBۿ" ~m+WUl ;[/E#FV-=c\fh?(gf(ꋟ?_/K}ckLr ӵnDY$Oy o-5<쀳 3vZeO榬&1`Os&[yxgH!"hT Ey}^c^2,n2JnT-Q7f)3v8i;UѬ k8N$:+t?_#N|&l<ĉw *;u==dA$&Mh!7x*&.'%wdUɎ4* JQdWj:{3[HP@P8)~뙪]O=% ̞σE͢z>ZpLMsRd52"%a2@Ԉ-@m'zwIbIsAڷ hus)Ǥk'1bEZ֭b^y5_6y5!pi0^mbId֘Vamce ǩ+!|ȊtS(`Q==JrHd@h[fqZ+ hѯ<ՃLҤ?)%0AnuEp(t!#[敿DE;S+%oq<CuS?U5e6hF̲ xÙ/s58 F݁<ۭqp+mO"+|Wɓ aQL)u\hD) )ZkS"f{SzDgҋ컺l]4!%hkEd-xkATRۿ|c43IVЖK}VRx2ZF;t+tb/ 5k2A2E搴fc;eiF1KKs JiYIQ׶Dl]ͥfTgkCqxY^tzL>Uo5:DN*xꭒVgPզzp˧J/m1|Kb]c[^I@Ǵy?&{֠$ca`ĊM'Ƒeu oGix.٬Y*qw}Cdk=k/iq£Bm-Y0sEyZF[S=vWђB K|B;aߑ"?`.}yofJJnL K渟Zy4 )JPvu}8qzTLW{ք׎0١.s-۽uG,QmKh'vnNo8e$;VO='N-hE:ܰ̍hಂ4(qQVPz xyU&֏ ܣP6 #;z~Nֳp`efk|JX|ӯuWd#R7+&us#>|,05#x.E<"4OxL}K Kw;,: 'M0 QeXntrSIhb0G @㪬X'']T w9 VKz~ b$ ʼn6 )uyT+GNj2Pf*>eA&ff@V0R='ޓvfVtI Y1QNE5CQ錵J EZJS9$&a-O@|AQ)URQ?NoUE+s1OD&@$-uJVjPRҤnT93?ePX*}ŁCMMspqә_"d`Y:)G03PZs82'3gsMز-IyIDk=UYpI o?!ƌR/xIp*/I-Ķ=[N%\4@n#I-\m06]lU?i^bT/SݤR['_Zx;;U\XE8B78~J1 isrsIDA/lm45iIm'fE3T<3P# 6,ւӾ,CXٓ?s[4嫿j(cƒ`1蕘iR@z"7/7VMx^Û8"dY$=B2/3? Q`,7(J<a$RkѴ.@"B?A~x9[ow< PT $dCɩP"I. /U" ح9C5R}R8'WGaD[<LPrOѢ7c)*piImQ' ?q}.J9[!R1S%,f;p+w+}p hqhGg۹d8O:ͤ2,ƐDŽ/!b1<`Qk{o3e ἀ.~'{DuOx%޴<@y/:Fw54q>q09k"+{&EKsV!y\:xh2vh V~dHHa=)Z%UUy}#7D!R(Fhb.cQϩY|{R(˦H1tcL:W-Nc6ggћ؊btYUaeZ按a( q.Ce LfӉg[s5~}O(ѨSH܊m⃱mL/o{AЂ+1n "`9g64<h9݉DrwmnӴ2[Slu6fUB "nb\/pi}fї⽶ـBeGW 'Zx|~ܹ5}BF2G.Lm7li"Z"^;etjl Vnxe xDWA[]C/j2na#g7󉈄6+ѨiåGft+ujJŤaiZb¬cƧ`4UO) 57B|/s⎦cA)+M ٦,-HmMoڎ`u1| sӔU0s fS7J6۝UYo iYIuQߛP"톞x iTJҶ fG37TVڧ<51ILK(ᷥ>oB{ɓU a*9_ʡ2緔a|J xw^߯%2D,K) A]JӺLN0 `21E a29u\*7ef]J@L?%$K1VCV޿}AƁ+s)l]izIOp #MS4瑉N䉤 E$?M+$ p<ٽ*ۚ ě܌q9/,o$ E50]J E;p&5}&)b:ܑh#󳚅85Bݦbׄg.AGfL WV&vIN>e]4CLS\M=m>7^sk^qlaZ`drSB\TWڍuhE;7\V-ĈӀ G oH0N`ZWw.hU`ցwjŒ;W5;wJQxW ;C,e}e)k f k^2&"iLL0[r/t/Z؇yR֔ǯ_V@)Z$皸Iw{Ms(8NQ>D 7~Υ~diޗ)U\3?2s-4MXr[&n l>L4{7|V$Ԙ.Зާ##foY\-5'Xg|N;*δz).0XUK𞩑5Uzm:b#R- Mn.B@e"/ 7E(eTp)`./.3( fSCKz1#sl&r%"t%6P&3/r Ny+-b'Z-м2hěJe"V WmAO5.N{;BnV:>\G|=]gSdFci2|l7 iZTڟBxOcR>DZ2l<9y3/Ċ܂pNyҧ$ʴIRl g sT˯ ہ:ƨNcZtGb3l1V"%(X@OS`ǡڅk@4 *HlsVюN_Urz,p4Mo{W0q~a6Itٯ ]#^OᵡluҍV#G+@4,g&eZf޾}V8|9lص'bssu4==S{Ua Vrw:diM] >$NL ͫS JuQ*~\jl,g;Dz,;wABM 2q\īF*&ɪva#{}6TV/4s tRO[> jM)K"̥D0Ҿ$Ux\j_OMf+nj& w4n5Md<@eU̇L0qor?>ު}n[%N)@g|(\Rw~0Q!AoНP7R~zg>N~u~1gyjq^R}x>> gS-*o9Җ+h0^ИmӼ'my\v9 U`K$q\ M9=B<ԅěxAC-*';vt3"IKk)`qGjm`?p{DFEi~W֒&_?T3,Ƒܸrry>Q(Vo\ZjfPZWd*wI>F@Le : mo$n1/Cu=Ki,pG9Pg%<"UX6kwk 8565 hK`ڬԎ{x3^Snn )dK8SzX]Xs&-Za<s-Ew|b[pY5JͅsV$2 d2 -ؑN=Ra4ۅK<oI1-*#G ]t}x;q8_L}{Cͼk xnf~4TMBӡukMkAҋ>?ф,bz.L|CzI s:O7Lcyy}LE٣pis+RiɶypMIoǍ}:uYXe"L}L$[U;+O]r_wƄOru¼ 7m !Е`9۴5\+0K5-ya|]8aW',r|":d+1em F2l C'/)=JQ;iL:|D;\I/zuXײ8g{fͅQ*jޫx^ ǞqkEm3|K؞$DO3@<u4WNJAn&_Z$R󄃍ʌnոĬx@/aK2a'>l" at$nӆW+&dxwL1Po3]E"ls$+Xa:.q`uoO:k >Qz8[Kv-ׄtq]O{dśr^$MlT%ѭj:W7(Fl4FHMy+3SyG)/y0+ h!D2ҭtdx-ͫZdY<_hFSPP kO[7b52KF2 >_uZU9xUα]}f+rhg}LS^{to=,h?>j(WB]aZya2S r JN+])* PTj˳v9 + uUtL=![F/ag)@!pm@R??'.WFqfZ'|slK$Y%<<Xkj)T}}@$c?jYeT)y1ʧ 6z>ˣy+DH*YչDwUAt 3aS,T,-߳[-g~=, 󨸐49i$7ˮ1oZZcxZ!6-QR#*ѱNݗ? p[ pz9,RAYf#ɢM4q0r/U=.{bg9J6Nh݇[s.E4;'M0k@{.^>6o0idw^E>T} O<)ڏ5ET؂ޓ;.E/lRdE#"\C`RLa;xY1haOZ8{ȩG0 8&-KI/Hk2-5S}W5REsoⒺwGl80x#$E&霕Wd7&LE}?j.7tcGf*.Y'}.4o?ew\ l>!eHD[} .j"BS n=b)}3t|%> endobj 6 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /TT6 13 0 R /TT3 10 0 R /TT8 15 0 R /TT5 12 0 R /TT2 9 0 R >> >> endobj 16 0 obj << /Length 17 0 R /N 3 /Alternate /DeviceRGB /Filter /FlateDecode >> stream xwTSϽ7" %z ;HQIP&vDF)VdTG"cE b PQDE݌k 5ޚYg}׺PtX4X\XffGD=HƳ.d,P&s"7C$ E6<~&S2)212 "įl+ɘ&Y4Pޚ%ᣌ\%g|eTI(L0_&l2E9r9hxgIbטifSb1+MxL 0oE%YmhYh~S=zU&ϞAYl/$ZUm@O ޜl^ ' lsk.+7oʿ9V;?#I3eE妧KD d9i,UQ h A1vjpԁzN6p\W p G@ K0ށiABZyCAP8C@&*CP=#t] 4}a ٰ;GDxJ>,_“@FXDBX$!k"EHqaYbVabJ0՘cVL6f3bձX'?v 6-V``[a;p~\2n5׌ &x*sb|! ߏƿ' Zk! $l$T4QOt"y\b)AI&NI$R$)TIj"]&=&!:dGrY@^O$ _%?P(&OJEBN9J@y@yCR nXZOD}J}/G3ɭk{%Oחw_.'_!JQ@SVF=IEbbbb5Q%O@%!BӥyҸM:e0G7ӓ e%e[(R0`3R46i^)*n*|"fLUo՝mO0j&jajj.ϧwϝ_4갺zj=U45nɚ4ǴhZ ZZ^0Tf%9->ݫ=cXgN].[7A\SwBOK/X/_Q>QG[ `Aaac#*Z;8cq>[&IIMST`ϴ kh&45ǢYYF֠9<|y+ =X_,,S-,Y)YXmĚk]c}džjcΦ浭-v};]N"&1=xtv(}'{'IߝY) Σ -rqr.d._xpUەZM׍vm=+KGǔ ^WWbj>:>>>v}/avO8 FV> 2 u/_$\BCv< 5 ]s.,4&yUx~xw-bEDCĻHGKwFGEGME{EEKX,YFZ ={$vrK .3\rϮ_Yq*©L_wד+]eD]cIIIOAu_䩔)3ѩiB%a+]3='/40CiU@ёL(sYfLH$%Y jgGeQn~5f5wugv5k֮\۹Nw]m mHFˍenQQ`hBBQ-[lllfjۗ"^bO%ܒY}WwvwXbY^Ю]WVa[q`id2JjGէ{׿m>PkAma꺿g_DHGGu;776ƱqoC{P38!9 ҝˁ^r۽Ug9];}}_~imp㭎}]/}.{^=}^?z8hc' O*?f`ϳgC/Oϩ+FFGGόzˌㅿ)ѫ~wgbk?Jި9mdwi獵ޫ?cǑOO?w| x&mf endstream endobj 17 0 obj 2612 endobj 7 0 obj [ /ICCBased 16 0 R ] endobj 19 0 obj << /Length 20 0 R /Filter /FlateDecode >> stream x͝Y$qSKrfu!–h)іA!?"2%,cȸ#n_=t?_]}۟mﮇn^sñ߮@?3x_0ȳ|aV/?}O .}闯㾣?4Qc\1V-A4p:]/Sp!EP޻F?_ޡsԃ iõ)2m!d`v߰tvW,ܺErr//` WX±;ٜn';T a$_H6p :C{a'*s=^N2Űslw2l͵\7rnh՚8uҝn$)r|n N  >(_L0{s{\/a G0q]Awg3ժ!4 rq*FGƊpHBa~ C#+,`"C<\WeAAYijE}+9,_QKn8^wXJA@nѿ 7]Ƕr׻\\0թ6ptAI aXzl%'#2CuNMN)ݪ'GXο&ҜOS/ ٢Ӣ!=)5R#*BJp;K|x*s(6E߻`?)<#L#(ro,oxyhH%@+( JK v7s% ]4}fO] >?J~. ]q}*! M !-hq!A7jhB溃}!i{M#`Go.iNK]zQ8DOԖM)L\2 _̏@ q^*.>pkaùq%+oq%I0wi'U`5Ӧqٜp:g123:H-5mOO05J|S)s`)]d~/MZt LݬQ봎v5_c.EX^oxx'oPu&y#aCj}{&= M4H‰"cጤcUdKCx~,#LŁp>uM 𫰾tƁ̬F#4{J=>Uy\G;bWki /Qx޽6޿QLqqO\NFn ͥ=y Ne;?0iJ̊H3hRiWK#YZI2BΈAX<wƠnJVFEq}0<)FJg@^pvX@Xw^t0,*P7g !ÔF<O`Ă lN \/*)^j0WsS|9\ș"¼0K4}F9y2*=2gA:3o[!a4.{mfPaQ( ^,AL @PKUPg39zmjat7Ďqt;?T,™vT5cS2(fط '&מ033gҠb 1+wK2Y>Y yPI]blUf.)EկRיDJ97|Z 86SS3-u=6=)9BȊ™$v)0zas:(^FZ83Y'6PGOhƃ#oVFP M["UtkFXÒѩy2!Yޓy>&~qi! L13 m\FQYTP|GF$H Tɒ?B&@ܸlF@gj'QVHB:DPN?w_1JI00PBͯRtՆj<&m%jSe{n^l`,ǶOuWUFW'^1Qeu|NiK^[|PE/ȃM 4\s._S'"VDR4ۢJIqyV8X~Ԋ̚.Khb$, Rq L:ysu~986zw]nTQ#lA e^߳UGA]ڶk#I@S"MxQq&г/ԝ%DVH3oIՙrgn 3)U,H\T f;n򭸧j4`oײLFѕ+ B#;EcE XFն5U\WGy,OICRL,„u6VO&UAVe֡Kʦ#ba7fn)7xnyiu01^"&s 6aFK!RKI۰2S7E A D5"IUyr騽G/3z;f`{Ïi9͎uTUӠvfwγ\i\cue'"e{V"e+iekJO.i;iS1b5X,uf~R _h"[wӅAm xbڙmNLFx}Bq 񲿘$so 87Lɬ7BM%i8+2 )d$δ5#'DY߈<ϣ"j@I.)CKw(x ADo{*7wG팿%d&@ jcWpPyQPQ4 H/U(WK<8Ȋy |# B1Q(q5QZBQi%RˢE/P%fwg{Fdz _5zn]VƫnF4w[gw^ 4mF_SN +Ǣ \=$ @OV]87P$Xas||{zō4|tJ[#[ۖ1'ȭeP_aZQ Lq5bIXf/I'b̴χ)s_J S}f\rШ4}ꀮ.\<ųɟ'pףI=|f+xpjɖ9KS':=:8udvb}5!+Yx+c 8i]ӊ3> :+ "tGVY5iʪ=)5R4JQF8EE+.n[#ژݣDŽnk[;R& \M`[=^9J(a"6 HN43N0kc^X6q޷_\,*s;<CfL]RW!WKJTx#ڬ3;(qWEvLI1IoCK6^NclzV Q[ +$hζ5<=!,: I L*;9f%_E8 =uqUcC OJ(\A>4' T`?0HY-4m^-y@jKzu_p(ؗd[XѪ5^ѱ@\$i|fd\ jpYgRd_3,4ޠ~V쩬/[NB ^,(0 dd܆{eˏi R'ճY,Im[vXȤX{yLsbyd?2~`Z a $~shi(CuR&]_Q}6y^XLnL5dBD TDhXTg]v/gy}GKxN@l,OBH[qZgbm]:NE@j];SFS$wF1?/6gAE<:"G"n^! &߼zU0daiXj $,7uhhG%0v[#  ܦq'88770-MD;Mx'shdz dgiVx<(cYNsciEVnĠcj^=/G3Y"uinj*ḤKw~)iڤYIF1NÅ`HGYg:rϖ 8FJřxLqq`k75'ծeٽ;%8Ud%{j1Pd۲X0㭮c6m8Rt+bQ|]01 H&fgsC텕š+|%(,=ͭv F/ޛU"E"3`ί8^FQ \0P\7mM59*=3P=d6y }'BJzZPw" Dx?: >xU2#7yXk6rNK3gp 3P`B@!•˳8]eZ#\.(~}v kcȐGl4B}HpR7W8l5Jx7e|v\RU W}W<g oM3oO0%Kb*6>dSIZ|n\YM݅@q''ɓj&`$ZFQ"hܜpm!JDAYU>4Ƒ v_>^vPuvRv7၅d$rÄ'$&%Ģ# )E 'k6'GxwFN"~kX΅:kt;^@) kOxsa/ J@ "~#ODFyBLz< r`HiSqX!Dxؘ`RxQjԇ-3sưXR"ĸק͇dDq)+6O`r;C<xqz8ɱvϚp5ݭpBoNa[;Ϗ7S!׶RD<:^-nI1nŃZWp4\Dr \th߾uJh,mUN&(`Ǒh#J,+ъ_=JaJyrECz‚E 5zA?IQvf!̍I9ݢ<Fw`MB)e$.VhZ^yܽ27GB*;cjd(0fujkg/='zdl} fm=ӈ4ȾQ/AvW3oO?h}m4i /2qW!f yEl^O=u?@Q9ڼPxo> roB?c4؟bS1Va5賾Mya>\_EMRlov1?t ZIJ9;sS\坪QFIeS59Gj[<`=EF?)VZ"GX|ɖ}N7&K0=dHЅ.G?$S ǖt_H|N)O|,6E`)S$vώ)'Z\8LKͣ3C2H\-. 삋盇ǚ G<59*fwuoN!䔗c{̬I8` LT jL[:>A}9q^\ROZ};E%\Wܓ~Cl٪(xM;>c_ g۝tEOq~8.1DH;xkɚP5#fq/9*Yjvr`1u1Yi. ƻR2*77k:GJFJzrGrAʼQNx]%p˚4p^cy+ڵQHĝ'0AɌ:e+Nrtbo"LSAdhAgNZMӾ3.2>.Ȭ5g"s4{47^δ:>!&A:e!`uETFjZR` x0ZlWG UHԐ{ӑnS ؁t١:_͑oJBQNe";C0JBe]8c^!SCI}/ z"?:;sTvWS[u=Ƶ,@Nߘ3/֊'CWzVr}6u;"u-,K|xh,0:Er}t@*^`O@ssha0 F=V2.٬T7F{dO!WTEuzMiGIE4b_xc&lcr$cxr}"=NI/Y.LpvEVP%Βe4>>o$uc*yi ') Zx\ͷ>`{~tW4}HbPp0ԬoHY? Kz5&¤jT{%ҳ %~`2cѠ9 k,dnmgnju / /l/9xFbxn|l \_|IY4j um\D#RjQl%@ i niipo'ZZtmppr C3ΧM0_8, _Ɠ B_T=\"n#.8DKP#;EMTRO N|:/:vp1XSQM5+ؚpɇ8*+ˣ8 6xz*.qY }3m`&4KSR#Ebef.IΥyșfv7ot§P|U6ý|33Uer2 _YXI Cf:& El7v 뫘|gkYǤ=ȥ4j> ?m7P Dy\>m ILc=Nc . [KDr!k臧 ]<£wOuǵj*EL]Z?HAK"d/c=PX?ݒi(^B|8'( BEeW1IYliuoT`P+^ڼ}\IZݩ+Od-9 lBw.c<[E=N]/F`ЙQr90A&n#b#X8\]1/!ETzIPg>B^%B(e}Xum9RB(%E &#OP%o O䠙WuSb"Gkm䥘PAJ,@5,}S @¸"ݏ%Hg%6XimѶE-{0 (!TʐY7/ g􎒗LryY˂0\̲tœQE:"ɻ͗w0Mg|ˤ= qƛeD {37&&j"z#ӫm`];PLH*}2LrTP*@Cl+@Z~N$[r8Dzzkw-y uWri3YJhARF޷2C?a37kPGʽxэ:?;r߄[VÉN|Z^ =w/1AxUexh /Fy endstream endobj 20 0 obj 11163 endobj 18 0 obj << /Type /Page /Parent 3 0 R /Resources 21 0 R /Contents 19 0 R /MediaBox [0 0 595.2756 841.8898] >> endobj 21 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /TT6 13 0 R /TT3 10 0 R /TT5 12 0 R /TT2 9 0 R >> >> endobj 3 0 obj << /Type /Pages /MediaBox [0 0 595.2756 841.8898] /Count 2 /Kids [ 2 0 R 18 0 R ] >> endobj 22 0 obj << /Type /Catalog /Pages 3 0 R >> endobj 9 0 obj << /Type /Font /Subtype /TrueType /BaseFont /LVIZXS+Calibri-Bold /FontDescriptor 23 0 R /ToUnicode 24 0 R /FirstChar 33 /LastChar 89 /Widths [ 423 538 494 537 503 355 226 246 418 537 591 399 507 276 507 507 331 537 246 474 507 507 267 316 258 537 473 474 347 537 813 745 537 473 637 606 507 547 631 495 488 705 906 267 659 686 653 507 537 898 630 312 312 874 507 563 459 ] >> endobj 24 0 obj << /Length 25 0 R /Filter /FlateDecode >> stream x]ˎ@E|E/'40c !EE8H1 [v&,.Uu ~VL!n鲴586uЮwgS='O\Y&mr^{MMg_..xt?_p\{}/)>;ևHֿ9::"c{kx6.xI}UVIwKoKRO~t\RϥymOU)y%R,ea67XS*EdwX 5alElEV#ak*0 xfH*hCdXUfp)JT`XÍWV[eXV=kV,`œVaX5Xx V`fJXM#UbdD o of/LaXmdaBbd&V5XmJJrsV>0,`X%rʍ+X3U9U- BLJ2 +jNK`2u~ٻsWXJi4mbOM1]At#_o/©tЇ1iV[?f2 endstream endobj 25 0 obj 584 endobj 23 0 obj << /Type /FontDescriptor /FontName /LVIZXS+Calibri-Bold /Flags 4 /FontBBox [-519 -306 1240 971] /ItalicAngle 0 /Ascent 952 /Descent -269 /CapHeight 632 /StemV 0 /XHeight 469 /AvgWidth 536 /MaxWidth 1328 /FontFile2 26 0 R >> endobj 26 0 obj << /Length 27 0 R /Length1 27544 /Filter /FlateDecode >> stream xy`[ŵ?>sk]Xmْ,yc_'^ĉ@!-),BBJH˃pQG#F8pB a*/L"K% ]T^6·o"O0bb.AC"h*%Ac A A8@DS!|pFX|,#&~Fx]?!@xo#&yi{7R9wMY!:k)K ~U_,mJ2/~U%?/"<~p 9g`?F6~4Cˎ OマV+{ GG4V0V}A|~|lawAo R&o"<2@y.})<{S~{RfXn,܅Eļc;0v; kxí̽PxM7"܀M*K^pmƒ \+eL#WLLFH.O\ybK>(z)߇$'T)!$!]Am߂M@}po@w)WopA-np!Wr_Z!\I}Ɲ$pqӋRFT.6 lnD؀a9kFXpB=B]J*EAFBD@(GH S@4-C!":-&LIS5 A@A42a! ?Bx[@5W! +^K`/ 1G < N`!NMmH"Є 4"4 .LF<ϥ}9('ؖز>^ysz t#t!t"t FhGhChE(@b=nB>B`Ga7 | $O` 7!G>p}ހ;:p < '~ G~?ppA M w \:eUB8ɲareK ,FEXa0$u)B B!D o>)G(H"L0 M Kq/Bx@x 2>⾔FܗtxΡ:v ]wǐjGݎj{wCvAnl7m:wj+U۱ehp[>› nYe󖛶}[n9OO=)nk߹- 9XwJ۾cЦ$7runm\l#8R6 Y銍G~clбnhuC֭[wѺ=X'hu}p s:f-%qSDIn*+&.7)L5@#g{{-, --;:(`hC#3HP޾yCs!'=4goPWcsoPoij+ݰwt璨s_/_^w9s:_gcg ^>wg.ް yk 1mty%wuS:>ݧ}B3-?ODN,m]qkQ _4ji4TDjoTS/Q{TPUN)9A *ܔz(|5ӔP)MiTw2wa^ ooARvU -X8kGSe0i[w&#ɝp!) -y$xӖM67 My @"P$W>dBiЦ-K@PY[EXRdֶ?{l[yo qbr1:KGOȋ_ ;6y| TN4͸_Th'X :9䷧vFʍJRrN|>mOe*sBmS'FS?UܕZ|{&M9FG|\H."+ȕ*Ep}5&גr#Ln!.'w[\sy||^r|| C i n H}ʱR!}/I9@0ɓ0y##1?||8R^?Mgɏȏad_ = ߧ~>c1o 9 L̈ͼf9dxL(䩫S,J@ŐLˡ jZUDt 9:ɥFjyi6NEK Ϯ4whL,Li2%x@۠?@B$oԙ"B4g2sP:Y[Jh<+}VM_->JIMs?ޯ_uf+'XG1Uz7}ۘ~JK|lq`8MFv}>!0\ZTkWo@zhj!vlgH~IRITMvs d җݲIkJ.F/D-!H\ a2[M.GH`DN8Y:pr!Kzr캛WF:g&/?D)&tʉH!) :"%Sc2%$F!R(w8"UpT-SKЇ^?EHX'Zs)++Q-P}Aќ^ۥя o.̝0e7g[6Yևm!ۨ~ۓ{u~v5bwy{wZ M+=G5% PK=ښS*g)%lUȮ kA8Wc5c4ꉗN'1DO?QA &-'dW V&<+rbZyeU8JbJy|ۄ(p6n&{|$GKsf֮*mM 8T{}3$>ƽY3mӨ8"B_7[HN#Oc*;'nsJ5 u@IR=H^g!ЯSoT_zMŮjϦ!UJ_ 3|@ .AE4WC~ϥX&[cccڐ0evI8zNĩ=xa;OQj;x/b1X+ᑀł| ^UexdZf.KԸԒ3&M~E8Rnu2!Q2ȞuKf)kdBkTIdbY*8eɁH pR ban)fPږAsX?i:@8 TDwF(~ނ`2'-p>Wɓ;zKKtC{%7o+gߴ=5qwwD$Է{ݵ7| d &h#nt >9lQZDSoО`Pf$)SM_Hib)A60@¬ܚhT"^swT  | f1h3)&ePhRR+:vSLn/3aZLJ{S5yTe*'oTBLJwN-C'؁< h"!6:c*w@͐D(H:e4]((jL"Mi$%DgD 6NϜ ' dc D"f ^efXE9?V2CJ( BS;v⯝][w J>51rCzQin;su=`,mlu%mvߴ+}{cDKJ+V6MLJy&ȷ?}Vo0/-H@Eϲr.:#G Ϡ* PΧNs7~HME靷s̯Αq\T\NgS/n ]5{YBhSjt2~%K,E=AbMb)3JJ̊49Uzu~"'4̓1Y&REҠrW5۾0PΗoifk譌縊jfEX/uJYQefsej.&>Ze;BKA D;qs@ veճo ~[]Vnp23 @_M$ S冏2e(…o`0mPX$+(s[8+ Dc5Ih]T O Iohl},%'*u&xZ\iIKs,rp8SFt:S+O"qU_ȝ rs #j=Rd4pHi/Ep愡& KeM4S:4FY,/⭉ S2CV;D>x^HTΈq( wˋ,`.yu^_+Ya5h厺L,TdTJF5d(<0[6nRJz%hS؁ud7Rқ[J< SsJMiV+ME:TJq(@ӬQB[ڙ澞"UDIK>K4-MNiрE4Vb? LB ȿZhD$呬n!Nfk\e-* D؜XUK{bm[D6R{TEUa!l[jlg[ءz ƲplݣeZ}B:4Ø 讞:ɿ # !Y ͒7쏃B~Z٠щclRYPTlbdf^=ViN>DK_ .=Ev'Qm/wU*{15L%y09[pÌήV6(m?k6S$_K?ulZPgjXPۭ]:G.gƄgCW عWe;dYOO)b{v+pGP &ͨݹڣ6W0; / QTX+/)ɋR{}eq a̡JOrpughSΘN9IvΫk8g߳2!pIlvѓѦ(-nNk?c7o{mYQv85yKlpŢ'`,9+{<@lG0;  }74?88 |̈Qfq9=øamz 5IR&7 ΐC}/SfM{yq,_hB:־P{V!39rC.ܯWE'"Y!B*Ъ^곇`9y#"i%Ct19_Jt=Żh?<@]6@| ;)1͜l?(4? Vh.B>'xb(T$All|zv}s͜րQ`|S"ڼAp"MknDTqbi3Q҂,q=j|+f1圭}lƙQ7G+þ⪥Wx`^og:;;X0u4pjLX,b坻m lj7o #:/Z7-{PRQ2IG=\ng+_Fǥ[Qf3\V _Z$/0f:%zPoA#7V3AT`ğr83v$0/<_- Yls᎞젧M]-)3~˘^3uEKڰO0~W?$ۗ`-|Itp6r`g08T7kh~vXw3S؜9u5%d& :zRR * n͔vljf3EGaK%-bslV^۰ToCiqxwpՓ2vM] ՝sN"0ŲYQD#Ze9ONg_H̜oQy5X Nk"PgY4="ت캱j-n(lLӓ3~n`~1 ;hᆐ]=ӳx'gxPldDw`wVq2SlҮbs ^rߡ3xd |Y0;65>5aⶀo%,g BT_ʹfGgqEqO.*_gYE+R[kVrKgo%а{f V[fVM1I0T %HV\6X5jqTVG4柭c + h &@ff+_~XDe/l/6OfF- fѼс8%$Xh߬7 &-VA98tJ25d纆9~ei:;%3~20%&&G젔f+z¡2<4})kq晦h=0SVq9kpIW(3o$2{VNZ1P^*PjTV D*,PĶQ~k@ԡH_ڬC&V=HNmPN鈾m,ύ%Zקx}SgSS:jS,V[9 (愈;)mLzKnl)F>t2k[Fd(TUVKn],> b(0><5h q=ȉkeԩVy\hɹߞlWVfed#Ag21m*{`*G5H.8NEUC'J|>KXGUS; YkZQFۤr’v 7}^6,Zqz 7d8:W+r$/ۨ7QSVRJū +U b(ɧ/Z pvi/uD&,s *aPhoͰ#(puBpwڕNedn>c.3@cAš^qZq rE<ݢ?a+kdFgH63w)%0 ge3|f*)6 !~}VmihgYȱG>Zg͗rTF ,ц򵛛.X@ӝZ{V7/f V낹=#tyn_ԥ^6ux_PQع~墡5T^[I+ OlP7h@ θDAah-{siY'bw w竌No8 ܤ}Yd=e.K0mG89!ېm9`[y].p W_o1ϾoTo#$:3'[8;ʘ)NO;(Qƒ+i~ 1ųE4⎍_\eb[k :h*m_vIYo} Ύ@gQ|7uV& rPet6P u߻a7v.JspSEKUr9ջpzH 7yyGa {jP#U b1)#!(|E:=UIa!|: -vd"m;B Y*3ҀZk%)pL"<i';wN{?M~V茵p >P+K cSd:@@ksYaۅ12{|"d.?uYzm\esV6U V^ɅP% fy,H΄8԰_]p՞Qzvw!=<,}鈶v$䖐;?`qgua;.ע7msVVkE.Af*SYÏiLCUT!G4wDBJs|_@~yA 2 /  y`yb"|ɇOkx &<|;ʐ"4<Ӑ5K$6"r3Q5),j}Cٗ Y-f^ s&+d& Z [ i:KϠog=oѐ',0r?m]^2<s4 o473sTZI ֪4>T/k厊 "IP 3TItk&(^zvFU8gJZ\E3MSI͌zJELtd_gC32|#Ϟ_e=[GE-c-K.aegb  0&}mCjڃ<:]P燸xO+'(ʖ&SSYgtm'e c|d9; [^tug/ endstream endobj 27 0 obj 15817 endobj 13 0 obj << /Type /Font /Subtype /TrueType /BaseFont /KYFRXK+Arial-BoldMT /FontDescriptor 28 0 R /Encoding /MacRomanEncoding /FirstChar 32 /LastChar 124 /Widths [ 278 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 556 556 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 280 ] >> endobj 28 0 obj << /Type /FontDescriptor /FontName /KYFRXK+Arial-BoldMT /Flags 32 /FontBBox [-628 -376 2000 1018] /ItalicAngle 0 /Ascent 905 /Descent -212 /CapHeight 716 /StemV 145 /Leading 33 /XHeight 519 /StemH 121 /AvgWidth 479 /MaxWidth 2000 /FontFile2 29 0 R >> endobj 29 0 obj << /Length 30 0 R /Length1 9380 /Filter /FlateDecode >> stream xZyxTE?Urot҉$a K$)t̀Q(*DK#e@6bEg(72ɛq{ST9uj[ߴ2\F\]ӊ cMDw~p-ߏe-Kcy: >q by6`7z<KD>ohOmHFX)SЈ!} JyhZ(AFJzEcizR_B9'/|vݯ,J̙h_*eygΜ="gt㤾@i3EgΜYY5[OS?Qy`!aGbOMF)dyơx5O B}ox[&U;56`cAYph/eݪ4OWvc(H;ЗP %OITPH]>1V HQ]f4?^yփcJf4X%cx89GTfP{82†p|R f8&27 Vrj aWfy#p31Uܤ)G/_,DNvw^ԫ4~%klʑjUrFɔ*J"SqX~H1d7 6YGҡZ%c/{K,'(Mf¢G(PnА?ESr)u*y4^).e#7W;<$Q?ԚNL*)Pk*cƻI*Cr):1jNT'#ZjSPh+d1[X íJ! $Y&2 IUG4 "ٕpfYGAKbH(L2~ y15b5/͏!/C71E|Gch+$ Gwy/UG~)x/8VZG`}!:_|c3$R3r;/>~<ӫ 4|9?,8Ia+I`ZYv(k g,o6*wZ!nixاPS*nUFú˻xYaxcR)-)ݩDw~V7# c PR+L9ISn)I$jOKoY 6ցփ:@wt-VmeI;U5Mڀh 6h 6 $BXnM"B@! B@$B"$@4DD4D#D#aaaaH!DQ D)Q D)@JD)@J.:::D@@ Dp%. \@$b  b@"bQH? H? KH? u]BL>`Ob>f}kaaaJ )&&Dt D7 D7@tKD7@tKD]@t%]@tDDtDDN6FtRut\۩GhRk+9ZvҜ*fVR:=PO0FɎY}>ǀ'gٷُmvsxG1Z~DVIg'37RNcEhW(b8~SNSGXHNӎ*Ӱ3N"p1|Ȟv6*@*EQCM/tPed515iD3J" ;;dEY`[5;@⭈=E|@31Bnk /*|]'y4bE~͇lK B<7% fmiS=& m'gv* Cʌa)!$Qy"l.y~ }> qS紝{-hyhkiD(~#MX=iZVVjZ9Z営P;,ܤ F48X[4C+&E|i0\rXDc֋"[ڼK1]Վcc#ϑLuIg;U'w3=0|✐n*2]\HgNN!3Mi sYٷs?{isW33I#:Ǭ5ƫ=D7E5",*6昩5cY )3cuUfU괔ߒdawg>07` B; <'$^'j[mj'fsԨP09IjO(zA/>R(T&zu=:xKf =^$r, XKȆ4 *%H'Ҙ4fRpAe͈ͤ>>:Z},\ju27^iv,uDn*%oi5W[kuZLQ.suMZ(sU%`+P-*EcaJa/-*in'Uk0xPN~:6MLީrv0_LpW QU/*3Q䡪uSsCU.@7f7sT1 lxdu&-vI+Ϸ(8 ~ڿYjv$|+̢ ဩPme/SYW'X0'$!F<.NGް0_c*AT bUa!\7RJ tyJ*=%v؉Bm)Tݷ|0 l%mrn!|AJ&u^"^ ,(yoaBDE-#f}ȉ'&H,,A(V,'DrAWI)W.U8cQy\=!c' Ǣ6fgh}ɲXM}teT]K1#Sd Y4n5I{5mmF?,Li9}&|=]E*+>}U.UUџD_ym}^*CDfb^_ҿz+,̋ͳ:Ny.Co*J&Yt4@tþy_ʏ)[oU v1jt~M1fC]Va?a'SSJ=g ~ gl.Sn%A3Ėmd$,e:AVU_Wom1xvC }>Ng9݁Y[h]Y?-`WJ=̞a7%8ŧZX]oB6ð~LtOo3ŦR&*7*S(_UU/SY5)]akmk{v^i_josEm a]'fZDI% bq^CDSB6g{2g l[Zv7{maٳ|.o.~7wAK6.TN[)eterrЎ=1-CFm:R]UWw7lߵ]vQ[ YY;gscw;&:8~"xc_xxH)ԑ0s*AU %I÷/߱Rw)7^Q?j+|B}*~3=̮e+i/;ŦY[O\vUFsű4C]JW_· l2n?|RMToG1O{gt٢')؍Z܋Į׌u1 ;ucq^a fT5vOVDK°h72+}̒#ȋBx%|FZ˳۱=5OD]A +fgX7VDJ{U.یuxŷv,\J}1dVpʶeco{ Ѿ`F9=XBosblI=@rjX6a͎>^=ԓhe' ?lzhq^D+i'FNFRE7;M@1hQZ}WD;**ƾP棭/{&R#Yk_#El)p!$\~Oq*ė+GEy7~rrv#HF?p6& ΁o1E5ٛҋykneu+PW;j Q5ʩS&O0~\yYJ}EcFz=Q62/7';+sDWrRb°8nSΨ]MoT+,yw ZV2u_cׂK4 h~FLӸ\z%Uun|֭G؂8MպyJ3%Duju:~κPmI1_i/)aA2Gz؈iL |DݔNDtv֙Yn@ьkYj6KMVĽ$*T#͘!vkfވ| KK[Lmԙ)>ح5G=y1|ksκP[7f-hXuq2`daU'tnƹ: ah;MsKmF(Nl ͪw6':2KkJ{\)$% _ZX\,>}ԗ$F&uu.d@K1"͸Pk(Gizg>uҒJ1O.L5M,*SQ1d~BInsnd"ԈضEocv iqEXxH`c5牚5!7f~qoAM7ٕVl2X}\wN7z]ghh64]Ջ"n̴Q&$ZLʅ . H0Uv4VI) ^oBW`|~ВgL(=-P] œ39%%t* MqxCӂK걗uvֻPgK$ڱحܝ|V](69fAtei˩61&_{qŤoj XPuu^[,J.r0-U9QUe/,)ђ^0, ?n&e%mû mP( 7X,/7:DHkpwx(TA#«4 ;AFYNm+pȤLs3]V8WIU]gxƐJYF_Hn0߈ߊ?)\1E6fw%d+?FTMUSUլlޑ2}\9CU6\VJ)vDŽ?~KOh/(<MyX\ޜvڢ>Gݙg4^}ZYU,,*V2|uS9O>'wO3\yz^Y꼻srH>>\9\_T ZJf,!Y\0 <@y4[ 0֒o$=S3]XY9unM[+^q"O2G endstream endobj 30 0 obj 6178 endobj 10 0 obj << /Type /Font /Subtype /TrueType /BaseFont /PQCOHM+ArialMT /FontDescriptor 31 0 R /Encoding /MacRomanEncoding /FirstChar 32 /LastChar 169 /Widths [ 278 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 778 0 0 0 0 556 0 0 0 667 0 0 667 0 0 0 0 0 0 0 0 0 0 0 0 0 556 0 500 556 556 0 556 0 222 0 0 0 0 556 556 0 0 333 0 278 556 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 737 ] >> endobj 31 0 obj << /Type /FontDescriptor /FontName /PQCOHM+ArialMT /Flags 32 /FontBBox [-665 -325 2000 1006] /ItalicAngle 0 /Ascent 905 /Descent -212 /CapHeight 716 /StemV 95 /Leading 33 /XHeight 519 /StemH 84 /AvgWidth 441 /MaxWidth 2000 /FontFile2 32 0 R >> endobj 32 0 obj << /Length 33 0 R /Length1 13808 /Filter /FlateDecode >> stream xz{|T̜Mdwl0 ! F &H$"w #-^Vh+(VQk\ ڒ j꭭B[6B})U4;B~w>3<33<3uk׷3 Qji# ynXH-nBu׵-Y,D%+o.YDGYڲ8Y&"d!/]|Jԕ{g]rHoYՊW1$DoC>mm?m sd=O2aD!d.~,g ʼ^cO_`+ޫYxCK7h& O' 9-J%^sr:iTO?CNKRFD'ziRoij~!LHUnt jn Vʐ|R-t&)@ ?| nVqjF!iB}z,Ŗdq~(\ݘ̧JUW$&$%ɗMNy){ xRʎOHi$`*( G vdB%&Q *,NA 6a=V{+~!d#;eppp e'p{{bc|@9``7@>Fƈ>B1>`$;%  gA{hT3 zA"He}O=O.z.Z~3̀6'{q o'c*`&@ώu>v;:_db$d _C+ݙ~RaB=3 ry>5=a?Qag A?|@9```@~^y6gs?Iu_VBpQ@BT0<-O/Pb*;ctjt/9ِ Yڐ ZѝS #.GZK0,AT3SJ;|ԖIgd&2Y q:vZ?-Pa`$qH };"~d:ZJ4|=(/F(u wo hMV\ێjώO| j;y__*AbI4FC%}t۩kЍtA_Rz^e=}j_^V,~Z(4%x R¨+Ij=]d/I 6Wuܱ9 g2Id4ƿ7'0~IOWWӿ󬱡_DҤ>:W;1H@{8 | dU]a$qHiw.y=8;ugO,C|G=EK䏰l]$%{#xxXN籜Eɱظh~uk9T huRwca еWRyᢥ4iKu\mum$`alDFr#"9H4I:yQOSg<^oGsySI?%<"? V,k@NfրMObg[ཚN'u'>'f(ŝ_ΒrL"/ˏʟʟjk|5jWihGT3I7S7Kפۡ;{W / i mleaMډl"NNQei! 3dP~c|Logfҍ19&A>SP6RdOI3?'iHPn ϳjBO/@<ZT"AVߒA6}X^B%6)yV^Mer'KOct4L%M*6IkOߑl$~e?ɧ5RXIl"7k"ѹ$"wM*7«̇O;>?P!M ͹ zQ0'dh2b":Gh^'5o &O]%d4mx> A÷6l%۾JSÎjjY'v^:v_p33IC݉ݣawXOb_)κ5R{J(iO TV9Paigus}Le]ȴRJn';5sɣr&@-`2}lCPŞ!\O+?"-D #AK#\u2`fn_Dl粋fI|L9yrK{V~֘go0+ K9'a $h#HpF Hߪ \j5MQ=TokΦSC Ib(D zJL(fGI @v3X_d|k^Y gT/ȦрN0G]MWĚ NSsS4e('ce 8JǎYC^ǗIے$N[J̩)JYHL M#4$,!;})eJم}{&DhqĮCYaV\N2juڴT\X0.]V>û{{wSztp՛_18~+e߽$Vc:tbm{?%}i6v1;8АU2Y}@r{JՓY!pp9'.(\@R &"% ұca8LʂA;qEh((˹~mxG{vA~,ѻ7ο# fNħ_acXHZ$Kd9],*+tWeT5sFGmO G$)Hyg{-I$$$j geYXʎ1R?/07TYiZnYa.}- p{dinݣl߲Ӷ3-K}::uxC4F 8䂱Q0b}wy#Nh>fMцL$2;ldM05Wi`#aŤ 22b`phZMwod;8) Й}TKh\M͛MW$%kz gT .S1Ѩ/7QRJqG#iI282;ɓ3|Dvh1iMkRJ2@RhqѸqN8h"-]N츴ZZrg̜?qxeKn_o=tw l懯.{^U$jm]dM(,\1j7?ھs|re3'ONV Cnœc-K+NȽ"Ҕܲ,yLeKGfELMؓό:¨#GG(}frY۹80SPM=/]~w,/T.ͻB7WN,vy5זc"+p ^:քUۺz*Y|Iv䌚(z{2fCjI>>. diQZVHF7ĩa*Nb9#G7 -*}՚ DDG5XX?52:TKfhL@)SJK]o;+?|H{Tr-ZHR70jV>\X'cǟwݱ5P՘Uz;xF >XImӅ4 Y)d(IPmh3-- E% 8-_)SW|VoxsF}۞\Y/\ _jǣw~<ʧ?CLF{.Dp[E֍6j3QgmndϤsdi\:!JT8V#BoJҶ4(c})9)s\)ͮG#Ö'' :l/чzY^/l)_JUnsTBrUF𥅏_Wc:+g9NnȊ)NGڑ/fpZ1v4_i:d_g؋m]Z|S?`k?Gm{;K7~zfP)۴1yp/T9;N2hɖRefu٩s|KbMaQjo潔?I$d'N?)syj=m–˜XU[jR]mkYbD=cUhd5)6>؉1 .$bE嘝*vl~Unpv.vָڵ\nQ%/:ώ}#|HwTw\|f$]P9aɺL"hZbڄҥgLF#9mpH`#2KPI>ge|{%kZa CFI"J[l|owhޙ3~֛MYz9||>gX92agi9O|i^j4M jCI6u62|WrOkqnޔvu+d\36gO٣0E>+.w` ,ܥZł  d@5d-\OB%SùEg*pLa b0S%TlIYsqBآ ^Sw'b{ƒPcמ71{.E<Ō]{0χOԏ×o?3vo^tly=MR?dV.n\$fo,/LKW&&5IT5dg䙢yRӸ+M5su sY/ eO Mʾ*=yqq9y5`uN].gnQpQNs^Gٟʶڴ>;ʗ^O 1ur >A-|6cultF *.pyY}0A0A>ڹ }ݤ r.Mȷ|\l4BC㶄Mm3`oޖfqc ;t鱼uAn#Kh*fC'ϖa1CMHN-.VGf(&NU\h^4"ünr݆mn+!}ϋ<សeדn-7k̊,W}w?Co~0mؖ[굆q\g31 1 m C`d&h5Z٨E4D-|BȧeF| %Y QQlʩ\q@]j2@".kE۵Aa*\mXJqaWѣߤo>໰83tI7)I)}ʟOSNKgShZfݬЇc[SNOZha7XVVz(RLU;W)KpAT>>p}CG`F|V+I-W0QLxݧݬͽwe Ӝ*vfv%u"Kd.\QƸ@u@uN̎!tr }LDSqҁyG)N`uFIDzR? YC0w8%H%87wWM+0ζ\7^[u&h!tŁ4=9.*:cyv~QDL/R&8&8JY힯o49ݫ4 UUiA\#iטWJVJuvx԰WP2?4BfէEA bQSt,c{ӯpkplrCDO\S̰WI96×L`O٦b-+,8W s4s 5 2m[p(%4܂R_:o]LJnټnaCo;4Z|_P͘W0v:1?*2 Er%pNz`I,DSO #FèTH),.L`@U]}(W9h.LH[SXVKDdcXXH *_6HZRBKswC׶Z7Z"4w:\ϾIʯvM͔:)k(jv4'FhOztxYH%T 1QD0jSH8"f ͺ6]>L7{tq݀N B[ g/WPD)~%i$tSHMMu]w 9 ;(' ׸/".g2!,f%PȞʣx*[2;{>%6*ʤ٢n=w}oZN7\E,,V E֧84i!&R茸]ܙzv rpe?qw /wv WI!m}E\5(;fik= 1G iNdG i-MAxf0=j8?]04˽,{djYV?jpòK,z'4 HA%8.޵{g(^Yީfz;e֜X)THg8մ_hx΂|Rgg]燞3+|MOy>2z xPUf]>7v1)qܭ7)<#^w=mrBS{nq4~67mPAPvܙ&2,’H.DkE2Ψ1z"~RAbj_ uFx?I(A!!I3īA" _/6mrH dΖ/|pe8_Ž"N󧁗lٟ寿:ȑ]|.wyK|z[3hq3hy4~*]zeÿ~b{O=Ec{{O~EWe\9]~Jd r&\ÿk2T Ě-@QmK˿͜U9cXe-+7 endstream endobj 33 0 obj 9740 endobj 12 0 obj << /Type /Font /Subtype /TrueType /BaseFont /ZXEFNW+Calibri /FontDescriptor 34 0 R /ToUnicode 35 0 R /FirstChar 33 /LastChar 92 /Widths [ 226 487 525 229 391 420 423 498 525 479 525 229 335 527 525 305 525 349 459 631 525 453 488 642 471 303 799 507 507 507 507 507 507 303 715 418 418 252 268 386 252 452 250 455 579 239 250 306 525 615 433 507 543 268 544 507 855 646 890 533 ] >> endobj 35 0 obj << /Length 36 0 R /Filter /FlateDecode >> stream x]ˎ@E|E/'c !EExb@/9LY\K=N7yi?[}C;xxdowͩŇyЍ(V)9/=|l:~лs~8{sLӯx|R>WӗJ-~>R7uXFj6s5cRx_//e]^Pwk5'E.I;ǣz{λ)j]%6P'IӲCz6nds,~nnd>a6WtE([av-[cH,"j[,"jSE,")"Z(X"e$j3$SY%x5sVVZK53NQX%m]X3šx'*ѹUɱfX$2'a5UNjġkEcڂAH֡mU% DT#j4 V"ZwN"j SsJΡ rP&IS3[2p?ɔN"1r%ZwwAD vn@NE9]9ֽvO4y抰n i@ endstream endobj 36 0 obj 599 endobj 34 0 obj << /Type /FontDescriptor /FontName /ZXEFNW+Calibri /Flags 4 /FontBBox [-503 -307 1240 964] /ItalicAngle 0 /Ascent 952 /Descent -269 /CapHeight 632 /StemV 0 /XHeight 464 /AvgWidth 521 /MaxWidth 1328 /FontFile2 37 0 R >> endobj 37 0 obj << /Length 38 0 R /Length1 30252 /Filter /FlateDecode >> stream xռw|\6cThc!B50-%WDHWXuʩOeϙ5^Gc}A=p]c"p}tʕ҉ 5t1vB\!A!. _}BbTK%N!v]mBlbXB\$ąB\ ! NBbX)ĐBbB,bKOB,WB,b=B[Bb]Bt !DmB "DMBbI!hbӅ&DuB Q#DUBT Q!DeB Q"DEB @|! "W!Bd ",DH,!2"(D@>!Bxp )C6!BX0 a(A:!BhP BB A҂N1.Ę BK !]!.߄8#_g!-!$xS7 [!~#įB&īB"B$ċB B<'ijBBL ?BH qJ}!'I!)!+wxR'8!ĨDžx\DŽ8&Q!F"%ģB<"ķxX#B<$ķxP_o !.ׄO{Gw !_KB!B&ĭB"B$čB|Q qH넸VkZR+8(B2! OBb q)! MBlbXB\$ąB\ ! NBbX)ĐBbB,bKOB,WB,bbS!څhU!h:z5d5gr#Y82!r崇nNd!d6vqiOC8m[F2g͜6qE.hpi=u֎d45<*N+9 q䴂r~-㴔N8-洈S/pϩ>D{o_'߁gW/H{?oo Ѳ>o" ׁ"_~a0,*EͱK/g~q?7o @|A ׇ~h^:s}8ϧczҴ-i{0 GcH;#ݡo6 1=| xx( ׁ/ }7Wk݉kƵ/w༛q sC7C_4 `f BWɹ+6t@G^ֻw}}Ը/kߞ}Gz_ҡ1ݻK{w^rdWdtUrz#;zU;\;AwT";l;;dޭێl%[m=5U5-J 'n f{m[z7n>{/ n]׻Ⱥ޵{YݻveP`ځGz.]zdIom_bvao j{z;sjzgm8Vۂ'plc707wBtVi0|#nPEɠB)M~Fl=瓒V9Io~q+=$4]; `8<<#0 < <<|uk}=Ww/_nnnnnnn|8\\ \\ \EV<@8\.`7p)p ۀ`3 \\\l뀵`5 X  `90,K~X ,z`>0.ځ6h&o/?ߊl!N$D 6rWȭik\uK'")= y휳/Uo$&8'!MU[܊S>3ayqu6>qrYzW''YXڪ|{ڻGz,#d Wd,s!l$+z-B+pE=jL6d;Avw3tmQ;.^B.%KRb"e{ Rː3c Wkאkuȱ]7y!r=FysRn"7-eW]~IMϰ3nG=||o+zJP:Fk7a'͔CB{~4IV":a28B%4RNw/K9 R&O²-іԁBR"=c+WgR&dJP굩`p5sm/I%ahߚ~KQ[* |~YxS\]O ,nGtt$Rd9:kz8 ߒ>sԎ8=s+?-6R{R&g?P~eM<(7gߧl6nADz=CQF.%9(p ED>9Jv-A%^Oi)Z'l&՚PenS“;Dr.ڵh$:uTEöp롡щ+ '6 G18X}(o:R)W;HZ8uϬ(g8I] F$LAZ߉0!I%Vb,fv'PRUJ^yY%8JVJ<&qI%?(a-,{?4  +GS4@RRSS]3+eb,kX6:+E=Eth0z .ׇ)~xyX9<AX,KhԹ8am}^uV2;V, ␎WЧ#Zs;UdrRS }öi#51E%r84eȽF0IėeO5WEj0PU .5|CT`:ǒsfCJ_ ic1.?m?^ ]>mKqG)L ͳC.3JG/AnZ*-S܎!T|#bd\Kˢ5NX-{r..x z)OQa51I:?"4QT;?֬D:3 ^:$[sHTYMxUr<ӃOq~H·_%QR%"=$RiĬXX' K(f[0[|t!<l90~nR]mfڲhF|iEVMeta75/ڝvḱYWOltvt4-b3"(dMXT}Ọ,HDc2M̨L=*1LңѧE)jrdw{Iccd`C+lgxbca/(&"GcFjrC\^iOg4#3JutDedzj OТ&=6ެW-AjhɲjalO=:»'@gsB6+0gGϊɼ;twn\.d OH94UGq$ݣ8XaQȊQɖ4k갡wU&uJʣdZ"mb_*1(J)T8a"0WO,V*R )+nW*/*ֹ+tٍse,i ԲJgYʻt/auMժSi4qaqǃ WepA 8\N}$re3c%1= %yٍ2]u2q2qN:dCci(ǀ>ObmLۘF,=QVs/xYx\B:%SE|)ٟ_yrCW?:uҝ~<{Áﱶ O&œÁx:GJ*+w㣒=;0n>0JuI=h,y=qаflE(Ո?V>p(^=Vg֩zDuftA#h׍_e8vLg^:7%0i$_KffdX},?},?}&>ǞLp<qkJ`J`+8:*+Qj8]W$5E4QZ8\W|U*kҵ~ul(5uTt&vK.|hgcoje]Gap/[쎿~}ypMKZtbűڴ͙'e8ƝXf4Qj(d3/ry!$m%b%aYKJ:D@ 0L=C4 masd.-}>r;*Mk*TLT_c ?Y,ߟU7ꝙn-ghjuPaHa.k0Re(..g,r,rfrvd7[*vUoyҕYەfpKb {eފk^ov5x4_!IB]a`~fj{] Gcfy\znߴ;:?JYz(/r@In)Pspcդܜ̲ktxS56ήafav*~T~Ja0V|+;l7QlC[$M&3'z,M.Yh :Fi# ODs!s6XFgE=,9=:=ZbNʨqd".jamzg (IβmsY•EyX<c6 `!8@Y/']xH-kmjeIL[6LZȴyŶ3*utN[L) ) +9*&I.OK/)7} +A 2 >=(GJz?.Ώ[t61?I"h,ikhMh?'j5vt4ُ xFiZigH L<\&*U!RiUŚtnF^մ뾁Od,t4p6ܼ0xtF%Xhff+f,؂XdCgTK"f6JqN a5$쒤}ӈ'`y`偕tyje6s!=.aͷG'V DaQz';{C6Fuiˁ7;:v9Aq;Op0'Xr5P."YeZ7`dtZ>:A9}.GD^ PlfҢZ5 GKa1F<~P8;:*`Nb,\=ugDc6%j)nr:$wZ1V>dnI(1iYE[Z?}˽.}~]35dc t]pk-~i5>gO\Ҽ+}΃^SZ\슻Z} Z#+J k-JmtJ5n#i~>鰡k8S)m϶AәFĩ {t*QY?5P\ŭذÔ?yQ+C%tJhYʰQvXѡjRwƺE7vFgtZzWSRk8sŸsh:qځ,7[MNɤjV:'+I6-lsmU/Fm7dluATj70^'f m0(R1'+X}E_؆ rFpT+vʴm{V_v.e;=ܨg^Ut"q*EuhlKfuSe\kPyQUA5 bds&cC1\\^(80fUY+/+ mpYIyn(rBf(m+YZ^]|ʝ]kh(gqKfzYb EfOk[쯌4V g rfٽHl]#m}ly}g6e60>hɥ+i3`c֔8Y2*^#;JadiDE9JZc4-SwڕF{vp 9x+(mی2"⶛: s7]/b]3^7jʫ0ף. ^|xc;)*{1೽tV,F J< L)ZڤZqCنc~[bW$MKealU9#6ogä + ux[j]VŔy.eW%aBU+VsWP@P% ~?)/fXg<jXR<"Y|b#=xVn9ݓ\.UT굚7ReAAbڴ= :0oC>isr堒ܞ45V2Zt9=TJ Qʞ[CĔ)]LqMLhf.9v'f2#kzq'/ҝ^F7lO麃6fnTf7)ފOKL֪8|>?ٓ}B<8;ɠ-Y(P1e4W)rGJ}vGad Xj~gedVd3cVyJ[DX'zV9QHW':ߚyfgCkQmGY~zՖKSj6;TDzN՝~[ ϝWiOϟVey^d,.wС͟=h!6PL+ƛw75w~6[vetA&…!F W>ӝMewbO̓aֺ:BC Tr 6v.gm*VjI;?0vvvg$B*|HmsXcg6}59Sy;V ?=vֳ^`ȥl3yE^kK[<ʰk?5~?&,EOEb mϝQn|'d0VzXYulsYNV̝ uV\)ʬX|.7OtƬpM'(սB4l\`DI鼙9niU}t(uuLs$'Ӥғe:W욱hV4WT7ǭ`[=gg٤벮GSnbQoOEX 2QQd0 2K/ 9x]- $e:zGM52_5['`F<^v)g%>`a gg+[cPŗDڂ`<9'ϟg~N{}[+wd賫;J 8\]9!'p΍U慲 W un6qFK3Uy Y&2wiӸX>GQaX) ÈE M^sљh{ʬӬXO=~: Y6lbod~mF'`oc6oNK֫UK3m&ˍ0*__sㆁz^m羍AURcyPyP-ƕJ* f|&.`* 6aeL؇X)tg'X}IbtG &&gBv+no]3׺ČaO 0~u';SzN_wJ)[ս)W!b}1nߟIfl&hYT`/:Nt-fa58 ;hf!bF6ɦ&+'BJliJwFhMrHzT04 ݤ ׈uD#%2 rHb@9$~4 }Jh'7\LnNo*i9*ҳ*5͌bJXnЛeoݔ* /692>cIeȬ]5JX~'3hZZ8"4eX&g<͗i4:N+ -Ce1&lh?hI8ce֙q̘n&e6-*:r;o*,՚6}]BzfW%N56-1X bj& !dzNk݉xɧH*ٛ*Jң98BU]x3:cIc/D_'!æaz6Smg!Tz=rȌ1I:tY)GB~O2B'V7]7Dyyk ;kHaXV53;v0-efg`e8/슨Sqb J&3ʊ_2=N7:̨ˠ:`3s~BӤ\K׌$MN =N8*YcЎn2Y^^'?Ax*T[X\Z4? u CtYlb/y+B8߇*'W}ĩǥ*.єvIWoǟr]z5{v8#ǮfW\ZrenEbUQv')n#o]ÈћAl/eZ5Nt_6;N@UW}9xcր3܉:6f=1ד/61$yJ4oR6[A ෾p(ϯטkfߡ&o߇[ , g1.u-zjY<9~^$8LkqCV\+N76+Dslrn}E%TCɗp?#w;6|eGQ/VcY5e2⒆ J[vy݆ #p&ϰf:()=5):/ Ck]r 5|L)ǷTfv\aj/Lh?#ü~7e{L5j21P3PUmwȪV8~s?&] wk{+&vbʳ~fU?m,vת6WQ719%RRc!ܦ\h]oS% Ԥy颙>22CTfg+*{flКfU2z-T,#Zl8)b'V"a_P,pT_7n5 2V?2^EKPZj|Xf;+V,USKp{= ^x/nTKz -6 *hIn[8AJ;Sfq?6!:0z5RO;L[d8vTUJzڞjلL3U/[|JexL%!/:6M?*} h4 #.vWvrDJs;ibG#]Hie#iRSe0TѮ*Z_EU c*)eeHbƁSDi:HGͣ=^1lxS`+!!=c^o=o&,]6#(uŹJ6ƪT \ݛ/fYT#%r;[ \ڑyѡo,_ׯ:fӂen}[ˬY>kݚW1{CPTڳFic`c+$M= N&sыi;6Wb##{u^A+ 0v#2{VGY<%0DffI=mҿG;S&X.-;,tF rAX~|$ sXlM6 G!b:~JqX((u`8e &)`'khCCgxh޿~xΝ5y_E7@}NWR=j/c2''dҜ  ҜS|ebzzIܥ0Ӓ|6A X) `Š`evJ[)ӈRSO0=θ9ht~>欵|7~yNnil*%|ͫ Snk5|]K/Ra=Y;Lk2j4xZ>b;TO T&jYf6.]/vʜl&Ӳ =`a]i"gQU:&=Jaf /Kۺ C#O4k:8_Mfg{ӆ;;/[rcΉnis[_;YunN&3'.e:5VeʘFaܹ>aⷓ1cC$bcIdgY7 ;0 )p|[. V|2D6X%qil:dU\9>>cб᰺m6Nn.9SgSa'Ruh({5YWSjv%6Vm@V& t6Y|K.ުjY .y--6t=UC l4Y߻)ڥE%Ӛ6+[o\[ Y,ds 5}lof'k:CѼXv\pGی =uFI[5B>Nֳ"/9q4Q\17h̆h9*RrڪJL٫a|y([$3q?%^F:C6^^x]-^ϳǶ|% HVJKA#Uf7hug#Dz1jؖP@ L6ltx}Ul/wg1SE30+ReO_J#wŌ7ilY>gkX f'?Jg5;|/ix&+5̲IZx瘽¶`~քa"Ug^ V a?fA6~4ybI_Kʼn6ܺ~k endstream endobj 38 0 obj 17209 endobj 15 0 obj << /Type /Font /Subtype /TrueType /BaseFont /ZRIORP+Calibri-Bold /FontDescriptor 39 0 R /ToUnicode 40 0 R /FirstChar 33 /LastChar 42 /Widths [ 423 676 606 630 488 563 226 267 529 659 ] >> endobj 40 0 obj << /Length 41 0 R /Filter /FlateDecode >> stream x]j0z C$mFPR>}YZ'ZrwzYGűyin):hAl++n g^\I ٺn mpu|͞86GCWrgb>$#g]-X]=I4apTٹoevU:IP!`pe-pq  @In+JT~a:}"J endstream endobj 41 0 obj 298 endobj 39 0 obj << /Type /FontDescriptor /FontName /ZRIORP+Calibri-Bold /Flags 4 /FontBBox [-519 -306 1240 971] /ItalicAngle 0 /Ascent 952 /Descent -269 /CapHeight 632 /StemV 0 /XHeight 469 /AvgWidth 536 /MaxWidth 1328 /FontFile2 42 0 R >> endobj 42 0 obj << /Length 43 0 R /Length1 12584 /Filter /FlateDecode >> stream xyxg7IIK4ۤ-iSP( -t-Xl-TTEY݊}A oRAw=-{s}z9s93 Zt mWBs4 6O rB"9 6 g!PڈBAyX|&ť>E||#Cxq6-ě7#^ID8x}-_B؏x@CAgo#Bgob7#^CD8^A/#^ɿ؏ af8^slYn3]OڏD;O @[-]GHpӇJeP{@_('ET6Kh M64YmX[w!kk yvqNV-W!n"*J )F)p>\ ^ X&O ۮCkV0=n(?RX71~/s {@^/t [AHȣ < ȃ rf{A6l٠i_rȝ kAրr;j@nE f 7UsrE$; l"tW&Z epbdbq9ь 11q)h(ȜD"F #rو,D PD(„0" ^EhjD0"HҳTc@>>{ A4S !iFzdbc1gb! Db"0A! .%G a ;y$Oylr%^3DT e RxD q!Q(@$ 8y"aE!bD "ˌD=wr{A9 r +I @V [o@ K/@>y+ /"~@<y2 @fz1툫s$M1,YLD=111 111Q!&!.Bx5tCHEC)dD‰p`nBP xx"g3$)]w@ ț oz'27u_[ὦû{uWW>׶[WwԮxʮ%^Œ%fqB^B[Uۉ6>mVۂ"mx`'mĨ[ڸphH50MR;իhnFh[)7Vֵr`51Yk5[zZˋ-]mV.m^miV.+l%1i~o=q}p bboSYƮYLo;=;kw{wJdokޮo[U-wON};;]-*V EB>'^AZV.%k;w1(OW7GL): h8j.6 C|0/SLOo鮩vJAR1bHoS9YT )nJoܸ1r2֗\IJŸj)T쀂P qf_>m.x>4 Og(+0qŃY@#XFf05Va&V_{1}{KHA_^C!.yï7rdXtߗc(dY`>΍amBG!Ҍ?ș|{m{#''O~!'+hC<=_N AeK DŽRQ-ղ*ӂji5[ZgZLr6'p/#i7㨕S;MgۢϴؠE,>7"E{a)t(].PΠtNsAL2IISWa^pt{fL6uZ|bل%sQ#sG NKMv:pѠjA*(I-lNT84VAQ?HQ'@UthcKX>҃3hMF x@JW6G.eSbC[aTSMuB芦ºTڭՌ5jRIF E-d&rK.͑`=VĊJ_anudKTd_9"̙uo$3\Y¬>N|agrS1eWQF1U((]LTT:$ =Gaփ4xF3ai@`>eCfBEaFfZ$IwՊ\k3e--g B(c--2+9Dm"﬛X) !ޝ@S`__PӅ1\h8 T.-lj^}avİ 2_Bo' ζYelR!ARY:,؟m>]Bj_c-˒`Spʽ`mYò GYZ-P؊I FQU63߂1:b6_d43sR$gКM(VX0h8<~oxE0` ,l im,rNYŲeI'4 !O%Zoi>%5԰}^ZGX*/g5 4:N6ArTΒ#BE) 6t5c)}!o,aCRZZ wO~@o'v{Y 5>Ou݉i##k9eJfbc *eU!+z|uh:JvȺZx j=\hsUmSg]-;\ ?*Ra 9a |fWDИ/j|c<ԫ>HBpp'u pO ads@~Sk#1dOTu@v2bGC=Qg'€C0)A,>l;B $PVuA}s،l6Hvtk;CLT83an5`p` 45  P [R hJT8eX [5 ᏕnpAx6ZsP(;@tsMT jd fQ(kA# L|Cm[;jx ŁmLb vReuggwxC3hRԒd%IV* A 5)L{sh'(LAY| e07(b"}S+=sIHjv=]G ӂvC#6z|QK0Lu_i* !y~coM9ԓ1&YC JHpsÒ9YYcaN!!u9YVKԌXY&r3刌 zGPZ&$(*^/xOxM{*CN 9=IQpz]oLj^)듭 ze%2&6(R\{W#Rt:/G(Hdye8I N9 뉁k t|籲CLCNFHB&!GKt4ћWUzIT^^^hdS " bz2iti1Q3ڗGMŌE!lx-cU01oI=(Μ$vENek)&T)q\pR1YLI<3qHWTU"LZךï())ؕ+0R,q^FzH;N V[' b3%"^LYPO{e Wd uɁ}ߧlG[a~UxƜ7`ٻk*|>Y3[`Ѩx&Dmp4-OhvI^ VC#Nl~'9-Xlj}h0{-8E`=7ē1Nϒ0nb2R=J HeW^*lDL9Yv8l7'&ŞiX{ZNw͸cސz7&*R*7~ yˈÚ`MWna0)&JIyMCO˜NU48E+W&+MQ Z)#[ozhnnz'V?ǹb3ԽWp׫C4J%lL\m`eo1}/KaD'Y F¥F%ׁS"Ȑ{p? pk: (aS6nzޚ̲ΤL>QcԨT?Pǃ x1t`vԆ9~:mpiA6/ &Br`$Aӻ՞fɳkA̚bAh1mt FCW 8I`sْ՝ N#RPNYIU$MtBe ˀ87ǔ낻8וGdJ9 V'|ā%Q3I0#L/윱W0+d?T k\}cGFr N1+)a]PXwb;Z3521J崏*^5wƨUz밤$WuuErѝ QTw*GJ #Igpn/;Vx֐賧q*dZ o %^;l|ﰭۿzt++6kkJV鶴 mBG=#޾iOu?-NZ8d}S.ydQ^U^&!d=F)E4V:[J398pԱ㉼`@q+#^,q­y.^`uk^ 2 x~g#<}`D4ۙt\e֑C2t!Uw|Ing踬yMߖYiW\?fDl֠}fQ{ƄX9fLXHrn;Y_Rw%"5=T䪒 ׸sfIoSC, endstream endobj 43 0 obj 6593 endobj 44 0 obj (Loader Licence \(v5 clean\) July 2014) endobj 45 0 obj (Mac OS X 10.9.4 Quartz PDFContext) endobj 46 0 obj (Adrian Teasdale) endobj 47 0 obj () endobj 48 0 obj (Word) endobj 49 0 obj (D:20140722141036Z00'00') endobj 50 0 obj () endobj 51 0 obj [ () ] endobj 1 0 obj << /Title 44 0 R /Author 46 0 R /Subject 47 0 R /Producer 45 0 R /Creator 48 0 R /CreationDate 49 0 R /ModDate 49 0 R /Keywords 50 0 R /AAPL:Keywords 51 0 R >> endobj xref 0 52 0000000000 65535 f 0000088767 00000 n 0000012854 00000 n 0000027397 00000 n 0000000022 00000 n 0000012833 00000 n 0000012968 00000 n 0000015849 00000 n 0000000000 00000 n 0000027547 00000 n 0000051713 00000 n 0000000000 00000 n 0000062305 00000 n 0000044788 00000 n 0000000000 00000 n 0000080956 00000 n 0000013113 00000 n 0000015828 00000 n 0000027146 00000 n 0000015885 00000 n 0000027124 00000 n 0000027263 00000 n 0000027497 00000 n 0000028618 00000 n 0000027938 00000 n 0000028598 00000 n 0000028858 00000 n 0000044766 00000 n 0000045157 00000 n 0000045424 00000 n 0000051692 00000 n 0000052193 00000 n 0000052453 00000 n 0000062284 00000 n 0000063399 00000 n 0000062704 00000 n 0000063379 00000 n 0000063634 00000 n 0000080934 00000 n 0000081554 00000 n 0000081160 00000 n 0000081534 00000 n 0000081794 00000 n 0000088478 00000 n 0000088499 00000 n 0000088555 00000 n 0000088607 00000 n 0000088641 00000 n 0000088660 00000 n 0000088683 00000 n 0000088725 00000 n 0000088744 00000 n trailer << /Size 52 /Root 22 0 R /Info 1 0 R /ID [ ] >> startxref 88942 %%EOF PK=1]l $doc/ea-php83-php-common/ZEND_LICENSEnu[-------------------------------------------------------------------- The Zend Engine License, Version 2.00 Copyright (c) 1999-2006 Zend Technologies Ltd. All rights reserved. -------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, is permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The names "Zend" and "Zend Engine" must not be used to endorse or promote products derived from this software without prior permission from Zend Technologies Ltd. For written permission, please contact license@zend.com. 4. Zend Technologies Ltd. may publish revised and/or new versions of the license from time to time. Each version will be given a distinguishing version number. Once covered code has been published under a particular version of the license, you may always continue to use it under the terms of that version. You may also choose to use such covered code under the terms of any subsequent version of the license published by Zend Technologies Ltd. No one other than Zend Technologies Ltd. has the right to modify the terms applicable to covered code created under this License. 5. Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes the Zend Engine, freely available at http://www.zend.com" 6. All advertising materials mentioning features or use of this software must display the following acknowledgment: "The Zend Engine is freely available at http://www.zend.com" THIS SOFTWARE IS PROVIDED BY ZEND TECHNOLOGIES LTD. ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ZEND TECHNOLOGIES LTD. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------- PK=1]^*doc/ea-php83-php-common/php.ini-productionnu[[PHP] ;;;;;;;;;;;;;;;;;;; ; About php.ini ; ;;;;;;;;;;;;;;;;;;; ; PHP's initialization file, generally called php.ini, is responsible for ; configuring many of the aspects of PHP's behavior. ; PHP attempts to find and load this configuration from a number of locations. ; The following is a summary of its search order: ; 1. SAPI module specific location. ; 2. The PHPRC environment variable. ; 3. A number of predefined registry keys on Windows ; 4. Current working directory (except CLI) ; 5. The web server's directory (for SAPI modules), or directory of PHP ; (otherwise in Windows) ; 6. The directory from the --with-config-file-path compile time option, or the ; Windows directory (usually C:\windows) ; See the PHP docs for more specific information. ; https://php.net/configuration.file ; The syntax of the file is extremely simple. Whitespace and lines ; beginning with a semicolon are silently ignored (as you probably guessed). ; Section headers (e.g. [Foo]) are also silently ignored, even though ; they might mean something in the future. ; Directives following the section heading [PATH=/www/mysite] only ; apply to PHP files in the /www/mysite directory. Directives ; following the section heading [HOST=www.example.com] only apply to ; PHP files served from www.example.com. Directives set in these ; special sections cannot be overridden by user-defined INI files or ; at runtime. Currently, [PATH=] and [HOST=] sections only work under ; CGI/FastCGI. ; https://php.net/ini.sections ; Directives are specified using the following syntax: ; directive = value ; Directive names are *case sensitive* - foo=bar is different from FOO=bar. ; Directives are variables used to configure PHP or PHP extensions. ; There is no name validation. If PHP can't find an expected ; directive because it is not set or is mistyped, a default value will be used. ; The value can be a string, a number, a PHP constant (e.g. E_ALL or M_PI), one ; of the INI constants (On, Off, True, False, Yes, No and None) or an expression ; (e.g. E_ALL & ~E_NOTICE), a quoted string ("bar"), or a reference to a ; previously set variable or directive (e.g. ${foo}) ; Expressions in the INI file are limited to bitwise operators and parentheses: ; | bitwise OR ; ^ bitwise XOR ; & bitwise AND ; ~ bitwise NOT ; ! boolean NOT ; Boolean flags can be turned on using the values 1, On, True or Yes. ; They can be turned off using the values 0, Off, False or No. ; An empty string can be denoted by simply not writing anything after the equal ; sign, or by using the None keyword: ; foo = ; sets foo to an empty string ; foo = None ; sets foo to an empty string ; foo = "None" ; sets foo to the string 'None' ; If you use constants in your value, and these constants belong to a ; dynamically loaded extension (either a PHP extension or a Zend extension), ; you may only use these constants *after* the line that loads the extension. ;;;;;;;;;;;;;;;;;;; ; About this file ; ;;;;;;;;;;;;;;;;;;; ; PHP comes packaged with two INI files. One that is recommended to be used ; in production environments and one that is recommended to be used in ; development environments. ; php.ini-production contains settings which hold security, performance and ; best practices at its core. But please be aware, these settings may break ; compatibility with older or less security-conscious applications. We ; recommending using the production ini in production and testing environments. ; php.ini-development is very similar to its production variant, except it is ; much more verbose when it comes to errors. We recommend using the ; development version only in development environments, as errors shown to ; application users can inadvertently leak otherwise secure information. ; This is the php.ini-production INI file. ;;;;;;;;;;;;;;;;;;; ; Quick Reference ; ;;;;;;;;;;;;;;;;;;; ; The following are all the settings which are different in either the production ; or development versions of the INIs with respect to PHP's default behavior. ; Please see the actual settings later in the document for more details as to why ; we recommend these changes in PHP's behavior. ; display_errors ; Default Value: On ; Development Value: On ; Production Value: Off ; display_startup_errors ; Default Value: On ; Development Value: On ; Production Value: Off ; error_reporting ; Default Value: E_ALL ; Development Value: E_ALL ; Production Value: E_ALL & ~E_DEPRECATED & ~E_STRICT ; log_errors ; Default Value: Off ; Development Value: On ; Production Value: On ; max_input_time ; Default Value: -1 (Unlimited) ; Development Value: 60 (60 seconds) ; Production Value: 60 (60 seconds) ; output_buffering ; Default Value: Off ; Development Value: 4096 ; Production Value: 4096 ; register_argc_argv ; Default Value: On ; Development Value: Off ; Production Value: Off ; request_order ; Default Value: None ; Development Value: "GP" ; Production Value: "GP" ; session.gc_divisor ; Default Value: 100 ; Development Value: 1000 ; Production Value: 1000 ; session.sid_bits_per_character ; Default Value: 4 ; Development Value: 5 ; Production Value: 5 ; session.sid_length ; Default Value: 32 ; Development Value: 26 ; Production Value: 26 ; short_open_tag ; Default Value: On ; Development Value: Off ; Production Value: Off ; variables_order ; Default Value: "EGPCS" ; Development Value: "GPCS" ; Production Value: "GPCS" ; zend.assertions ; Default Value: 1 ; Development Value: 1 ; Production Value: -1 ; zend.exception_ignore_args ; Default Value: Off ; Development Value: Off ; Production Value: On ; zend.exception_string_param_max_len ; Default Value: 15 ; Development Value: 15 ; Production Value: 0 ;;;;;;;;;;;;;;;;;;;; ; php.ini Options ; ;;;;;;;;;;;;;;;;;;;; ; Name for user-defined php.ini (.htaccess) files. Default is ".user.ini" ;user_ini.filename = ".user.ini" ; To disable this feature set this option to an empty value ;user_ini.filename = ; TTL for user-defined php.ini files (time-to-live) in seconds. Default is 300 seconds (5 minutes) ;user_ini.cache_ttl = 300 ;;;;;;;;;;;;;;;;;;;; ; Language Options ; ;;;;;;;;;;;;;;;;;;;; ; Enable the PHP scripting language engine under Apache. ; https://php.net/engine engine = On ; This directive determines whether or not PHP will recognize code between ; tags as PHP source which should be processed as such. It is ; generally recommended that should be used and that this feature ; should be disabled, as enabling it may result in issues when generating XML ; documents, however this remains supported for backward compatibility reasons. ; Note that this directive does not control the would work. ; https://php.net/syntax-highlighting ;highlight.string = #DD0000 ;highlight.comment = #FF9900 ;highlight.keyword = #007700 ;highlight.default = #0000BB ;highlight.html = #000000 ; If enabled, the request will be allowed to complete even if the user aborts ; the request. Consider enabling it if executing long requests, which may end up ; being interrupted by the user or a browser timing out. PHP's default behavior ; is to disable this feature. ; https://php.net/ignore-user-abort ;ignore_user_abort = On ; Determines the size of the realpath cache to be used by PHP. This value should ; be increased on systems where PHP opens many files to reflect the quantity of ; the file operations performed. ; Note: if open_basedir is set, the cache is disabled ; https://php.net/realpath-cache-size ;realpath_cache_size = 4096k ; Duration of time, in seconds for which to cache realpath information for a given ; file or directory. For systems with rarely changing files, consider increasing this ; value. ; https://php.net/realpath-cache-ttl ;realpath_cache_ttl = 120 ; Enables or disables the circular reference collector. ; https://php.net/zend.enable-gc zend.enable_gc = On ; If enabled, scripts may be written in encodings that are incompatible with ; the scanner. CP936, Big5, CP949 and Shift_JIS are the examples of such ; encodings. To use this feature, mbstring extension must be enabled. ;zend.multibyte = Off ; Allows to set the default encoding for the scripts. This value will be used ; unless "declare(encoding=...)" directive appears at the top of the script. ; Only affects if zend.multibyte is set. ;zend.script_encoding = ; Allows to include or exclude arguments from stack traces generated for exceptions. ; In production, it is recommended to turn this setting on to prohibit the output ; of sensitive information in stack traces ; Default Value: Off ; Development Value: Off ; Production Value: On zend.exception_ignore_args = On ; Allows setting the maximum string length in an argument of a stringified stack trace ; to a value between 0 and 1000000. ; This has no effect when zend.exception_ignore_args is enabled. ; Default Value: 15 ; Development Value: 15 ; Production Value: 0 ; In production, it is recommended to set this to 0 to reduce the output ; of sensitive information in stack traces. zend.exception_string_param_max_len = 0 ;;;;;;;;;;;;;;;;; ; Miscellaneous ; ;;;;;;;;;;;;;;;;; ; Decides whether PHP may expose the fact that it is installed on the server ; (e.g. by adding its signature to the Web server header). It is no security ; threat in any way, but it makes it possible to determine whether you use PHP ; on your server or not. ; https://php.net/expose-php expose_php = On ;;;;;;;;;;;;;;;;;;; ; Resource Limits ; ;;;;;;;;;;;;;;;;;;; ; Maximum execution time of each script, in seconds ; https://php.net/max-execution-time ; Note: This directive is hardcoded to 0 for the CLI SAPI max_execution_time = 30 ; Maximum amount of time each script may spend parsing request data. It's a good ; idea to limit this time on productions servers in order to eliminate unexpectedly ; long running scripts. ; Note: This directive is hardcoded to -1 for the CLI SAPI ; Default Value: -1 (Unlimited) ; Development Value: 60 (60 seconds) ; Production Value: 60 (60 seconds) ; https://php.net/max-input-time max_input_time = 60 ; Maximum input variable nesting level ; https://php.net/max-input-nesting-level ;max_input_nesting_level = 64 ; How many GET/POST/COOKIE input variables may be accepted ;max_input_vars = 1000 ; How many multipart body parts (combined input variable and file uploads) may ; be accepted. ; Default Value: -1 (Sum of max_input_vars and max_file_uploads) ;max_multipart_body_parts = 1500 ; Maximum amount of memory a script may consume ; https://php.net/memory-limit memory_limit = 128M ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Error handling and logging ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; This directive informs PHP of which errors, warnings and notices you would like ; it to take action for. The recommended way of setting values for this ; directive is through the use of the error level constants and bitwise ; operators. The error level constants are below here for convenience as well as ; some common settings and their meanings. ; By default, PHP is set to take action on all errors, notices and warnings EXCEPT ; those related to E_NOTICE and E_STRICT, which together cover best practices and ; recommended coding standards in PHP. For performance reasons, this is the ; recommend error reporting setting. Your production server shouldn't be wasting ; resources complaining about best practices and coding standards. That's what ; development servers and development settings are for. ; Note: The php.ini-development file has this setting as E_ALL. This ; means it pretty much reports everything which is exactly what you want during ; development and early testing. ; ; Error Level Constants: ; E_ALL - All errors and warnings ; E_ERROR - fatal run-time errors ; E_RECOVERABLE_ERROR - almost fatal run-time errors ; E_WARNING - run-time warnings (non-fatal errors) ; E_PARSE - compile-time parse errors ; E_NOTICE - run-time notices (these are warnings which often result ; from a bug in your code, but it's possible that it was ; intentional (e.g., using an uninitialized variable and ; relying on the fact it is automatically initialized to an ; empty string) ; E_STRICT - run-time notices, enable to have PHP suggest changes ; to your code which will ensure the best interoperability ; and forward compatibility of your code ; E_CORE_ERROR - fatal errors that occur during PHP's initial startup ; E_CORE_WARNING - warnings (non-fatal errors) that occur during PHP's ; initial startup ; E_COMPILE_ERROR - fatal compile-time errors ; E_COMPILE_WARNING - compile-time warnings (non-fatal errors) ; E_USER_ERROR - user-generated error message ; E_USER_WARNING - user-generated warning message ; E_USER_NOTICE - user-generated notice message ; E_DEPRECATED - warn about code that will not work in future versions ; of PHP ; E_USER_DEPRECATED - user-generated deprecation warnings ; ; Common Values: ; E_ALL (Show all errors, warnings and notices including coding standards.) ; E_ALL & ~E_NOTICE (Show all errors, except for notices) ; E_ALL & ~E_NOTICE & ~E_STRICT (Show all errors, except for notices and coding standards warnings.) ; E_COMPILE_ERROR|E_RECOVERABLE_ERROR|E_ERROR|E_CORE_ERROR (Show only errors) ; Default Value: E_ALL ; Development Value: E_ALL ; Production Value: E_ALL & ~E_DEPRECATED & ~E_STRICT ; https://php.net/error-reporting error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT ; This directive controls whether or not and where PHP will output errors, ; notices and warnings too. Error output is very useful during development, but ; it could be very dangerous in production environments. Depending on the code ; which is triggering the error, sensitive information could potentially leak ; out of your application such as database usernames and passwords or worse. ; For production environments, we recommend logging errors rather than ; sending them to STDOUT. ; Possible Values: ; Off = Do not display any errors ; stderr = Display errors to STDERR (affects only CGI/CLI binaries!) ; On or stdout = Display errors to STDOUT ; Default Value: On ; Development Value: On ; Production Value: Off ; https://php.net/display-errors display_errors = Off ; The display of errors which occur during PHP's startup sequence are handled ; separately from display_errors. We strongly recommend you set this to 'off' ; for production servers to avoid leaking configuration details. ; Default Value: On ; Development Value: On ; Production Value: Off ; https://php.net/display-startup-errors display_startup_errors = Off ; Besides displaying errors, PHP can also log errors to locations such as a ; server-specific log, STDERR, or a location specified by the error_log ; directive found below. While errors should not be displayed on productions ; servers they should still be monitored and logging is a great way to do that. ; Default Value: Off ; Development Value: On ; Production Value: On ; https://php.net/log-errors log_errors = On ; Do not log repeated messages. Repeated errors must occur in same file on same ; line unless ignore_repeated_source is set true. ; https://php.net/ignore-repeated-errors ignore_repeated_errors = Off ; Ignore source of message when ignoring repeated messages. When this setting ; is On you will not log errors with repeated messages from different files or ; source lines. ; https://php.net/ignore-repeated-source ignore_repeated_source = Off ; If this parameter is set to Off, then memory leaks will not be shown (on ; stdout or in the log). This is only effective in a debug compile, and if ; error reporting includes E_WARNING in the allowed list ; https://php.net/report-memleaks report_memleaks = On ; This setting is off by default. ;report_zend_debug = 0 ; Turn off normal error reporting and emit XML-RPC error XML ; https://php.net/xmlrpc-errors ;xmlrpc_errors = 0 ; An XML-RPC faultCode ;xmlrpc_error_number = 0 ; When PHP displays or logs an error, it has the capability of formatting the ; error message as HTML for easier reading. This directive controls whether ; the error message is formatted as HTML or not. ; Note: This directive is hardcoded to Off for the CLI SAPI ; https://php.net/html-errors ;html_errors = On ; If html_errors is set to On *and* docref_root is not empty, then PHP ; produces clickable error messages that direct to a page describing the error ; or function causing the error in detail. ; You can download a copy of the PHP manual from https://php.net/docs ; and change docref_root to the base URL of your local copy including the ; leading '/'. You must also specify the file extension being used including ; the dot. PHP's default behavior is to leave these settings empty, in which ; case no links to documentation are generated. ; Note: Never use this feature for production boxes. ; https://php.net/docref-root ; Examples ;docref_root = "/phpmanual/" ; https://php.net/docref-ext ;docref_ext = .html ; String to output before an error message. PHP's default behavior is to leave ; this setting blank. ; https://php.net/error-prepend-string ; Example: ;error_prepend_string = "" ; String to output after an error message. PHP's default behavior is to leave ; this setting blank. ; https://php.net/error-append-string ; Example: ;error_append_string = "" ; Log errors to specified file. PHP's default behavior is to leave this value ; empty. ; https://php.net/error-log ; Example: ;error_log = php_errors.log ; Log errors to syslog (Event Log on Windows). ;error_log = syslog ; The syslog ident is a string which is prepended to every message logged ; to syslog. Only used when error_log is set to syslog. ;syslog.ident = php ; The syslog facility is used to specify what type of program is logging ; the message. Only used when error_log is set to syslog. ;syslog.facility = user ; Set this to disable filtering control characters (the default). ; Some loggers only accept NVT-ASCII, others accept anything that's not ; control characters. If your logger accepts everything, then no filtering ; is needed at all. ; Allowed values are: ; ascii (all printable ASCII characters and NL) ; no-ctrl (all characters except control characters) ; all (all characters) ; raw (like "all", but messages are not split at newlines) ; https://php.net/syslog.filter ;syslog.filter = ascii ;windows.show_crt_warning ; Default value: 0 ; Development value: 0 ; Production value: 0 ;;;;;;;;;;;;;;;;; ; Data Handling ; ;;;;;;;;;;;;;;;;; ; The separator used in PHP generated URLs to separate arguments. ; PHP's default setting is "&". ; https://php.net/arg-separator.output ; Example: ;arg_separator.output = "&" ; List of separator(s) used by PHP to parse input URLs into variables. ; PHP's default setting is "&". ; NOTE: Every character in this directive is considered as separator! ; https://php.net/arg-separator.input ; Example: ;arg_separator.input = ";&" ; This directive determines which super global arrays are registered when PHP ; starts up. G,P,C,E & S are abbreviations for the following respective super ; globals: GET, POST, COOKIE, ENV and SERVER. There is a performance penalty ; paid for the registration of these arrays and because ENV is not as commonly ; used as the others, ENV is not recommended on productions servers. You ; can still get access to the environment variables through getenv() should you ; need to. ; Default Value: "EGPCS" ; Development Value: "GPCS" ; Production Value: "GPCS"; ; https://php.net/variables-order variables_order = "GPCS" ; This directive determines which super global data (G,P & C) should be ; registered into the super global array REQUEST. If so, it also determines ; the order in which that data is registered. The values for this directive ; are specified in the same manner as the variables_order directive, ; EXCEPT one. Leaving this value empty will cause PHP to use the value set ; in the variables_order directive. It does not mean it will leave the super ; globals array REQUEST empty. ; Default Value: None ; Development Value: "GP" ; Production Value: "GP" ; https://php.net/request-order request_order = "GP" ; This directive determines whether PHP registers $argv & $argc each time it ; runs. $argv contains an array of all the arguments passed to PHP when a script ; is invoked. $argc contains an integer representing the number of arguments ; that were passed when the script was invoked. These arrays are extremely ; useful when running scripts from the command line. When this directive is ; enabled, registering these variables consumes CPU cycles and memory each time ; a script is executed. For performance reasons, this feature should be disabled ; on production servers. ; Note: This directive is hardcoded to On for the CLI SAPI ; Default Value: On ; Development Value: Off ; Production Value: Off ; https://php.net/register-argc-argv register_argc_argv = Off ; When enabled, the ENV, REQUEST and SERVER variables are created when they're ; first used (Just In Time) instead of when the script starts. If these ; variables are not used within a script, having this directive on will result ; in a performance gain. The PHP directive register_argc_argv must be disabled ; for this directive to have any effect. ; https://php.net/auto-globals-jit auto_globals_jit = On ; Whether PHP will read the POST data. ; This option is enabled by default. ; Most likely, you won't want to disable this option globally. It causes $_POST ; and $_FILES to always be empty; the only way you will be able to read the ; POST data will be through the php://input stream wrapper. This can be useful ; to proxy requests or to process the POST data in a memory efficient fashion. ; https://php.net/enable-post-data-reading ;enable_post_data_reading = Off ; Maximum size of POST data that PHP will accept. ; Its value may be 0 to disable the limit. It is ignored if POST data reading ; is disabled through enable_post_data_reading. ; https://php.net/post-max-size post_max_size = 8M ; Automatically add files before PHP document. ; https://php.net/auto-prepend-file auto_prepend_file = ; Automatically add files after PHP document. ; https://php.net/auto-append-file auto_append_file = ; By default, PHP will output a media type using the Content-Type header. To ; disable this, simply set it to be empty. ; ; PHP's built-in default media type is set to text/html. ; https://php.net/default-mimetype default_mimetype = "text/html" ; PHP's default character set is set to UTF-8. ; https://php.net/default-charset default_charset = "UTF-8" ; PHP internal character encoding is set to empty. ; If empty, default_charset is used. ; https://php.net/internal-encoding ;internal_encoding = ; PHP input character encoding is set to empty. ; If empty, default_charset is used. ; https://php.net/input-encoding ;input_encoding = ; PHP output character encoding is set to empty. ; If empty, default_charset is used. ; See also output_buffer. ; https://php.net/output-encoding ;output_encoding = ;;;;;;;;;;;;;;;;;;;;;;;;; ; Paths and Directories ; ;;;;;;;;;;;;;;;;;;;;;;;;; ; UNIX: "/path1:/path2" ;include_path = ".:/php/includes" ; ; Windows: "\path1;\path2" ;include_path = ".;c:\php\includes" ; ; PHP's default setting for include_path is ".;/path/to/php/pear" ; https://php.net/include-path ; The root of the PHP pages, used only if nonempty. ; if PHP was not compiled with FORCE_REDIRECT, you SHOULD set doc_root ; if you are running php as a CGI under any web server (other than IIS) ; see documentation for security issues. The alternate is to use the ; cgi.force_redirect configuration below ; https://php.net/doc-root doc_root = ; The directory under which PHP opens the script using /~username used only ; if nonempty. ; https://php.net/user-dir user_dir = ; Directory in which the loadable extensions (modules) reside. ; https://php.net/extension-dir ;extension_dir = "./" ; On windows: ;extension_dir = "ext" ; Directory where the temporary files should be placed. ; Defaults to the system default (see sys_get_temp_dir) ;sys_temp_dir = "/tmp" ; Whether or not to enable the dl() function. The dl() function does NOT work ; properly in multithreaded servers, such as IIS or Zeus, and is automatically ; disabled on them. ; https://php.net/enable-dl enable_dl = Off ; cgi.force_redirect is necessary to provide security running PHP as a CGI under ; most web servers. Left undefined, PHP turns this on by default. You can ; turn it off here AT YOUR OWN RISK ; **You CAN safely turn this off for IIS, in fact, you MUST.** ; https://php.net/cgi.force-redirect ;cgi.force_redirect = 1 ; if cgi.nph is enabled it will force cgi to always sent Status: 200 with ; every request. PHP's default behavior is to disable this feature. ;cgi.nph = 1 ; if cgi.force_redirect is turned on, and you are not running under Apache or Netscape ; (iPlanet) web servers, you MAY need to set an environment variable name that PHP ; will look for to know it is OK to continue execution. Setting this variable MAY ; cause security issues, KNOW WHAT YOU ARE DOING FIRST. ; https://php.net/cgi.redirect-status-env ;cgi.redirect_status_env = ; cgi.fix_pathinfo provides *real* PATH_INFO/PATH_TRANSLATED support for CGI. PHP's ; previous behaviour was to set PATH_TRANSLATED to SCRIPT_FILENAME, and to not grok ; what PATH_INFO is. For more information on PATH_INFO, see the cgi specs. Setting ; this to 1 will cause PHP CGI to fix its paths to conform to the spec. A setting ; of zero causes PHP to behave as before. Default is 1. You should fix your scripts ; to use SCRIPT_FILENAME rather than PATH_TRANSLATED. ; https://php.net/cgi.fix-pathinfo ;cgi.fix_pathinfo=1 ; if cgi.discard_path is enabled, the PHP CGI binary can safely be placed outside ; of the web tree and people will not be able to circumvent .htaccess security. ;cgi.discard_path=1 ; FastCGI under IIS supports the ability to impersonate ; security tokens of the calling client. This allows IIS to define the ; security context that the request runs under. mod_fastcgi under Apache ; does not currently support this feature (03/17/2002) ; Set to 1 if running under IIS. Default is zero. ; https://php.net/fastcgi.impersonate ;fastcgi.impersonate = 1 ; Disable logging through FastCGI connection. PHP's default behavior is to enable ; this feature. ;fastcgi.logging = 0 ; cgi.rfc2616_headers configuration option tells PHP what type of headers to ; use when sending HTTP response code. If set to 0, PHP sends Status: header that ; is supported by Apache. When this option is set to 1, PHP will send ; RFC2616 compliant header. ; Default is zero. ; https://php.net/cgi.rfc2616-headers ;cgi.rfc2616_headers = 0 ; cgi.check_shebang_line controls whether CGI PHP checks for line starting with #! ; (shebang) at the top of the running script. This line might be needed if the ; script support running both as stand-alone script and via PHP CGI<. PHP in CGI ; mode skips this line and ignores its content if this directive is turned on. ; https://php.net/cgi.check-shebang-line ;cgi.check_shebang_line=1 ;;;;;;;;;;;;;;;; ; File Uploads ; ;;;;;;;;;;;;;;;; ; Whether to allow HTTP file uploads. ; https://php.net/file-uploads file_uploads = On ; Temporary directory for HTTP uploaded files (will use system default if not ; specified). ; https://php.net/upload-tmp-dir ;upload_tmp_dir = ; Maximum allowed size for uploaded files. ; https://php.net/upload-max-filesize upload_max_filesize = 2M ; Maximum number of files that can be uploaded via a single request max_file_uploads = 20 ;;;;;;;;;;;;;;;;;; ; Fopen wrappers ; ;;;;;;;;;;;;;;;;;; ; Whether to allow the treatment of URLs (like http:// or ftp://) as files. ; https://php.net/allow-url-fopen allow_url_fopen = On ; Whether to allow include/require to open URLs (like https:// or ftp://) as files. ; https://php.net/allow-url-include allow_url_include = Off ; Define the anonymous ftp password (your email address). PHP's default setting ; for this is empty. ; https://php.net/from ;from="john@doe.com" ; Define the User-Agent string. PHP's default setting for this is empty. ; https://php.net/user-agent ;user_agent="PHP" ; Default timeout for socket based streams (seconds) ; https://php.net/default-socket-timeout default_socket_timeout = 60 ; If your scripts have to deal with files from Macintosh systems, ; or you are running on a Mac and need to deal with files from ; unix or win32 systems, setting this flag will cause PHP to ; automatically detect the EOL character in those files so that ; fgets() and file() will work regardless of the source of the file. ; https://php.net/auto-detect-line-endings ;auto_detect_line_endings = Off ;;;;;;;;;;;;;;;;;;;;;; ; Dynamic Extensions ; ;;;;;;;;;;;;;;;;;;;;;; ; If you wish to have an extension loaded automatically, use the following ; syntax: ; ; extension=modulename ; ; For example: ; ; extension=mysqli ; ; When the extension library to load is not located in the default extension ; directory, You may specify an absolute path to the library file: ; ; extension=/path/to/extension/mysqli.so ; ; Note : The syntax used in previous PHP versions ('extension=.so' and ; 'extension='php_.dll') is supported for legacy reasons and may be ; deprecated in a future PHP major version. So, when it is possible, please ; move to the new ('extension=) syntax. ; ; Notes for Windows environments : ; ; - Many DLL files are located in the ext/ ; extension folders as well as the separate PECL DLL download. ; Be sure to appropriately set the extension_dir directive. ; ;extension=bz2 ; The ldap extension must be before curl if OpenSSL 1.0.2 and OpenLDAP is used ; otherwise it results in segfault when unloading after using SASL. ; See https://github.com/php/php-src/issues/8620 for more info. ;extension=ldap ;extension=curl ;extension=ffi ;extension=ftp ;extension=fileinfo ;extension=gd ;extension=gettext ;extension=gmp ;extension=intl ;extension=imap ;extension=mbstring ;extension=exif ; Must be after mbstring as it depends on it ;extension=mysqli ;extension=oci8_12c ; Use with Oracle Database 12c Instant Client ;extension=oci8_19 ; Use with Oracle Database 19 Instant Client ;extension=odbc ;extension=openssl ;extension=pdo_firebird ;extension=pdo_mysql ;extension=pdo_oci ;extension=pdo_odbc ;extension=pdo_pgsql ;extension=pdo_sqlite ;extension=pgsql ;extension=shmop ; The MIBS data available in the PHP distribution must be installed. ; See https://www.php.net/manual/en/snmp.installation.php ;extension=snmp ;extension=soap ;extension=sockets ;extension=sodium ;extension=sqlite3 ;extension=tidy ;extension=xsl ;extension=zip ;zend_extension=opcache ;;;;;;;;;;;;;;;;;;; ; Module Settings ; ;;;;;;;;;;;;;;;;;;; [CLI Server] ; Whether the CLI web server uses ANSI color coding in its terminal output. cli_server.color = On [Date] ; Defines the default timezone used by the date functions ; https://php.net/date.timezone ;date.timezone = ; https://php.net/date.default-latitude ;date.default_latitude = 31.7667 ; https://php.net/date.default-longitude ;date.default_longitude = 35.2333 ; https://php.net/date.sunrise-zenith ;date.sunrise_zenith = 90.833333 ; https://php.net/date.sunset-zenith ;date.sunset_zenith = 90.833333 [filter] ; https://php.net/filter.default ;filter.default = unsafe_raw ; https://php.net/filter.default-flags ;filter.default_flags = [iconv] ; Use of this INI entry is deprecated, use global input_encoding instead. ; If empty, default_charset or input_encoding or iconv.input_encoding is used. ; The precedence is: default_charset < input_encoding < iconv.input_encoding ;iconv.input_encoding = ; Use of this INI entry is deprecated, use global internal_encoding instead. ; If empty, default_charset or internal_encoding or iconv.internal_encoding is used. ; The precedence is: default_charset < internal_encoding < iconv.internal_encoding ;iconv.internal_encoding = ; Use of this INI entry is deprecated, use global output_encoding instead. ; If empty, default_charset or output_encoding or iconv.output_encoding is used. ; The precedence is: default_charset < output_encoding < iconv.output_encoding ; To use an output encoding conversion, iconv's output handler must be set ; otherwise output encoding conversion cannot be performed. ;iconv.output_encoding = [imap] ; rsh/ssh logins are disabled by default. Use this INI entry if you want to ; enable them. Note that the IMAP library does not filter mailbox names before ; passing them to rsh/ssh command, thus passing untrusted data to this function ; with rsh/ssh enabled is insecure. ;imap.enable_insecure_rsh=0 [intl] ;intl.default_locale = ; This directive allows you to produce PHP errors when some error ; happens within intl functions. The value is the level of the error produced. ; Default is 0, which does not produce any errors. ;intl.error_level = E_WARNING ;intl.use_exceptions = 0 [sqlite3] ; Directory pointing to SQLite3 extensions ; https://php.net/sqlite3.extension-dir ;sqlite3.extension_dir = ; SQLite defensive mode flag (only available from SQLite 3.26+) ; When the defensive flag is enabled, language features that allow ordinary ; SQL to deliberately corrupt the database file are disabled. This forbids ; writing directly to the schema, shadow tables (eg. FTS data tables), or ; the sqlite_dbpage virtual table. ; https://www.sqlite.org/c3ref/c_dbconfig_defensive.html ; (for older SQLite versions, this flag has no use) ;sqlite3.defensive = 1 [Pcre] ; PCRE library backtracking limit. ; https://php.net/pcre.backtrack-limit ;pcre.backtrack_limit=100000 ; PCRE library recursion limit. ; Please note that if you set this value to a high number you may consume all ; the available process stack and eventually crash PHP (due to reaching the ; stack size limit imposed by the Operating System). ; https://php.net/pcre.recursion-limit ;pcre.recursion_limit=100000 ; Enables or disables JIT compilation of patterns. This requires the PCRE ; library to be compiled with JIT support. ;pcre.jit=1 [Pdo] ; Whether to pool ODBC connections. Can be one of "strict", "relaxed" or "off" ; https://php.net/pdo-odbc.connection-pooling ;pdo_odbc.connection_pooling=strict [Pdo_mysql] ; Default socket name for local MySQL connects. If empty, uses the built-in ; MySQL defaults. pdo_mysql.default_socket= [Phar] ; https://php.net/phar.readonly ;phar.readonly = On ; https://php.net/phar.require-hash ;phar.require_hash = On ;phar.cache_list = [mail function] ; For Win32 only. ; https://php.net/smtp SMTP = localhost ; https://php.net/smtp-port smtp_port = 25 ; For Win32 only. ; https://php.net/sendmail-from ;sendmail_from = me@example.com ; For Unix only. You may supply arguments as well (default: "sendmail -t -i"). ; https://php.net/sendmail-path ;sendmail_path = ; Force the addition of the specified parameters to be passed as extra parameters ; to the sendmail binary. These parameters will always replace the value of ; the 5th parameter to mail(). ;mail.force_extra_parameters = ; Add X-PHP-Originating-Script: that will include uid of the script followed by the filename mail.add_x_header = Off ; Use mixed LF and CRLF line separators to keep compatibility with some ; RFC 2822 non conformant MTA. mail.mixed_lf_and_crlf = Off ; The path to a log file that will log all mail() calls. Log entries include ; the full path of the script, line number, To address and headers. ;mail.log = ; Log mail to syslog (Event Log on Windows). ;mail.log = syslog [ODBC] ; https://php.net/odbc.default-db ;odbc.default_db = Not yet implemented ; https://php.net/odbc.default-user ;odbc.default_user = Not yet implemented ; https://php.net/odbc.default-pw ;odbc.default_pw = Not yet implemented ; Controls the ODBC cursor model. ; Default: SQL_CURSOR_STATIC (default). ;odbc.default_cursortype ; Allow or prevent persistent links. ; https://php.net/odbc.allow-persistent odbc.allow_persistent = On ; Check that a connection is still valid before reuse. ; https://php.net/odbc.check-persistent odbc.check_persistent = On ; Maximum number of persistent links. -1 means no limit. ; https://php.net/odbc.max-persistent odbc.max_persistent = -1 ; Maximum number of links (persistent + non-persistent). -1 means no limit. ; https://php.net/odbc.max-links odbc.max_links = -1 ; Handling of LONG fields. Returns number of bytes to variables. 0 means ; passthru. ; https://php.net/odbc.defaultlrl odbc.defaultlrl = 4096 ; Handling of binary data. 0 means passthru, 1 return as is, 2 convert to char. ; See the documentation on odbc_binmode and odbc_longreadlen for an explanation ; of odbc.defaultlrl and odbc.defaultbinmode ; https://php.net/odbc.defaultbinmode odbc.defaultbinmode = 1 [MySQLi] ; Maximum number of persistent links. -1 means no limit. ; https://php.net/mysqli.max-persistent mysqli.max_persistent = -1 ; Allow accessing, from PHP's perspective, local files with LOAD DATA statements ; https://php.net/mysqli.allow_local_infile ;mysqli.allow_local_infile = On ; It allows the user to specify a folder where files that can be sent via LOAD DATA ; LOCAL can exist. It is ignored if mysqli.allow_local_infile is enabled. ;mysqli.local_infile_directory = ; Allow or prevent persistent links. ; https://php.net/mysqli.allow-persistent mysqli.allow_persistent = On ; Maximum number of links. -1 means no limit. ; https://php.net/mysqli.max-links mysqli.max_links = -1 ; Default port number for mysqli_connect(). If unset, mysqli_connect() will use ; the $MYSQL_TCP_PORT or the mysql-tcp entry in /etc/services or the ; compile-time value defined MYSQL_PORT (in that order). Win32 will only look ; at MYSQL_PORT. ; https://php.net/mysqli.default-port mysqli.default_port = 3306 ; Default socket name for local MySQL connects. If empty, uses the built-in ; MySQL defaults. ; https://php.net/mysqli.default-socket mysqli.default_socket = ; Default host for mysqli_connect() (doesn't apply in safe mode). ; https://php.net/mysqli.default-host mysqli.default_host = ; Default user for mysqli_connect() (doesn't apply in safe mode). ; https://php.net/mysqli.default-user mysqli.default_user = ; Default password for mysqli_connect() (doesn't apply in safe mode). ; Note that this is generally a *bad* idea to store passwords in this file. ; *Any* user with PHP access can run 'echo get_cfg_var("mysqli.default_pw") ; and reveal this password! And of course, any users with read access to this ; file will be able to reveal the password as well. ; https://php.net/mysqli.default-pw mysqli.default_pw = ; If this option is enabled, closing a persistent connection will rollback ; any pending transactions of this connection, before it is put back ; into the persistent connection pool. ;mysqli.rollback_on_cached_plink = Off [mysqlnd] ; Enable / Disable collection of general statistics by mysqlnd which can be ; used to tune and monitor MySQL operations. mysqlnd.collect_statistics = On ; Enable / Disable collection of memory usage statistics by mysqlnd which can be ; used to tune and monitor MySQL operations. mysqlnd.collect_memory_statistics = Off ; Records communication from all extensions using mysqlnd to the specified log ; file. ; https://php.net/mysqlnd.debug ;mysqlnd.debug = ; Defines which queries will be logged. ;mysqlnd.log_mask = 0 ; Default size of the mysqlnd memory pool, which is used by result sets. ;mysqlnd.mempool_default_size = 16000 ; Size of a pre-allocated buffer used when sending commands to MySQL in bytes. ;mysqlnd.net_cmd_buffer_size = 2048 ; Size of a pre-allocated buffer used for reading data sent by the server in ; bytes. ;mysqlnd.net_read_buffer_size = 32768 ; Timeout for network requests in seconds. ;mysqlnd.net_read_timeout = 31536000 ; SHA-256 Authentication Plugin related. File with the MySQL server public RSA ; key. ;mysqlnd.sha256_server_public_key = [OCI8] ; Connection: Enables privileged connections using external ; credentials (OCI_SYSOPER, OCI_SYSDBA) ; https://php.net/oci8.privileged-connect ;oci8.privileged_connect = Off ; Connection: The maximum number of persistent OCI8 connections per ; process. Using -1 means no limit. ; https://php.net/oci8.max-persistent ;oci8.max_persistent = -1 ; Connection: The maximum number of seconds a process is allowed to ; maintain an idle persistent connection. Using -1 means idle ; persistent connections will be maintained forever. ; https://php.net/oci8.persistent-timeout ;oci8.persistent_timeout = -1 ; Connection: The number of seconds that must pass before issuing a ; ping during oci_pconnect() to check the connection validity. When ; set to 0, each oci_pconnect() will cause a ping. Using -1 disables ; pings completely. ; https://php.net/oci8.ping-interval ;oci8.ping_interval = 60 ; Connection: Set this to a user chosen connection class to be used ; for all pooled server requests with Oracle Database Resident ; Connection Pooling (DRCP). To use DRCP, this value should be set to ; the same string for all web servers running the same application, ; the database pool must be configured, and the connection string must ; specify to use a pooled server. ;oci8.connection_class = ; High Availability: Using On lets PHP receive Fast Application ; Notification (FAN) events generated when a database node fails. The ; database must also be configured to post FAN events. ;oci8.events = Off ; Tuning: This option enables statement caching, and specifies how ; many statements to cache. Using 0 disables statement caching. ; https://php.net/oci8.statement-cache-size ;oci8.statement_cache_size = 20 ; Tuning: Enables row prefetching and sets the default number of ; rows that will be fetched automatically after statement execution. ; https://php.net/oci8.default-prefetch ;oci8.default_prefetch = 100 ; Tuning: Sets the amount of LOB data that is internally returned from ; Oracle Database when an Oracle LOB locator is initially retrieved as ; part of a query. Setting this can improve performance by reducing ; round-trips. ; https://php.net/oci8.prefetch-lob-size ; oci8.prefetch_lob_size = 0 ; Compatibility. Using On means oci_close() will not close ; oci_connect() and oci_new_connect() connections. ; https://php.net/oci8.old-oci-close-semantics ;oci8.old_oci_close_semantics = Off [PostgreSQL] ; Allow or prevent persistent links. ; https://php.net/pgsql.allow-persistent pgsql.allow_persistent = On ; Detect broken persistent links always with pg_pconnect(). ; Auto reset feature requires a little overheads. ; https://php.net/pgsql.auto-reset-persistent pgsql.auto_reset_persistent = Off ; Maximum number of persistent links. -1 means no limit. ; https://php.net/pgsql.max-persistent pgsql.max_persistent = -1 ; Maximum number of links (persistent+non persistent). -1 means no limit. ; https://php.net/pgsql.max-links pgsql.max_links = -1 ; Ignore PostgreSQL backends Notice message or not. ; Notice message logging require a little overheads. ; https://php.net/pgsql.ignore-notice pgsql.ignore_notice = 0 ; Log PostgreSQL backends Notice message or not. ; Unless pgsql.ignore_notice=0, module cannot log notice message. ; https://php.net/pgsql.log-notice pgsql.log_notice = 0 [bcmath] ; Number of decimal digits for all bcmath functions. ; https://php.net/bcmath.scale bcmath.scale = 0 [browscap] ; https://php.net/browscap ;browscap = extra/browscap.ini [Session] ; Handler used to store/retrieve data. ; https://php.net/session.save-handler session.save_handler = files ; Argument passed to save_handler. In the case of files, this is the path ; where data files are stored. Note: Windows users have to change this ; variable in order to use PHP's session functions. ; ; The path can be defined as: ; ; session.save_path = "N;/path" ; ; where N is an integer. Instead of storing all the session files in ; /path, what this will do is use subdirectories N-levels deep, and ; store the session data in those directories. This is useful if ; your OS has problems with many files in one directory, and is ; a more efficient layout for servers that handle many sessions. ; ; NOTE 1: PHP will not create this directory structure automatically. ; You can use the script in the ext/session dir for that purpose. ; NOTE 2: See the section on garbage collection below if you choose to ; use subdirectories for session storage ; ; The file storage module creates files using mode 600 by default. ; You can change that by using ; ; session.save_path = "N;MODE;/path" ; ; where MODE is the octal representation of the mode. Note that this ; does not overwrite the process's umask. ; https://php.net/session.save-path ;session.save_path = "/tmp" ; Whether to use strict session mode. ; Strict session mode does not accept an uninitialized session ID, and ; regenerates the session ID if the browser sends an uninitialized session ID. ; Strict mode protects applications from session fixation via a session adoption ; vulnerability. It is disabled by default for maximum compatibility, but ; enabling it is encouraged. ; https://wiki.php.net/rfc/strict_sessions session.use_strict_mode = 0 ; Whether to use cookies. ; https://php.net/session.use-cookies session.use_cookies = 1 ; https://php.net/session.cookie-secure ;session.cookie_secure = ; This option forces PHP to fetch and use a cookie for storing and maintaining ; the session id. We encourage this operation as it's very helpful in combating ; session hijacking when not specifying and managing your own session id. It is ; not the be-all and end-all of session hijacking defense, but it's a good start. ; https://php.net/session.use-only-cookies session.use_only_cookies = 1 ; Name of the session (used as cookie name). ; https://php.net/session.name session.name = PHPSESSID ; Initialize session on request startup. ; https://php.net/session.auto-start session.auto_start = 0 ; Lifetime in seconds of cookie or, if 0, until browser is restarted. ; https://php.net/session.cookie-lifetime session.cookie_lifetime = 0 ; The path for which the cookie is valid. ; https://php.net/session.cookie-path session.cookie_path = / ; The domain for which the cookie is valid. ; https://php.net/session.cookie-domain session.cookie_domain = ; Whether or not to add the httpOnly flag to the cookie, which makes it ; inaccessible to browser scripting languages such as JavaScript. ; https://php.net/session.cookie-httponly session.cookie_httponly = ; Add SameSite attribute to cookie to help mitigate Cross-Site Request Forgery (CSRF/XSRF) ; Current valid values are "Strict", "Lax" or "None". When using "None", ; make sure to include the quotes, as `none` is interpreted like `false` in ini files. ; https://tools.ietf.org/html/draft-west-first-party-cookies-07 session.cookie_samesite = ; Handler used to serialize data. php is the standard serializer of PHP. ; https://php.net/session.serialize-handler session.serialize_handler = php ; Defines the probability that the 'garbage collection' process is started on every ; session initialization. The probability is calculated by using gc_probability/gc_divisor, ; e.g. 1/100 means there is a 1% chance that the GC process starts on each request. ; Default Value: 1 ; Development Value: 1 ; Production Value: 1 ; https://php.net/session.gc-probability session.gc_probability = 1 ; Defines the probability that the 'garbage collection' process is started on every ; session initialization. The probability is calculated by using gc_probability/gc_divisor, ; e.g. 1/100 means there is a 1% chance that the GC process starts on each request. ; For high volume production servers, using a value of 1000 is a more efficient approach. ; Default Value: 100 ; Development Value: 1000 ; Production Value: 1000 ; https://php.net/session.gc-divisor session.gc_divisor = 1000 ; After this number of seconds, stored data will be seen as 'garbage' and ; cleaned up by the garbage collection process. ; https://php.net/session.gc-maxlifetime session.gc_maxlifetime = 1440 ; NOTE: If you are using the subdirectory option for storing session files ; (see session.save_path above), then garbage collection does *not* ; happen automatically. You will need to do your own garbage ; collection through a shell script, cron entry, or some other method. ; For example, the following script is the equivalent of setting ; session.gc_maxlifetime to 1440 (1440 seconds = 24 minutes): ; find /path/to/sessions -cmin +24 -type f | xargs rm ; Check HTTP Referer to invalidate externally stored URLs containing ids. ; HTTP_REFERER has to contain this substring for the session to be ; considered as valid. ; https://php.net/session.referer-check session.referer_check = ; Set to {nocache,private,public,} to determine HTTP caching aspects ; or leave this empty to avoid sending anti-caching headers. ; https://php.net/session.cache-limiter session.cache_limiter = nocache ; Document expires after n minutes. ; https://php.net/session.cache-expire session.cache_expire = 180 ; trans sid support is disabled by default. ; Use of trans sid may risk your users' security. ; Use this option with caution. ; - User may send URL contains active session ID ; to other person via. email/irc/etc. ; - URL that contains active session ID may be stored ; in publicly accessible computer. ; - User may access your site with the same session ID ; always using URL stored in browser's history or bookmarks. ; https://php.net/session.use-trans-sid session.use_trans_sid = 0 ; Set session ID character length. This value could be between 22 to 256. ; Shorter length than default is supported only for compatibility reason. ; Users should use 32 or more chars. ; https://php.net/session.sid-length ; Default Value: 32 ; Development Value: 26 ; Production Value: 26 session.sid_length = 26 ; The URL rewriter will look for URLs in a defined set of HTML tags. ;
is special; if you include them here, the rewriter will ; add a hidden field with the info which is otherwise appended ; to URLs. tag's action attribute URL will not be modified ; unless it is specified. ; Note that all valid entries require a "=", even if no value follows. ; Default Value: "a=href,area=href,frame=src,form=" ; Development Value: "a=href,area=href,frame=src,form=" ; Production Value: "a=href,area=href,frame=src,form=" ; https://php.net/url-rewriter.tags session.trans_sid_tags = "a=href,area=href,frame=src,form=" ; URL rewriter does not rewrite absolute URLs by default. ; To enable rewrites for absolute paths, target hosts must be specified ; at RUNTIME. i.e. use ini_set() ; tags is special. PHP will check action attribute's URL regardless ; of session.trans_sid_tags setting. ; If no host is defined, HTTP_HOST will be used for allowed host. ; Example value: php.net,www.php.net,wiki.php.net ; Use "," for multiple hosts. No spaces are allowed. ; Default Value: "" ; Development Value: "" ; Production Value: "" ;session.trans_sid_hosts="" ; Define how many bits are stored in each character when converting ; the binary hash data to something readable. ; Possible values: ; 4 (4 bits: 0-9, a-f) ; 5 (5 bits: 0-9, a-v) ; 6 (6 bits: 0-9, a-z, A-Z, "-", ",") ; Default Value: 4 ; Development Value: 5 ; Production Value: 5 ; https://php.net/session.hash-bits-per-character session.sid_bits_per_character = 5 ; Enable upload progress tracking in $_SESSION ; Default Value: On ; Development Value: On ; Production Value: On ; https://php.net/session.upload-progress.enabled ;session.upload_progress.enabled = On ; Cleanup the progress information as soon as all POST data has been read ; (i.e. upload completed). ; Default Value: On ; Development Value: On ; Production Value: On ; https://php.net/session.upload-progress.cleanup ;session.upload_progress.cleanup = On ; A prefix used for the upload progress key in $_SESSION ; Default Value: "upload_progress_" ; Development Value: "upload_progress_" ; Production Value: "upload_progress_" ; https://php.net/session.upload-progress.prefix ;session.upload_progress.prefix = "upload_progress_" ; The index name (concatenated with the prefix) in $_SESSION ; containing the upload progress information ; Default Value: "PHP_SESSION_UPLOAD_PROGRESS" ; Development Value: "PHP_SESSION_UPLOAD_PROGRESS" ; Production Value: "PHP_SESSION_UPLOAD_PROGRESS" ; https://php.net/session.upload-progress.name ;session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS" ; How frequently the upload progress should be updated. ; Given either in percentages (per-file), or in bytes ; Default Value: "1%" ; Development Value: "1%" ; Production Value: "1%" ; https://php.net/session.upload-progress.freq ;session.upload_progress.freq = "1%" ; The minimum delay between updates, in seconds ; Default Value: 1 ; Development Value: 1 ; Production Value: 1 ; https://php.net/session.upload-progress.min-freq ;session.upload_progress.min_freq = "1" ; Only write session data when session data is changed. Enabled by default. ; https://php.net/session.lazy-write ;session.lazy_write = On [Assertion] ; Switch whether to compile assertions at all (to have no overhead at run-time) ; -1: Do not compile at all ; 0: Jump over assertion at run-time ; 1: Execute assertions ; Changing from or to a negative value is only possible in php.ini! ; (For turning assertions on and off at run-time, toggle zend.assertions between the values 1 and 0) ; Default Value: 1 ; Development Value: 1 ; Production Value: -1 ; https://php.net/zend.assertions zend.assertions = -1 [COM] ; path to a file containing GUIDs, IIDs or filenames of files with TypeLibs ; https://php.net/com.typelib-file ;com.typelib_file = ; allow Distributed-COM calls ; https://php.net/com.allow-dcom ;com.allow_dcom = true ; autoregister constants of a component's typelib on com_load() ; https://php.net/com.autoregister-typelib ;com.autoregister_typelib = true ; register constants casesensitive ; https://php.net/com.autoregister-casesensitive ;com.autoregister_casesensitive = false ; show warnings on duplicate constant registrations ; https://php.net/com.autoregister-verbose ;com.autoregister_verbose = true ; The default character set code-page to use when passing strings to and from COM objects. ; Default: system ANSI code page ;com.code_page= ; The version of the .NET framework to use. The value of the setting are the first three parts ; of the framework's version number, separated by dots, and prefixed with "v", e.g. "v4.0.30319". ;com.dotnet_version= [mbstring] ; language for internal character representation. ; This affects mb_send_mail() and mbstring.detect_order. ; https://php.net/mbstring.language ;mbstring.language = Japanese ; Use of this INI entry is deprecated, use global internal_encoding instead. ; internal/script encoding. ; Some encoding cannot work as internal encoding. (e.g. SJIS, BIG5, ISO-2022-*) ; If empty, default_charset or internal_encoding or iconv.internal_encoding is used. ; The precedence is: default_charset < internal_encoding < iconv.internal_encoding ;mbstring.internal_encoding = ; Use of this INI entry is deprecated, use global input_encoding instead. ; http input encoding. ; mbstring.encoding_translation = On is needed to use this setting. ; If empty, default_charset or input_encoding or mbstring.input is used. ; The precedence is: default_charset < input_encoding < mbstring.http_input ; https://php.net/mbstring.http-input ;mbstring.http_input = ; Use of this INI entry is deprecated, use global output_encoding instead. ; http output encoding. ; mb_output_handler must be registered as output buffer to function. ; If empty, default_charset or output_encoding or mbstring.http_output is used. ; The precedence is: default_charset < output_encoding < mbstring.http_output ; To use an output encoding conversion, mbstring's output handler must be set ; otherwise output encoding conversion cannot be performed. ; https://php.net/mbstring.http-output ;mbstring.http_output = ; enable automatic encoding translation according to ; mbstring.internal_encoding setting. Input chars are ; converted to internal encoding by setting this to On. ; Note: Do _not_ use automatic encoding translation for ; portable libs/applications. ; https://php.net/mbstring.encoding-translation ;mbstring.encoding_translation = Off ; automatic encoding detection order. ; "auto" detect order is changed according to mbstring.language ; https://php.net/mbstring.detect-order ;mbstring.detect_order = auto ; substitute_character used when character cannot be converted ; one from another ; https://php.net/mbstring.substitute-character ;mbstring.substitute_character = none ; Enable strict encoding detection. ;mbstring.strict_detection = Off ; This directive specifies the regex pattern of content types for which mb_output_handler() ; is activated. ; Default: mbstring.http_output_conv_mimetypes=^(text/|application/xhtml\+xml) ;mbstring.http_output_conv_mimetypes= ; This directive specifies maximum stack depth for mbstring regular expressions. It is similar ; to the pcre.recursion_limit for PCRE. ;mbstring.regex_stack_limit=100000 ; This directive specifies maximum retry count for mbstring regular expressions. It is similar ; to the pcre.backtrack_limit for PCRE. ;mbstring.regex_retry_limit=1000000 [gd] ; Tell the jpeg decode to ignore warnings and try to create ; a gd image. The warning will then be displayed as notices ; disabled by default ; https://php.net/gd.jpeg-ignore-warning ;gd.jpeg_ignore_warning = 1 [exif] ; Exif UNICODE user comments are handled as UCS-2BE/UCS-2LE and JIS as JIS. ; With mbstring support this will automatically be converted into the encoding ; given by corresponding encode setting. When empty mbstring.internal_encoding ; is used. For the decode settings you can distinguish between motorola and ; intel byte order. A decode setting cannot be empty. ; https://php.net/exif.encode-unicode ;exif.encode_unicode = ISO-8859-15 ; https://php.net/exif.decode-unicode-motorola ;exif.decode_unicode_motorola = UCS-2BE ; https://php.net/exif.decode-unicode-intel ;exif.decode_unicode_intel = UCS-2LE ; https://php.net/exif.encode-jis ;exif.encode_jis = ; https://php.net/exif.decode-jis-motorola ;exif.decode_jis_motorola = JIS ; https://php.net/exif.decode-jis-intel ;exif.decode_jis_intel = JIS [Tidy] ; The path to a default tidy configuration file to use when using tidy ; https://php.net/tidy.default-config ;tidy.default_config = /usr/local/lib/php/default.tcfg ; Should tidy clean and repair output automatically? ; WARNING: Do not use this option if you are generating non-html content ; such as dynamic images ; https://php.net/tidy.clean-output tidy.clean_output = Off [soap] ; Enables or disables WSDL caching feature. ; https://php.net/soap.wsdl-cache-enabled soap.wsdl_cache_enabled=1 ; Sets the directory name where SOAP extension will put cache files. ; https://php.net/soap.wsdl-cache-dir soap.wsdl_cache_dir="/tmp" ; (time to live) Sets the number of second while cached file will be used ; instead of original one. ; https://php.net/soap.wsdl-cache-ttl soap.wsdl_cache_ttl=86400 ; Sets the size of the cache limit. (Max. number of WSDL files to cache) soap.wsdl_cache_limit = 5 [sysvshm] ; A default size of the shared memory segment ;sysvshm.init_mem = 10000 [ldap] ; Sets the maximum number of open links or -1 for unlimited. ldap.max_links = -1 [dba] ;dba.default_handler= [opcache] ; Determines if Zend OPCache is enabled ;opcache.enable=1 ; Determines if Zend OPCache is enabled for the CLI version of PHP ;opcache.enable_cli=0 ; The OPcache shared memory storage size. ;opcache.memory_consumption=128 ; The amount of memory for interned strings in Mbytes. ;opcache.interned_strings_buffer=8 ; The maximum number of keys (scripts) in the OPcache hash table. ; Only numbers between 200 and 1000000 are allowed. ;opcache.max_accelerated_files=10000 ; The maximum percentage of "wasted" memory until a restart is scheduled. ;opcache.max_wasted_percentage=5 ; When this directive is enabled, the OPcache appends the current working ; directory to the script key, thus eliminating possible collisions between ; files with the same name (basename). Disabling the directive improves ; performance, but may break existing applications. ;opcache.use_cwd=1 ; When disabled, you must reset the OPcache manually or restart the ; webserver for changes to the filesystem to take effect. ;opcache.validate_timestamps=1 ; How often (in seconds) to check file timestamps for changes to the shared ; memory storage allocation. ("1" means validate once per second, but only ; once per request. "0" means always validate) ;opcache.revalidate_freq=2 ; Enables or disables file search in include_path optimization ;opcache.revalidate_path=0 ; If disabled, all PHPDoc comments are dropped from the code to reduce the ; size of the optimized code. ;opcache.save_comments=1 ; If enabled, compilation warnings (including notices and deprecations) will ; be recorded and replayed each time a file is included. Otherwise, compilation ; warnings will only be emitted when the file is first cached. ;opcache.record_warnings=0 ; Allow file existence override (file_exists, etc.) performance feature. ;opcache.enable_file_override=0 ; A bitmask, where each bit enables or disables the appropriate OPcache ; passes ;opcache.optimization_level=0x7FFFBFFF ;opcache.dups_fix=0 ; The location of the OPcache blacklist file (wildcards allowed). ; Each OPcache blacklist file is a text file that holds the names of files ; that should not be accelerated. The file format is to add each filename ; to a new line. The filename may be a full path or just a file prefix ; (i.e., /var/www/x blacklists all the files and directories in /var/www ; that start with 'x'). Line starting with a ; are ignored (comments). ;opcache.blacklist_filename= ; Allows exclusion of large files from being cached. By default all files ; are cached. ;opcache.max_file_size=0 ; How long to wait (in seconds) for a scheduled restart to begin if the cache ; is not being accessed. ;opcache.force_restart_timeout=180 ; OPcache error_log file name. Empty string assumes "stderr". ;opcache.error_log= ; All OPcache errors go to the Web server log. ; By default, only fatal errors (level 0) or errors (level 1) are logged. ; You can also enable warnings (level 2), info messages (level 3) or ; debug messages (level 4). ;opcache.log_verbosity_level=1 ; Preferred Shared Memory back-end. Leave empty and let the system decide. ;opcache.preferred_memory_model= ; Protect the shared memory from unexpected writing during script execution. ; Useful for internal debugging only. ;opcache.protect_memory=0 ; Allows calling OPcache API functions only from PHP scripts which path is ; started from specified string. The default "" means no restriction ;opcache.restrict_api= ; Mapping base of shared memory segments (for Windows only). All the PHP ; processes have to map shared memory into the same address space. This ; directive allows to manually fix the "Unable to reattach to base address" ; errors. ;opcache.mmap_base= ; Facilitates multiple OPcache instances per user (for Windows only). All PHP ; processes with the same cache ID and user share an OPcache instance. ;opcache.cache_id= ; Enables and sets the second level cache directory. ; It should improve performance when SHM memory is full, at server restart or ; SHM reset. The default "" disables file based caching. ;opcache.file_cache= ; Enables or disables opcode caching in shared memory. ;opcache.file_cache_only=0 ; Enables or disables checksum validation when script loaded from file cache. ;opcache.file_cache_consistency_checks=1 ; Implies opcache.file_cache_only=1 for a certain process that failed to ; reattach to the shared memory (for Windows only). Explicitly enabled file ; cache is required. ;opcache.file_cache_fallback=1 ; Enables or disables copying of PHP code (text segment) into HUGE PAGES. ; Under certain circumstances (if only a single global PHP process is ; started from which all others fork), this can increase performance ; by a tiny amount because TLB misses are reduced. On the other hand, this ; delays PHP startup, increases memory usage and degrades performance ; under memory pressure - use with care. ; Requires appropriate OS configuration. ;opcache.huge_code_pages=0 ; Validate cached file permissions. ;opcache.validate_permission=0 ; Prevent name collisions in chroot'ed environment. ;opcache.validate_root=0 ; If specified, it produces opcode dumps for debugging different stages of ; optimizations. ;opcache.opt_debug_level=0 ; Specifies a PHP script that is going to be compiled and executed at server ; start-up. ; https://php.net/opcache.preload ;opcache.preload= ; Preloading code as root is not allowed for security reasons. This directive ; facilitates to let the preloading to be run as another user. ; https://php.net/opcache.preload_user ;opcache.preload_user= ; Prevents caching files that are less than this number of seconds old. It ; protects from caching of incompletely updated files. In case all file updates ; on your site are atomic, you may increase performance by setting it to "0". ;opcache.file_update_protection=2 ; Absolute path used to store shared lockfiles (for *nix only). ;opcache.lockfile_path=/tmp [curl] ; A default value for the CURLOPT_CAINFO option. This is required to be an ; absolute path. ;curl.cainfo = [openssl] ; The location of a Certificate Authority (CA) file on the local filesystem ; to use when verifying the identity of SSL/TLS peers. Most users should ; not specify a value for this directive as PHP will attempt to use the ; OS-managed cert stores in its absence. If specified, this value may still ; be overridden on a per-stream basis via the "cafile" SSL stream context ; option. ;openssl.cafile= ; If openssl.cafile is not specified or if the CA file is not found, the ; directory pointed to by openssl.capath is searched for a suitable ; certificate. This value must be a correctly hashed certificate directory. ; Most users should not specify a value for this directive as PHP will ; attempt to use the OS-managed cert stores in its absence. If specified, ; this value may still be overridden on a per-stream basis via the "capath" ; SSL stream context option. ;openssl.capath= [ffi] ; FFI API restriction. Possible values: ; "preload" - enabled in CLI scripts and preloaded files (default) ; "false" - always disabled ; "true" - always enabled ;ffi.enable=preload ; List of headers files to preload, wildcard patterns allowed. ;ffi.preload= PK=1]   !doc/ea-php83-php-common/README.mdnu[ # The PHP Interpreter PHP is a popular general-purpose scripting language that is especially suited to web development. Fast, flexible and pragmatic, PHP powers everything from your blog to the most popular websites in the world. PHP is distributed under the [PHP License v3.01](LICENSE). [![Push](https://github.com/php/php-src/actions/workflows/push.yml/badge.svg)](https://github.com/php/php-src/actions/workflows/push.yml) [![Build status](https://travis-ci.com/php/php-src.svg?branch=master)](https://travis-ci.com/github/php/php-src) [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/php.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:php) ## Documentation The PHP manual is available at [php.net/docs](https://php.net/docs). ## Installation ### Prebuilt packages and binaries Prebuilt packages and binaries can be used to get up and running fast with PHP. For Windows, the PHP binaries can be obtained from [windows.php.net](https://windows.php.net). After extracting the archive the `*.exe` files are ready to use. For other systems, see the [installation chapter](https://php.net/install). ### Building PHP source code *For Windows, see [Build your own PHP on Windows](https://wiki.php.net/internals/windows/stepbystepbuild_sdk_2).* For a minimal PHP build from Git, you will need autoconf, bison, and re2c. For a default build, you will additionally need libxml2 and libsqlite3. On Ubuntu, you can install these using: sudo apt install -y pkg-config build-essential autoconf bison re2c \ libxml2-dev libsqlite3-dev On Fedora, you can install these using: sudo dnf install re2c bison autoconf make libtool ccache libxml2-devel sqlite-devel Generate configure: ./buildconf Configure your build. `--enable-debug` is recommended for development, see `./configure --help` for a full list of options. # For development ./configure --enable-debug # For production ./configure Build PHP. To speed up the build, specify the maximum number of jobs using `-j`: make -j4 The number of jobs should usually match the number of available cores, which can be determined using `nproc`. ## Testing PHP source code PHP ships with an extensive test suite, the command `make test` is used after successful compilation of the sources to run this test suite. It is possible to run tests using multiple cores by setting `-jN` in `TEST_PHP_ARGS`: make TEST_PHP_ARGS=-j4 test Shall run `make test` with a maximum of 4 concurrent jobs: Generally the maximum number of jobs should not exceed the number of cores available. The [qa.php.net](https://qa.php.net) site provides more detailed info about testing and quality assurance. ## Installing PHP built from source After a successful build (and test), PHP may be installed with: make install Depending on your permissions and prefix, `make install` may need super user permissions. ## PHP extensions Extensions provide additional functionality on top of PHP. PHP consists of many essential bundled extensions. Additional extensions can be found in the PHP Extension Community Library - [PECL](https://pecl.php.net). ## Contributing The PHP source code is located in the Git repository at [github.com/php/php-src](https://github.com/php/php-src). Contributions are most welcome by forking the repository and sending a pull request. Discussions are done on GitHub, but depending on the topic can also be relayed to the official PHP developer mailing list internals@lists.php.net. New features require an RFC and must be accepted by the developers. See [Request for comments - RFC](https://wiki.php.net/rfc) and [Voting on PHP features](https://wiki.php.net/rfc/voting) for more information on the process. Bug fixes don't require an RFC. If the bug has a GitHub issue, reference it in the commit message using `GH-NNNNNN`. Use `#NNNNNN` for tickets in the old [bugs.php.net](https://bugs.php.net) bug tracker. Fix GH-7815: php_uname doesn't recognise latest Windows versions Fix #55371: get_magic_quotes_gpc() throws deprecation warning See [Git workflow](https://wiki.php.net/vcs/gitworkflow) for details on how pull requests are merged. ### Guidelines for contributors See further documents in the repository for more information on how to contribute: - [Contributing to PHP](/CONTRIBUTING.md) - [PHP coding standards](/CODING_STANDARDS.md) - [Mailing list rules](/docs/mailinglist-rules.md) - [PHP release process](/docs/release-process.md) ## Credits For the list of people who've put work into PHP, please see the [PHP credits page](https://php.net/credits.php). PK=1]+mO##+doc/ea-php83-php-common/php.ini-developmentnu[[PHP] ;;;;;;;;;;;;;;;;;;; ; About php.ini ; ;;;;;;;;;;;;;;;;;;; ; PHP's initialization file, generally called php.ini, is responsible for ; configuring many of the aspects of PHP's behavior. ; PHP attempts to find and load this configuration from a number of locations. ; The following is a summary of its search order: ; 1. SAPI module specific location. ; 2. The PHPRC environment variable. ; 3. A number of predefined registry keys on Windows ; 4. Current working directory (except CLI) ; 5. The web server's directory (for SAPI modules), or directory of PHP ; (otherwise in Windows) ; 6. The directory from the --with-config-file-path compile time option, or the ; Windows directory (usually C:\windows) ; See the PHP docs for more specific information. ; https://php.net/configuration.file ; The syntax of the file is extremely simple. Whitespace and lines ; beginning with a semicolon are silently ignored (as you probably guessed). ; Section headers (e.g. [Foo]) are also silently ignored, even though ; they might mean something in the future. ; Directives following the section heading [PATH=/www/mysite] only ; apply to PHP files in the /www/mysite directory. Directives ; following the section heading [HOST=www.example.com] only apply to ; PHP files served from www.example.com. Directives set in these ; special sections cannot be overridden by user-defined INI files or ; at runtime. Currently, [PATH=] and [HOST=] sections only work under ; CGI/FastCGI. ; https://php.net/ini.sections ; Directives are specified using the following syntax: ; directive = value ; Directive names are *case sensitive* - foo=bar is different from FOO=bar. ; Directives are variables used to configure PHP or PHP extensions. ; There is no name validation. If PHP can't find an expected ; directive because it is not set or is mistyped, a default value will be used. ; The value can be a string, a number, a PHP constant (e.g. E_ALL or M_PI), one ; of the INI constants (On, Off, True, False, Yes, No and None) or an expression ; (e.g. E_ALL & ~E_NOTICE), a quoted string ("bar"), or a reference to a ; previously set variable or directive (e.g. ${foo}) ; Expressions in the INI file are limited to bitwise operators and parentheses: ; | bitwise OR ; ^ bitwise XOR ; & bitwise AND ; ~ bitwise NOT ; ! boolean NOT ; Boolean flags can be turned on using the values 1, On, True or Yes. ; They can be turned off using the values 0, Off, False or No. ; An empty string can be denoted by simply not writing anything after the equal ; sign, or by using the None keyword: ; foo = ; sets foo to an empty string ; foo = None ; sets foo to an empty string ; foo = "None" ; sets foo to the string 'None' ; If you use constants in your value, and these constants belong to a ; dynamically loaded extension (either a PHP extension or a Zend extension), ; you may only use these constants *after* the line that loads the extension. ;;;;;;;;;;;;;;;;;;; ; About this file ; ;;;;;;;;;;;;;;;;;;; ; PHP comes packaged with two INI files. One that is recommended to be used ; in production environments and one that is recommended to be used in ; development environments. ; php.ini-production contains settings which hold security, performance and ; best practices at its core. But please be aware, these settings may break ; compatibility with older or less security-conscious applications. We ; recommending using the production ini in production and testing environments. ; php.ini-development is very similar to its production variant, except it is ; much more verbose when it comes to errors. We recommend using the ; development version only in development environments, as errors shown to ; application users can inadvertently leak otherwise secure information. ; This is the php.ini-development INI file. ;;;;;;;;;;;;;;;;;;; ; Quick Reference ; ;;;;;;;;;;;;;;;;;;; ; The following are all the settings which are different in either the production ; or development versions of the INIs with respect to PHP's default behavior. ; Please see the actual settings later in the document for more details as to why ; we recommend these changes in PHP's behavior. ; display_errors ; Default Value: On ; Development Value: On ; Production Value: Off ; display_startup_errors ; Default Value: On ; Development Value: On ; Production Value: Off ; error_reporting ; Default Value: E_ALL ; Development Value: E_ALL ; Production Value: E_ALL & ~E_DEPRECATED & ~E_STRICT ; log_errors ; Default Value: Off ; Development Value: On ; Production Value: On ; max_input_time ; Default Value: -1 (Unlimited) ; Development Value: 60 (60 seconds) ; Production Value: 60 (60 seconds) ; output_buffering ; Default Value: Off ; Development Value: 4096 ; Production Value: 4096 ; register_argc_argv ; Default Value: On ; Development Value: Off ; Production Value: Off ; request_order ; Default Value: None ; Development Value: "GP" ; Production Value: "GP" ; session.gc_divisor ; Default Value: 100 ; Development Value: 1000 ; Production Value: 1000 ; session.sid_bits_per_character ; Default Value: 4 ; Development Value: 5 ; Production Value: 5 ; session.sid_length ; Default Value: 32 ; Development Value: 26 ; Production Value: 26 ; short_open_tag ; Default Value: On ; Development Value: Off ; Production Value: Off ; variables_order ; Default Value: "EGPCS" ; Development Value: "GPCS" ; Production Value: "GPCS" ; zend.assertions ; Default Value: 1 ; Development Value: 1 ; Production Value: -1 ; zend.exception_ignore_args ; Default Value: Off ; Development Value: Off ; Production Value: On ; zend.exception_string_param_max_len ; Default Value: 15 ; Development Value: 15 ; Production Value: 0 ;;;;;;;;;;;;;;;;;;;; ; php.ini Options ; ;;;;;;;;;;;;;;;;;;;; ; Name for user-defined php.ini (.htaccess) files. Default is ".user.ini" ;user_ini.filename = ".user.ini" ; To disable this feature set this option to an empty value ;user_ini.filename = ; TTL for user-defined php.ini files (time-to-live) in seconds. Default is 300 seconds (5 minutes) ;user_ini.cache_ttl = 300 ;;;;;;;;;;;;;;;;;;;; ; Language Options ; ;;;;;;;;;;;;;;;;;;;; ; Enable the PHP scripting language engine under Apache. ; https://php.net/engine engine = On ; This directive determines whether or not PHP will recognize code between ; tags as PHP source which should be processed as such. It is ; generally recommended that should be used and that this feature ; should be disabled, as enabling it may result in issues when generating XML ; documents, however this remains supported for backward compatibility reasons. ; Note that this directive does not control the would work. ; https://php.net/syntax-highlighting ;highlight.string = #DD0000 ;highlight.comment = #FF9900 ;highlight.keyword = #007700 ;highlight.default = #0000BB ;highlight.html = #000000 ; If enabled, the request will be allowed to complete even if the user aborts ; the request. Consider enabling it if executing long requests, which may end up ; being interrupted by the user or a browser timing out. PHP's default behavior ; is to disable this feature. ; https://php.net/ignore-user-abort ;ignore_user_abort = On ; Determines the size of the realpath cache to be used by PHP. This value should ; be increased on systems where PHP opens many files to reflect the quantity of ; the file operations performed. ; Note: if open_basedir is set, the cache is disabled ; https://php.net/realpath-cache-size ;realpath_cache_size = 4096k ; Duration of time, in seconds for which to cache realpath information for a given ; file or directory. For systems with rarely changing files, consider increasing this ; value. ; https://php.net/realpath-cache-ttl ;realpath_cache_ttl = 120 ; Enables or disables the circular reference collector. ; https://php.net/zend.enable-gc zend.enable_gc = On ; If enabled, scripts may be written in encodings that are incompatible with ; the scanner. CP936, Big5, CP949 and Shift_JIS are the examples of such ; encodings. To use this feature, mbstring extension must be enabled. ;zend.multibyte = Off ; Allows to set the default encoding for the scripts. This value will be used ; unless "declare(encoding=...)" directive appears at the top of the script. ; Only affects if zend.multibyte is set. ;zend.script_encoding = ; Allows to include or exclude arguments from stack traces generated for exceptions. ; In production, it is recommended to turn this setting on to prohibit the output ; of sensitive information in stack traces ; Default Value: Off ; Development Value: Off ; Production Value: On zend.exception_ignore_args = Off ; Allows setting the maximum string length in an argument of a stringified stack trace ; to a value between 0 and 1000000. ; This has no effect when zend.exception_ignore_args is enabled. ; Default Value: 15 ; Development Value: 15 ; Production Value: 0 zend.exception_string_param_max_len = 15 ;;;;;;;;;;;;;;;;; ; Miscellaneous ; ;;;;;;;;;;;;;;;;; ; Decides whether PHP may expose the fact that it is installed on the server ; (e.g. by adding its signature to the Web server header). It is no security ; threat in any way, but it makes it possible to determine whether you use PHP ; on your server or not. ; https://php.net/expose-php expose_php = On ;;;;;;;;;;;;;;;;;;; ; Resource Limits ; ;;;;;;;;;;;;;;;;;;; ; Maximum execution time of each script, in seconds ; https://php.net/max-execution-time ; Note: This directive is hardcoded to 0 for the CLI SAPI max_execution_time = 30 ; Maximum amount of time each script may spend parsing request data. It's a good ; idea to limit this time on productions servers in order to eliminate unexpectedly ; long running scripts. ; Note: This directive is hardcoded to -1 for the CLI SAPI ; Default Value: -1 (Unlimited) ; Development Value: 60 (60 seconds) ; Production Value: 60 (60 seconds) ; https://php.net/max-input-time max_input_time = 60 ; Maximum input variable nesting level ; https://php.net/max-input-nesting-level ;max_input_nesting_level = 64 ; How many GET/POST/COOKIE input variables may be accepted ;max_input_vars = 1000 ; How many multipart body parts (combined input variable and file uploads) may ; be accepted. ; Default Value: -1 (Sum of max_input_vars and max_file_uploads) ;max_multipart_body_parts = 1500 ; Maximum amount of memory a script may consume ; https://php.net/memory-limit memory_limit = 128M ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Error handling and logging ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; This directive informs PHP of which errors, warnings and notices you would like ; it to take action for. The recommended way of setting values for this ; directive is through the use of the error level constants and bitwise ; operators. The error level constants are below here for convenience as well as ; some common settings and their meanings. ; By default, PHP is set to take action on all errors, notices and warnings EXCEPT ; those related to E_NOTICE and E_STRICT, which together cover best practices and ; recommended coding standards in PHP. For performance reasons, this is the ; recommend error reporting setting. Your production server shouldn't be wasting ; resources complaining about best practices and coding standards. That's what ; development servers and development settings are for. ; Note: The php.ini-development file has this setting as E_ALL. This ; means it pretty much reports everything which is exactly what you want during ; development and early testing. ; ; Error Level Constants: ; E_ALL - All errors and warnings ; E_ERROR - fatal run-time errors ; E_RECOVERABLE_ERROR - almost fatal run-time errors ; E_WARNING - run-time warnings (non-fatal errors) ; E_PARSE - compile-time parse errors ; E_NOTICE - run-time notices (these are warnings which often result ; from a bug in your code, but it's possible that it was ; intentional (e.g., using an uninitialized variable and ; relying on the fact it is automatically initialized to an ; empty string) ; E_STRICT - run-time notices, enable to have PHP suggest changes ; to your code which will ensure the best interoperability ; and forward compatibility of your code ; E_CORE_ERROR - fatal errors that occur during PHP's initial startup ; E_CORE_WARNING - warnings (non-fatal errors) that occur during PHP's ; initial startup ; E_COMPILE_ERROR - fatal compile-time errors ; E_COMPILE_WARNING - compile-time warnings (non-fatal errors) ; E_USER_ERROR - user-generated error message ; E_USER_WARNING - user-generated warning message ; E_USER_NOTICE - user-generated notice message ; E_DEPRECATED - warn about code that will not work in future versions ; of PHP ; E_USER_DEPRECATED - user-generated deprecation warnings ; ; Common Values: ; E_ALL (Show all errors, warnings and notices including coding standards.) ; E_ALL & ~E_NOTICE (Show all errors, except for notices) ; E_ALL & ~E_NOTICE & ~E_STRICT (Show all errors, except for notices and coding standards warnings.) ; E_COMPILE_ERROR|E_RECOVERABLE_ERROR|E_ERROR|E_CORE_ERROR (Show only errors) ; Default Value: E_ALL ; Development Value: E_ALL ; Production Value: E_ALL & ~E_DEPRECATED & ~E_STRICT ; https://php.net/error-reporting error_reporting = E_ALL ; This directive controls whether or not and where PHP will output errors, ; notices and warnings too. Error output is very useful during development, but ; it could be very dangerous in production environments. Depending on the code ; which is triggering the error, sensitive information could potentially leak ; out of your application such as database usernames and passwords or worse. ; For production environments, we recommend logging errors rather than ; sending them to STDOUT. ; Possible Values: ; Off = Do not display any errors ; stderr = Display errors to STDERR (affects only CGI/CLI binaries!) ; On or stdout = Display errors to STDOUT ; Default Value: On ; Development Value: On ; Production Value: Off ; https://php.net/display-errors display_errors = On ; The display of errors which occur during PHP's startup sequence are handled ; separately from display_errors. We strongly recommend you set this to 'off' ; for production servers to avoid leaking configuration details. ; Default Value: On ; Development Value: On ; Production Value: Off ; https://php.net/display-startup-errors display_startup_errors = On ; Besides displaying errors, PHP can also log errors to locations such as a ; server-specific log, STDERR, or a location specified by the error_log ; directive found below. While errors should not be displayed on productions ; servers they should still be monitored and logging is a great way to do that. ; Default Value: Off ; Development Value: On ; Production Value: On ; https://php.net/log-errors log_errors = On ; Do not log repeated messages. Repeated errors must occur in same file on same ; line unless ignore_repeated_source is set true. ; https://php.net/ignore-repeated-errors ignore_repeated_errors = Off ; Ignore source of message when ignoring repeated messages. When this setting ; is On you will not log errors with repeated messages from different files or ; source lines. ; https://php.net/ignore-repeated-source ignore_repeated_source = Off ; If this parameter is set to Off, then memory leaks will not be shown (on ; stdout or in the log). This is only effective in a debug compile, and if ; error reporting includes E_WARNING in the allowed list ; https://php.net/report-memleaks report_memleaks = On ; This setting is off by default. ;report_zend_debug = 0 ; Turn off normal error reporting and emit XML-RPC error XML ; https://php.net/xmlrpc-errors ;xmlrpc_errors = 0 ; An XML-RPC faultCode ;xmlrpc_error_number = 0 ; When PHP displays or logs an error, it has the capability of formatting the ; error message as HTML for easier reading. This directive controls whether ; the error message is formatted as HTML or not. ; Note: This directive is hardcoded to Off for the CLI SAPI ; https://php.net/html-errors ;html_errors = On ; If html_errors is set to On *and* docref_root is not empty, then PHP ; produces clickable error messages that direct to a page describing the error ; or function causing the error in detail. ; You can download a copy of the PHP manual from https://php.net/docs ; and change docref_root to the base URL of your local copy including the ; leading '/'. You must also specify the file extension being used including ; the dot. PHP's default behavior is to leave these settings empty, in which ; case no links to documentation are generated. ; Note: Never use this feature for production boxes. ; https://php.net/docref-root ; Examples ;docref_root = "/phpmanual/" ; https://php.net/docref-ext ;docref_ext = .html ; String to output before an error message. PHP's default behavior is to leave ; this setting blank. ; https://php.net/error-prepend-string ; Example: ;error_prepend_string = "" ; String to output after an error message. PHP's default behavior is to leave ; this setting blank. ; https://php.net/error-append-string ; Example: ;error_append_string = "" ; Log errors to specified file. PHP's default behavior is to leave this value ; empty. ; https://php.net/error-log ; Example: ;error_log = php_errors.log ; Log errors to syslog (Event Log on Windows). ;error_log = syslog ; The syslog ident is a string which is prepended to every message logged ; to syslog. Only used when error_log is set to syslog. ;syslog.ident = php ; The syslog facility is used to specify what type of program is logging ; the message. Only used when error_log is set to syslog. ;syslog.facility = user ; Set this to disable filtering control characters (the default). ; Some loggers only accept NVT-ASCII, others accept anything that's not ; control characters. If your logger accepts everything, then no filtering ; is needed at all. ; Allowed values are: ; ascii (all printable ASCII characters and NL) ; no-ctrl (all characters except control characters) ; all (all characters) ; raw (like "all", but messages are not split at newlines) ; https://php.net/syslog.filter ;syslog.filter = ascii ;windows.show_crt_warning ; Default value: 0 ; Development value: 0 ; Production value: 0 ;;;;;;;;;;;;;;;;; ; Data Handling ; ;;;;;;;;;;;;;;;;; ; The separator used in PHP generated URLs to separate arguments. ; PHP's default setting is "&". ; https://php.net/arg-separator.output ; Example: ;arg_separator.output = "&" ; List of separator(s) used by PHP to parse input URLs into variables. ; PHP's default setting is "&". ; NOTE: Every character in this directive is considered as separator! ; https://php.net/arg-separator.input ; Example: ;arg_separator.input = ";&" ; This directive determines which super global arrays are registered when PHP ; starts up. G,P,C,E & S are abbreviations for the following respective super ; globals: GET, POST, COOKIE, ENV and SERVER. There is a performance penalty ; paid for the registration of these arrays and because ENV is not as commonly ; used as the others, ENV is not recommended on productions servers. You ; can still get access to the environment variables through getenv() should you ; need to. ; Default Value: "EGPCS" ; Development Value: "GPCS" ; Production Value: "GPCS"; ; https://php.net/variables-order variables_order = "GPCS" ; This directive determines which super global data (G,P & C) should be ; registered into the super global array REQUEST. If so, it also determines ; the order in which that data is registered. The values for this directive ; are specified in the same manner as the variables_order directive, ; EXCEPT one. Leaving this value empty will cause PHP to use the value set ; in the variables_order directive. It does not mean it will leave the super ; globals array REQUEST empty. ; Default Value: None ; Development Value: "GP" ; Production Value: "GP" ; https://php.net/request-order request_order = "GP" ; This directive determines whether PHP registers $argv & $argc each time it ; runs. $argv contains an array of all the arguments passed to PHP when a script ; is invoked. $argc contains an integer representing the number of arguments ; that were passed when the script was invoked. These arrays are extremely ; useful when running scripts from the command line. When this directive is ; enabled, registering these variables consumes CPU cycles and memory each time ; a script is executed. For performance reasons, this feature should be disabled ; on production servers. ; Note: This directive is hardcoded to On for the CLI SAPI ; Default Value: On ; Development Value: Off ; Production Value: Off ; https://php.net/register-argc-argv register_argc_argv = Off ; When enabled, the ENV, REQUEST and SERVER variables are created when they're ; first used (Just In Time) instead of when the script starts. If these ; variables are not used within a script, having this directive on will result ; in a performance gain. The PHP directive register_argc_argv must be disabled ; for this directive to have any effect. ; https://php.net/auto-globals-jit auto_globals_jit = On ; Whether PHP will read the POST data. ; This option is enabled by default. ; Most likely, you won't want to disable this option globally. It causes $_POST ; and $_FILES to always be empty; the only way you will be able to read the ; POST data will be through the php://input stream wrapper. This can be useful ; to proxy requests or to process the POST data in a memory efficient fashion. ; https://php.net/enable-post-data-reading ;enable_post_data_reading = Off ; Maximum size of POST data that PHP will accept. ; Its value may be 0 to disable the limit. It is ignored if POST data reading ; is disabled through enable_post_data_reading. ; https://php.net/post-max-size post_max_size = 8M ; Automatically add files before PHP document. ; https://php.net/auto-prepend-file auto_prepend_file = ; Automatically add files after PHP document. ; https://php.net/auto-append-file auto_append_file = ; By default, PHP will output a media type using the Content-Type header. To ; disable this, simply set it to be empty. ; ; PHP's built-in default media type is set to text/html. ; https://php.net/default-mimetype default_mimetype = "text/html" ; PHP's default character set is set to UTF-8. ; https://php.net/default-charset default_charset = "UTF-8" ; PHP internal character encoding is set to empty. ; If empty, default_charset is used. ; https://php.net/internal-encoding ;internal_encoding = ; PHP input character encoding is set to empty. ; If empty, default_charset is used. ; https://php.net/input-encoding ;input_encoding = ; PHP output character encoding is set to empty. ; If empty, default_charset is used. ; See also output_buffer. ; https://php.net/output-encoding ;output_encoding = ;;;;;;;;;;;;;;;;;;;;;;;;; ; Paths and Directories ; ;;;;;;;;;;;;;;;;;;;;;;;;; ; UNIX: "/path1:/path2" ;include_path = ".:/php/includes" ; ; Windows: "\path1;\path2" ;include_path = ".;c:\php\includes" ; ; PHP's default setting for include_path is ".;/path/to/php/pear" ; https://php.net/include-path ; The root of the PHP pages, used only if nonempty. ; if PHP was not compiled with FORCE_REDIRECT, you SHOULD set doc_root ; if you are running php as a CGI under any web server (other than IIS) ; see documentation for security issues. The alternate is to use the ; cgi.force_redirect configuration below ; https://php.net/doc-root doc_root = ; The directory under which PHP opens the script using /~username used only ; if nonempty. ; https://php.net/user-dir user_dir = ; Directory in which the loadable extensions (modules) reside. ; https://php.net/extension-dir ;extension_dir = "./" ; On windows: ;extension_dir = "ext" ; Directory where the temporary files should be placed. ; Defaults to the system default (see sys_get_temp_dir) ;sys_temp_dir = "/tmp" ; Whether or not to enable the dl() function. The dl() function does NOT work ; properly in multithreaded servers, such as IIS or Zeus, and is automatically ; disabled on them. ; https://php.net/enable-dl enable_dl = Off ; cgi.force_redirect is necessary to provide security running PHP as a CGI under ; most web servers. Left undefined, PHP turns this on by default. You can ; turn it off here AT YOUR OWN RISK ; **You CAN safely turn this off for IIS, in fact, you MUST.** ; https://php.net/cgi.force-redirect ;cgi.force_redirect = 1 ; if cgi.nph is enabled it will force cgi to always sent Status: 200 with ; every request. PHP's default behavior is to disable this feature. ;cgi.nph = 1 ; if cgi.force_redirect is turned on, and you are not running under Apache or Netscape ; (iPlanet) web servers, you MAY need to set an environment variable name that PHP ; will look for to know it is OK to continue execution. Setting this variable MAY ; cause security issues, KNOW WHAT YOU ARE DOING FIRST. ; https://php.net/cgi.redirect-status-env ;cgi.redirect_status_env = ; cgi.fix_pathinfo provides *real* PATH_INFO/PATH_TRANSLATED support for CGI. PHP's ; previous behaviour was to set PATH_TRANSLATED to SCRIPT_FILENAME, and to not grok ; what PATH_INFO is. For more information on PATH_INFO, see the cgi specs. Setting ; this to 1 will cause PHP CGI to fix its paths to conform to the spec. A setting ; of zero causes PHP to behave as before. Default is 1. You should fix your scripts ; to use SCRIPT_FILENAME rather than PATH_TRANSLATED. ; https://php.net/cgi.fix-pathinfo ;cgi.fix_pathinfo=1 ; if cgi.discard_path is enabled, the PHP CGI binary can safely be placed outside ; of the web tree and people will not be able to circumvent .htaccess security. ;cgi.discard_path=1 ; FastCGI under IIS supports the ability to impersonate ; security tokens of the calling client. This allows IIS to define the ; security context that the request runs under. mod_fastcgi under Apache ; does not currently support this feature (03/17/2002) ; Set to 1 if running under IIS. Default is zero. ; https://php.net/fastcgi.impersonate ;fastcgi.impersonate = 1 ; Disable logging through FastCGI connection. PHP's default behavior is to enable ; this feature. ;fastcgi.logging = 0 ; cgi.rfc2616_headers configuration option tells PHP what type of headers to ; use when sending HTTP response code. If set to 0, PHP sends Status: header that ; is supported by Apache. When this option is set to 1, PHP will send ; RFC2616 compliant header. ; Default is zero. ; https://php.net/cgi.rfc2616-headers ;cgi.rfc2616_headers = 0 ; cgi.check_shebang_line controls whether CGI PHP checks for line starting with #! ; (shebang) at the top of the running script. This line might be needed if the ; script support running both as stand-alone script and via PHP CGI<. PHP in CGI ; mode skips this line and ignores its content if this directive is turned on. ; https://php.net/cgi.check-shebang-line ;cgi.check_shebang_line=1 ;;;;;;;;;;;;;;;; ; File Uploads ; ;;;;;;;;;;;;;;;; ; Whether to allow HTTP file uploads. ; https://php.net/file-uploads file_uploads = On ; Temporary directory for HTTP uploaded files (will use system default if not ; specified). ; https://php.net/upload-tmp-dir ;upload_tmp_dir = ; Maximum allowed size for uploaded files. ; https://php.net/upload-max-filesize upload_max_filesize = 2M ; Maximum number of files that can be uploaded via a single request max_file_uploads = 20 ;;;;;;;;;;;;;;;;;; ; Fopen wrappers ; ;;;;;;;;;;;;;;;;;; ; Whether to allow the treatment of URLs (like http:// or ftp://) as files. ; https://php.net/allow-url-fopen allow_url_fopen = On ; Whether to allow include/require to open URLs (like https:// or ftp://) as files. ; https://php.net/allow-url-include allow_url_include = Off ; Define the anonymous ftp password (your email address). PHP's default setting ; for this is empty. ; https://php.net/from ;from="john@doe.com" ; Define the User-Agent string. PHP's default setting for this is empty. ; https://php.net/user-agent ;user_agent="PHP" ; Default timeout for socket based streams (seconds) ; https://php.net/default-socket-timeout default_socket_timeout = 60 ; If your scripts have to deal with files from Macintosh systems, ; or you are running on a Mac and need to deal with files from ; unix or win32 systems, setting this flag will cause PHP to ; automatically detect the EOL character in those files so that ; fgets() and file() will work regardless of the source of the file. ; https://php.net/auto-detect-line-endings ;auto_detect_line_endings = Off ;;;;;;;;;;;;;;;;;;;;;; ; Dynamic Extensions ; ;;;;;;;;;;;;;;;;;;;;;; ; If you wish to have an extension loaded automatically, use the following ; syntax: ; ; extension=modulename ; ; For example: ; ; extension=mysqli ; ; When the extension library to load is not located in the default extension ; directory, You may specify an absolute path to the library file: ; ; extension=/path/to/extension/mysqli.so ; ; Note : The syntax used in previous PHP versions ('extension=.so' and ; 'extension='php_.dll') is supported for legacy reasons and may be ; deprecated in a future PHP major version. So, when it is possible, please ; move to the new ('extension=) syntax. ; ; Notes for Windows environments : ; ; - Many DLL files are located in the ext/ ; extension folders as well as the separate PECL DLL download. ; Be sure to appropriately set the extension_dir directive. ; ;extension=bz2 ; The ldap extension must be before curl if OpenSSL 1.0.2 and OpenLDAP is used ; otherwise it results in segfault when unloading after using SASL. ; See https://github.com/php/php-src/issues/8620 for more info. ;extension=ldap ;extension=curl ;extension=ffi ;extension=ftp ;extension=fileinfo ;extension=gd ;extension=gettext ;extension=gmp ;extension=intl ;extension=imap ;extension=mbstring ;extension=exif ; Must be after mbstring as it depends on it ;extension=mysqli ;extension=oci8_12c ; Use with Oracle Database 12c Instant Client ;extension=oci8_19 ; Use with Oracle Database 19 Instant Client ;extension=odbc ;extension=openssl ;extension=pdo_firebird ;extension=pdo_mysql ;extension=pdo_oci ;extension=pdo_odbc ;extension=pdo_pgsql ;extension=pdo_sqlite ;extension=pgsql ;extension=shmop ; The MIBS data available in the PHP distribution must be installed. ; See https://www.php.net/manual/en/snmp.installation.php ;extension=snmp ;extension=soap ;extension=sockets ;extension=sodium ;extension=sqlite3 ;extension=tidy ;extension=xsl ;extension=zip ;zend_extension=opcache ;;;;;;;;;;;;;;;;;;; ; Module Settings ; ;;;;;;;;;;;;;;;;;;; [CLI Server] ; Whether the CLI web server uses ANSI color coding in its terminal output. cli_server.color = On [Date] ; Defines the default timezone used by the date functions ; https://php.net/date.timezone ;date.timezone = ; https://php.net/date.default-latitude ;date.default_latitude = 31.7667 ; https://php.net/date.default-longitude ;date.default_longitude = 35.2333 ; https://php.net/date.sunrise-zenith ;date.sunrise_zenith = 90.833333 ; https://php.net/date.sunset-zenith ;date.sunset_zenith = 90.833333 [filter] ; https://php.net/filter.default ;filter.default = unsafe_raw ; https://php.net/filter.default-flags ;filter.default_flags = [iconv] ; Use of this INI entry is deprecated, use global input_encoding instead. ; If empty, default_charset or input_encoding or iconv.input_encoding is used. ; The precedence is: default_charset < input_encoding < iconv.input_encoding ;iconv.input_encoding = ; Use of this INI entry is deprecated, use global internal_encoding instead. ; If empty, default_charset or internal_encoding or iconv.internal_encoding is used. ; The precedence is: default_charset < internal_encoding < iconv.internal_encoding ;iconv.internal_encoding = ; Use of this INI entry is deprecated, use global output_encoding instead. ; If empty, default_charset or output_encoding or iconv.output_encoding is used. ; The precedence is: default_charset < output_encoding < iconv.output_encoding ; To use an output encoding conversion, iconv's output handler must be set ; otherwise output encoding conversion cannot be performed. ;iconv.output_encoding = [imap] ; rsh/ssh logins are disabled by default. Use this INI entry if you want to ; enable them. Note that the IMAP library does not filter mailbox names before ; passing them to rsh/ssh command, thus passing untrusted data to this function ; with rsh/ssh enabled is insecure. ;imap.enable_insecure_rsh=0 [intl] ;intl.default_locale = ; This directive allows you to produce PHP errors when some error ; happens within intl functions. The value is the level of the error produced. ; Default is 0, which does not produce any errors. ;intl.error_level = E_WARNING ;intl.use_exceptions = 0 [sqlite3] ; Directory pointing to SQLite3 extensions ; https://php.net/sqlite3.extension-dir ;sqlite3.extension_dir = ; SQLite defensive mode flag (only available from SQLite 3.26+) ; When the defensive flag is enabled, language features that allow ordinary ; SQL to deliberately corrupt the database file are disabled. This forbids ; writing directly to the schema, shadow tables (eg. FTS data tables), or ; the sqlite_dbpage virtual table. ; https://www.sqlite.org/c3ref/c_dbconfig_defensive.html ; (for older SQLite versions, this flag has no use) ;sqlite3.defensive = 1 [Pcre] ; PCRE library backtracking limit. ; https://php.net/pcre.backtrack-limit ;pcre.backtrack_limit=100000 ; PCRE library recursion limit. ; Please note that if you set this value to a high number you may consume all ; the available process stack and eventually crash PHP (due to reaching the ; stack size limit imposed by the Operating System). ; https://php.net/pcre.recursion-limit ;pcre.recursion_limit=100000 ; Enables or disables JIT compilation of patterns. This requires the PCRE ; library to be compiled with JIT support. ;pcre.jit=1 [Pdo] ; Whether to pool ODBC connections. Can be one of "strict", "relaxed" or "off" ; https://php.net/pdo-odbc.connection-pooling ;pdo_odbc.connection_pooling=strict [Pdo_mysql] ; Default socket name for local MySQL connects. If empty, uses the built-in ; MySQL defaults. pdo_mysql.default_socket= [Phar] ; https://php.net/phar.readonly ;phar.readonly = On ; https://php.net/phar.require-hash ;phar.require_hash = On ;phar.cache_list = [mail function] ; For Win32 only. ; https://php.net/smtp SMTP = localhost ; https://php.net/smtp-port smtp_port = 25 ; For Win32 only. ; https://php.net/sendmail-from ;sendmail_from = me@example.com ; For Unix only. You may supply arguments as well (default: "sendmail -t -i"). ; https://php.net/sendmail-path ;sendmail_path = ; Force the addition of the specified parameters to be passed as extra parameters ; to the sendmail binary. These parameters will always replace the value of ; the 5th parameter to mail(). ;mail.force_extra_parameters = ; Add X-PHP-Originating-Script: that will include uid of the script followed by the filename mail.add_x_header = Off ; Use mixed LF and CRLF line separators to keep compatibility with some ; RFC 2822 non conformant MTA. mail.mixed_lf_and_crlf = Off ; The path to a log file that will log all mail() calls. Log entries include ; the full path of the script, line number, To address and headers. ;mail.log = ; Log mail to syslog (Event Log on Windows). ;mail.log = syslog [ODBC] ; https://php.net/odbc.default-db ;odbc.default_db = Not yet implemented ; https://php.net/odbc.default-user ;odbc.default_user = Not yet implemented ; https://php.net/odbc.default-pw ;odbc.default_pw = Not yet implemented ; Controls the ODBC cursor model. ; Default: SQL_CURSOR_STATIC (default). ;odbc.default_cursortype ; Allow or prevent persistent links. ; https://php.net/odbc.allow-persistent odbc.allow_persistent = On ; Check that a connection is still valid before reuse. ; https://php.net/odbc.check-persistent odbc.check_persistent = On ; Maximum number of persistent links. -1 means no limit. ; https://php.net/odbc.max-persistent odbc.max_persistent = -1 ; Maximum number of links (persistent + non-persistent). -1 means no limit. ; https://php.net/odbc.max-links odbc.max_links = -1 ; Handling of LONG fields. Returns number of bytes to variables. 0 means ; passthru. ; https://php.net/odbc.defaultlrl odbc.defaultlrl = 4096 ; Handling of binary data. 0 means passthru, 1 return as is, 2 convert to char. ; See the documentation on odbc_binmode and odbc_longreadlen for an explanation ; of odbc.defaultlrl and odbc.defaultbinmode ; https://php.net/odbc.defaultbinmode odbc.defaultbinmode = 1 [MySQLi] ; Maximum number of persistent links. -1 means no limit. ; https://php.net/mysqli.max-persistent mysqli.max_persistent = -1 ; Allow accessing, from PHP's perspective, local files with LOAD DATA statements ; https://php.net/mysqli.allow_local_infile ;mysqli.allow_local_infile = On ; It allows the user to specify a folder where files that can be sent via LOAD DATA ; LOCAL can exist. It is ignored if mysqli.allow_local_infile is enabled. ;mysqli.local_infile_directory = ; Allow or prevent persistent links. ; https://php.net/mysqli.allow-persistent mysqli.allow_persistent = On ; Maximum number of links. -1 means no limit. ; https://php.net/mysqli.max-links mysqli.max_links = -1 ; Default port number for mysqli_connect(). If unset, mysqli_connect() will use ; the $MYSQL_TCP_PORT or the mysql-tcp entry in /etc/services or the ; compile-time value defined MYSQL_PORT (in that order). Win32 will only look ; at MYSQL_PORT. ; https://php.net/mysqli.default-port mysqli.default_port = 3306 ; Default socket name for local MySQL connects. If empty, uses the built-in ; MySQL defaults. ; https://php.net/mysqli.default-socket mysqli.default_socket = ; Default host for mysqli_connect() (doesn't apply in safe mode). ; https://php.net/mysqli.default-host mysqli.default_host = ; Default user for mysqli_connect() (doesn't apply in safe mode). ; https://php.net/mysqli.default-user mysqli.default_user = ; Default password for mysqli_connect() (doesn't apply in safe mode). ; Note that this is generally a *bad* idea to store passwords in this file. ; *Any* user with PHP access can run 'echo get_cfg_var("mysqli.default_pw") ; and reveal this password! And of course, any users with read access to this ; file will be able to reveal the password as well. ; https://php.net/mysqli.default-pw mysqli.default_pw = ; If this option is enabled, closing a persistent connection will rollback ; any pending transactions of this connection, before it is put back ; into the persistent connection pool. ;mysqli.rollback_on_cached_plink = Off [mysqlnd] ; Enable / Disable collection of general statistics by mysqlnd which can be ; used to tune and monitor MySQL operations. mysqlnd.collect_statistics = On ; Enable / Disable collection of memory usage statistics by mysqlnd which can be ; used to tune and monitor MySQL operations. mysqlnd.collect_memory_statistics = On ; Records communication from all extensions using mysqlnd to the specified log ; file. ; https://php.net/mysqlnd.debug ;mysqlnd.debug = ; Defines which queries will be logged. ;mysqlnd.log_mask = 0 ; Default size of the mysqlnd memory pool, which is used by result sets. ;mysqlnd.mempool_default_size = 16000 ; Size of a pre-allocated buffer used when sending commands to MySQL in bytes. ;mysqlnd.net_cmd_buffer_size = 2048 ; Size of a pre-allocated buffer used for reading data sent by the server in ; bytes. ;mysqlnd.net_read_buffer_size = 32768 ; Timeout for network requests in seconds. ;mysqlnd.net_read_timeout = 31536000 ; SHA-256 Authentication Plugin related. File with the MySQL server public RSA ; key. ;mysqlnd.sha256_server_public_key = [OCI8] ; Connection: Enables privileged connections using external ; credentials (OCI_SYSOPER, OCI_SYSDBA) ; https://php.net/oci8.privileged-connect ;oci8.privileged_connect = Off ; Connection: The maximum number of persistent OCI8 connections per ; process. Using -1 means no limit. ; https://php.net/oci8.max-persistent ;oci8.max_persistent = -1 ; Connection: The maximum number of seconds a process is allowed to ; maintain an idle persistent connection. Using -1 means idle ; persistent connections will be maintained forever. ; https://php.net/oci8.persistent-timeout ;oci8.persistent_timeout = -1 ; Connection: The number of seconds that must pass before issuing a ; ping during oci_pconnect() to check the connection validity. When ; set to 0, each oci_pconnect() will cause a ping. Using -1 disables ; pings completely. ; https://php.net/oci8.ping-interval ;oci8.ping_interval = 60 ; Connection: Set this to a user chosen connection class to be used ; for all pooled server requests with Oracle Database Resident ; Connection Pooling (DRCP). To use DRCP, this value should be set to ; the same string for all web servers running the same application, ; the database pool must be configured, and the connection string must ; specify to use a pooled server. ;oci8.connection_class = ; High Availability: Using On lets PHP receive Fast Application ; Notification (FAN) events generated when a database node fails. The ; database must also be configured to post FAN events. ;oci8.events = Off ; Tuning: This option enables statement caching, and specifies how ; many statements to cache. Using 0 disables statement caching. ; https://php.net/oci8.statement-cache-size ;oci8.statement_cache_size = 20 ; Tuning: Enables row prefetching and sets the default number of ; rows that will be fetched automatically after statement execution. ; https://php.net/oci8.default-prefetch ;oci8.default_prefetch = 100 ; Tuning: Sets the amount of LOB data that is internally returned from ; Oracle Database when an Oracle LOB locator is initially retrieved as ; part of a query. Setting this can improve performance by reducing ; round-trips. ; https://php.net/oci8.prefetch-lob-size ; oci8.prefetch_lob_size = 0 ; Compatibility. Using On means oci_close() will not close ; oci_connect() and oci_new_connect() connections. ; https://php.net/oci8.old-oci-close-semantics ;oci8.old_oci_close_semantics = Off [PostgreSQL] ; Allow or prevent persistent links. ; https://php.net/pgsql.allow-persistent pgsql.allow_persistent = On ; Detect broken persistent links always with pg_pconnect(). ; Auto reset feature requires a little overheads. ; https://php.net/pgsql.auto-reset-persistent pgsql.auto_reset_persistent = Off ; Maximum number of persistent links. -1 means no limit. ; https://php.net/pgsql.max-persistent pgsql.max_persistent = -1 ; Maximum number of links (persistent+non persistent). -1 means no limit. ; https://php.net/pgsql.max-links pgsql.max_links = -1 ; Ignore PostgreSQL backends Notice message or not. ; Notice message logging require a little overheads. ; https://php.net/pgsql.ignore-notice pgsql.ignore_notice = 0 ; Log PostgreSQL backends Notice message or not. ; Unless pgsql.ignore_notice=0, module cannot log notice message. ; https://php.net/pgsql.log-notice pgsql.log_notice = 0 [bcmath] ; Number of decimal digits for all bcmath functions. ; https://php.net/bcmath.scale bcmath.scale = 0 [browscap] ; https://php.net/browscap ;browscap = extra/browscap.ini [Session] ; Handler used to store/retrieve data. ; https://php.net/session.save-handler session.save_handler = files ; Argument passed to save_handler. In the case of files, this is the path ; where data files are stored. Note: Windows users have to change this ; variable in order to use PHP's session functions. ; ; The path can be defined as: ; ; session.save_path = "N;/path" ; ; where N is an integer. Instead of storing all the session files in ; /path, what this will do is use subdirectories N-levels deep, and ; store the session data in those directories. This is useful if ; your OS has problems with many files in one directory, and is ; a more efficient layout for servers that handle many sessions. ; ; NOTE 1: PHP will not create this directory structure automatically. ; You can use the script in the ext/session dir for that purpose. ; NOTE 2: See the section on garbage collection below if you choose to ; use subdirectories for session storage ; ; The file storage module creates files using mode 600 by default. ; You can change that by using ; ; session.save_path = "N;MODE;/path" ; ; where MODE is the octal representation of the mode. Note that this ; does not overwrite the process's umask. ; https://php.net/session.save-path ;session.save_path = "/tmp" ; Whether to use strict session mode. ; Strict session mode does not accept an uninitialized session ID, and ; regenerates the session ID if the browser sends an uninitialized session ID. ; Strict mode protects applications from session fixation via a session adoption ; vulnerability. It is disabled by default for maximum compatibility, but ; enabling it is encouraged. ; https://wiki.php.net/rfc/strict_sessions session.use_strict_mode = 0 ; Whether to use cookies. ; https://php.net/session.use-cookies session.use_cookies = 1 ; https://php.net/session.cookie-secure ;session.cookie_secure = ; This option forces PHP to fetch and use a cookie for storing and maintaining ; the session id. We encourage this operation as it's very helpful in combating ; session hijacking when not specifying and managing your own session id. It is ; not the be-all and end-all of session hijacking defense, but it's a good start. ; https://php.net/session.use-only-cookies session.use_only_cookies = 1 ; Name of the session (used as cookie name). ; https://php.net/session.name session.name = PHPSESSID ; Initialize session on request startup. ; https://php.net/session.auto-start session.auto_start = 0 ; Lifetime in seconds of cookie or, if 0, until browser is restarted. ; https://php.net/session.cookie-lifetime session.cookie_lifetime = 0 ; The path for which the cookie is valid. ; https://php.net/session.cookie-path session.cookie_path = / ; The domain for which the cookie is valid. ; https://php.net/session.cookie-domain session.cookie_domain = ; Whether or not to add the httpOnly flag to the cookie, which makes it ; inaccessible to browser scripting languages such as JavaScript. ; https://php.net/session.cookie-httponly session.cookie_httponly = ; Add SameSite attribute to cookie to help mitigate Cross-Site Request Forgery (CSRF/XSRF) ; Current valid values are "Strict", "Lax" or "None". When using "None", ; make sure to include the quotes, as `none` is interpreted like `false` in ini files. ; https://tools.ietf.org/html/draft-west-first-party-cookies-07 session.cookie_samesite = ; Handler used to serialize data. php is the standard serializer of PHP. ; https://php.net/session.serialize-handler session.serialize_handler = php ; Defines the probability that the 'garbage collection' process is started on every ; session initialization. The probability is calculated by using gc_probability/gc_divisor, ; e.g. 1/100 means there is a 1% chance that the GC process starts on each request. ; Default Value: 1 ; Development Value: 1 ; Production Value: 1 ; https://php.net/session.gc-probability session.gc_probability = 1 ; Defines the probability that the 'garbage collection' process is started on every ; session initialization. The probability is calculated by using gc_probability/gc_divisor, ; e.g. 1/100 means there is a 1% chance that the GC process starts on each request. ; For high volume production servers, using a value of 1000 is a more efficient approach. ; Default Value: 100 ; Development Value: 1000 ; Production Value: 1000 ; https://php.net/session.gc-divisor session.gc_divisor = 1000 ; After this number of seconds, stored data will be seen as 'garbage' and ; cleaned up by the garbage collection process. ; https://php.net/session.gc-maxlifetime session.gc_maxlifetime = 1440 ; NOTE: If you are using the subdirectory option for storing session files ; (see session.save_path above), then garbage collection does *not* ; happen automatically. You will need to do your own garbage ; collection through a shell script, cron entry, or some other method. ; For example, the following script is the equivalent of setting ; session.gc_maxlifetime to 1440 (1440 seconds = 24 minutes): ; find /path/to/sessions -cmin +24 -type f | xargs rm ; Check HTTP Referer to invalidate externally stored URLs containing ids. ; HTTP_REFERER has to contain this substring for the session to be ; considered as valid. ; https://php.net/session.referer-check session.referer_check = ; Set to {nocache,private,public,} to determine HTTP caching aspects ; or leave this empty to avoid sending anti-caching headers. ; https://php.net/session.cache-limiter session.cache_limiter = nocache ; Document expires after n minutes. ; https://php.net/session.cache-expire session.cache_expire = 180 ; trans sid support is disabled by default. ; Use of trans sid may risk your users' security. ; Use this option with caution. ; - User may send URL contains active session ID ; to other person via. email/irc/etc. ; - URL that contains active session ID may be stored ; in publicly accessible computer. ; - User may access your site with the same session ID ; always using URL stored in browser's history or bookmarks. ; https://php.net/session.use-trans-sid session.use_trans_sid = 0 ; Set session ID character length. This value could be between 22 to 256. ; Shorter length than default is supported only for compatibility reason. ; Users should use 32 or more chars. ; https://php.net/session.sid-length ; Default Value: 32 ; Development Value: 26 ; Production Value: 26 session.sid_length = 26 ; The URL rewriter will look for URLs in a defined set of HTML tags. ; is special; if you include them here, the rewriter will ; add a hidden field with the info which is otherwise appended ; to URLs. tag's action attribute URL will not be modified ; unless it is specified. ; Note that all valid entries require a "=", even if no value follows. ; Default Value: "a=href,area=href,frame=src,form=" ; Development Value: "a=href,area=href,frame=src,form=" ; Production Value: "a=href,area=href,frame=src,form=" ; https://php.net/url-rewriter.tags session.trans_sid_tags = "a=href,area=href,frame=src,form=" ; URL rewriter does not rewrite absolute URLs by default. ; To enable rewrites for absolute paths, target hosts must be specified ; at RUNTIME. i.e. use ini_set() ; tags is special. PHP will check action attribute's URL regardless ; of session.trans_sid_tags setting. ; If no host is defined, HTTP_HOST will be used for allowed host. ; Example value: php.net,www.php.net,wiki.php.net ; Use "," for multiple hosts. No spaces are allowed. ; Default Value: "" ; Development Value: "" ; Production Value: "" ;session.trans_sid_hosts="" ; Define how many bits are stored in each character when converting ; the binary hash data to something readable. ; Possible values: ; 4 (4 bits: 0-9, a-f) ; 5 (5 bits: 0-9, a-v) ; 6 (6 bits: 0-9, a-z, A-Z, "-", ",") ; Default Value: 4 ; Development Value: 5 ; Production Value: 5 ; https://php.net/session.hash-bits-per-character session.sid_bits_per_character = 5 ; Enable upload progress tracking in $_SESSION ; Default Value: On ; Development Value: On ; Production Value: On ; https://php.net/session.upload-progress.enabled ;session.upload_progress.enabled = On ; Cleanup the progress information as soon as all POST data has been read ; (i.e. upload completed). ; Default Value: On ; Development Value: On ; Production Value: On ; https://php.net/session.upload-progress.cleanup ;session.upload_progress.cleanup = On ; A prefix used for the upload progress key in $_SESSION ; Default Value: "upload_progress_" ; Development Value: "upload_progress_" ; Production Value: "upload_progress_" ; https://php.net/session.upload-progress.prefix ;session.upload_progress.prefix = "upload_progress_" ; The index name (concatenated with the prefix) in $_SESSION ; containing the upload progress information ; Default Value: "PHP_SESSION_UPLOAD_PROGRESS" ; Development Value: "PHP_SESSION_UPLOAD_PROGRESS" ; Production Value: "PHP_SESSION_UPLOAD_PROGRESS" ; https://php.net/session.upload-progress.name ;session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS" ; How frequently the upload progress should be updated. ; Given either in percentages (per-file), or in bytes ; Default Value: "1%" ; Development Value: "1%" ; Production Value: "1%" ; https://php.net/session.upload-progress.freq ;session.upload_progress.freq = "1%" ; The minimum delay between updates, in seconds ; Default Value: 1 ; Development Value: 1 ; Production Value: 1 ; https://php.net/session.upload-progress.min-freq ;session.upload_progress.min_freq = "1" ; Only write session data when session data is changed. Enabled by default. ; https://php.net/session.lazy-write ;session.lazy_write = On [Assertion] ; Switch whether to compile assertions at all (to have no overhead at run-time) ; -1: Do not compile at all ; 0: Jump over assertion at run-time ; 1: Execute assertions ; Changing from or to a negative value is only possible in php.ini! ; (For turning assertions on and off at run-time, toggle zend.assertions between the values 1 and 0) ; Default Value: 1 ; Development Value: 1 ; Production Value: -1 ; https://php.net/zend.assertions zend.assertions = 1 [COM] ; path to a file containing GUIDs, IIDs or filenames of files with TypeLibs ; https://php.net/com.typelib-file ;com.typelib_file = ; allow Distributed-COM calls ; https://php.net/com.allow-dcom ;com.allow_dcom = true ; autoregister constants of a component's typelib on com_load() ; https://php.net/com.autoregister-typelib ;com.autoregister_typelib = true ; register constants casesensitive ; https://php.net/com.autoregister-casesensitive ;com.autoregister_casesensitive = false ; show warnings on duplicate constant registrations ; https://php.net/com.autoregister-verbose ;com.autoregister_verbose = true ; The default character set code-page to use when passing strings to and from COM objects. ; Default: system ANSI code page ;com.code_page= ; The version of the .NET framework to use. The value of the setting are the first three parts ; of the framework's version number, separated by dots, and prefixed with "v", e.g. "v4.0.30319". ;com.dotnet_version= [mbstring] ; language for internal character representation. ; This affects mb_send_mail() and mbstring.detect_order. ; https://php.net/mbstring.language ;mbstring.language = Japanese ; Use of this INI entry is deprecated, use global internal_encoding instead. ; internal/script encoding. ; Some encoding cannot work as internal encoding. (e.g. SJIS, BIG5, ISO-2022-*) ; If empty, default_charset or internal_encoding or iconv.internal_encoding is used. ; The precedence is: default_charset < internal_encoding < iconv.internal_encoding ;mbstring.internal_encoding = ; Use of this INI entry is deprecated, use global input_encoding instead. ; http input encoding. ; mbstring.encoding_translation = On is needed to use this setting. ; If empty, default_charset or input_encoding or mbstring.input is used. ; The precedence is: default_charset < input_encoding < mbstring.http_input ; https://php.net/mbstring.http-input ;mbstring.http_input = ; Use of this INI entry is deprecated, use global output_encoding instead. ; http output encoding. ; mb_output_handler must be registered as output buffer to function. ; If empty, default_charset or output_encoding or mbstring.http_output is used. ; The precedence is: default_charset < output_encoding < mbstring.http_output ; To use an output encoding conversion, mbstring's output handler must be set ; otherwise output encoding conversion cannot be performed. ; https://php.net/mbstring.http-output ;mbstring.http_output = ; enable automatic encoding translation according to ; mbstring.internal_encoding setting. Input chars are ; converted to internal encoding by setting this to On. ; Note: Do _not_ use automatic encoding translation for ; portable libs/applications. ; https://php.net/mbstring.encoding-translation ;mbstring.encoding_translation = Off ; automatic encoding detection order. ; "auto" detect order is changed according to mbstring.language ; https://php.net/mbstring.detect-order ;mbstring.detect_order = auto ; substitute_character used when character cannot be converted ; one from another ; https://php.net/mbstring.substitute-character ;mbstring.substitute_character = none ; Enable strict encoding detection. ;mbstring.strict_detection = Off ; This directive specifies the regex pattern of content types for which mb_output_handler() ; is activated. ; Default: mbstring.http_output_conv_mimetypes=^(text/|application/xhtml\+xml) ;mbstring.http_output_conv_mimetypes= ; This directive specifies maximum stack depth for mbstring regular expressions. It is similar ; to the pcre.recursion_limit for PCRE. ;mbstring.regex_stack_limit=100000 ; This directive specifies maximum retry count for mbstring regular expressions. It is similar ; to the pcre.backtrack_limit for PCRE. ;mbstring.regex_retry_limit=1000000 [gd] ; Tell the jpeg decode to ignore warnings and try to create ; a gd image. The warning will then be displayed as notices ; disabled by default ; https://php.net/gd.jpeg-ignore-warning ;gd.jpeg_ignore_warning = 1 [exif] ; Exif UNICODE user comments are handled as UCS-2BE/UCS-2LE and JIS as JIS. ; With mbstring support this will automatically be converted into the encoding ; given by corresponding encode setting. When empty mbstring.internal_encoding ; is used. For the decode settings you can distinguish between motorola and ; intel byte order. A decode setting cannot be empty. ; https://php.net/exif.encode-unicode ;exif.encode_unicode = ISO-8859-15 ; https://php.net/exif.decode-unicode-motorola ;exif.decode_unicode_motorola = UCS-2BE ; https://php.net/exif.decode-unicode-intel ;exif.decode_unicode_intel = UCS-2LE ; https://php.net/exif.encode-jis ;exif.encode_jis = ; https://php.net/exif.decode-jis-motorola ;exif.decode_jis_motorola = JIS ; https://php.net/exif.decode-jis-intel ;exif.decode_jis_intel = JIS [Tidy] ; The path to a default tidy configuration file to use when using tidy ; https://php.net/tidy.default-config ;tidy.default_config = /usr/local/lib/php/default.tcfg ; Should tidy clean and repair output automatically? ; WARNING: Do not use this option if you are generating non-html content ; such as dynamic images ; https://php.net/tidy.clean-output tidy.clean_output = Off [soap] ; Enables or disables WSDL caching feature. ; https://php.net/soap.wsdl-cache-enabled soap.wsdl_cache_enabled=1 ; Sets the directory name where SOAP extension will put cache files. ; https://php.net/soap.wsdl-cache-dir soap.wsdl_cache_dir="/tmp" ; (time to live) Sets the number of second while cached file will be used ; instead of original one. ; https://php.net/soap.wsdl-cache-ttl soap.wsdl_cache_ttl=86400 ; Sets the size of the cache limit. (Max. number of WSDL files to cache) soap.wsdl_cache_limit = 5 [sysvshm] ; A default size of the shared memory segment ;sysvshm.init_mem = 10000 [ldap] ; Sets the maximum number of open links or -1 for unlimited. ldap.max_links = -1 [dba] ;dba.default_handler= [opcache] ; Determines if Zend OPCache is enabled ;opcache.enable=1 ; Determines if Zend OPCache is enabled for the CLI version of PHP ;opcache.enable_cli=0 ; The OPcache shared memory storage size. ;opcache.memory_consumption=128 ; The amount of memory for interned strings in Mbytes. ;opcache.interned_strings_buffer=8 ; The maximum number of keys (scripts) in the OPcache hash table. ; Only numbers between 200 and 1000000 are allowed. ;opcache.max_accelerated_files=10000 ; The maximum percentage of "wasted" memory until a restart is scheduled. ;opcache.max_wasted_percentage=5 ; When this directive is enabled, the OPcache appends the current working ; directory to the script key, thus eliminating possible collisions between ; files with the same name (basename). Disabling the directive improves ; performance, but may break existing applications. ;opcache.use_cwd=1 ; When disabled, you must reset the OPcache manually or restart the ; webserver for changes to the filesystem to take effect. ;opcache.validate_timestamps=1 ; How often (in seconds) to check file timestamps for changes to the shared ; memory storage allocation. ("1" means validate once per second, but only ; once per request. "0" means always validate) ;opcache.revalidate_freq=2 ; Enables or disables file search in include_path optimization ;opcache.revalidate_path=0 ; If disabled, all PHPDoc comments are dropped from the code to reduce the ; size of the optimized code. ;opcache.save_comments=1 ; If enabled, compilation warnings (including notices and deprecations) will ; be recorded and replayed each time a file is included. Otherwise, compilation ; warnings will only be emitted when the file is first cached. ;opcache.record_warnings=0 ; Allow file existence override (file_exists, etc.) performance feature. ;opcache.enable_file_override=0 ; A bitmask, where each bit enables or disables the appropriate OPcache ; passes ;opcache.optimization_level=0x7FFFBFFF ;opcache.dups_fix=0 ; The location of the OPcache blacklist file (wildcards allowed). ; Each OPcache blacklist file is a text file that holds the names of files ; that should not be accelerated. The file format is to add each filename ; to a new line. The filename may be a full path or just a file prefix ; (i.e., /var/www/x blacklists all the files and directories in /var/www ; that start with 'x'). Line starting with a ; are ignored (comments). ;opcache.blacklist_filename= ; Allows exclusion of large files from being cached. By default all files ; are cached. ;opcache.max_file_size=0 ; How long to wait (in seconds) for a scheduled restart to begin if the cache ; is not being accessed. ;opcache.force_restart_timeout=180 ; OPcache error_log file name. Empty string assumes "stderr". ;opcache.error_log= ; All OPcache errors go to the Web server log. ; By default, only fatal errors (level 0) or errors (level 1) are logged. ; You can also enable warnings (level 2), info messages (level 3) or ; debug messages (level 4). ;opcache.log_verbosity_level=1 ; Preferred Shared Memory back-end. Leave empty and let the system decide. ;opcache.preferred_memory_model= ; Protect the shared memory from unexpected writing during script execution. ; Useful for internal debugging only. ;opcache.protect_memory=0 ; Allows calling OPcache API functions only from PHP scripts which path is ; started from specified string. The default "" means no restriction ;opcache.restrict_api= ; Mapping base of shared memory segments (for Windows only). All the PHP ; processes have to map shared memory into the same address space. This ; directive allows to manually fix the "Unable to reattach to base address" ; errors. ;opcache.mmap_base= ; Facilitates multiple OPcache instances per user (for Windows only). All PHP ; processes with the same cache ID and user share an OPcache instance. ;opcache.cache_id= ; Enables and sets the second level cache directory. ; It should improve performance when SHM memory is full, at server restart or ; SHM reset. The default "" disables file based caching. ;opcache.file_cache= ; Enables or disables opcode caching in shared memory. ;opcache.file_cache_only=0 ; Enables or disables checksum validation when script loaded from file cache. ;opcache.file_cache_consistency_checks=1 ; Implies opcache.file_cache_only=1 for a certain process that failed to ; reattach to the shared memory (for Windows only). Explicitly enabled file ; cache is required. ;opcache.file_cache_fallback=1 ; Enables or disables copying of PHP code (text segment) into HUGE PAGES. ; Under certain circumstances (if only a single global PHP process is ; started from which all others fork), this can increase performance ; by a tiny amount because TLB misses are reduced. On the other hand, this ; delays PHP startup, increases memory usage and degrades performance ; under memory pressure - use with care. ; Requires appropriate OS configuration. ;opcache.huge_code_pages=0 ; Validate cached file permissions. ;opcache.validate_permission=0 ; Prevent name collisions in chroot'ed environment. ;opcache.validate_root=0 ; If specified, it produces opcode dumps for debugging different stages of ; optimizations. ;opcache.opt_debug_level=0 ; Specifies a PHP script that is going to be compiled and executed at server ; start-up. ; https://php.net/opcache.preload ;opcache.preload= ; Preloading code as root is not allowed for security reasons. This directive ; facilitates to let the preloading to be run as another user. ; https://php.net/opcache.preload_user ;opcache.preload_user= ; Prevents caching files that are less than this number of seconds old. It ; protects from caching of incompletely updated files. In case all file updates ; on your site are atomic, you may increase performance by setting it to "0". ;opcache.file_update_protection=2 ; Absolute path used to store shared lockfiles (for *nix only). ;opcache.lockfile_path=/tmp [curl] ; A default value for the CURLOPT_CAINFO option. This is required to be an ; absolute path. ;curl.cainfo = [openssl] ; The location of a Certificate Authority (CA) file on the local filesystem ; to use when verifying the identity of SSL/TLS peers. Most users should ; not specify a value for this directive as PHP will attempt to use the ; OS-managed cert stores in its absence. If specified, this value may still ; be overridden on a per-stream basis via the "cafile" SSL stream context ; option. ;openssl.cafile= ; If openssl.cafile is not specified or if the CA file is not found, the ; directory pointed to by openssl.capath is searched for a suitable ; certificate. This value must be a correctly hashed certificate directory. ; Most users should not specify a value for this directive as PHP will ; attempt to use the OS-managed cert stores in its absence. If specified, ; this value may still be overridden on a per-stream basis via the "capath" ; SSL stream context option. ;openssl.capath= [ffi] ; FFI API restriction. Possible values: ; "preload" - enabled in CLI scripts and preloaded files (default) ; "false" - always disabled ; "true" - always enabled ;ffi.enable=preload ; List of headers files to preload, wildcard patterns allowed. ;ffi.preload= PK=1]e W!!doc/ea-php83-php-common/NEWSnu[PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| 30 Jul 2026, PHP 8.3.33 - Date: . Fixed leak on double DatePeriod::__construct() call. (ilutov) - GD: . Upgrade libgd. (CVE-2026-9672) (Pierre Joye) - PGSQL: . Fixed GHSA-7qpv-r5mr-78m4 (SQL injection via E'...' backslash breakout). (CVE-2026-17543) (ilutov) - Phar: . Fixed GHSA-vc5h-9ppw-p5f3 (Crash via recursive symlinks). (CVE-2026-7260) (Jakub Zelenka) 02 Jul 2026, PHP 8.3.32 - Streams: . Fixed bug GH-21468 (Segfault in file_get_contents w/ a https URL and a proxy set). (CVE-2026-12184) (ndossche) - OpenSSL: . Fixed bug GH-22187 (Memory corruption (zend_mm_heap corrupted) in openssl_encrypt with AES-WRAP-PAD). (CVE-2026-14355) (David Carlier) 07 May 2026, PHP 8.3.31 - Curl: . Add support for brotli and zstd on Windows. (Shivam Mathur) - FPM: . Fixed GHSA-7qg2-v9fj-4mwv (XSS within status endpoint). (CVE-2026-6735) (Jakub Zelenka) - MBString: . Fixed GHSA-wm6j-2649-pv75 (Null pointer dereference in php_mb_check_encoding() via mb_ereg_search_init()). (CVE-2026-7259) (vi3tL0u1s) - OpenSSL: . Fix compatibility issues with OpenSSL 4.0. (jordikroon, Remi) - PDO_Firebird: . Fixed GHSA-w476-322c-wpvm (SQL injection via NUL bytes in quoted strings). (CVE-2025-14179) (SakiTakamachi) - SOAP: . Fixed GHSA-85c2-q967-79q5 (Stale SOAP_GLOBAL(ref_map) pointer with Apache Map). (CVE-2026-6722) (ilutov) . Fixed GHSA-m33r-qmcv-p97q (Use-after-free after header parsing failure with SOAP_PERSISTENCE_SESSION). (CVE-2026-7261) (ilutov) . Fixed GHSA-hmxp-6pc4-f3vv (Broken Apache map value NULL check). (CVE-2026-7262) (ilutov) - Standard: . Fixed GHSA-96wq-48vp-hh57 (Signed integer overflow of char array offset). (CVE-2026-7568) (TimWolla) . Fixed GHSA-m8rr-4c36-8gq4 (Consistently pass unsigned char to ctype.h functions). (CVE-2026-7258) (ilutov) 15 Jan 2026, PHP 8.3.30 - Core: . Fix OSS-Fuzz #465488618 (Wrong assumptions when dumping function signature with dynamic class const lookup default argument). (ilutov) . Fixed bug GH-20695 (Assertion failure in normalize_value() when parsing malformed INI input via parse_ini_string()). (ndossche) . Fixed bug GH-20714 (Uncatchable exception thrown in generator). (ilutov) . Fixed bug GH-20352 (UAF in php_output_handler_free via re-entrant ob_start() during error deactivation). (ndossche) - Bz2: . Fixed bug GH-20620 (bzcompress overflow on large source size). (David Carlier) - DOM: . Fixed bug GH-20722 (Null pointer dereference in DOM namespace node cloning via clone on malformed objects). (ndossche) - GD: . Fixed bug GH-20622 (imagestring/imagestringup overflow). (David Carlier) - Intl: . Fix leak in umsg_format_helper(). (ndossche) - LDAP: . Fix memory leak in ldap_set_options(). (ndossche) - Mbstring: . Fixed bug GH-20674 (mb_decode_mimeheader does not handle separator). (Yuya Hamada) - Phar: . Fixed bug GH-20732 (Phar::LoadPhar undefined behavior when reading fails). (ndossche) . Fix SplFileInfo::openFile() in write mode. (ndossche) . Fix build on legacy OpenSSL 1.1.0 systems. (Giovanni Giacobbi) - POSIX: . Fixed crash on posix groups to php array creation on macos. (David Carlier) - SPL: . Fixed bug GH-20678 (resource created by GlobIterator crashes with fclose()). (David Carlier) - Sqlite3: . Fixed bug GH-20699 (SQLite3Result fetchArray return array|false, null returned). (ndossche, plusminmax) - Standard: . Fix error check for proc_open() command. (ndossche) . Fixed bug GH-20582 (Heap Buffer Overflow in iptcembed). (ndossche) - Zlib: . Fix OOB gzseek() causing assertion failure. (ndossche) 18 Dec 2025, PHP 8.3.29 - Core: . Sync all boost.context files with release 1.86.0. (mvorisek) . Fixed bug GH-20435 (SensitiveParameter doesn't work for named argument passing to variadic parameter). (ndossche) . Fixed bug GH-20286 (use-after-destroy during userland stream_close()). (ndossche, David Carlier) - Bz2: . Fix assertion failures resulting in crashes with stream filter object parameters. (ndossche) - Date: . Fix crashes when trying to instantiate uninstantiable classes via date static constructors. (ndossche) - DOM: . Fix missing NUL byte check on C14NFile(). (ndossche) - Fibers: . Fixed bug GH-20483 (ASAN stack overflow with fiber.stack_size INI small value). (David Carlier) - FTP: . Fixed bug GH-20601 (ftp_connect overflow on timeout). (David Carlier) - GD: . Fixed bug GH-20511 (imagegammacorrect out of range input/output values). (David Carlier) . Fixed bug GH-20602 (imagescale overflow with large height values). (David Carlier) - Intl: . Fixed bug GH-20426 (Spoofchecker::setRestrictionLevel() error message suggests missing constants). (DanielEScherzer) - LibXML: . Fix some deprecations on newer libxml versions regarding input buffer/parser handling. (ndossche) - MbString: . Fixed bug GH-20491 (SLES15 compile error with mbstring oniguruma). (ndossche) . Fixed bug GH-20492 (mbstring compile warning due to non-strings). (ndossche) - mysqli: . Make mysqli_begin_transaction() report errors properly. (Kamil Tekiela) - MySQLnd: . Fixed bug GH-20528 (Regression breaks mysql connexion using an IPv6 address enclosed in square brackets). (Remi) - Opcache: . Fixed bug GH-20329 (opcache.file_cache broken with full interned string buffer). (Arnaud) - PDO: . Fixed GHSA-8xr5-qppj-gvwj (PDO quoting result null deref). (CVE-2025-14180) (Jakub Zelenka) - Phar: . Fixed bug GH-20442 (Phar does not respect case-insensitiveness of __halt_compiler() when reading stub). (ndossche, TimWolla) . Fix broken return value of fflush() for phar file entries. (ndossche) . Fix assertion failure when fseeking a phar file out of bounds. (ndossche) - PHPDBG: . Fixed ZPP type violation in phpdbg_get_executable() and phpdbg_end_oplog(). (Girgias) - SPL: . Fixed bug GH-20614 (SplFixedArray incorrectly handles references in deserialization). (ndossche) - Standard: . Fix memory leak in array_diff() with custom type checks. (ndossche) . Fixed bug GH-20583 (Stack overflow in http_build_query via deep structures). (ndossche) . Fixed GHSA-www2-q4fc-65wf (Null byte termination in dns_get_record()). (ndossche) . Fixed GHSA-h96m-rvf9-jgm2 (Heap buffer overflow in array_merge()). (CVE-2025-14178) (ndossche) . Fixed GHSA-3237-qqm7-mfv7 (Information Leak of Memory in getimagesize). (CVE-2025-14177) (ndossche) - Tidy: . Fixed bug GH-20374 (PHP with tidy and custom-tags). (ndossche) - XML: . Fixed bug GH-20439 (xml_set_default_handler() does not properly handle special characters in attributes when passing data to callback). (ndossche) - Zip: . Fix crash in property existence test. (ndossche) . Don't truncate return value of zip_fread() with user sizes. (ndossche) - Zlib: . Fix assertion failures resulting in crashes with stream filter object parameters. (ndossche) 20 Nov 2025, PHP 8.3.28 - Core: . Fixed bug GH-19934 (CGI with auto_globals_jit=0 causes uouv). (ilutov) . Fixed bug GH-20073 (Assertion failure in WeakMap offset operations on reference). (nielsdos) . Fixed bug GH-19844 (Don't bail when closing resources on shutdown). (ilutov) . Fixed bug GH-20177 (Accessing overridden private property in get_object_vars() triggers assertion error). (ilutov) . Fixed bug GH-20183 (Stale EG(opline_before_exception) pointer through eval). (ilutov) - DOM: . Partially fixed bug GH-16317 (DOM classes do not allow __debugInfo() overrides to work). (nielsdos) - Exif: . Fix possible memory leak when tag is empty. (nielsdos) - FPM: . Fixed bug GH-19974 (fpm_status_export_to_zval segfault for parallel execution). (Jakub Zelenka, txuna) - FTP: . Fixed bug GH-20240 (FTP with SSL: ftp_fput(): Connection timed out on successful writes). (nielsdos) - GD: . Fixed bug GH-20070 (Return type violation in imagefilter when an invalid filter is provided). (Girgias) - Intl: . Fix memory leak on error in locale_filter_matches(). (nielsdos) - LibXML: . Fix not thread safe schema/relaxng calls. (SpencerMalone, nielsdos) - MySQLnd: . Fixed bug GH-8978 (SSL certificate verification fails (port doubled)). (nielsdos) . Fixed bug GH-20122 (getColumnMeta() for JSON-column in MySQL). (nielsdos) - Opcache: . Fixed bug GH-20081 (access to uninitialized vars in preload_load()). (Arnaud) . Fixed bug GH-20121 (JIT broken in ZTS builds on MacOS 15). (Arnaud, Shivam Mathur) - PgSql: . Fix memory leak when first string conversion fails. (nielsdos) . Fix segfaults when attempting to fetch row into a non-instantiable class name. (Girgias, nielsdos) - Phar: . Fix memory leak of argument in webPhar. (nielsdos) . Fix memory leak when setAlias() fails. (nielsdos) . Fix a bunch of memory leaks in phar_parse_zipfile() error handling. (nielsdos) . Fix file descriptor/memory leak when opening central fp fails. (nielsdos) . Fix memleak+UAF when opening temp stream in buildFromDirectory() fails. (nielsdos) . Fix potential buffer length truncation due to usage of type int instead of type size_t. (Girgias) . Fix memory leak when openssl polyfill returns garbage. (nielsdos) . Fix file descriptor leak in phar_zip_flush() on failure. (nielsdos) . Fix memory leak when opening temp file fails while trying to open gzip-compressed archive. (nielsdos) . Fixed bug GH-20302 (Freeing a phar alias may invalidate PharFileInfo objects). (nielsdos) - Random: . Fix Randomizer::__serialize() w.r.t. INDIRECTs. (nielsdos) - SimpleXML: . Partially fixed bug GH-16317 (SimpleXML does not allow __debugInfo() overrides to work). (nielsdos) - Standard: . Fix shm corruption with coercion in options of unserialize(). (nielsdos) - Streams: . Fixed bug GH-19798: XP_SOCKET XP_SSL (Socket stream modules): Incorrect condition for Win32/Win64. (Jakub Zelenka) . Fixed bug GH-20370 (User stream filters could violate typed property constraints). (alexandre-daubois) - Tidy: . Fixed GH-19021 (improved tidyOptGetCategory detection). (arjendekorte, David Carlier, Peter Kokot) . Fix UAF in tidy when tidySetErrorBuffer() fails. (nielsdos) - XMLReader: . Fix arginfo/zpp violations when LIBXML_SCHEMAS_ENABLED is not available. (nielsdos) - Windows: . Fix GH-19722 (_get_osfhandle asserts in debug mode when given a socket). (dktapps) - Zip: . Fix memory leak when passing enc_method/enc_password is passed as option for ZipArchive::addGlob()/addPattern() and with consecutive calls. (David Carlier) 23 Oct 2025, PHP 8.3.27 - Core: . Fixed bug GH-19765 (object_properties_load() bypasses readonly property checks). (timwolla) . Fixed hard_timeout with --enable-zend-max-execution-timers. (Appla) . Fixed bug GH-19792 (SCCP causes UAF for return value if both warning and exception are triggered). (nielsdos) . Fixed bug GH-19653 (Closure named argument unpacking between temporary closures can cause a crash). (nielsdos, Arnaud, Bob) . Fixed bug GH-19839 (Incorrect HASH_FLAG_HAS_EMPTY_IND flag on userland array). (ilutov) . Fixed bug GH-19480 (error_log php.ini cannot be unset when open_basedir is configured). (nielsdos) . Fixed bug GH-20002 (Broken build on *BSD with MSAN). (outtersg) - CLI: . Fix useless "Failed to poll event" error logs due to EAGAIN in CLI server with PHP_CLI_SERVER_WORKERS. (leotaku) - Curl: . Fix cloning of CURLOPT_POSTFIELDS when using the clone operator instead of the curl_copy_handle() function to clone a CurlHandle. (timwolla) . Fix curl build and test failures with version 8.16. (nielsdos, ilutov, Jakub Zelenka) - Date: . Fixed GH-17159: "P" format for ::createFromFormat swallows string literals. (nielsdos) - DBA: . Fixed GH-19885 (dba_fetch() overflow on skip argument). (David Carlier) - GD: . Fixed GH-19955 (imagefttext() memory leak). (David Carlier) - MySQLnd: . Fixed bug #67563 (mysqli compiled with mysqlnd does not take ipv6 adress as parameter). (nielsdos) - Phar: . Fix memory leak and invalid continuation after tar header writing fails. (nielsdos) . Fix memory leaks when creating temp file fails when applying zip signature. (nielsdos) - SimpleXML: . Fixed bug GH-19988 (zend_string_init with NULL pointer in simplexml (UB)). (nielsdos) - Soap: . Fixed bug GH-19784 (SoapServer memory leak). (nielsdos) . Fixed bug GH-20011 (Array of SoapVar of unknown type causes crash). (nielsdos) - Standard: . Fixed bug GH-12265 (Cloning an object breaks serialization recursion). (nielsdos) . Fixed bug GH-19701 (Serialize/deserialize loses some data). (nielsdos) . Fixed bug GH-19801 (leaks in var_dump() and debug_zval_dump()). (alexandre-daubois) . Fixed bug GH-20043 (array_unique assertion failure with RC1 array causing an exception on sort). (nielsdos) . Fixed bug GH-19926 (reset internal pointer earlier while splicing array while COW violation flag is still set). (alexandre-daubois) . Fixed bug GH-19570 (unable to fseek in /dev/zero and /dev/null). (nielsdos, divinity76) - Streams: . Fixed bug GH-19248 (Use strerror_r instead of strerror in main). (Jakub Zelenka) . Fixed bug GH-17345 (Bug #35916 was not completely fixed). (nielsdos) . Fixed bug GH-19705 (segmentation when attempting to flush on non seekable stream. (bukka/David Carlier) - XMLReader: . Fixed bug GH-20009 (XMLReader leak on RelaxNG schema failure). (nielsdos) - Zip: . Fixed bug GH-19688 (Remove pattern overflow in zip addGlob()). (nielsdos) . Fixed bug GH-19932 (Memory leak in zip setEncryptionName()/setEncryptionIndex()). (David Carlier) - Zlib: . Fixed bug GH-19922 (Double free on gzopen). (David Carlier) 25 Sep 2025, PHP 8.3.26 - Core: . Fixed bug GH-18850 (Repeated inclusion of file with __halt_compiler() triggers "Constant already defined" warning). (ilutov) . Partially fixed bug GH-19542 (Scanning of string literals >=2GB will fail due to signed int overflow). (ilutov) . Fixed bug GH-19544 (GC treats ZEND_WEAKREF_TAG_MAP references as WeakMap references). (Arnaud, timwolla) . Fixed bug GH-19613 (Stale array iterator pointer). (ilutov) . Fixed bug GH-19679 (zend_ssa_range_widening may fail to converge). (Arnaud) . Fixed bug GH-19681 (PHP_EXPAND_PATH broken with bash 5.3.0). (Remi) . Fixed bug GH-19720 (Assertion failure when error handler throws when accessing a deprecated constant). (nielsdos) - CLI: . Fixed bug GH-19461 (Improve error message on listening error with IPv6 address). (alexandre-daubois) - Date: . Fixed date_sunrise() and date_sunset() with partial-hour UTC offset. (ilutov) - DOM: . Fixed bug GH-19612 (Mitigate libxml2 tree dictionary bug). (nielsdos) - FPM: . Fixed failed debug assertion when php_admin_value setting fails. (ilutov) - GD: . Fixed bug GH-19579 (imagefilledellipse underflow on width argument). (David Carlier) - Intl: . Fixed bug GH-11952 (Fix locale strings canonicalization for IntlDateFormatter and NumberFormatter). (alexandre-daubois) - OpenSSL: . Fixed bug GH-19245 (Success error message on TLS stream accept failure). (Jakub Zelenka) - PGSQL: . Fixed bug GH-19485 (potential use after free when using persistent pgsql connections). (Mark Karpeles) - Phar: . Fixed memory leaks when verifying OpenSSL signature. (Girgias) . Fix memory leak in phar tar temporary file error handling code. (nielsdos) . Fix metadata leak when phar convert logic fails. (nielsdos) . Fix memory leak on failure in phar_convert_to_other(). (nielsdos) . Fixed bug GH-19752 (Phar decompression with invalid extension can cause UAF). (nielsdos) - Standard: . Fixed bug GH-16649 (UAF during array_splice). (alexandre-daubois) . Fixed bug GH-19577 (Avoid integer overflow when using a small offset and PHP_INT_MAX with LimitIterator). (alexandre-daubois) - Streams: . Remove incorrect call to zval_ptr_dtor() in user_wrapper_metadata(). (nielsdos) . Fix OSS-Fuzz #385993744. (nielsdos) - Tidy: . Fixed GH-19021 build issue with libtidy in regard of tidyOptIsReadonly deprecation and TidyInternalCategory being available later than tidyOptGetCategory. (arjendekorte) - Zip: . Fix memory leak in zip when encountering empty glob result. (nielsdos) 28 Aug 2025, PHP 8.3.25 - Core: . Fixed GH-19169 build issue with C++17 and ZEND_STATIC_ASSERT macro. (psumbera) . Fixed bug GH-18581 (Coerce numeric string keys from iterators when argument unpacking). (ilutov) . Fixed OSS-Fuzz #434346548 (Failed assertion with throwing __toString in binary const expr). (ilutov) . Fixed bug GH-19305 (Operands may be being released during comparison). (Arnaud) . Fixed bug GH-19303 (Unpacking empty packed array into uninitialized array causes assertion failure). (nielsdos) . Fixed bug GH-19306 (Generator can be resumed while fetching next value from delegated Generator). (Arnaud) . Fixed bug GH-19326 (Calling Generator::throw() on a running generator with a non-Generator delegate crashes). (Arnaud) . Fixed bug GH-18736 (Circumvented type check with return by ref + finally). (ilutov) . Fixed zend call stack size for macOs/arm64. (David Carlier) . Fixed bug GH-19065 (Long match statement can segfault compiler during recursive SSA renaming). (nielsdos, Arnaud) - Calendar: . Fixed bug GH-19371 (integer overflow in calendar.c). (nielsdos) - FTP: . Fix theoretical issues with hrtime() not being available. (nielsdos) - GD: . Fix incorrect comparison with result of php_stream_can_cast(). (Girgias) - Hash: . Fix crash on clone failure. (nielsdos) - Intl: . Fixed GH-19261: msgfmt_parse_message leaks on message creation failure. (David Carlier) . Fix return value on failure for resourcebundle count handler. (Girgias) - LDAP: . Fixed bug GH-18529 (additional inheriting of TLS int options). (Jakub Zelenka) - LibXML: . Fixed bug GH-19098 (libxml<2.13 segmentation fault caused by php_libxml_node_free). (nielsdos) - MbString: . Fixed bug GH-19397 (mb_list_encodings() can cause crashes on shutdown). (nielsdos) - Opcache: . Reset global pointers to prevent use-after-free in zend_jit_status(). (Florian Engelhardt) - OpenSSL: . Fixed bug GH-18986 (OpenSSL backend: incorrect RAND_{load,write}_file() return value check). (nielsdos, botovq) . Fix error return check of EVP_CIPHER_CTX_ctrl(). (nielsdos) . Fixed bug GH-19428 (openssl_pkey_derive segfaults for DH derive with low key_length param). (Jakub Zelenka) - PDO Pgsql: . Fixed dangling pointer access on _pdo_pgsql_trim_message helper. (dixyes) - Readline: . Fixed bug GH-19250 and bug #51360 (Invalid conftest for rl_pending_input). (petk, nielsdos) - SOAP: . Fixed bug GH-18640 (heap-use-after-free ext/soap/php_encoding.c:299:32 in soap_check_zval_ref). (nielsdos) - Sockets: . Fix some potential crashes on incorrect argument value. (nielsdos) - Standard: . Fixed OSS Fuzz #433303828 (Leak in failed unserialize() with opcache). (ilutov) . Fix theoretical issues with hrtime() not being available. (nielsdos) . Fixed bug GH-19300 (Nested array_multisort invocation with error breaks). (nielsdos) - Windows: . Free opened_path when opened_path_len >= MAXPATHLEN. (dixyes) 31 Jul 2025, PHP 8.3.24 - Calendar: . Fixed jewishtojd overflow on year argument. (David Carlier) - Core: . Fixed bug GH-18833 (Use after free with weakmaps dependent on destruction order). (Daniil Gentili) . Fix OSS-Fuzz #427814456. (nielsdos) . Fix OSS-Fuzz #428983568 and #428760800. (nielsdos) . Fixed bug GH-17204 -Wuseless-escape warnings emitted by re2c. (Peter Kokot) - Curl: . Fix memory leaks when returning refcounted value from curl callback. (nielsdos) . Remove incorrect string release. (nielsdos) - Intl: . Fix memleak on failure in collator_get_sort_key(). (nielsdos) - LDAP: . Fixed GH-18902 ldap_exop/ldap_exop_sync assert triggered on empty request OID. (David Carlier) - MbString: . Fixed bug GH-18901 (integer overflow mb_split). (nielsdos) - OCI8: . Fixed bug GH-18873 (OCI_RETURN_LOBS flag causes oci8 to leak memory). (Saki Takamachi) - Opcache: . Fixed bug GH-18639 (Internal class aliases can break preloading + JIT). (nielsdos) . Fixed bug GH-14082 (Segmentation fault on unknown address 0x600000000018 in ext/opcache/jit/zend_jit.c). (nielsdos) - OpenSSL: . Fixed bug #80770 (It is not possible to get client peer certificate with stream_socket_server). (Jakub Zelenka) - PCNTL: . Fixed bug GH-18958 (Fatal error during shutdown after pcntl_rfork() or pcntl_forkx() with zend-max-execution-timers). (Arnaud) - Phar: . Fix stream double free in phar. (nielsdos, dixyes) . Fix phar crash and file corruption with SplFileObject. (nielsdos) - SOAP: . Fixed bug GH-18990, bug #81029, bug #47314 (SOAP HTTP socket not closing on object destruction). (nielsdos) . Fix memory leak when URL parsing fails in redirect. (Girgias) - SPL: . Fixed bug GH-19094 (Attaching class with no Iterator implementation to MultipleIterator causes crash). (nielsdos) - Standard: . Fix misleading errors in printf(). (nielsdos) . Fix RCN violations in array functions. (nielsdos) . Fixed GH-18976 pack() overflow with h/H format and INT_MAX repeater value. (David Carlier) - Streams: . Fixed GH-13264 (fgets() and stream_get_line() do not return false on filter fatal error). (Jakub Zelenka) - Zip: . Fix leak when path is too long in ZipArchive::extractTo(). (nielsdos) 03 Jul 2025, PHP 8.3.23 - Core: . Fixed GH-18695 (zend_ast_export() - float number is not preserved). (Oleg Efimov) . Do not delete main chunk in zend_gc. (danog, Arnaud) . Fix compile issues with zend_alloc and some non-default options. (nielsdos) - Curl: . Fix memory leak when setting a list via curl_setopt fails. (nielsdos) . Fix incorrect OpenSSL version detection. (Peter Kokot) - Date: . Fix leaks with multiple calls to DatePeriod iterator current(). (nielsdos) - FPM: . Fixed GH-18662 (fpm_get_status segfault). (txuna) - Hash: . Fixed bug GH-14551 (PGO build fails with xxhash). (nielsdos) - Intl: . Fix memory leak in intl_datetime_decompose() on failure. (nielsdos) . Fix memory leak in locale lookup on failure. (nielsdos) - ODBC: . Fix memory leak on php_odbc_fetch_hash() failure. (nielsdos) - Opcache: . Fixed bug GH-18743 (Incompatibility in Inline TLS Assembly on Alpine 3.22). (nielsdos, Arnaud) - OpenSSL: . Fix memory leak of X509_STORE in php_openssl_setup_verify() on failure. (nielsdos) . Fixed bug #74796 (Requests through http proxy set peer name). (Jakub Zelenka) - Phar: . Add missing filter cleanups on phar failure. (nielsdos) . Fixed bug GH-18642 (Signed integer overflow in ext/phar fseek). (nielsdos) - PHPDBG: . Fix 'phpdbg --help' segfault on shutdown with USE_ZEND_ALLOC=0. (nielsdos) - PDO ODBC: . Fix memory leak if WideCharToMultiByte() fails. (nielsdos) - PGSQL: . Fix warning not being emitted when failure to cancel a query with pg_cancel_query(). (Girgias) . Fixed GHSA-hrwm-9436-5mv3 (pgsql extension does not check for errors during escaping). (CVE-2025-1735) (Jakub Zelenka) - Random: . Fix reference type confusion and leak in user random engine. (nielsdos, timwolla) - Readline: . Fix memory leak when calloc() fails in php_readline_completion_cb(). (nielsdos) - Soap: . Fix memory leaks in php_http.c when call_user_function() fails. (nielsdos) . Fixed GHSA-453j-q27h-5p8x (NULL Pointer Dereference in PHP SOAP ExtensionAdd commentMore actions via Large XML Namespace Prefix). (CVE-2025-6491) (Lekssays, nielsdos) - Standard: . Fixed GHSA-3cr5-j632-f35r (Null byte termination in hostnames). (CVE-2025-1220) (Jakub Zelenka) - Tidy: . Fix memory leak in tidy output handler on error. (nielsdos) . Fix tidyOptIsReadonly deprecation, using tidyOptGetCategory. (David Carlier) 05 Jun 2025, PHP 8.3.22 - Core: . Fixed GH-18480 (array_splice with large values for offset/length arguments). (nielsdos/David Carlier) . Partially fixed GH-18572 (nested object comparisons leading to stack overflow). (David Carlier) . Fixed OSS-Fuzz #417078295. (nielsdos) . Fixed OSS-Fuzz #418106144. (nielsdos) - Curl: . Fixed GH-18460 (curl_easy_setopt with CURLOPT_USERPWD/CURLOPT_USERNAME/ CURLOPT_PASSWORD set the Authorization header when set to NULL). (David Carlier) - Date: . Fixed bug GH-18076 (Since PHP 8, the date_sun_info() function returns inaccurate sunrise and sunset times, but other calculated times are correct) (JiriJozif). . Fixed bug GH-18481 (date_sunrise with unexpected nan value for the offset). (nielsdos/David Carlier) - Intl: . Fix various reference issues. (nielsdos) - LDAP: . Fixed bug GH-18529 (ldap no longer respects TLS_CACERT from ldaprc in ldap_start_tls()). (Remi) - Opcache: . Fixed bug GH-18417 (Windows SHM reattachment fails when increasing memory_consumption or jit_buffer_size). (nielsdos) . Fixed bug GH-18567 (Preloading with internal class alias triggers assertion failure). (nielsdos) . Fix leak of accel_globals->key. (nielsdos) - OpenSSL: . Fix missing checks against php_set_blocking() in xp_ssl.c. (nielsdos) - PDO_OCI: . Fixed bug GH-18494 (PDO OCI segfault in statement GC). (nielsdos) - Sockets: - Fixed bug GH-18617 (socket_import_file_descriptor return value unchecked). (David Carlier) - SPL: . Fixed bug GH-18421 (Integer overflow with large numbers in LimitIterator). (nielsdos) - Standard: . Fixed bug GH-17403 (Potential deadlock when putenv fails). (nielsdos) . Fixed bug GH-18509 (Dynamic calls to assert() ignore zend.assertions). (timwolla) - Windows: . Fix leak+crash with sapi_windows_set_ctrl_handler(). (nielsdos) - Zip: . Fixed bug GH-18431 (Registering ZIP progress callback twice doesn't work). (nielsdos) . Fixed bug GH-18438 (Handling of empty data and errors in ZipArchive::addPattern). (nielsdos) 08 May 2025, PHP 8.3.21 - Core: . Fixed bug GH-18304 (Changing the properties of a DateInterval through dynamic properties triggers a SegFault). (nielsdos) . Fix some leaks in php_scandir. (nielsdos) - Filter: . Fixed bug GH-18309 (ipv6 filter integer overflow). (nielsdos) - GD: . Fixed imagecrop() overflow with rect argument with x/width y/heigh usage in gdImageCrop(). (David Carlier) . Fixed GH-18243 imagettftext() overflow/underflow on font size value. (David Carlier) - Intl: . Fix reference support for intltz_get_offset(). (nielsdos) - LDAP: . Fixed bug GH-17776 (LDAP_OPT_X_TLS_* options can't be overridden). (Remi) . Fix NULL deref on high modification key. (nielsdos) - libxml: . Fixed custom external entity loader returning an invalid resource leading to a confusing TypeError message. (Girgias) - OpenSSL: . Fix memory leak in openssl_sign() when passing invalid algorithm. (nielsdos) . Fix potential leaks when writing to BIO fails. (nielsdos) - PDO Firebird: . Fixed GH-18276 - persistent connection - "zend_mm_heap corrupted" with setAttribute() (SakiTakamachi). - SPL: . Fixed bug GH-18322 (SplObjectStorage debug handler mismanages memory). (nielsdos) - Standard: . Fixed bug GH-18145 (php8ts crashes in php_clear_stat_cache()). (Jakub Zelenka) . Fixed bug GH-18209 (Use-after-free in extract() with EXTR_REFS). (ilutov) . Fixed bug GH-18212 (fseek with SEEK_CUR whence value and negative offset leads to negative stream position). (David Carlier) . Fix resource leak in iptcembed() on error. (nielsdos) - Zip: . Fix uouv when handling empty options in ZipArchive::addGlob(). (nielsdos) . Fix memory leak when handling a too long path in ZipArchive::addGlob(). (nielsdos) 10 Apr 2025, PHP 8.3.20 - Core: . Fixed bug GH-17961 (use-after-free during dl()'ed module class destruction). (Arnaud) . Fixed bug GH-15367 (dl() of module with aliased class crashes in shutdown). (Arnaud) . Fixed bug GH-13193 again (Significant performance degradation in 'foreach'). (nielsdos) - DOM: . Fix weird unpack behaviour in DOM. (nielsdos) . Fix xinclude destruction of live attributes. (nielsdos) - Embed: . Fixed bug GH-8533 (Unable to link dynamic libphp on Mac). (Kévin Dunglas) - Fuzzer: . Fixed bug GH-18081 (Memory leaks in error paths of fuzzer SAPI). (Lung-Alexandra) - GD: . Fixed bug GH-17984 (calls with arguments as array with references). (David Carlier) - Intl: . Fix locale_compose and locale_lookup to work with their array argument with values as references. (David Carlier) . Fix dateformat_format when the time is an array of references. (David Carlier) . Fix UConverter::transcode with substitutes as references. (David Carlier) - Mbstring: . Fixed bug GH-17989 (mb_output_handler crash with unset http_output_conv_mimetypes). (nielsdos) - Opcache: . Fixed bug GH-18112 (NULL access with preloading and INI option). (nielsdos) . Fixed bug GH-18107 (Opcache CFG jmp optimization with try-finally breaks the exception table). (nielsdos) - PDO: . Fix memory leak when destroying PDORow. (nielsdos) - SOAP: . Fixed bug #66049 (Typemap can break parsing in parse_packet_soap leading to a segfault) . (Remi) - SPL: . Fixed bug GH-18018 (RC1 data returned from offsetGet causes UAF in ArrayObject). (nielsdos) - Treewide: . Fixed bug GH-17736 (Assertion failure zend_reference_destroy()). (nielsdos) - Windows: . Fixed bug GH-17836 (zend_vm_gen.php shouldn't break on Windows line endings). (DanielEScherzer) 13 Mar 2025, PHP 8.3.19 - BCMath: . Fixed bug GH-17398 (bcmul memory leak). (SakiTakamachi) - Core: . Fixed bug GH-17623 (Broken stack overflow detection for variable compilation). (ilutov) . Fixed bug GH-17618 (UnhandledMatchError does not take zend.exception_ignore_args=1 into account). (timwolla) . Fix fallback paths in fast_long_{add,sub}_function. (nielsdos) . Fixed bug GH-17718 (Calling static methods on an interface that has `__callStatic` is allowed). (timwolla) . Fixed bug GH-17797 (zend_test_compile_string crash on invalid script path). (David Carlier) . Fixed GHSA-rwp7-7vc6-8477 (Reference counting in php_request_shutdown causes Use-After-Free). (CVE-2024-11235) (ilutov) - DOM: . Fixed bug GH-17847 (xinclude destroys live node). (nielsdos) - FFI: . Fix FFI Parsing of Pointer Declaration Lists. (davnotdev) - FPM: . Fixed bug GH-17643 (FPM with httpd ProxyPass encoded PATH_INFO env). (Jakub Zelenka) - GD: . Fixed bug GH-17772 (imagepalettetotruecolor crash with memory_limit=2M). (David Carlier) - LDAP: . Fixed bug GH-17704 (ldap_search fails when $attributes contains a non-packed array with numerical keys). (nielsdos, 7u83) - LibXML: . Fixed GHSA-wg4p-4hqh-c3g9 (Reocurrence of #72714). (nielsdos) . Fixed GHSA-p3x9-6h7p-cgfc (libxml streams use wrong `content-type` header when requesting a redirected resource). (CVE-2025-1219) (timwolla) - MBString: . Fixed bug GH-17503 (Undefined float conversion in mb_convert_variables). (cmb) - Opcache: . Fixed bug GH-17654 (Multiple classes using same trait causes function JIT crash). (nielsdos) . Fixed bug GH-17577 (JIT packed type guard crash). (nielsdos, Dmitry) . Fixed bug GH-17899 (zend_test_compile_string with invalid path when opcache is enabled). (David Carlier) . Fixed bug GH-17868 (Cannot allocate memory with tracing JIT). (nielsdos) - PDO_SQLite: . Fixed GH-17837 ()::getColumnMeta() on unexecuted statement segfaults). (cmb) . Fix cycle leak in sqlite3 setAuthorizer(). (nielsdos) - Phar: . Fixed bug GH-17808: PharFileInfo refcount bug. (nielsdos) - PHPDBG: . Partially fixed bug GH-17387 (Trivial crash in phpdbg lexer). (nielsdos) . Fix memory leak in phpdbg calling registered function. (nielsdos) - Reflection: . Fixed bug GH-15902 (Core dumped in ext/reflection/php_reflection.c). (DanielEScherzer) - Sockets: . Fixed bug GH-17921 (socket_read/socket_recv overflow on buffer size). (David Carlier) - Standard: . Fixed bug #72666 (stat cache clearing inconsistent between file:// paths and plain paths). (Jakub Zelenka) - Streams: . Fixed bug GH-17650 (realloc with size 0 in user_filters.c). (nielsdos) . Fix memory leak on overflow in _php_stream_scandir(). (nielsdos) . Fixed GHSA-hgf5-96fm-v528 (Stream HTTP wrapper header check might omit basic auth header). (CVE-2025-1736) (Jakub Zelenka) . Fixed GHSA-52jp-hrpf-2jff (Stream HTTP wrapper truncate redirect location to 1024 bytes). (CVE-2025-1861) (Jakub Zelenka) . Fixed GHSA-pcmh-g36c-qc44 (Streams HTTP wrapper does not fail for headers without colon). (CVE-2025-1734) (Jakub Zelenka) . Fixed GHSA-v8xr-gpvj-cx9g (Header parser of `http` stream wrapper does not handle folded headers). (CVE-2025-1217) (Jakub Zelenka) - Windows: . Fixed phpize for Windows 11 (24H2). (bwoebi) . Fixed GH-17855 (CURL_STATICLIB flag set even if linked with shared lib). (cmb) - Zlib: . Fixed bug GH-17745 (zlib extension incorrectly handles object arguments). (nielsdos) . Fix memory leak when encoding check fails. (nielsdos) . Fix zlib support for large files. (nielsdos) 13 Feb 2025, PHP 8.3.17 - Core: . Fixed bug GH-16892 (ini_parse_quantity() fails to parse inputs starting with 0x0b). (nielsdos) . Fixed bug GH-16886 (ini_parse_quantity() fails to emit warning for 0x+0). (nielsdos) . Fixed bug GH-17214 (Relax final+private warning for trait methods with inherited final). (ilutov) . Fixed NULL arithmetic during system program execution on Windows. (cmb, nielsdos) . Fixed potential OOB when checking for trailing spaces on Windows. (cmb) . Fixed bug GH-17408 (Assertion failure Zend/zend_exceptions.c). (nielsdos, ilutov) . Fix may_have_extra_named_args flag for ZEND_AST_UNPACK. (nielsdos) . Fix NULL arithmetic in System V shared memory emulation for Windows. (cmb) - DOM: . Fixed bug GH-17500 (Segfault with requesting nodeName on nameless doctype). (nielsdos) - Enchant: . Fix crashes in enchant when passing null bytes. (nielsdos) - FTP: . Fixed bug GH-16800 (ftp functions can abort with EINTR). (nielsdos) - GD: . Fixed bug GH-17349 (Tiled truecolor filling looses single color transparency). (cmb) . Fixed bug GH-17373 (imagefttext() ignores clipping rect for palette images). (cmb) . Ported fix for libgd 223 (gdImageRotateGeneric() does not properly interpolate). (cmb) - Intl: . Fixed bug GH-11874 (intl causing segfault in docker images). (nielsdos) . Fixed bug GH-17469 (UConverter::transcode always emit E_WARNING on invalid encoding). (David Carlier) - Opcache: . Fixed bug GH-17307 (Internal closure causes JIT failure). (nielsdos) . Fixed bug GH-17564 (Potential UB when reading from / writing to struct padding). (ilutov) - PDO: . Fixed a memory leak when the GC is used to free a PDOStatment. (Girgias) . Fixed a crash in the PDO Firebird Statement destructor. (nielsdos) . Fixed UAFs when changing default fetch class ctor args. (Girgias, nielsdos) - Phar: . Fixed bug GH-17518 (offset overflow phar extractTo()). (nielsdos) - PHPDBG: . Fix crashes in function registration + test. (nielsdos, Girgias) - Session: . Fix type confusion with session SID constant. (nielsdos) . Fixed bug GH-17541 (ext/session NULL pointer dereferencement during ID reset). (Girgias) - SimpleXML: . Fixed bug GH-17409 (Assertion failure Zend/zend_hash.c:1730). (nielsdos) - SNMP: . Fixed bug GH-17330 (SNMP::setSecurity segfault on closed session). (David Carlier) - SPL: . Fixed bug GH-17463 (crash on SplTempFileObject::ftruncate with negative value). (David Carlier) - Zip: . Fixed bug GH-17139 (Fix zip_entry_name() crash on invalid entry). (nielsdos) 16 Jan 2025, PHP 8.3.16 - Core: . Fixed bug GH-17106 (ZEND_MATCH_ERROR misoptimization). (ilutov) . Fixed bug GH-17162 (zend_array_try_init() with dtor can cause engine UAF). (nielsdos) . Fixed bug GH-17101 (AST->string does not reproduce constructor property promotion correctly). (nielsdos) . Fixed bug GH-17211 (observer segfault on function loaded with dl()). (Arnaud) . Fixed bug GH-17216 (Trampoline crash on error). (nielsdos) - Date: . Fixed bug GH-14709 DatePeriod::__construct() overflow on recurrences. (David Carlier) - DBA: . Skip test if inifile is disabled. (orlitzky) - DOM: . Fixed bug GH-17224 (UAF in importNode). (nielsdos) - Embed: . Make build command for program using embed portable. (dunglas) - FFI: . Fixed bug #79075 (FFI header parser chokes on comments). (nielsdos) . Fix memory leak on ZEND_FFI_TYPE_CHAR conversion failure. (nielsdos) . Fixed bug GH-16013 and bug #80857 (Big endian issues). (Dmitry, nielsdos) - Filter: . Fixed bug GH-16944 (Fix filtering special IPv4 and IPv6 ranges, by using information from RFC 6890). (Derick) - FPM: . Fixed bug GH-13437 (FPM: ERROR: scoreboard: failed to lock (already locked)). (Jakub Zelenka) . Fixed bug GH-17112 (Macro redefinitions). (cmb, nielsdos) . Fixed bug GH-17208 (bug64539-status-json-encoding.phpt fail on 32-bits). (nielsdos) - GD: . Fixed bug GH-16255 (Unexpected nan value in ext/gd/libgd/gd_filter.c). (nielsdos, cmb) . Ported fix for libgd bug 276 (Sometimes pixels are missing when storing images as BMPs). (cmb) - Gettext: . Fixed bug GH-17202 (Segmentation fault ext/gettext/gettext.c bindtextdomain()). (Michael Orlitzky) - Iconv: . Fixed bug GH-17047 (UAF on iconv filter failure). (nielsdos) - LDAP: . Fixed bug GH-17280 (ldap_search() fails when $attributes array has holes). (nielsdos) - LibXML: . Fixed bug GH-17223 (Memory leak in libxml encoding handling). (nielsdos) - MBString: . Fixed bug GH-17112 (Macro redefinitions). (nielsdos, cmb) - Opcache: . opcache_get_configuration() properly reports jit_prof_threshold. (cmb) . Fixed bug GH-17246 (GC during SCCP causes segfault). (Dmitry) - PCNTL: . Fix memory leak in cleanup code of pcntl_exec() when a non stringable value is encountered past the first entry. (Girgias) - PgSql: . Fixed bug GH-17158 (pg_fetch_result Shows Incorrect ArgumentCountError Message when Called With 1 Argument). (nielsdos) . Fixed further ArgumentCountError for calls with flexible number of arguments. (David Carlier) - Phar: . Fixed bug GH-17137 (Segmentation fault ext/phar/phar.c). (nielsdos) - SimpleXML: . Fixed bug GH-17040 (SimpleXML's unset can break DOM objects). (nielsdos) . Fixed bug GH-17153 (SimpleXML crash when using autovivification on document). (nielsdos) - Sockets: . Fixed bug GH-16276 (socket_strerror overflow handling with INT_MIN). (David Carlier / cmb) . Fixed overflow on SO_LINGER values setting, strengthening values check on SO_SNDTIMEO/SO_RCVTIMEO for socket_set_option(). (David Carlier) - SPL: . Fixed bug GH-17225 (NULL deref in spl_directory.c). (nielsdos) - Streams: . Fixed bug GH-17037 (UAF in user filter when adding existing filter name due to incorrect error handling). (nielsdos) . Fixed bug GH-16810 (overflow on fopen HTTP wrapper timeout value). (David Carlier) . Fixed bug GH-17067 (glob:// wrapper doesn't cater to CWD for ZTS builds). (cmb) - Windows: . Hardened proc_open() against cmd.exe hijacking. (cmb) - XML: . Fixed bug GH-1718 (unreachable program point in zend_hash). (nielsdos) 19 Dec 2024, PHP 8.3.15 - Calendar: . Fixed jdtogregorian overflow. (David Carlier) . Fixed cal_to_jd julian_days argument overflow. (David Carlier) - COM: . Fixed bug GH-16991 (Getting typeinfo of non DISPATCH variant segfaults). (cmb) - Core: . Fail early in *nix configuration build script. (hakre) . Fixed bug GH-16727 (Opcache bad signal 139 crash in ZTS bookworm (frankenphp)). (nielsdos) . Fixed bug GH-16799 (Assertion failure at Zend/zend_vm_execute.h:7469). (nielsdos) . Fixed bug GH-16630 (UAF in lexer with encoding translation and heredocs). (nielsdos) . Fix is_zend_ptr() huge block comparison. (nielsdos) . Fixed potential OOB read in zend_dirname() on Windows. (cmb) - Curl: . Fixed bug GH-16802 (open_basedir bypass using curl extension). (nielsdos) . Fix various memory leaks in curl mime handling. (nielsdos) - DOM: . Fixed bug GH-16777 (Calling the constructor again on a DOM object after it is in a document causes UAF). (nielsdos) . Fixed bug GH-16906 (Reloading document can cause UAF in iterator). (nielsdos) - FPM: . Fixed GH-16432 (PHP-FPM 8.2 SIGSEGV in fpm_get_status). (Jakub Zelenka) - GD: . Fixed GH-16776 (imagecreatefromstring overflow). (David Carlier) - GMP: . Fixed bug GH-16890 (array_sum() with GMP can loose precision (LLP64)). (cmb) - Hash: . Fixed GH-16711: Segfault in mhash(). (Girgias) - Opcache: . Fixed bug GH-16770 (Tracing JIT type mismatch when returning UNDEF). (nielsdos, Dmitry) . Fixed bug GH-16851 (JIT_G(enabled) not set correctly on other threads). (dktapps) . Fixed bug GH-16902 (Set of opcache tests fail zts+aarch64). (nielsdos) - OpenSSL: . Prevent unexpected array entry conversion when reading key. (nielsdos) . Fix various memory leaks related to openssl exports. (nielsdos) . Fix memory leak in php_openssl_pkey_from_zval(). (nielsdos) - PDO: . Fixed memory leak of `setFetchMode()`. (SakiTakamachi) - Phar: . Fixed bug GH-16695 (phar:// tar parser and zero-length file header blocks). (nielsdos, Hans Krentel) - PHPDBG: . Fixed bug GH-15208 (Segfault with breakpoint map and phpdbg_clear()). (nielsdos) - SAPI: . Fixed bug GH-16998 (UBSAN warning in rfc1867). (nielsdos) - SimpleXML: . Fixed bug GH-16808 (Segmentation fault in RecursiveIteratorIterator ->current() with a xml element input). (nielsdos) - SOAP: . Fix make check being invoked in ext/soap. (Ma27) - Standard: . Fixed bug GH-16905 (Internal iterator functions can't handle UNDEF properties). (nielsdos) . Fixed bug GH-16957 (Assertion failure in array_shift with self-referencing array). (nielsdos) - Streams: . Fixed network connect poll interuption handling. (Jakub Zelenka) - Windows: . Fixed bug GH-16849 (Error dialog causes process to hang). (cmb) 21 Nov 2024, PHP 8.3.14 - CLI: . Fixed bug GH-16373 (Shebang is not skipped for router script in cli-server started through shebang). (ilutov) . Fixed bug GHSA-4w77-75f9-2c8w (Heap-Use-After-Free in sapi_read_post_data Processing in CLI SAPI Interface). (nielsdos) - COM: . Fixed out of bound writes to SafeArray data. (cmb) - Core: . Fixed bug GH-16168 (php 8.1 and earlier crash immediately when compiled with Xcode 16 clang on macOS 15). (nielsdos) . Fixed bug GH-16371 (Assertion failure in Zend/zend_weakrefs.c:646). (Arnaud) . Fixed bug GH-16515 (Incorrect propagation of ZEND_ACC_RETURN_REFERENCE for call trampoline). (ilutov) . Fixed bug GH-16509 (Incorrect line number in function redeclaration error). (ilutov) . Fixed bug GH-16508 (Incorrect line number in inheritance errors of delayed early bound classes). (ilutov) . Fixed bug GH-16648 (Use-after-free during array sorting). (ilutov) . Fixed bug GH-15915 (overflow with a high value for precision INI). (David Carlier / cmb) - Curl: . Fixed bug GH-16302 (CurlMultiHandle holds a reference to CurlHandle if curl_multi_add_handle fails). (timwolla) - Date: . Fixed bug GH-16454 (Unhandled INF in date_sunset() with tiny $utcOffset). (cmb) . Fixed bug GH-14732 (date_sun_info() fails for non-finite values). (cmb) - DBA: . Fixed bug GH-16390 (dba_open() can segfault for "pathless" streams). (cmb) - DOM: . Fixed bug GH-16316 (DOMXPath breaks when not initialized properly). (nielsdos) . Add missing hierarchy checks to replaceChild. (nielsdos) . Fixed bug GH-16336 (Attribute intern document mismanagement). (nielsdos) . Fixed bug GH-16338 (Null-dereference in ext/dom/node.c). (nielsdos) . Fixed bug GH-16473 (dom_import_simplexml stub is wrong). (nielsdos) . Fixed bug GH-16533 (Segfault when adding attribute to parent that is not an element). (nielsdos) . Fixed bug GH-16535 (UAF when using document as a child). (nielsdos) . Fixed bug GH-16593 (Assertion failure in DOM->replaceChild). (nielsdos) . Fixed bug GH-16595 (Another UAF in DOM -> cloneNode). (nielsdos) - EXIF: . Fixed bug GH-16409 (Segfault in exif_thumbnail when not dealing with a real file). (nielsdos, cmb) - FFI: . Fixed bug GH-16397 (Segmentation fault when comparing FFI object). (nielsdos) - Filter: . Fixed bug GH-16523 (FILTER_FLAG_HOSTNAME accepts ending hyphen). (cmb) - FPM: . Fixed bug GH-16628 (FPM logs are getting corrupted with this log statement). (nielsdos) - GD: . Fixed bug GH-16334 (imageaffine overflow on matrix elements). (David Carlier) . Fixed bug GH-16427 (Unchecked libavif return values). (cmb) . Fixed bug GH-16559 (UBSan abort in ext/gd/libgd/gd_interpolation.c:1007). (nielsdos) - GMP: . Fixed floating point exception bug with gmp_pow when using large exposant values. (David Carlier). . Fixed bug GH-16411 (gmp_export() can cause overflow). (cmb) . Fixed bug GH-16501 (gmp_random_bits() can cause overflow). (David Carlier) . Fixed gmp_pow() overflow bug with large base/exponents. (David Carlier) . Fixed segfaults and other issues related to operator overloading with GMP objects. (Girgias) - LDAP: . Fixed bug GHSA-g665-fm4p-vhff (OOB access in ldap_escape). (CVE-2024-8932) (nielsdos) - MBstring: . Fixed bug GH-16361 (mb_substr overflow on start/length arguments). (David Carlier) - MySQLnd: . Fixed bug GHSA-h35g-vwh6-m678 (Leak partial content of the heap through heap buffer over-read). (CVE-2024-8929) (Jakub Zelenka) - Opcache: . Fixed bug GH-16408 (Array to string conversion warning emitted in optimizer). (ilutov) - OpenSSL: . Fixed bug GH-16357 (openssl may modify member types of certificate arrays). (cmb) . Fixed bug GH-16433 (Large values for openssl_csr_sign() $days overflow). (cmb) . Fix various memory leaks on error conditions in openssl_x509_parse(). (nielsdos) - PDO DBLIB: . Fixed bug GHSA-5hqh-c84r-qjcv (Integer overflow in the dblib quoter causing OOB writes). (CVE-2024-11236) (nielsdos) - PDO Firebird: . Fixed bug GHSA-5hqh-c84r-qjcv (Integer overflow in the firebird quoter causing OOB writes). (CVE-2024-11236) (nielsdos) - PDO ODBC: . Fixed bug GH-16450 (PDO_ODBC can inject garbage into field values). (cmb) - Phar: . Fixed bug GH-16406 (Assertion failure in ext/phar/phar.c:2808). (nielsdos) - PHPDBG: . Fixed bug GH-16174 (Empty string is an invalid expression for ev). (cmb) - Reflection: . Fixed bug GH-16601 (Memory leak in Reflection constructors). (nielsdos) - Session: . Fixed bug GH-16385 (Unexpected null returned by session_set_cookie_params). (nielsdos) . Fixed bug GH-16290 (overflow on cookie_lifetime ini value). (David Carlier) - SOAP: . Fixed bug GH-16318 (Recursive array segfaults soap encoding). (nielsdos) . Fixed bug GH-16429 (Segmentation fault access null pointer in SoapClient). (nielsdos) - Sockets: . Fixed bug with overflow socket_recvfrom $length argument. (David Carlier) - SPL: . Fixed bug GH-16337 (Use-after-free in SplHeap). (nielsdos) . Fixed bug GH-16464 (Use-after-free in SplDoublyLinkedList::offsetSet()). (ilutov) . Fixed bug GH-16479 (Use-after-free in SplObjectStorage::setInfo()). (ilutov) . Fixed bug GH-16478 (Use-after-free in SplFixedArray::unset()). (ilutov) . Fixed bug GH-16588 (UAF in Observer->serialize). (nielsdos) . Fix GH-16477 (Segmentation fault when calling __debugInfo() after failed SplFileObject::__constructor). (Girgias) . Fixed bug GH-16589 (UAF in SplDoublyLinked->serialize()). (nielsdos) . Fixed bug GH-14687 (segfault on SplObjectIterator instance). (David Carlier) . Fixed bug GH-16604 (Memory leaks in SPL constructors). (nielsdos) . Fixed bug GH-16646 (UAF in ArrayObject::unset() and ArrayObject::exchangeArray()). (ilutov) - Standard: . Fixed bug GH-16293 (Failed assertion when throwing in assert() callback with bail enabled). (ilutov) - Streams: . Fixed bug GHSA-c5f2-jwm7-mmq2 (Configuring a proxy in a stream context might allow for CRLF injection in URIs). (CVE-2024-11234) (Jakub Zelenka) . Fixed bug GHSA-r977-prxv-hc43 (Single byte overread with convert.quoted-printable-decode filter). (CVE-2024-11233) (nielsdos) - SysVMsg: . Fixed bug GH-16592 (msg_send() crashes when a type does not properly serialized). (David Carlier / cmb) - SysVShm: . Fixed bug GH-16591 (Assertion error in shm_put_var). (nielsdos, cmb) - XMLReader: . Fixed bug GH-16292 (Segmentation fault in ext/xmlreader/php_xmlreader.c). (nielsdos) - Zlib: . Fixed bug GH-16326 (Memory management is broken for bad dictionaries.) (cmb) 24 Oct 2024, PHP 8.3.13 - Calendar: . Fixed GH-16240: jdtounix overflow on argument value. (David Carlier) . Fixed GH-16241: easter_days/easter_date overflow on year argument. (David Carlier) . Fixed GH-16263: jddayofweek overflow. (cmb) . Fixed GH-16234: jewishtojd overflow. (nielsdos) - CLI: . Fixed bug GH-16137: duplicate http headers when set several times by the client. (David Carlier) - Core: . Fixed bug GH-16054 (Segmentation fault when resizing hash table iterator list while adding). (nielsdos) . Fixed bug GH-15905 (Assertion failure for TRACK_VARS_SERVER). (cmb) . Fixed bug GH-15907 (Failed assertion when promoting Serialize deprecation to exception). (ilutov) . Fixed bug GH-15851 (Segfault when printing backtrace during cleanup of nested generator frame). (ilutov) . Fixed bug GH-15866 (Core dumped in Zend/zend_generators.c). (Arnaud) . Fixed bug GH-16188 (Assertion failure in Zend/zend_exceptions.c). (Arnaud) . Fixed bug GH-16233 (Observer segfault when calling user function in internal function via trampoline). (nielsdos) - DOM: . Fixed bug GH-16039 (Segmentation fault (access null pointer) in ext/dom/parentnode/tree.c). (nielsdos) . Fixed bug GH-16149 (Null pointer dereference in DOMElement->getAttributeNames()). (nielsdos) . Fixed bug GH-16151 (Assertion failure in ext/dom/parentnode/tree.c). (nielsdos) . Fixed bug GH-16150 (Use after free in php_dom.c). (nielsdos) . Fixed bug GH-16152 (Memory leak in DOMProcessingInstruction/DOMDocument). (nielsdos) - JSON: . Fixed bug GH-15168 (stack overflow in json_encode()). (nielsdos) - GD: . Fixed bug GH-16232 (bitshift overflow on wbmp file content reading / fix backport from upstream). (David Carlier) . Fixed bug GH-12264 (overflow/underflow on imagerotate degrees value) (David Carlier) . Fixed bug GH-16274 (imagescale underflow on RBG channels / fix backport from upstream). (David Carlier) - LDAP: . Fixed bug GH-16032 (Various NULL pointer dereferencements in ldap_modify_batch()). (Girgias) . Fixed bug GH-16101 (Segfault in ldap_list(), ldap_read(), and ldap_search() when LDAPs array is not a list). (Girgias) . Fix GH-16132 (php_ldap_do_modify() attempts to free pointer not allocated by ZMM.). (Girgias) . Fix GH-16136 (Memory leak in php_ldap_do_modify() when entry is not a proper dictionary). (Girgias) - MBString: . Fixed bug GH-16261 (Reference invariant broken in mb_convert_variables()). (nielsdos) - OpenSSL: . Fixed stub for openssl_csr_new. (Jakub Zelenka) - PCRE: . Fixed bug GH-16189 (underflow on offset argument). (David Carlier) . Fixed bug GH-16184 (UBSan address overflowed in ext/pcre/php_pcre.c). (nielsdos) - PHPDBG: . Fixed bug GH-15901 (phpdbg: Assertion failure on i funcs). (cmb) . Fixed bug GH-16181 (phpdbg: exit in exception handler reports fatal error). (cmb) - Reflection: . Fixed bug GH-16187 (Assertion failure in ext/reflection/php_reflection.c). (DanielEScherzer) - SAPI: . Fixed bug GH-15395 (php-fpm: zend_mm_heap corrupted with cgi-fcgi request). (Jakub Zelenka, David Carlier) - SimpleXML: . Fixed bug GH-15837 (Segmentation fault in ext/simplexml/simplexml.c). (nielsdos) - Sockets: . Fixed bug GH-16267 (socket_strerror overflow on errno argument). (David Carlier) - SOAP: . Fixed bug #73182 (PHP SOAPClient does not support stream context HTTP headers in array form). (nielsdos) . Fixed bug #62900 (Wrong namespace on xsd import error message). (nielsdos) . Fixed bug GH-15711 (SoapClient can't convert BackedEnum to scalar value). (nielsdos) . Fixed bug GH-16237 (Segmentation fault when cloning SoapServer). (nielsdos) . Fix Soap leaking http_msg on error. (nielsdos) . Fixed bug GH-16256 (Assertion failure in ext/soap/php_encoding.c:460). (nielsdos) . Fixed bug GH-16259 (Soap segfault when classmap instantiation fails). (nielsdos) - SPL: . Fixed bug GH-15918 (Assertion failure in ext/spl/spl_fixedarray.c). (nielsdos) - Standard: . Fixed bug GH-16053 (Assertion failure in Zend/zend_hash.c). (Arnaud) . Fixed bug GH-15169 (stack overflow when var serialization in ext/standard/var). (nielsdos) - Streams: . Fixed bugs GH-15908 and GH-15026 (leak / assertion failure in streams.c). (nielsdos) . Fixed bug GH-15980 (Signed integer overflow in main/streams/streams.c). (cmb) - TSRM: . Prevent closing of unrelated handles. (cmb) - Windows: . Fixed minimal Windows version. (cmb) 26 Sep 2024, PHP 8.3.12 - CGI: . Fixed bug GHSA-p99j-rfp4-xqvq (Bypass of CVE-2024-4577, Parameter Injection Vulnerability). (CVE-2024-8926) (nielsdos) . Fixed bug GHSA-94p6-54jq-9mwp (cgi.force_redirect configuration is bypassable due to the environment variable collision). (CVE-2024-8927) (nielsdos) - Core: . Fixed bug GH-15408 (MSan false-positve on zend_max_execution_timer). (zeriyoshi) . Fixed bug GH-15515 (Configure error grep illegal option q). (Peter Kokot) . Fixed bug GH-15514 (Configure error: genif.sh: syntax error). (Peter Kokot) . Fixed bug GH-15565 (--disable-ipv6 during compilation produces error EAI_SYSTEM not found). (nielsdos) . Fixed bug GH-15587 (CRC32 API build error on arm 32-bit). (Bernd Kuhls, Thomas Petazzoni) . Fixed bug GH-15330 (Do not scan generator frames more than once). (Arnaud) . Fixed uninitialized lineno in constant AST of internal enums. (ilutov) - Curl: . FIxed bug GH-15547 (curl_multi_select overflow on timeout argument). (David Carlier) - DOM: . Fixed bug GH-15551 (Segmentation fault (access null pointer) in ext/dom/xml_common.h). (nielsdos) . Fixed bug GH-15654 (Signed integer overflow in ext/dom/nodelist.c). (nielsdos) - Fileinfo: . Fixed bug GH-15752 (Incorrect error message for finfo_file with an empty filename argument). (DanielEScherzer) - FPM: . Fixed bug GHSA-865w-9rf3-2wh5 (Logs from childrens may be altered). (CVE-2024-9026) (Jakub Zelenka) - MySQLnd: . Fixed bug GH-15432 (Heap corruption when querying a vector). (cmb, Kamil Tekiela) - Opcache: . Fixed bug GH-15661 (Access null pointer in Zend/Optimizer/zend_inference.c). (nielsdos) . Fixed bug GH-15658 (Segmentation fault in Zend/zend_vm_execute.h). (nielsdos) - SAPI: . Fixed bug GHSA-9pqp-7h25-4f32 (Erroneous parsing of multipart form data). (CVE-2024-8925) (Arnaud) - Standard: . Fixed bug GH-15552 (Signed integer overflow in ext/standard/scanf.c). (cmb) - Streams: . Fixed bug GH-15628 (php_stream_memory_get_buffer() not zero-terminated). (cmb) 29 Aug 2024, PHP 8.3.11 - Core: . Fixed bug GH-15020 (Memory leak in Zend/Optimizer/escape_analysis.c). (nielsdos) . Fixed bug GH-15023 (Memory leak in Zend/zend_ini.c). (nielsdos) . Fixed bug GH-13330 (Append -Wno-implicit-fallthrough flag conditionally). (Peter Kokot) . Fix uninitialized memory in network.c. (nielsdos) . Fixed bug GH-15108 (Segfault when destroying generator during shutdown). (Arnaud) . Fixed bug GH-15275 (Crash during GC of suspended generator delegate). (Arnaud) - Curl: . Fixed case when curl_error returns an empty string. (David Carlier) - DOM: . Fix UAF when removing doctype and using foreach iteration. (nielsdos) - FFI: . Fixed bug GH-14286 (ffi enum type (when enum has no name) make memory leak). (nielsdos, dstogov) - Hash: . Fix crash when converting array data for array in shm in xxh3. (nielsdos) - Intl: . Fixed bug GH-15087 (IntlChar::foldCase()'s $option is not optional). (cmb) - Opcache: . Fixed bug GH-13817 (Segmentation fault for enabled observers after pass 4). (Bob) . Fixed bug GH-13775 (Memory leak possibly related to opcache SHM placement). (Arnaud, nielsdos) - Output: . Fixed bug GH-15179 (Segmentation fault (null pointer dereference) in ext/standard/url_scanner_ex.re). (nielsdos) - PDO_Firebird: . Fix bogus fallthrough path in firebird_handle_get_attribute(). (nielsdos) - PHPDBG: . Fixed bug GH-13199 (EOF emits redundant prompt in phpdbg local console mode with libedit/readline). (Peter Kokot) . Fixed bug GH-15268 (heap buffer overflow in phpdbg (zend_hash_num_elements() Zend/zend_hash.h)). (nielsdos) . Fixed bug GH-15210 use-after-free on watchpoint allocations. (nielsdos) - Random: . Fixed part of bug GH-15381, checking getrandom availability on solaris. (David Carlier) - Soap: . Fixed bug #55639 (Digest autentication dont work). (nielsdos) . Fix SoapFault property destruction. (nielsdos) . Fixed bug GH-15252 (SOAP XML broken since PHP 8.3.9 when using classmap constructor option). (nielsdos) - Standard: . Fix passing non-finite timeout values in stream functions. (nielsdos) . Fixed GH-14780 p(f)sockopen timeout overflow. (David Carlier) . Fixed GH-15653 overflow on fgetcsv length parameter. (David Carlier) - Streams: . Fixed bug GH-15028 (Memory leak in ext/phar/stream.c). (nielsdos) . Fixed bug GH-15034 (Integer overflow on stream_notification_callback byte_max parameter with files bigger than 2GB). (nielsdos) . Reverted fix for GH-14930 (Custom stream wrapper dir_readdir output truncated to 255 characters). (Jakub Zelenka) - Tidy: . Fix memory leaks in ext/tidy basedir restriction code. (nielsdos) 01 Aug 2024, PHP 8.3.10 - Core: . Fixed bug GH-13922 (Fixed support for systems with sysconf(_SC_GETPW_R_SIZE_MAX) == -1). (Arnaud) . Fixed bug GH-14626 (Fix is_zend_ptr() for huge blocks). (Arnaud) . Fixed bug GH-14590 (Memory leak in FPM test gh13563-conf-bool-env.phpt. (nielsdos) . Fixed OSS-Fuzz #69765. (nielsdos) . Fixed bug GH-14741 (Segmentation fault in Zend/zend_types.h). (nielsdos) . Fixed bug GH-14969 (Use-after-free in property coercion with __toString()). (ilutov) . Fixed bug GH-14961 (Comment between -> and keyword results in parse error). (ilutov) - Dom: . Fixed bug GH-14702 (DOMDocument::xinclude() crash). (nielsdos) - Fileinfo: . Fixed bug GH-14888 (README.REDIST.BINS refers to non-existing LICENSE). (cmb) - Gd: . ext/gd/tests/gh10614.phpt: skip if no PNG support. (orlitzky) . restored warning instead of fata error. (dryabov) - LibXML: . Fixed bug GH-14563 (Build failure with libxml2 v2.13.0). (nielsdos) - Opcache: . Fixed bug GH-14550 (No warning message when Zend DTrace is enabled that opcache.jit is implictly disabled). (nielsdos) - Output: . Fixed bug GH-14808 (Unexpected null pointer in Zend/zend_string.h with empty output buffer). (nielsdos) - PDO: . Fixed bug GH-14712 (Crash with PDORow access to null property). (David Carlier) - Phar: . Fixed bug GH-14603 (null string from zip entry). (David Carlier) - PHPDBG: . Fixed bug GH-14596 (crashes with ASAN and ZEND_RC_DEBUG=1). (David Carlier) . Fixed bug GH-14553 (echo output trimmed at NULL byte). (nielsdos) - Shmop: . Fixed bug GH-14537 (shmop Windows 11 crashes the process). (nielsdos) - SPL: . Fixed bug GH-14639 (Member access within null pointer in ext/spl/spl_observer.c). (nielsdos) - Standard: . Fixed bug GH-14775 (range function overflow with negative step argument). (David Carlier) . Fix 32-bit wordwrap test failures. (orlitzky) . Fixed bug GH-14774 (time_sleep_until overflow). (David Carlier) - Streams: . Fixed bug GH-14930 (Custom stream wrapper dir_readdir output truncated to 255 characters in PHP 8.3). (Joe Cai) - Tidy: . Fix memory leak in tidy_repair_file(). (nielsdos) - Treewide: . Fix compatibility with libxml2 2.13.2. (nielsdos) - XML: . Move away from to-be-deprecated libxml fields. (nielsdos) . Fixed bug GH-14834 (Error installing PHP when --with-pear is used). (nielsdos) 04 Jul 2024, PHP 8.3.9 - Core: . Fixed bug GH-14315 (Incompatible pointer type warnings). (Peter Kokot) . Fixed bug GH-12814 (max_execution_time reached too early on MacOS 14 when running on Apple Silicon). (Manuel Kress) . Fixed bug GH-14387 (Crash when stack walking in destructor of yielded from values during Generator->throw()). (Bob) . Fixed bug GH-14456 (Attempting to initialize class with private constructor calls destructor). (Girgias) . Fixed bug GH-14510 (memleak due to missing pthread_attr_destroy()-call). (Florian Engelhardt) . Fixed bug GH-14549 (Incompatible function pointer type for fclose). (Ryan Carsten Schmidt) - BCMath: . Fixed bug (bcpowmod() with mod = -1 returns 1 when it must be 0). (Girgias) - Curl: . Fixed bug GH-14307 (Test curl_basic_024 fails with curl 8.8.0). (nielsdos) - DOM: . Fixed bug GH-14343 (Memory leak in xml and dom). (nielsdos) - FPM: . Fixed bug GH-14037 (PHP-FPM ping.path and ping.response config vars are ignored in status pool). (Wilhansen Li, Pierrick Charron) - GD: . Fix parameter numbers for imagecolorset(). (Giovanni Giacobbi) - Intl: . Fix reference handling in SpoofChecker. (nielsdos) - MySQLnd: . Partially fix bug GH-10599 (Apache crash on Windows when using a self-referencing anonymous function inside a class with an active mysqli connection). (nielsdos) - Opcache: . Fixed bug GH-14267 (opcache.jit=off does not allow enabling JIT at runtime). (ilutov) . Fixed TLS access in JIT on FreeBSD/amd64. (Arnaud) . Fixed bug GH-11188 (Error when building TSRM in ARM64). (nielsdos) - PDO ODBC: . Fixed bug GH-14367 (incompatible SDWORD type with iODBC). (Calvin Buckley) - PHPDBG: . Fixed bug GH-13681 (segfault on watchpoint addition failure). (David Carlier) - Soap: . Fixed bug #47925 (PHPClient can't decompress response). (nielsdos) . Fix missing error restore code. (nielsdos) . Fix memory leak if calling SoapServer::setObject() twice. (nielsdos) . Fix memory leak if calling SoapServer::setClass() twice. (nielsdos) . Fix reading zlib ini settings in ext-soap. (nielsdos) . Fix memory leaks with string function name lookups. (nielsdos) . Fixed bug #69280 (SoapClient classmap doesn't support fully qualified class name). (nielsdos) . Fixed bug #76232 (SoapClient Cookie Header Semicolon). (nielsdos) . Fixed memory leaks when calling SoapFault::__construct() twice. (Girgias) - Sodium: . Fix memory leaks in ext/sodium on failure of some functions. (nielsdos) - SPL: . Fixed bug GH-14290 (Member access within null pointer in extension spl). (nielsdos) - Standard: . Fixed bug GH-14483 (Fixed off-by-one error in checking length of abstract namespace Unix sockets). (Derick) - Streams: . Fixed bug GH-11078 (PHP Fatal error triggers pointer being freed was not allocated and malloc: double free for ptr errors). (nielsdos) 06 Jun 2024, PHP 8.3.8 - CGI: . Fixed buffer limit on Windows, replacing read call usage by _read. (David Carlier) . Fixed bug GHSA-3qgc-jrrr-25jv (Bypass of CVE-2012-1823, Argument Injection in PHP-CGI). (CVE-2024-4577) (nielsdos) - CLI: . Fixed bug GH-14189 (PHP Interactive shell input state incorrectly handles quoted heredoc literals.). (nielsdos) - Core: . Fixed bug GH-13970 (Incorrect validation of #[Attribute] flags type for non-compile-time expressions). (ilutov) - DOM: . Fix crashes when entity declaration is removed while still having entity references. (nielsdos) . Fix references not handled correctly in C14N. (nielsdos) . Fix crash when calling childNodes next() when iterator is exhausted. (nielsdos) . Fix crash in ParentNode::append() when dealing with a fragment containing text nodes. (nielsdos) - Filter: . Fixed bug GHSA-w8qr-v226-r27w (Filter bypass in filter_var FILTER_VALIDATE_URL). (CVE-2024-5458) (nielsdos) - FPM: . Fix bug GH-14175 (Show decimal number instead of scientific notation in systemd status). (Benjamin Cremer) - Hash: . ext/hash: Swap the checking order of `__has_builtin` and `__GNUC__` (Saki Takamachi) - Intl: . Fixed build regression on systems without C++17 compilers. (Calvin Buckley, Peter Kokot) - MySQLnd: . Fix bug GH-14255 (mysqli_fetch_assoc reports error from nested query). (Kamil Tekiela) - Opcache: . Fixed bug GH-14109 (Fix accidental persisting of internal class constant in shm). (ilutov) - OpenSSL: . The openssl_private_decrypt function in PHP, when using PKCS1 padding (OPENSSL_PKCS1_PADDING, which is the default), is vulnerable to the Marvin Attack unless it is used with an OpenSSL version that includes the changes from this pull request: https://github.com/openssl/openssl/pull/13817 (rsa_pkcs1_implicit_rejection). These changes are part of OpenSSL 3.2 and have also been backported to stable versions of various Linux distributions, as well as to the PHP builds provided for Windows since the previous release. All distributors and builders should ensure that this version is used to prevent PHP from being vulnerable. (CVE-2024-2408) - Standard: . Fixed bug GHSA-9fcc-425m-g385 (Bypass of CVE-2024-1874). (CVE-2024-5585) (nielsdos) - XML: . Fixed bug GH-14124 (Segmentation fault with XML extension under certain memory limit). (nielsdos) - XMLReader: . Fixed bug GH-14183 (XMLReader::open() can't be overridden). (nielsdos) 09 May 2024, PHP 8.3.7 - Core: . Fixed zend_call_stack build with Linux/uclibc-ng without thread support. (Fabrice Fontaine) . Fixed bug GH-13772 (Invalid execute_data->opline pointers in observer fcall handlers when JIT is enabled). (Bob) . Fixed bug GH-13931 (Applying zero offset to null pointer in Zend/zend_opcode.c). (nielsdos) . Fixed bug GH-13942 (Align the behavior of zend-max-execution-timers with other timeout implementations). (Kévin Dunglas) . Fixed bug GH-14003 (Broken cleanup of unfinished calls with callable convert parameters). (ilutov) . Fixed bug GH-14013 (Erroneous dnl appended in configure). (Peter Kokot) . Fixed bug GH-10232 (If autoloading occurs during constant resolution filename and lineno are identified incorrectly). (ranvis) . Fixed bug GH-13727 (Missing void keyword). (Peter Kokot) - Fibers: . Fixed bug GH-13903 (ASAN false positive underflow when executing copy()). (nielsdos) - Fileinfo: . Fixed bug GH-13795 (Test failing in ext/fileinfo/tests/bug78987.phpt on big-endian PPC). (orlitzky) - FPM: . Fixed bug GH-13563 (Setting bool values via env in FPM config fails). (Jakub Zelenka) - Intl: . Fixed build for icu 74 and onwards. (dunglas) - MySQLnd: . Fix shift out of bounds on 32-bit non-fast-path platforms. (nielsdos) - Opcache: . Fixed bug GH-13433 (Segmentation Fault in zend_class_init_statics when using opcache.preload). (nielsdos) . Fixed incorrect assumptions across compilation units for static calls. (ilutov) - OpenSSL: . Fixed bug GH-10495 (feof on OpenSSL stream hangs indefinitely). (Jakub Zelenka) - PDO SQLite: . Fix GH-13984 (Buffer size is now checked before memcmp). (Saki Takamachi) . Fix GH-13998 (Manage refcount of agg_context->val correctly). (Saki Takamachi) - Phar: . Fixed bug GH-13836 (Renaming a file in a Phar to an already existing filename causes a NULL pointer dereference). (nielsdos) . Fixed bug GH-13833 (Applying zero offset to null pointer in zend_hash.c). (nielsdos) . Fix potential NULL pointer dereference before calling EVP_SignInit. (icy17) - PHPDBG: . Fixed bug GH-13827 (Null pointer access of type 'zval' in phpdbg_frame). (nielsdos) - Posix: . Fix usage of reentrant functions in ext/posix. (Arnaud) - Session: . Fixed bug GH-13856 (Member access within null pointer of type 'ps_files' in ext/session/mod_files.c). (nielsdos) . Fixed bug GH-13891 (memleak and segfault when using ini_set with session.trans_sid_hosts). (nielsdos, kamil-tekiela) . Fixed buffer _read/_write size limit on windows for the file mode. (David Carlier) - Streams: . Fixed file_get_contents() on Windows fails with "errno=22 Invalid argument". (Damian Wójcik) . Fixed bug GH-13264 (Part 1 - Memory leak on stream filter failure). (Jakub Zelenka) . Fixed bug GH-13860 (Incorrect PHP_STREAM_OPTION_CHECK_LIVENESS case in ext/openssl/xp_ssl.c - causing use of dead socket). (nielsdos) . Fixed bug GH-11678 (Build fails on musl 1.2.4 - lfs64). (Arnaud) - Treewide: . Fix gcc-14 Wcalloc-transposed-args warnings. (Cristian Rodríguez) 11 Apr 2024, PHP 8.3.6 - Core: . Fixed GH-13569 (GC buffer unnecessarily grows up to GC_MAX_BUF_SIZE when scanning WeakMaps). (Arnaud) . Fixed bug GH-13612 (Corrupted memory in destructor with weak references). (nielsdos) . Fixed bug GH-13446 (Restore exception handler after it finishes). (ilutov) . Fixed bug GH-13784 (AX_GCC_FUNC_ATTRIBUTE failure). (Remi) . Fixed bug GH-13670 (GC does not scale well with a lot of objects created in destructor). (Arnaud) - DOM: . Add some missing ZPP checks. (nielsdos) . Fix potential memory leak in XPath evaluation results. (nielsdos) - FPM: . Fixed GH-11086 (FPM: config test runs twice in daemonised mode). (Jakub Zelenka) . Fixed incorrect check in fpm_shm_free(). (nielsdos) - GD: . Fixed bug GH-12019 (add GDLIB_CFLAGS in feature tests). (Michael Orlitzky) - Gettext: . Fixed sigabrt raised with dcgettext/dcngettext calls with gettext 0.22.5 with category set to LC_ALL. (David Carlier) - MySQLnd: . Fix GH-13452 (Fixed handshake response [mysqlnd]). (Saki Takamachi) . Fix incorrect charset length in check_mb_eucjpms(). (nielsdos) - Opcache: . Fixed GH-13508 (JITed QM_ASSIGN may be optimized out when op1 is null). (Arnaud, Dmitry) . Fixed GH-13712 (Segmentation fault for enabled observers when calling trait method of internal trait when opcache is loaded). (Bob) - Random: . Fixed bug GH-13544 (Pre-PHP 8.2 compatibility for mt_srand with unknown modes). (timwolla) . Fixed bug GH-13690 (Global Mt19937 is not properly reset in-between requests when MT_RAND_PHP is used). (timwolla) - Session: . Fixed bug GH-13680 (Segfault with session_decode and compilation error). (nielsdos) - SPL: . Fixed bug GH-13685 (Unexpected null pointer in zend_string.h). (nielsdos) - Standard: . Fixed bug GH-11808 (Live filesystem modified by tests). (nielsdos) . Fixed GH-13402 (Added validation of `\n` in $additional_headers of mail()). (SakiTakamachi) . Fixed bug GH-13203 (file_put_contents fail on strings over 4GB on Windows). (divinity76) . Fixed bug GHSA-pc52-254m-w9w7 (Command injection via array-ish $command parameter of proc_open). (CVE-2024-1874) (Jakub Zelenka) . Fixed bug GHSA-wpj3-hf5j-x4v4 (__Host-/__Secure- cookie bypass due to partial CVE-2022-31629 fix). (CVE-2024-2756) (nielsdos) . Fixed bug GHSA-h746-cjrr-wfmr (password_verify can erroneously return true, opening ATO risk). (CVE-2024-3096) (Jakub Zelenka) . Fixed bug GHSA-fjp9-9hwx-59fq (mb_encode_mimeheader runs endlessly for some inputs). (CVE-2024-2757) (Alex Dowad) 14 Mar 2024, PHP 8.3.4 - Core: . Fix ZTS persistent resource crashes on shutdown. (nielsdos) - Curl: . Fix failing tests due to string changes in libcurl 8.6.0. (Ayesh) - DOM: . Fix unlikely memory leak in case of namespace removal with extremely deep trees. (nielsdos) . Fix reference access in dimensions for DOMNodeList and DOMNodeMap. (nielsdos) - Fileinfo: . Fixed bug GH-13344 (finfo::buffer(): Failed identify data 0:(null), backport). (nielsdos) - FPM: . Fixed bug #75712 (getenv in php-fpm should not read $_ENV, $_SERVER). (Jakub Zelenka) - GD: . Fixed bug GH-12019 (detection of image formats in system gd library). (Michael Orlitzky) - MySQLnd: . Fixed bug GH-11950 ([mysqlnd] Fixed not to set CR_MALFORMED_PACKET to error if CR_SERVER_GONE_ERROR is already set). (Saki Takamachi) - PDO: . Fix various PDORow bugs. (Girgias) - PGSQL: . Fixed bug GH-13354 (pg_execute/pg_send_query_params/pg_send_execute with null value passed by reference). (George Barbarosie) - SPL: . Fixed bug GH-13531 (Unable to resize SplfixedArray after being unserialized in PHP 8.2.15). (nielsdos) - Standard: . Fixed bug GH-13279 (Instable array during in-place modification in uksort). (ilutov) . Fixed array key as hash to string (case insensitive) comparison typo for the second operand buffer size (albeit unused for now). (A. Slepykh) - XML: . Fixed bug GH-13517 (Multiple test failures when building with --with-expat). (nielsdos) 15 Feb 2024, PHP 8.3.3 - Core: . Fixed timer leak in zend-max-execution-timers builds. (withinboredom) . Fixed bug GH-12349 (linking failure on ARM with mold). (Jan Palus) . Fixed bug GH-13097 (Anonymous class reference in trigger_error / thrown Exception). (nielsdos) . Fixed bug GH-13177 (PHP 8.3.2: final private constructor not allowed when used in trait). (nielsdos) . Fixed bug GH-13215 (GCC 14 build failure). (Remi) - Curl: . Fix missing error check in curl_multi_init(). (divinity76) - FPM: . Fixed bug GH-12996 (Incorrect SCRIPT_NAME with Apache ProxyPassMatch when plus in path). (Jakub Zelenka) - GD: . Fixed bug GH-10344 (imagettfbbox(): Could not find/open font UNC path). (nielsdos) . Fixed bug GH-10614 (imagerotate will turn the picture all black, when rotated 90). (nielsdos) - LibXML: . Fix crashes with entity references and predefined entities. (nielsdos) - MySQLnd: . Fixed bug GH-12107 (When running a stored procedure (that returns a result set) twice, PHP crashes). (nielsdos) - Opcache: . Fixed bug GH-13145 (strtok() is not comptime). (ilutov) . Fixed type inference of range(). (ilutov) . Fixed bug GH-13232 (Segmentation fault will be reported when JIT is off but JIT_debug is still on). (nielsdos) - OpenSSL: . Fixed LibreSSL undefined reference when OPENSSL_NO_ENGINE not set. (David Carlier). - PDO_Firebird: . Fix GH-13119 (Changed to convert float and double values into strings using `H` format). (SakiTakamachi) - Phar: . Fixed bug #71465 (PHAR doesn't know about litespeed). (nielsdos) . Fixed bug GH-13037 (PharData incorrectly extracts zip file). (nielsdos) - Random: . Fixed bug GH-13138 (Randomizer::pickArrayKeys() does not detect broken engines). (timwolla) - Session: . Fixed bug GH-12504 (Corrupted session written when there's a fatal error in autoloader). (nielsdos) - Standard: . Fixed bug GH-13094 (range(9.9, '0') causes segmentation fault). (nielsdos) - Streams: . Fixed bug GH-13071 (Copying large files using mmap-able source streams may exhaust available memory and fail). (nielsdos) 18 Jan 2024, PHP 8.3.2 - Core: . Fixed bug GH-12953 (false positive SSA integrity verification failed when loading composer classmaps with more than 11k elements). (nielsdos) . Fixed bug GH-12999 (zend_strnlen build when strnlen is unsupported). (rainerjung) . Fixed bug GH-12966 (missing cross-compiling 3rd argument so Autoconf doesn't emit warnings). (Peter Kokot) . Fixed bug GH-12854 (8.3 - as final trait-used method does not correctly report visibility in Reflection). (nielsdos) - Cli: . Fix incorrect timeout in built-in web server when using router script and max_input_time. (ilutov) - DOM: . Fixed bug GH-12870 (Creating an xmlns attribute results in a DOMException). (nielsdos) . Fix crash when toggleAttribute() is used without a document. (nielsdos) . Fix crash in adoptNode with attribute references. (nielsdos) . Fixed bug GH-13012 (DOMNode::isEqualNode() is incorrect when attribute order is different). (nielsdos) - FFI: . Fixed bug GH-9698 (stream_wrapper_register crashes with FFI\CData). (Jakub Zelenka) . Fixed bug GH-12905 (FFI::new interacts badly with observers). (nielsdos) - GD: . Fixed GH-13082 undefined behavior with GdFont instances handling with imageload* and imagechar*. (David Carlier) - Intl: . Fixed GH-12943 (IntlDateFormatter::__construct accepts 'C' as valid locale). (David Carlier) - Hash: . Fixed bug GH-12936 (hash() function hangs endlessly if using sha512 on strings >= 4GiB). (nielsdos) - MBString: . When operating on a string with invalid encoding, mb_substr (as well as mb_strstr and its variants) defines character indices in the same way as other mbstring functions such as mb_strpos. (Alex Dowad) - ODBC: . Fix crash on Apache shutdown with persistent connections. (nielsdos) - Opcache: . Fixed oss-fuzz #64727 (JIT undefined array key warning may overwrite DIM with NULL when DIM is the same var as result). (ilutov) . Added workaround for SELinux mprotect execheap issue. See https://bugzilla.kernel.org/show_bug.cgi?id=218258. (ilutov) - OpenSSL: . Fixed bug GH-12987 (openssl_csr_sign might leak new cert on error). (Jakub Zelenka) - PDO: . Fix GH-12969 (Fixed PDO::getAttribute() to get PDO::ATTR_STRINGIFY_FETCHES). (SakiTakamachi) - PDO_ODBC: . Fixed bug GH-12767 (Unable to turn on autocommit mode with setAttribute()). (SakiTakamachi) - PGSQL: . Fixed auto_reset_persistent handling and allow_persistent type. (David Carlier) . Fixed bug GH-12974 (Apache crashes on shutdown when using pg_pconnect()). (nielsdos) - Phar: . Fixed bug #77432 (Segmentation fault on including phar file). (nielsdos) - PHPDBG: . Fixed bug GH-12962 (Double free of init_file in phpdbg_prompt.c). (nielsdos) - SimpleXML: . Fix getting the address of an uninitialized property of a SimpleXMLElement resulting in a crash. (nielsdos) . Fixed bug GH-12929 (SimpleXMLElement with stream_wrapper_register can segfault). (nielsdos) - Tidy: . Fixed bug GH-12980 (tidynode.props.attribute is missing "Boolean Attributes" and empty attributes). (nielsdos) 21 Dec 2023, PHP 8.3.1 - Core: . Fixed bug GH-12758 / GH-12768 (Invalid opline in OOM handlers within ZEND_FUNC_GET_ARGS and ZEND_BIND_STATIC). (Florian Engelhardt) . Fix various missing NULL checks. (nielsdos, dstogov) . Fixed bug GH-12835 (Leak of call->extra_named_params on internal __call). (ilutov) . Fixed bug GH-12826 (Weird pointers issue in nested loops). (nielsdos) - FPM: . Fixed bug GH-12705 (Segmentation fault in fpm_status_export_to_zval). (Patrick Prasse) - FTP: . Fixed bug GH-9348 (FTP & SSL session reuse). (nielsdos) - LibXML: . Fixed test failures for libxml2 2.12.0. (nielsdos) - MySQLnd: . Avoid using uninitialised struct. (mikhainin) . Fixed bug GH-12791 (Possible dereference of NULL in MySQLnd debug code). (nielsdos) - Opcache: . Fixed JIT bug (Function JIT emits "Uninitialized string offset" warning at the same time as invalid offset Error). (Girgias) . Fixed JIT bug (JIT emits "Attempt to assign property of non-object" warning at the same time as Error is being thrown). (Girgias) - PDO PGSQL: . Fixed the default value of $fetchMode in PDO::pgsqlGetNotify() (kocsismate) - SOAP: . Fixed bug GH-12838 ([SOAP] Temporary WSDL cache files not being deleted). (nielsdos) - Standard . Fixed GH-12745 (http_build_query() default null argument for $arg_separator is implicitly coerced to string). (Girgias) 23 Nov 2023, PHP 8.3.0 - Bcmath . Fixed GH-11761 (removing trailing zeros from numbers) (jorgsowa) - CLI: . Added pdeathsig to builtin server to terminate workers when the master process is killed. (ilutov) . Fixed bug GH-11104 (STDIN/STDOUT/STDERR is not available for CLI without a script). (nielsdos) . Implement GH-10024 (support linting multiple files at once using php -l). (nielsdos) - Core: . Fix GH-11388 (Allow "final" modifier when importing a method from a trait). (nielsdos) . Fixed bug GH-11406 (segfault with unpacking and magic method closure). (nielsdos) . Fixed bug GH-9388 (Improve unset property and __get type incompatibility error message). (ilutov) . SA_ONSTACK is now set for signal handlers to be friendlier to other in-process code such as Go's cgo. (Kévin Dunglas) . SA_ONSTACK is now set when signals are disabled. (Kévin Dunglas) . Fix GH-9649: Signal handlers now do a no-op instead of crashing when executed on threads not managed by TSRM. (Kévin Dunglas) . Added shadow stack support for fibers. (Chen Hu) . Fix bug GH-9965 (Fix accidental caching of default arguments with side effects). (ilutov) . Implement GH-10217 (Use strlen() for determining the class_name length). (Dennis Buteyn) . Fix bug GH-8821 (Improve line numbers for errors in constant expressions). (ilutov) . Fix bug GH-10083 (Allow comments between & and parameter). (ilutov) . Zend Max Execution Timers is now enabled by default for ZTS builds on Linux. (Kévin Dunglas) . Fix bug GH-10469 (Disallow .. in open_basedir paths set at runtime). (ilutov) . Fix bug GH-10168, GH-10582 (Various segfaults with destructors and VM return values). (dstogov, nielsdos, ilutov) . Fix bug GH-10935 (Use of trait doesn't redeclare static property if class has inherited it from its parent). (ilutov) . Fix bug GH-11154 (Negative indices on empty array don't affect next chosen index). (ColinHDev) . Fix bug GH-8846 (Implement delayed early binding for classes without parents). (ilutov) . Fix bug #79836 (Segfault in concat_function). (nielsdos) . Fix bug #81705 (type confusion/UAF on set_error_handler with concat operation). (nielsdos) . Fix GH-11348 (Closure created from magic method does not accept named arguments). (nielsdos) . Fix GH-11388 (Allow "final" modifier when importing a method from a trait). (nielsdos) . Fixed bug GH-11406 (segfault with unpacking and magic method closure). (nielsdos) . Fixed bug GH-11507 (String concatenation performance regression in 8.3). (nielsdos) . Fixed GH-11488 (Missing "Optional parameter before required" deprecation on union null type). (ilutov) . Implement the #[\Override] attribute RFC. (timwolla) . Fixed bug GH-11601 (Incorrect handling of unwind and graceful exit exceptions). (ilutov) . Added zend_call_stack_get implementation for OpenBSD. (David Carlier) . Add stack limit check in zend_eval_const_expr(). (Arnaud) . Expose time spent collecting cycles in gc_status(). (Arnaud) . Remove WeakMap entries whose key is only reachable through the entry value. (Arnaud) . Resolve open_basedir paths on INI update. (ilutov) . Fixed oss-fuzz #60741 (Leak in open_basedir). (ilutov) . Fixed segfault during freeing of some incompletely initialized objects due to OOM error (PDO, SPL, XSL). (ilutov) . Introduced Zend guard recursion protection to fix __debugInfo issue. (Jakub Zelenka) . Fixed oss-fuzz #61712 (assertion failure with error handler during binary op). (nielsdos) . Fixed GH-11847 (DTrace enabled build is broken). (Filip Zrůst) . Fixed OSS Fuzz #61865 (Undef variable in ++/-- for declared property that is unset in error handler). (Girgias) . Fixed warning emitted when checking if a user stream is castable. (Girgias) . Fixed bug GH-12123 (Compile error on MacOS with C++ extension when using ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX). (kocsismate) . Fixed bug GH-12189 (#[Override] attribute in trait does not check for parent class implementations). (timwolla) . Fixed OSS Fuzz #62294 (Unsetting variable after ++/-- on string variable warning). (Girgias) . Fixed buffer underflow when compiling memoized expression. (ilutov) . Fixed oss-fuzz #63802 (OP1 leak in error path of post inc/dec). (ilutov) - Curl: . Added Curl options and constants up to (including) version 7.87. (nielsdos, adoy) - Date: . Implement More Appropriate Date/Time Exceptions RFC. (Derick) - DOM: . Fix bug GH-8388 (DOMAttr unescapes character reference). (Tim Starling) . Fix bug GH-11308 (getElementsByTagName() is O(N^2)). (nielsdos) . Fix #79700 (wrong use of libxml oldNs leads to performance problem). (nielsdos) . Fix #77894 (DOMNode::C14N() very slow on generated DOMDocuments even after normalisation). (nielsdos) . Revert changes to DOMAttr::$value and DOMAttr::$nodeValue expansion. (nielsdos) . Fixed bug GH-11500 (Namespace reuse in createElementNS() generates wrong output). (nielsdos) . Implemented DOMDocument::adoptNode(). Previously this always threw a "not yet implemented" exception. (nielsdos) . Fixed bug GH-9628 (Implicitly removing nodes from \DOMDocument breaks existing references). (nielsdos) . Added DOMNode::contains() and DOMNameSpaceNode::contains(). (nielsdos) . Added DOMElement::getAttributeNames(). (nielsdos) . Added DOMNode::getRootNode(). (nielsdos) . Added DOMElement::className and DOMElement::id. (nielsdos) . Added DOMParentNode::replaceChildren(). (nielsdos) . Added DOMNode::isConnected and DOMNameSpaceNode::isConnected. (nielsdos) . Added DOMNode::parentElement and DOMNameSpaceNode::parentElement. (nielsdos) . Added DOMNode::isEqualNode(). (nielsdos) . Added DOMElement::insertAdjacentElement() and DOMElement::insertAdjacentText(). (nielsdos) . Added DOMElement::toggleAttribute(). (nielsdos) . Fixed bug GH-11792 (LIBXML_NOXMLDECL is not implemented or broken). (nielsdos) . adoptNode now respects the strict error checking property. (nielsdos) . Align DOMChildNode parent checks with spec. (nielsdos) . Fixed bug #80927 (Removing documentElement after creating attribute node: possible use-after-free). (nielsdos) . Fix various namespace prefix conflict resolution bugs. (nielsdos) . Fix calling createAttributeNS() without prefix causing the default namespace of the element to change. (nielsdos) . Fixed GH-11952 (Confusing warning when blocking entity loading via libxml_set_external_entity_loader). (nielsdos) . Fix broken cache invalidation with deallocated and reallocated document node. (nielsdos) . Fix compile error when php_libxml.h header is included in C++. (Remi, nielsdos) . Fixed bug #47531 (No way of removing redundant xmlns: declarations). (nielsdos) - Exif: . Removed unneeded codepaths in exif_process_TIFF_in_JPEG(). (nielsdos) - FFI: . Implement GH-11934 (Allow to pass CData into struct and/or union fields). (nielsdos, KapitanOczywisty) - Fileinfo: . Upgrade bundled libmagic to 5.43. (Anatol) . Fix GH-11408 (Unable to build PHP 8.3.0 alpha 1 / fileinfo extension). (nielsdos) - FPM: . The status.listen shared pool now uses the same php_values (including expose_php) and php_admin_value as the pool it is shared with. (dwxh) . Added warning to log when fpm socket was not registered on the expected path. (Joshua Behrens, Jakub Zelenka) . Fixed bug #76067 (system() function call leaks php-fpm listening sockets). (Mikhail Galanin, Jakub Zelenka) . Fixed GH-12077 (PHP 8.3.0RC1 borked socket-close-on-exec.phpt). (Jakub Zelenka) - GD: . Removed imagerotate "ignore_transparent" argument since it has no effect. (David Carlier) - Intl: . Added pattern format error infos for numfmt_set_pattern. (David Carlier) . Added MIXED_NUMBERS and HIDDEN_OVERLAY constants for the Spoofchecker's class. (David Carlier) . Updated datefmt_set_timezone/IntlDateformatter::setTimezone returns type. (David Carlier). . Updated IntlBreakInterator::setText return type. (David Carlier) . Updated IntlChar::enumCharNames return type. (David Carlier) . Removed the BC break on IntlDateFormatter::construct which threw an exception with an invalid locale. (David Carlier) - JSON: . Added json_validate(). (Juan Morales) - LDAP: . Deprecate calling ldap_connect() with separate hostname and port. (heiglandreas) - LibXML: . Fix compile error with -Werror=incompatible-function-pointer-types and old libxml2. (nielsdos) - MBString: . mb_detect_encoding is better able to identify the correct encoding for Turkish text. (Alex Dowad) . mb_detect_encoding's "non-strict" mode now behaves as described in the documentation. Previously, it would return false if the same byte (for example, the first byte) of the input string was invalid in all candidate encodings. More generally, it would eliminate candidate encodings from consideration when an invalid byte was seen, and if the same input byte eliminated all remaining encodings still under consideration, it would return false. On the other hand, if all candidate encodings but one were eliminated from consideration, it would return the last remaining one without regard for how many encoding errors might be encountered later in the string. This is different from the behavior described in the documentation, which says: "If strict is set to false, the closest matching encoding will be returned." (Alex Dowad) . mb_strtolower, mb_strtotitle, and mb_convert_case implement conditional casing rules for the Greek letter sigma. For mb_convert_case, conditional casing only applies to MB_CASE_LOWER and MB_CASE_TITLE modes, not to MB_CASE_LOWER_SIMPLE and MB_CASE_TITLE_SIMPLE. (Alex Dowad) . mb_detect_encoding is better able to identify UTF-8 and UTF-16 strings with a byte-order mark. (Alex Dowad) . mb_decode_mimeheader interprets underscores in QPrint-encoded MIME encoded words as required by RFC 2047; they are converted to spaces. Underscores must be encoded as "=5F" in such MIME encoded words. (Alex Dowad) . mb_encode_mimeheader no longer drops NUL (zero) bytes when QPrint-encoding the input string. This previously caused strings in certain text encodings, especially UTF-16 and UTF-32, to be corrupted by mb_encode_mimeheader. (Alex Dowad) . Implement mb_str_pad() RFC. (nielsdos) . Fixed bug GH-11514 (PHP 8.3 build fails with --enable-mbstring enabled). (nielsdos) . Fix use-after-free of mb_list_encodings() return value. (ilutov) . Fixed bug GH-11992 (utf_encodings.phpt fails on Windows 32-bit). (nielsdos) - mysqli: . mysqli_fetch_object raises a ValueError instead of an Exception. (David Carlier) - Opcache: . Added start, restart and force restart time to opcache's phpinfo section. (Mikhail Galanin) . Fix GH-9139: Allow FFI in opcache.preload when opcache.preload_user=root. (Arnaud, Kapitan Oczywisty) . Made opcache.preload_user always optional in the cli and phpdbg SAPIs. (Arnaud) . Allows W/X bits on page creation on FreeBSD despite system settings. (David Carlier) . Added memfd api usage, on Linux, for zend_shared_alloc_create_lock() to create an abstract anonymous file for the opcache's lock. (Max Kellermann) . Avoid resetting JIT counter handlers from multiple processes/threads. (ilutov) . Fixed COPY_TMP type inference for references. (ilutov) - OpenSSL: . Added OPENSSL_CMS_OLDMIMETYPE and PKCS7_NOOLDMIMETYPE contants to switch between mime content types. (Daniel Kesselberg) . Fixed GH-11054: Reset OpenSSL errors when using a PEM public key. (Florian Moser) . Added support for additional EC parameters in openssl_pkey_new. (Eno-CN) - PCNTL: . SA_ONSTACK is now set for pcntl_signal. (Kévin Dunglas) . Added SIGINFO constant. (David Carlier) - PCRE: . Update bundled libpcre2 to 10.42. (nielsdos) - PGSQL: . pg_fetch_object raises a ValueError instead of an Exception. (David Carlier) . pg_cancel use thread safe PQcancel api instead. (David Carlier) . pg_trace new PGSQL_TRACE_SUPPRESS_TIMESTAMPS/PGSQL_TRACE_REGRESS_MODE contants support. (David Carlier) . pg_set_error_verbosity adding PGSQL_ERRORS_STATE constant. (David Carlier) . pg_convert/pg_insert E_WARNING on type errors had been converted to ValueError/TypeError exceptions. (David Carlier) . Added pg_set_error_context_visibility to set the context's visibility within the error messages. (David Carlier) - Phar: . Fix memory leak in phar_rename_archive(). (stkeke) - POSIX: . Added posix_sysconf. (David Carlier) . Added posix_pathconf. (David Carlier) . Added posix_fpathconf. (David Carlier) . Fixed zend_parse_arg_long's bool pointer argument assignment. (Cristian Rodriguez) . Added posix_eaccess. (David Carlier) - Random: . Added Randomizer::getBytesFromString(). (Joshua Rüsweg) . Added Randomizer::nextFloat(), ::getFloat(), and IntervalBoundary. (timwolla) . Enable getrandom() for NetBSD (from 10.x). (David Carlier) . Deprecate MT_RAND_PHP. (timwolla) . Fix Randomizer::getFloat() returning incorrect results under certain circumstances. (timwolla) - Reflection: . Fix GH-9470 (ReflectionMethod constructor should not find private parent method). (ilutov) . Fix GH-10259 (ReflectionClass::getStaticProperties doesn't need null return type). (kocsismate) - SAPI: . Fixed GH-11141 (Could not open input file: should be sent to stderr). (nielsdos) - Session: . Fixed bug GH-11529 (Crash after dealing with an Apache request). (nielsdos) - SimpleXML: . Fixed bug GH-12192 (SimpleXML infinite loop when getName() is called within foreach). (nielsdos) . Fixed bug GH-12208 (SimpleXML infinite loop when a cast is used inside a foreach). (nielsdos) . Fixed bug #55098 (SimpleXML iteration produces infinite loop). (nielsdos) - Sockets: . Added SO_ATTACH_REUSEPORT_CBPF socket option, to give tighter control over socket binding for a cpu core. (David Carlier) . Added SKF_AD_QUEUE for cbpf filters. (David Carlier) . Added socket_atmark if send/recv needs using MSG_OOB. (David Carlier) . Added TCP_QUICKACK constant, to give tigher control over ACK delays. (David Carlier) . Added DONTFRAGMENT support for path MTU discovery purpose. (David Carlier) . Added AF_DIVERT for raw socket for divert ports. (David Carlier) . Added SOL_UPDLITE, UDPLITE_RECV_CSCOV and UDPLITE_SEND_CSCOV for updlite protocol support. (David Carlier) . Added SO_RERROR, SO_ZEROIZE and SO_SPLICE netbsd and openbsd constants. (David Carlier) . Added TCP_REPAIR for quietly close a connection. (David Carlier) . Added SO_REUSEPORT_LB freebsd constant. (David Carlier) . Added IP_BIND_ADDRESS_NO_PORT. (David Carlier) - SPL: . Fixed GH-11573 (RecursiveDirectoryIterator::hasChildren is slow). (nielsdos) - Standard: . E_NOTICEs emitted by unserialize() have been promoted to E_WARNING. (timwolla) . unserialize() now emits a new E_WARNING if the input contains unconsumed bytes. (timwolla) . Make array_pad's $length warning less confusing. (nielsdos) . E_WARNING emitted by strtok in the caase both arguments are not provided when starting tokenisation. (David Carlier) . password_hash() will now chain the original RandomException to the ValueError on salt generation failure. (timwolla) . Fix GH-10239 (proc_close after proc_get_status always returns -1). (nielsdos) . Improve the warning message for unpack() in case not enough values were provided. (nielsdos) . Fix GH-11010 (parse_ini_string() now preserves formatting of unquoted strings starting with numbers when the INI_SCANNER_TYPED flag is specified). (ilutov) . Fix GH-10742 (http_response_code emits no error when headers were already sent). (NattyNarwhal) . Added support for rounding negative places in number_format(). (Marc Bennewitz) . Prevent precision loss on formatting decimal integers in number_format(). (Marc Bennewitz) . Added usage of posix_spawn for proc_open when supported by OS. (Cristian Rodriguez) . Added $before_needle argument to strrchr(). (HypeMC) . Fixed GH-11982 (str_getcsv returns null byte for unterminated enclosure). (Jakub Zelenka) . Fixed str_decrement() on "1". (ilutov) - Streams: . Fixed bug #51056: blocking fread() will block even if data is available. (Jakub Zelenka) . Added storing of the original path used to open xport stream. (Luc Vieillescazes) . Implement GH-8641 (STREAM_NOTIFY_COMPLETED over HTTP never emitted). (nielsdos, Jakub Zelenka) . Fix bug GH-10406 (fgets on a redis socket connection fails on PHP 8.3). (Jakub Zelenka) . Implemented GH-11242 (_php_stream_copy_to_mem: Allow specifying a maximum length without allocating a buffer of that size). (Jakub Zelenka) . Fixed bug #52335 (fseek() on memory stream behavior different than file). (Jakub Zelenka) . Fixed bug #76857 (Can read "non-existant" files). (Jakub Zelenka) - XSLTProcessor: . Fixed bug #69168 (DomNode::getNodePath() returns invalid path). (nielsdos) - ZIP: . zip extension version 1.22.0 for libzip 1.10.0. (Remi) . add new error macros (ER_DATA_LENGTH and ER_NOT_ALLOWED). (Remi) . add new archive global flags (ER_AFL_*). (Remi) . add ZipArchive::setArchiveFlag and ZipArchive::getArchiveFlag methods. (Remi) PK=1]Mr""$doc/ea-php83-php-common/TSRM_LICENSEnu[Copyright (c) 1999-2006, Andi Gutmans, Sascha Schumann, Zeev Suraski. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Neither name of the copyright holders nor the names of their contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PK=1]`/vv*doc/ea-php83-php-common/README.REDIST.BINSnu[ 1. libmagic (ext/fileinfo) see ext/fileinfo/libmagic/LICENSE 2. libmbfl (ext/mbstring) see ext/mbstring/libmbfl/LICENSE 3. pcre2lib (ext/pcre) 4. ext/standard crypt 5. ext/standard crypt's blowfish implementation 6. ext/standard/rand 7. ext/standard/scanf 8. ext/standard/strnatcmp.c 9. ext/standard/uuencode 10. main/snprintf.c 11. main/strlcat 12. main/strlcpy 13. libgd (ext/gd) 14. ext/phar portions of tar implementations 15. ext/phar/zip.c portion extracted from libzip 16. libbcmath (ext/bcmath) see ext/bcmath/libbcmath/LICENSE 17. ext/mbstring/ucgendat portions based on the ucgendat.c from the OpenLDAP 18. avifinfo (ext/standard/libavifinfo) see ext/standard/libavifinfo/LICENSE 19. xxHash (ext/hash/xxhash) 3. pcre2lib (ext/pcre) PCRE2 LICENCE ------------- PCRE2 is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Releases 10.00 and above of PCRE2 are distributed under the terms of the "BSD" licence, as specified below, with one exemption for certain binary redistributions. The documentation for PCRE2, supplied in the "doc" directory, is distributed under the same terms as the software itself. The data in the testdata directory is not copyrighted and is in the public domain. The basic library functions are written in C and are freestanding. Also included in the distribution is a just-in-time compiler that can be used to optimize pattern matching. This is an optional feature that can be omitted when the library is built. THE BASIC LIBRARY FUNCTIONS --------------------------- Written by: Philip Hazel Email local part: ph10 Email domain: cam.ac.uk University of Cambridge Computing Service, Cambridge, England. Copyright (c) 1997-2019 University of Cambridge All rights reserved. PCRE2 JUST-IN-TIME COMPILATION SUPPORT -------------------------------------- Written by: Zoltan Herczeg Email local part: hzmester Email domain: freemail.hu Copyright(c) 2010-2019 Zoltan Herczeg All rights reserved. STACK-LESS JUST-IN-TIME COMPILER -------------------------------- Written by: Zoltan Herczeg Email local part: hzmester Email domain: freemail.hu Copyright(c) 2009-2019 Zoltan Herczeg All rights reserved. THE "BSD" LICENCE ----------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notices, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notices, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. EXEMPTION FOR BINARY LIBRARY-LIKE PACKAGES ------------------------------------------ The second condition in the BSD licence (covering binary redistributions) does not apply all the way down a chain of software. If binary package A includes PCRE2, it must respect the condition, but if package B is software that includes package A, the condition is not imposed on package B unless it uses PCRE2 independently. End 4. ext/standard crypt FreeSec: libcrypt for NetBSD Copyright (c) 1994 David Burren All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the author nor the names of other contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 5. ext/standard crypt's blowfish implementation The crypt_blowfish homepage is: http://www.openwall.com/crypt/ This code comes from John the Ripper password cracker, with reentrant and crypt(3) interfaces added, but optimizations specific to password cracking removed. Written by Solar Designer in 1998-2011. No copyright is claimed, and the software is hereby placed in the public domain. In case this attempt to disclaim copyright and place the software in the public domain is deemed null and void, then the software is Copyright (c) 1998-2011 Solar Designer and it is hereby released to the general public under the following terms: Redistribution and use in source and binary forms, with or without modification, are permitted. There's ABSOLUTELY NO WARRANTY, express or implied. It is my intent that you should be able to use this on your system, as part of a software package, or anywhere else to improve security, ensure compatibility, or for any other purpose. I would appreciate it if you give credit where it is due and keep your modifications in the public domain as well, but I don't require that in order to let you place this code and any modifications you make under a license of your choice. This implementation is mostly compatible with OpenBSD's bcrypt.c (prefix "$2a$") by Niels Provos , and uses some of his ideas. The password hashing algorithm was designed by David Mazieres . For more information on the level of compatibility, please refer to the comments in BF_set_key() and to the crypt(3) man page included in the crypt_blowfish tarball. There's a paper on the algorithm that explains its design decisions: http://www.usenix.org/events/usenix99/provos.html Some of the tricks in BF_ROUND might be inspired by Eric Young's Blowfish library (I can't be sure if I would think of something if I hadn't seen his code). 6. ext/standard/rand The following php_mt_...() functions are based on a C++ class MTRand by Richard J. Wagner. For more information see the web page at http://www-personal.engin.umich.edu/~wagnerr/MersenneTwister.html Mersenne Twister random number generator -- a C++ class MTRand Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus Richard J. Wagner v1.0 15 May 2003 rjwagner@writeme.com The Mersenne Twister is an algorithm for generating random numbers. It was designed with consideration of the flaws in various other generators. The period, 2^19937-1, and the order of equidistribution, 623 dimensions, are far greater. The generator is also fast; it avoids multiplication and division, and it benefits from caches and pipelines. For more information see the inventors' web page at http://www.math.keio.ac.jp/~matumoto/emt.html Reference M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30. Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, Copyright (C) 2000 - 2003, Richard J. Wagner All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 7. ext/standard/scanf scanf.c -- This file contains the base code which implements sscanf and by extension fscanf. Original code is from TCL8.3.0 and bears the following copyright: This software is copyrighted by the Regents of the University of California, Sun Microsystems, Inc., Scriptics Corporation, and other parties. The following terms apply to all files associated with the software unless explicitly disclaimed in individual files. The authors hereby grant permission to use, copy, modify, distribute, and license this software and its documentation for any purpose, provided that existing copyright notices are retained in all copies and that this notice is included verbatim in any distributions. No written agreement, license, or royalty fee is required for any of the authorized uses. Modifications to this software may be copyrighted by their authors and need not follow the licensing terms described here, provided that the new terms are clearly indicated on the first page of each file where they apply. IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. GOVERNMENT USE: If you are acquiring this software on behalf of the U.S. government, the Government shall have only "Restricted Rights" in the software and related documentation as defined in the Federal Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you are acquiring the software on behalf of the Department of Defense, the software shall be classified as "Commercial Computer Software" and the Government shall have only "Restricted Rights" as defined in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the authors grant the U.S. Government and others acting in its behalf permission to use and distribute the software in accordance with the terms specified in this license. 8. ext/standard/strnatcmp.c strnatcmp.c -- Perform 'natural order' comparisons of strings in C. Copyright (C) 2000 by Martin Pool This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. 9. ext/standard/uuencode Portions of this code are based on Berkeley's uuencode/uudecode implementation. Copyright (c) 1983, 1993 The Regents of the University of California. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the University of California, Berkeley and its contributors. 4. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10. main/snprintf.c Copyright (c) 2002, 2006 Todd C. Miller Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Sponsored in part by the Defense Advanced Research Projects Agency (DARPA) and Air Force Research Laboratory, Air Force Materiel Command, USAF, under agreement number F39502-99-1-0512. main/spprintf Copyright (c) 1995-1998 The Apache Group. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. All advertising materials mentioning features or use of this software must display the following acknowledgment: "This product includes software developed by the Apache Group for use in the Apache HTTP server project (http://www.apache.org/)." 4. The names "Apache Server" and "Apache Group" must not be used to endorse or promote products derived from this software without prior written permission. 5. Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes software developed by the Apache Group for use in the Apache HTTP server project (http://www.apache.org/)." THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================== This software consists of voluntary contributions made by many individuals on behalf of the Apache Group and was originally based on public domain software written at the National Center for Supercomputing Applications, University of Illinois, Urbana-Champaign. For more information on the Apache Group and the Apache HTTP server project, please see . This code is based on, and used with the permission of, the SIO stdio-replacement strx_* functions by Panos Tsirigotis for xinetd. 11. main/strlcat 12. main/strlcpy Copyright (c) 1998 Todd C. Miller All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13. libgd (ext/gd) * Portions copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 by Cold Spring Harbor Laboratory. Funded under Grant P41-RR02188 by the National Institutes of Health. * Portions copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 by Boutell.Com, Inc. * Portions relating to GD2 format copyright 1999, 2000, 2001, 2002, 2003, 2004 Philip Warner. * Portions relating to PNG copyright 1999, 2000, 2001, 2002, 2003, 2004 Greg Roelofs. * Portions relating to gdttf.c copyright 1999, 2000, 2001, 2002, 2003, 2004 John Ellson (ellson@graphviz.org). * Portions relating to gdft.c copyright 2001, 2002, 2003, 2004 John Ellson (ellson@graphviz.org). * Portions copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Pierre-Alain Joye (pierre@libgd.org). * Portions relating to JPEG and to color quantization copyright 2000, 2001, 2002, 2003, 2004, Doug Becker and copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Thomas G. Lane. This software is based in part on the work of the Independent JPEG Group. See the file README-JPEG.TXT for more information. * Portions relating to GIF compression copyright 1989 by Jef Poskanzer and David Rowley, with modifications for thread safety by Thomas Boutell. * Portions relating to GIF decompression copyright 1990, 1991, 1993 by David Koblas, with modifications for thread safety by Thomas Boutell. * Portions relating to WBMP copyright 2000, 2001, 2002, 2003, 2004 Maurice Szmurlo and Johan Van den Brande. * Portions relating to GIF animations copyright 2004 Jaakko Hyvätti (jaakko.hyvatti@iki.fi) Permission has been granted to copy, distribute and modify gd in any context without fee, including a commercial application, provided that this notice is present in user-accessible supporting documentation. This does not affect your ownership of the derived work itself, and the intent is to assure proper credit for the authors of gd, not to interfere with your productive use of gd. If you have questions, ask. "Derived works" includes all programs that utilize the library. Credit must be given in user-accessible documentation. This software is provided "AS IS." The copyright holders disclaim all warranties, either express or implied, including but not limited to implied warranties of merchantability and fitness for a particular purpose, with respect to this code and accompanying documentation. Although their code does not appear in the current release, the authors wish to thank David Koblas, David Rowley, and Hutchison Avenue Software Corporation for their prior contributions. END OF COPYRIGHT STATEMENT 14. ext/phar portions of tar implementations portions of tar implementations in ext/phar - phar_tar_octal() are based on an implementation by Tim Kientzle from libarchive, licensed with this license: Copyright (c) 2003-2007 Tim Kientzle All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 15. ext/phar/zip.c portion extracted from libzip zip_dirent.c -- read directory entry (local or central), clean dirent Copyright (C) 1999, 2003, 2004, 2005 Dieter Baron and Thomas Klausner This function is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 17. ext/mbstring/ucgendat portions based on the ucgendat.c from the OpenLDAP The OpenLDAP Public License Version 2.8, 17 August 2003 Redistribution and use of this software and associated documentation ("Software"), with or without modification, are permitted provided that the following conditions are met: 1. Redistributions in source form must retain copyright statements and notices, 2. Redistributions in binary form must reproduce applicable copyright statements and notices, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution, and 3. Redistributions must contain a verbatim copy of this document. The OpenLDAP Foundation may revise this license from time to time. Each revision is distinguished by a version number. You may use this Software under terms of this license revision or under the terms of any subsequent revision of the license. THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S) OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The names of the authors and copyright holders must not be used in advertising or otherwise to promote the sale, use or other dealing in this Software without specific, written prior permission. Title to copyright in this Software shall at all times remain with copyright holders. OpenLDAP is a registered trademark of the OpenLDAP Foundation. Copyright 1999-2003 The OpenLDAP Foundation, Redwood City, California, USA. All Rights Reserved. Permission to copy and distribute verbatim copies of this document is granted. 19. xxHash xxHash - Extremely Fast Hash algorithm Header File Copyright (C) 2012-2020 Yann Collet BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at: - xxHash homepage: https://www.xxhash.com - xxHash source repository: https://github.com/Cyan4973/xxHash PK=1]YCQ,Q,+doc/ea-php83-php-common/CODING_STANDARDS.mdnu[# PHP coding standards This file lists standards that any programmer adding or changing code in PHP should follow. The code base does not yet fully follow it, but new features are going in that general direction. Many sections have been rewritten to comply with these rules. ## Code implementation 1. Document your code in source files and the manual. (tm) 1. PHP is implemented in C99. The optional fixed-width integers from stdint.h (int8_t, int16_t, int32_t, int64_t and their unsigned counterparts) must be available. 1. Functions that are given pointers to resources should not free them. For instance, `function int mail(char *to, char *from)` should NOT free `to` and/or `from`. Exceptions: * The function's designated behavior is freeing that resource. E.g. `efree()` * The function is given a boolean argument, that controls whether or not the function may free its arguments (if true, the function must free its arguments; if false, it must not) * Low-level parser routines, that are tightly integrated with the token cache and the bison code for minimum memory copying overhead. 1. Functions that are tightly integrated with other functions within the same module, and rely on each other's non-trivial behavior, should be documented as such and declared `static`. They should be avoided if possible. 1. Use definitions and macros whenever possible, so that constants have meaningful names and can be easily manipulated. Any use of a numeric constant to specify different behavior or actions should be done through a `#define`. 1. When writing functions that deal with strings, be sure to remember that PHP holds the length property of each string, and that it shouldn't be calculated with `strlen()`. Write your functions in such a way so that they'll take advantage of the length property, both for efficiency and in order for them to be binary-safe. Functions that change strings and obtain their new lengths while doing so, should return that new length, so it doesn't have to be recalculated with `strlen()` (e.g. `php_addslashes()`). 1. NEVER USE `strncat()`. If you're absolutely sure you know what you're doing, check its man page again, and only then, consider using it, and even then, try avoiding it. 1. Use `PHP_*` macros in the PHP source, and `ZEND_*` macros in the Zend part of the source. Although the `PHP_*` macros are mostly aliased to the `ZEND_*` macros it gives a better understanding on what kind of macro you're calling. 1. When commenting out code using a `#if` statement, do NOT use `0` only. Instead, use `"_0"`. For example, `#if FOO_0`, where `FOO` is your git user `foo`. This allows easier tracking of why code was commented out, especially in bundled libraries. 1. Do not define functions that are not available. For instance, if a library is missing a function, do not define the PHP version of the function, and do not raise a run-time error about the function not existing. End users should use `function_exists()` to test for the existence of a function. 1. Prefer `emalloc()`, `efree()`, `estrdup()`, etc. to their standard C library counterparts. These functions implement an internal "safety-net" mechanism that ensures the deallocation of any unfreed memory at the end of a request. They also provide useful allocation and overflow information while running in debug mode. In almost all cases, memory returned to the engine must be allocated using `emalloc()`. The use of `malloc()` should be limited to cases where a third-party library may need to control or free the memory, or when the memory in question needs to survive between multiple requests. 1. The return type of "is" or "has" style functions should be `bool`, which return a "yes"/"no" answer. `zend_result` is an appropriate return value for functions that perform some operation that may succeed or fail. ## User functions/methods naming conventions 1. Function names for user-level functions should be enclosed with in the `PHP_FUNCTION()` macro. They should be in lowercase, with words underscore delimited, with care taken to minimize the letter count. Abbreviations should not be used when they greatly decrease the readability of the function name itself: Good: ```php str_word_count array_key_exists ``` Ok: ```php date_interval_create_from_date_string // Could be 'date_intvl_create_from_date_str'? get_html_translation_table() // Could be 'html_get_trans_table'? ``` Bad: ```php hw_GetObjectByQueryCollObj pg_setclientencoding jf_n_s_i ``` 1. If they are part of a "parent set" of functions, that parent should be included in the user function name, and should be clearly related to the parent program or function family. This should be in the form of `parent_*`: A family of `foo` functions, for example: Good: ```php foo_select_bar foo_insert_baz foo_delete_baz ``` Bad: ```php fooselect_bar fooinsertbaz delete_foo_baz ``` 1. Function names used by user functions should be prefixed with `_php_`, and followed by a word or an underscore-delimited list of words, in lowercase letters, that describes the function. If applicable, they should be declared `static`. 1. Variable names must be meaningful. One letter variable names must be avoided, except for places where the variable has no real meaning or a trivial meaning (e.g. `for (i=0; i<100; i++) ...`). 1. Variable names should be in lowercase. Use underscores to separate between words. 1. Method names follow the *studlyCaps* (also referred to as *bumpy case* or *camel caps*) naming convention, with care taken to minimize the letter count. The initial letter of the name is lowercase, and each letter that starts a new `word` is capitalized: Good: ```php connect() getData() buildSomeWidget() ``` Bad: ```php get_Data() buildsomewidget() getI() ``` 1. Class names should be descriptive nouns in *PascalCase* and as short as possible. Each word in the class name should start with a capital letter, without underscore delimiters. The class name should be prefixed with the name of the "parent set" (e.g. the name of the extension) if no namespaces are used. Abbreviations and acronyms as well as initialisms should be avoided wherever possible, unless they are much more widely used than the long form (e.g. HTTP or URL). Abbreviations start with a capital letter followed by lowercase letters, whereas acronyms and initialisms are written according to their standard notation. Usage of acronyms and initialisms is not allowed if they are not widely adopted and recognized as such. Good: ```php Curl CurlResponse HTTPStatusCode URL BTreeMap // B-tree Map Id // Identifier ID // Identity Document Char // Character Intl // Internationalization Radar // Radio Detecting and Ranging ``` Bad: ```php curl curl_response HttpStatusCode Url BtreeMap ID // Identifier CHAR INTL RADAR // Radio Detecting and Ranging ``` ## Internal function naming conventions 1. Functions that are part of the external API should be named `php_modulename_function()` to avoid symbol collision. They should be in lowercase, with words underscore delimited. Exposed API must be defined in `php_modulename.h`. ```c PHPAPI char *php_session_create_id(PS_CREATE_SID_ARGS); ``` Unexposed module function should be static and should not be defined in `php_modulename.h`. ```c static int php_session_destroy() ``` 1. Main module source file must be named `modulename.c`. 1. Header file that is used by other sources must be named `php_modulename.h`. ## Syntax and indentation 1. Use K&R-style. Of course, we can't and don't want to force anybody to use a style he or she is not used to, but, at the very least, when you write code that goes into the core of PHP or one of its standard modules, please maintain the K&R style. This applies to just about everything, starting with indentation and comment styles and up to function declaration syntax. Also see [Indentstyle](http://www.catb.org/~esr/jargon/html/I/indent-style.html). 1. Be generous with whitespace and braces. Keep one empty line between the variable declaration section and the statements in a block, as well as between logical statement groups in a block. Maintain at least one empty line between two functions, preferably two. Always prefer: ```c if (foo) { bar; } ``` to: ```c if(foo)bar; ``` 1. When indenting, use the tab character. A tab is expected to represent four spaces. It is important to maintain consistency in indentation so that definitions, comments, and control structures line up correctly. 1. Preprocessor statements (`#if` and such) MUST start at column one. To indent preprocessor directives you should put the `#` at the beginning of a line, followed by any number of spaces. 1. The length of constant string literals should be calculated via ``strlen()`` instead of using ``sizeof()-1`` as it is clearer and any modern compiler will optimize it away. Legacy usages of the latter style exists within the codebase but should not be refactored, unless larger refactoring around that code is taking place. ## Testing 1. Extensions should be well tested using `*.phpt` tests. Read more at [qa.php.net](https://qa.php.net/write-test.php) documentation. ## New and experimental functions To reduce the problems normally associated with the first public implementation of a new set of functions, it has been suggested that the first implementation include a file labeled `EXPERIMENTAL` in the function directory, and that the functions follow the standard prefixing conventions during their initial implementation. The file labelled `EXPERIMENTAL` should include the following information: * Any authoring information (known bugs, future directions of the module). * Ongoing status notes which may not be appropriate for Git comments. In general, new features should go to PECL or experimental branches until there are specific reasons for directly adding them to the core distribution. ## Aliases & legacy documentation You may also have some deprecated aliases with close to duplicate names, for example, `somedb_select_result` and `somedb_selectresult`. For documentation purposes, these will only be documented by the most current name, with the aliases listed in the documentation for the parent function. For ease of reference, user-functions with completely different names, that alias to the same function (such as `highlight_file` and `show_source`), will be separately documented. Backwards compatible functions and names should be maintained as long as the code can be reasonably be kept as part of the codebase. See the `README` in the PHP documentation repository for more information on documentation. PK=1]I doc/ea-php83-php-common/LICENSEnu[-------------------------------------------------------------------- The PHP License, version 3.01 Copyright (c) 1999 - 2024 The PHP Group. All rights reserved. -------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, is permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name "PHP" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact group@php.net. 4. Products derived from this software may not be called "PHP", nor may "PHP" appear in their name, without prior written permission from group@php.net. You may indicate that your software works in conjunction with PHP by saying "Foo for PHP" instead of calling it "PHP Foo" or "phpfoo" 5. The PHP Group may publish revised and/or new versions of the license from time to time. Each version will be given a distinguishing version number. Once covered code has been published under a particular version of the license, you may always continue to use it under the terms of that version. You may also choose to use such covered code under the terms of any subsequent version of the license published by the PHP Group. No one other than the PHP Group has the right to modify the terms applicable to covered code created under this License. 6. Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes PHP software, freely available from ". THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------- This software consists of voluntary contributions made by many individuals on behalf of the PHP Group. The PHP Group can be contacted via Email at group@php.net. For more information on the PHP Group and the PHP project, please see . PHP includes the Zend Engine, freely available at . PK=1]9B?LaLa"doc/ea-php83-php-common/EXTENSIONSnu[ List of PHP maintainers ======================= Maintenance legend ------------------ Maintained: Someone actually looks after it. Odd Fixes: It has a maintainer but they don't have time to do much other than throw the odd patch in. See below. Orphan: No current maintainer [but maybe you could take the role as you write your new code]. Obsolete: Old code. Something tagged obsolete generally means it has been replaced by a better system and you should be using that. Unknown: Not known at this time. Status legend ------------- Working: Working under both Windows and Unix. Windows: Working only under Windows. Unix: Working only under Unix. Experimental: Under development or initial release. Not Working: Not working. Unknown: Status unknown. == Server APIs == ------------------------------------------------------------------------------- EXTENSION: apache2handler PRIMARY MAINTAINER: Aaron Bannert , Ilia Alshanetsky MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: cgi MAINTENANCE: Unknown STATUS: Working ------------------------------------------------------------------------------- EXTENSION: cli PRIMARY MAINTAINER: Marcus Börger , Edin Kadribasic MAINTENANCE: Xinchen Hui (2013 - 2020) STATUS: Working SINCE: 4.3.0 ------------------------------------------------------------------------------- EXTENSION: embed PRIMARY MAINTAINER: Edin Kadribasic MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: fpm PRIMARY MAINTAINER: Jakub Zelenka MAINTENANCE: Maintained STATUS: Working SINCE: 5.3.3 ------------------------------------------------------------------------------- EXTENSION: litespeed PRIMARY MAINTAINER: George Wang MAINTENANCE: Maintained STATUS: Working SINCE: 5.3 ------------------------------------------------------------------------------- EXTENSION: phpdbg PRIMARY MAINTAINER: Joe Watkins , Bob Weinand MAINTENANCE: Unknown STATUS: 5.6 ------------------------------------------------------------------------------- == Database extensions == ------------------------------------------------------------------------------- EXTENSION: dba PRIMARY MAINTAINER: Marcus Börger (2002 - 2007) Christopher Jones (2008 - 2013) Pierre-Alain Joye (2011 - 2011) MAINTENANCE: Maintained STATUS: Working COMMENT: DBM abstraction for db2, db3, db4, dbm, ndbm, gdbm, ini ------------------------------------------------------------------------------- EXTENSION: mysqli PRIMARY MAINTAINER: Georg Richter (2003 - 2006) Andrey Hristov (2003 - 2016) Johannes Schlüter (2008 - 2014) Ulf Wendel (2007 - 2013) MAINTENANCE: Maintained STATUS: Working SINCE: 5.0 ------------------------------------------------------------------------------- EXTENSION: mysqlnd PRIMARY MAINTAINER: Andrey Hristov (2007 - 2017) Johannes Schlüter (2008 - 2018) Ulf Wendel (2009 - 2011) MAINTENANCE: Maintained STATUS: Working SINCE: 5.3 ------------------------------------------------------------------------------- EXTENSION: oci8 PRIMARY MAINTAINER: Christopher Jones (2007 - 2017) Antony Dovgal (2003 - 2009) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: odbc PRIMARY MAINTAINER: Daniel R. Kalowsky (2000 - 2004) MAINTENANCE: Maintained STATUS: Working COMMENT: Working ------------------------------------------------------------------------------- EXTENSION: pdo PRIMARY MAINTAINER: Ilia Alshanetsky (2004 - 2011) Wez Furlong (2004 - 2006) MAINTENANCE: Odd fixes STATUS: Working SINCE: 5.1 ------------------------------------------------------------------------------- EXTENSION: pdo_dblib PRIMARY MAINTAINER: Adam Baratz (2016 - 2017) MAINTENANCE: Maintained STATUS: Working SINCE: 5.1 ------------------------------------------------------------------------------- EXTENSION: pdo_firebird PRIMARY MAINTAINER: Lars Westermann (2007 - 2007) MAINTENANCE: Maintained STATUS: Working SINCE: 5.1 ------------------------------------------------------------------------------- EXTENSION: pdo_mysql PRIMARY MAINTAINER: Ilia Alshanetsky (2004 - 2010) Johannes Schlüter (2008 - 2014) Andrey Hristov (2005 - 2015) Ulf Wendel (2008 - 2012) MAINTENANCE: Odd fixes STATUS: Working SINCE: 5.1 ------------------------------------------------------------------------------- EXTENSION: pdo_odbc PRIMARY MAINTAINER: Unknown MAINTENANCE: Odd fixes STATUS: Working SINCE: 5.1 ------------------------------------------------------------------------------- EXTENSION: pdo_oci PRIMARY MAINTAINER: Christopher Jones (2007 - 2017) MAINTENANCE: Odd fixes STATUS: Working SINCE: 5.1 ------------------------------------------------------------------------------- EXTENSION: pdo_pgsql PRIMARY MAINTAINER: Ilia Alshanetsky (2004 - 2011) MAINTENANCE: Odd fixes STATUS: Working SINCE: 5.1 ------------------------------------------------------------------------------- EXTENSION: pdo_sqlite PRIMARY MAINTAINER: Ilia Alshanetsky (2005 - 2011) MAINTENANCE: Odd fixes STATUS: Working SINCE: 5.1 ------------------------------------------------------------------------------- EXTENSION: pgsql PRIMARY MAINTAINER: Marcus Börger (2002 - 2007) Yasuo Ohgaki (2001 - 2015) MAINTENANCE: Maintained STATUS: Working COMMENT: Use PostgreSQL 7.0.x or later. PostgreSQL 6.5.3 or less have fatal bug. ------------------------------------------------------------------------------- EXTENSION: sqlite3 PRIMARY MAINTAINER: Scott MacVicar (2008 - 2011) Christoph M. Becker (2016 - 2020) MAINTENANCE: Maintained STATUS: Working SINCE: 5.3 COMMENT: Integrates SQLite 3 embeddable SQL database engine. ------------------------------------------------------------------------------- == XML extensions == ------------------------------------------------------------------------------- EXTENSION: dom PRIMARY MAINTAINER: Christian Stocker (2003 - 2011) Rob Richards (2003 - 2012) Marcus Börger (2003 - 2006) Niels Dossche (2023 - 2025) MAINTENANCE: Maintained STATUS: Working SINCE: 5.0 ------------------------------------------------------------------------------- EXTENSION: simplexml PRIMARY MAINTAINER: Marcus Börger (2003 - 2008) Niels Dossche (2023 - 2025) MAINTENANCE: Maintained STATUS: Working SINCE: 5.0 ------------------------------------------------------------------------------- EXTENSION: soap PRIMARY MAINTAINER: Dmitry Stogov (2004 - 2018) Niels Dossche (2024 - 2025) MAINTENANCE: Odd fixes STATUS: Working ------------------------------------------------------------------------------- EXTENSION: xml PRIMARY MAINTAINER: Thies C. Arntzen (1999 - 2002) Rob Richards (2003 - 2013) Niels Dossche (2023 - 2025) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: libxml PRIMARY MAINTAINER: Rob Richards (2003 - 2009) Christian Stocker (2004 - 2011) Niels Dossche (2023 - 2025) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: xmlreader PRIMARY MAINTAINER: Rob Richards (2004 - 2010) Christian Stocker (2004 - 2004) Niels Dossche (2023 - 2025) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: xmlwriter PRIMARY MAINTAINER: Rob Richards (2004 - 2010) Pierre-Alain Joye (2005-2009) Niels Dossche (2023 - 2025) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: xsl PRIMARY MAINTAINER: Christian Stocker (2003 - 2011) Rob Richards (2003 - 2010) Niels Dossche (2023 - 2025) MAINTENANCE: Maintained STATUS: Working SINCE: 5.0 ------------------------------------------------------------------------------- == Other extensions == ------------------------------------------------------------------------------- EXTENSION: bcmath PRIMARY MAINTAINER: Andi Gutmans (2000 - 2004) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: bz2 PRIMARY MAINTAINER: Hartmut Holzgraefe (2004 - 2004) MAINTENANCE: Odd Fixes STATUS: Working SINCE: 4.0.3 ------------------------------------------------------------------------------- EXTENSION: calendar PRIMARY MAINTAINER: Hartmut Holzgraefe (2000 - 2004) MAINTENANCE: Odd Fixes STATUS: Working ------------------------------------------------------------------------------- EXTENSION: com_dotnet PRIMARY MAINTAINER: Wez Furlong (2003 - 2005) Christoph M. Becker (2018 - 2022) MAINTENANCE: Maintained STATUS: Windows SINCE: 5.0 ------------------------------------------------------------------------------- EXTENSION: ctype PRIMARY MAINTAINER: Hartmut Holzgraefe (2000 - 2004) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: curl PRIMARY MAINTAINER: Sterling Hughes (2000 - 2004) Ilia Alshanetsky (2002 - 2011) Pierrick Charron (2010 - 2016) MAINTENANCE: Maintained STATUS: Working SINCE: 4.0.2 ------------------------------------------------------------------------------- EXTENSION: date PRIMARY MAINTAINER: Derick Rethans (2005 - 2018) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: enchant PRIMARY MAINTAINER: Unknown MAINTENANCE: Maintained STATUS: Working SINCE: 5.3 ------------------------------------------------------------------------------- EXTENSION: exif PRIMARY MAINTAINER: Kalle Sommer Nielsen (2010 - 2018) MAINTENANCE: Maintained STATUS: Working SINCE: 4.2 ------------------------------------------------------------------------------- EXTENSION: ffi PRIMARY MAINTAINER: Dmitry Stogov (2018 - 2019) MAINTENANCE: Maintained STATUS: Experimental SINCE: 7.4.0 ------------------------------------------------------------------------------- EXTENSION: fileinfo PRIMARY MAINTAINER: Derick Rethans (2004 - 2008) Anatol Belski (2012 - 2020) MAINTENANCE: Maintained STATUS: Working SINCE: 5.3 ------------------------------------------------------------------------------- EXTENSION: filter PRIMARY MAINTAINER: Derick Rethans (2006 - 2006) Pierre-Alain Joye (2006 - 2011) Ilia Alshanetsky (2006 - 2011) MAINTENANCE: Maintained STATUS: Working SINCE: 5.2 ------------------------------------------------------------------------------- EXTENSION: ftp PRIMARY MAINTAINER: Unknown MAINTENANCE: Odd fixes STATUS: Working ------------------------------------------------------------------------------- EXTENSION: gd PRIMARY MAINTAINER: Pierre-Alain Joye (2002 - 2016) Christoph M. Becker (2015 - 2022) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: gettext PRIMARY MAINTAINER: Unknown MAINTENANCE: Unknown STATUS: Working ------------------------------------------------------------------------------- EXTENSION: gmp PRIMARY MAINTAINER: Stanislav Malyshev (2000 - 2019) Antony Dovgal (2005 - 2010) Nikita Popov (2013 - 2020) MAINTENANCE: Maintained STATUS: Working SINCE: 4.0.4 ------------------------------------------------------------------------------- EXTENSION: hash PRIMARY MAINTAINER: Sara Golemon (2005 - 2017) Mike Wallner (2005 - 2013) Anatol Belski (2014 - 2018) MAINTENANCE: Maintained STATUS: Working SINCE: 5.1.2 ------------------------------------------------------------------------------- EXTENSION: iconv PRIMARY MAINTAINER: Moriyoshi Koizumi (2002 - 2010) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: intl PRIMARY MAINTAINER: Stanislav Malyshev (2008 - 2019) Anatol Belski (2017 - 2018) MAINTENANCE: Maintained STATUS: Working SINCE: 5.3 ------------------------------------------------------------------------------- EXTENSION: imap PRIMARY MAINTAINER: Chuck Hagenbuch (1999 - 2004) Ilia Alshanetsky (2002 - 2010) Pierre-Alain Joye (2008 - 2010) Bishop Bettini (2018 - 2019) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: json PRIMARY MAINTAINER: Jakub Zelenka (2014 - 2018) MAINTENANCE: Maintained STATUS: Working SINCE: 5.2 ------------------------------------------------------------------------------- EXTENSION: ldap PRIMARY MAINTAINER: Stig Venaas (2000 - 2002) Douglas Goldstein (2007 - 2007) Pierre-Alain Joye (2008 - 2010) Côme Bernigaud (2015 - 2017) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: mbstring PRIMARY MAINTAINER: Rui Hirokawa (2001 - 2013) Nikita Popov (2017 - 2020) Alex Dowad (2021 - 2022) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: opcache PRIMARY MAINTAINER: Dmitry Stogov (2013 - 2018) Xinchen Hui (2013 - 2020) Nikita Popov (2016 - 2020) MAINTENANCE: Maintained STATUS: Working SINCE: 5.5.0 ------------------------------------------------------------------------------- EXTENSION: openssl PRIMARY MAINTAINER: Wez Furlong (2001 - 2006) Pierre-Alain Joye (2006 - 2011) Jakub Zelenka (2015 - 2019) MAINTENANCE: Maintained STATUS: Working SINCE: 4.0.4 ------------------------------------------------------------------------------- EXTENSION: pcntl PRIMARY MAINTAINER: Arnaud Le Blanc (2008 - 2010) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: pcre PRIMARY MAINTAINER: Andrei Zmievski (1999 - 2006) Nuno Lopes (2006 - 2009) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: phar PRIMARY MAINTAINER: Greg Beaver (2008 - 2009) Marcus Börger (2008 - 2008) Steph Fox (2008 - 2008) Bishop Bettini (2018 - 2019) MAINTENANCE: Maintained STATUS: Working SINCE: 5.3 ------------------------------------------------------------------------------- EXTENSION: posix PRIMARY MAINTAINER: Kristian Köhntopp (2000 - 2000) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: pspell PRIMARY MAINTAINER: Vlad Krupin (2000 - 2004) MAINTENANCE: Unknown STATUS: Working SINCE: 4.0.2 ------------------------------------------------------------------------------- EXTENSION: random PRIMARY MAINTAINER Go Kudo (2022 - 2022) Tim Düsterhus (2022 - 2023) MAINTENANCE: Maintained STATUS: Working SINCE: 8.2.0 ------------------------------------------------------------------------------- EXTENSION: readline PRIMARY MAINTAINER: Unknown MAINTENANCE: Unknown STATUS: Working ------------------------------------------------------------------------------- EXTENSION: reflection PRIMARY MAINTAINER: Marcus Börger (2003 - 2009) Johannes Schlüter (2006 - 2014) Nikita Popov (2019 - 2020) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: session PRIMARY MAINTAINER: Sascha Schumann (1999 - 2004) Ilia Alshanetsky (2002 - 2012) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: shmop PRIMARY MAINTAINER: Ilia Alshanetsky (2002 - 2008) MAINTENANCE: Maintained STATUS: Working SINCE: 4.0.3 ------------------------------------------------------------------------------- EXTENSION: snmp PRIMARY MAINTAINER: Boris Lytochkin (2011 - 2013) Rasmus Lerdorf (1999 - 2002) Pierre-Alain Joye (2010 - 2011) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: sockets PRIMARY MAINTAINER: Chris Vandomelen (2000 - 2000) MAINTENANCE: Maintained STATUS: Working SINCE: 4.0.2 ------------------------------------------------------------------------------- EXTENSION: sodium PRIMARY MAINTAINER: Frank Denis (2017 - 2017) MAINTENANCE: Maintained STATUS: Working SINCE: 7.2.0 ------------------------------------------------------------------------------- EXTENSION: spl PRIMARY MAINTAINER: Marcus Börger (2003 - 2009) Etienne Kneuss (2008 - 2014) MAINTENANCE: Maintained STATUS: Working SINCE: 5.0.0 ------------------------------------------------------------------------------- EXTENSION: sysvmsg PRIMARY MAINTAINER: Unknown MAINTENANCE: Unknown STATUS: Working ------------------------------------------------------------------------------- EXTENSION: sysvsem PRIMARY MAINTAINER: Unknown MAINTENANCE: Unknown STATUS: Working ------------------------------------------------------------------------------- EXTENSION: sysvshm PRIMARY MAINTAINER: Unknown MAINTENANCE: Unknown STATUS: Working ------------------------------------------------------------------------------- EXTENSION: tidy PRIMARY MAINTAINER: John Coggeshall (2003 - 2006) Ilia Alshanetsky (2003 - 2009) Nuno Lopes (2006 - 2012) Niels Dossche (2025 - 2025) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: tokenizer PRIMARY MAINTAINER: Andrei Zmievski (2002 - 2002) Nikita Popov (2013 - 2020) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: zip PRIMARY MAINTAINER: Pierre-Alain Joye (2006 - 2011) Remi Collet (2013-2020) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: zlib PRIMARY MAINTAINER: Stefan Roehrich (1999 - 2003) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- PK=1]   doc/ea-php83-runtime/READMEnu[ # The PHP Interpreter PHP is a popular general-purpose scripting language that is especially suited to web development. Fast, flexible and pragmatic, PHP powers everything from your blog to the most popular websites in the world. PHP is distributed under the [PHP License v3.01](LICENSE). [![Push](https://github.com/php/php-src/actions/workflows/push.yml/badge.svg)](https://github.com/php/php-src/actions/workflows/push.yml) [![Build status](https://travis-ci.com/php/php-src.svg?branch=master)](https://travis-ci.com/github/php/php-src) [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/php.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:php) ## Documentation The PHP manual is available at [php.net/docs](https://php.net/docs). ## Installation ### Prebuilt packages and binaries Prebuilt packages and binaries can be used to get up and running fast with PHP. For Windows, the PHP binaries can be obtained from [windows.php.net](https://windows.php.net). After extracting the archive the `*.exe` files are ready to use. For other systems, see the [installation chapter](https://php.net/install). ### Building PHP source code *For Windows, see [Build your own PHP on Windows](https://wiki.php.net/internals/windows/stepbystepbuild_sdk_2).* For a minimal PHP build from Git, you will need autoconf, bison, and re2c. For a default build, you will additionally need libxml2 and libsqlite3. On Ubuntu, you can install these using: sudo apt install -y pkg-config build-essential autoconf bison re2c \ libxml2-dev libsqlite3-dev On Fedora, you can install these using: sudo dnf install re2c bison autoconf make libtool ccache libxml2-devel sqlite-devel Generate configure: ./buildconf Configure your build. `--enable-debug` is recommended for development, see `./configure --help` for a full list of options. # For development ./configure --enable-debug # For production ./configure Build PHP. To speed up the build, specify the maximum number of jobs using `-j`: make -j4 The number of jobs should usually match the number of available cores, which can be determined using `nproc`. ## Testing PHP source code PHP ships with an extensive test suite, the command `make test` is used after successful compilation of the sources to run this test suite. It is possible to run tests using multiple cores by setting `-jN` in `TEST_PHP_ARGS`: make TEST_PHP_ARGS=-j4 test Shall run `make test` with a maximum of 4 concurrent jobs: Generally the maximum number of jobs should not exceed the number of cores available. The [qa.php.net](https://qa.php.net) site provides more detailed info about testing and quality assurance. ## Installing PHP built from source After a successful build (and test), PHP may be installed with: make install Depending on your permissions and prefix, `make install` may need super user permissions. ## PHP extensions Extensions provide additional functionality on top of PHP. PHP consists of many essential bundled extensions. Additional extensions can be found in the PHP Extension Community Library - [PECL](https://pecl.php.net). ## Contributing The PHP source code is located in the Git repository at [github.com/php/php-src](https://github.com/php/php-src). Contributions are most welcome by forking the repository and sending a pull request. Discussions are done on GitHub, but depending on the topic can also be relayed to the official PHP developer mailing list internals@lists.php.net. New features require an RFC and must be accepted by the developers. See [Request for comments - RFC](https://wiki.php.net/rfc) and [Voting on PHP features](https://wiki.php.net/rfc/voting) for more information on the process. Bug fixes don't require an RFC. If the bug has a GitHub issue, reference it in the commit message using `GH-NNNNNN`. Use `#NNNNNN` for tickets in the old [bugs.php.net](https://bugs.php.net) bug tracker. Fix GH-7815: php_uname doesn't recognise latest Windows versions Fix #55371: get_magic_quotes_gpc() throws deprecation warning See [Git workflow](https://wiki.php.net/vcs/gitworkflow) for details on how pull requests are merged. ### Guidelines for contributors See further documents in the repository for more information on how to contribute: - [Contributing to PHP](/CONTRIBUTING.md) - [PHP coding standards](/CODING_STANDARDS.md) - [Mailing list rules](/docs/mailinglist-rules.md) - [PHP release process](/docs/release-process.md) ## Credits For the list of people who've put work into PHP, please see the [PHP credits page](https://php.net/credits.php). PK=1]kO doc/ea-php83-runtime/LICENSEnu[-------------------------------------------------------------------- The PHP License, version 3.01 Copyright (c) 1999 - 2023 The PHP Group. All rights reserved. -------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, is permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name "PHP" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact group@php.net. 4. Products derived from this software may not be called "PHP", nor may "PHP" appear in their name, without prior written permission from group@php.net. You may indicate that your software works in conjunction with PHP by saying "Foo for PHP" instead of calling it "PHP Foo" or "phpfoo" 5. The PHP Group may publish revised and/or new versions of the license from time to time. Each version will be given a distinguishing version number. Once covered code has been published under a particular version of the license, you may always continue to use it under the terms of that version. You may also choose to use such covered code under the terms of any subsequent version of the license published by the PHP Group. No one other than the PHP Group has the right to modify the terms applicable to covered code created under this License. 6. Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes PHP software, freely available from ". THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------- This software consists of voluntary contributions made by many individuals on behalf of the PHP Group. The PHP Group can be contacted via Email at group@php.net. For more information on the PHP Group and the PHP project, please see . PHP includes the Zend Engine, freely available at . PK=1]E doc/ea-php83-pear/README.rstnu[************************* PEAR - The PEAR Installer ************************* .. image:: https://travis-ci.org/pear/pear-core.svg?branch=stable :target: https://travis-ci.org/pear/pear-core ========================================= What is the PEAR Installer? What is PEAR? ========================================= PEAR is the PHP Extension and Application Repository, found at http://pear.php.net. The **PEAR Installer** is this software, which contains executable files and PHP code that is used to **download and install** PEAR code from pear.php.net. PEAR contains useful **software libraries and applications** such as MDB2 (database abstraction), HTML_QuickForm (HTML forms management), PhpDocumentor (auto-documentation generator), DB_DataObject (Data Access Abstraction), and many hundreds more. Browse all available packages at http://pear.php.net, the list is constantly growing and updating to reflect improvements in the PHP language. .. warning:: Do not run PEAR without installing it - if you downloaded this tarball manually, you MUST install it. Read the instructions in INSTALL prior to use. ============= Documentation ============= Documentation for PEAR can be found at http://pear.php.net/manual/. Installation documentation can be found in the INSTALL file included in this tarball. ===== Tests ===== Run the tests without installation as follows:: $ ./scripts/pear.sh run-tests -r tests You should have the ``Text_Diff`` package installed to get nicer error output. To run the tests with another PHP version, modify ``php_bin`` and set the ``PHP_PEAR_PHP_BIN`` environment variable:: $ pear config-set php_bin /usr/local/bin/php7 $ PHP_PEAR_PHP_BIN=/usr/local/bin/php7 ./scripts/pear.sh run-tests -r tests Happy PHPing, we hope PEAR will be a great tool for your development work! Test dependencies ================= * ``zlib`` ========= Releasing ========= Create a PEAR package, as well as phars for pear-less installation, simply run ``build-release.sh``). ``go-pear.phar`` contains the PEAR installer installer that asks where to install it. It is available from http://pear.php.net/go-pear.phar. ``install-pear-nozlib.phar`` installs PEAR automatically without asking anything. It is shipped with PHP itself. PK=1]-ll)doc/ea-php83-php-mbstring/libmbfl_LICENSEnu[ GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS "streamable kanji code filter and converter" Copyright (c) 1998,1999,2000,2001 HappySize, Inc. All rights reserved. This software is released under the GNU Lesser General Public License. (Version 2.1, February 1999) Please read the following detail of the licence (in japanese). ◆使用許諾条件◆ このソフトウェアは株式会社ハッピーサイズによって開発されました。株式会社ハッ ピーサイズは、著作権法および万国著作権条約の定めにより、このソフトウェアに関 するすべての権利を留保する権利を持ち、ここに行使します。株式会社ハッピーサイ ズは以下に明記した条件に従って、このソフトウェアを使用する排他的ではない権利 をお客様に許諾します。何人たりとも、以下の条件に反してこのソフトウェアを使用 することはできません。 このソフトウェアを「GNU Lesser General Public License (Version 2.1, February 1999)」に示された条件で使用することを、全ての方に許諾します。「GNU Lesser General Public License」を満たさない使用には、株式会社ハッピーサイズから書面 による許諾を得る必要があります。 「GNU Lesser General Public License」の全文は以下のウェブページから取得でき ます。「GNU Lesser General Public License」とは、これまでLibrary General Public Licenseと呼ばれていたものです。 http://www.gnu.org/ --- GNUウェブサイト http://www.gnu.org/copyleft/lesser.html --- ライセンス文面 このライセンスの内容がわからない方、守れない方には使用を許諾しません。 しかしながら、当社とGNUプロジェクトとの特定の関係を示唆または主張するもので はありません。 ◆保証内容◆ このソフトウェアは、期待された動作・機能・性能を持つことを目標として設計され 開発されていますが、これを保証するものではありません。このソフトウェアは「こ のまま」の状態で提供されており、たとえばこのソフトウェアの有用性ないし特定の 目的に合致することといった、何らかの保証内容が、明示されたり暗黙に示されてい る場合であっても、その保証は無効です。このソフトウェアを使用した結果ないし使 用しなかった結果によって、直接あるいは間接に受けた身体的な傷害、財産上の損害 、データの損失あるいはその他の全ての損害については、その損害の可能性が使用者 、当社あるいは第三者によって警告されていた場合であっても、当社はその損害の賠 償および補填を行いません。この規定は他の全ての、書面上または書面に無い保証・ 契約・規定に優先します。 ◆著作権者の連絡先および使用条件についての問い合わせ先◆ 〒102-0073 東京都千代田区九段北1-13-5日本地所第一ビル4F 株式会社ハッピーサイズ Phone: 03-3512-3655, Fax: 03-3512-3656 Email: sales@happysize.co.jp Web: http://happysize.com/ ◆著者◆ 金本 茂 PK=1] r¦JJ)doc/pear/Archive_Tar/docs/Archive_Tar.txtnu[Documentation for class Archive_Tar =================================== Last update : 2001-08-15 Overview : ---------- The Archive_Tar class helps in creating and managing GNU TAR format files compressed by GNU ZIP or not. The class offers basic functions like creating an archive, adding files in the archive, extracting files from the archive and listing the archive content. It also provide advanced functions that allow the adding and extraction of files with path manipulation. Sample : -------- // ----- Creating the object (uncompressed archive) $tar_object = new Archive_Tar("tarname.tar"); $tar_object->setErrorHandling(PEAR_ERROR_PRINT); // ----- Creating the archive $v_list[0]="file.txt"; $v_list[1]="data/"; $v_list[2]="file.log"; $tar_object->create($v_list); // ----- Adding files $v_list[0]="dev/file.txt"; $v_list[1]="dev/data/"; $v_list[2]="log/file.log"; $tar_object->add($v_list); // ----- Adding more files $tar_object->add("release/newfile.log release/readme.txt"); // ----- Listing the content if (($v_list = $tar_object->listContent()) != 0) for ($i=0; $i"; echo " .size :'".$v_list[$i][size]."'
"; echo " .mtime :'".$v_list[$i][mtime]."' (".date("l dS of F Y h:i:s A", $v_list[$i][mtime]).")
"; echo " .mode :'".$v_list[$i][mode]."'
"; echo " .uid :'".$v_list[$i][uid]."'
"; echo " .gid :'".$v_list[$i][gid]."'
"; echo " .typeflag :'".$v_list[$i][typeflag]."'
"; } // ----- Extracting the archive in directory "install" $tar_object->extract("install"); Public arguments : ------------------ None Public Methods : ---------------- Method : Archive_Tar($p_tarname, $compress = null) Description : Archive_Tar Class constructor. This flavour of the constructor only declare a new Archive_Tar object, identifying it by the name of the tar file. If the compress argument is set the tar will be read or created as a gzip or bz2 compressed TAR file. Arguments : $p_tarname : A valid filename for the tar archive file. $p_compress : can be null, 'gz' or 'bz2'. For compatibility reason it can also be true. This parameter indicates if gzip or bz2 compression is required. Return value : The Archive_Tar object. Sample : $tar_object = new Archive_Tar("tarname.tar"); $tar_object_compressed = new Archive_Tar("tarname.tgz", true); How it works : Initialize the object. Method : create($p_filelist) Description : This method creates the archive file and add the files / directories that are listed in $p_filelist. If the file already exists and is writable, it is replaced by the new tar. It is a create and not an add. If the file exists and is read-only or is a directory it is not replaced. The method return false and a PEAR error text. The $p_filelist parameter can be an array of string, each string representing a filename or a directory name with their path if needed. It can also be a single string with names separated by a single blank. See also createModify() method for more details. Arguments : $p_filelist : An array of filenames and directory names, or a single string with names separated by a single blank space. Return value : true on success, false on error. Sample 1 : $tar_object = new Archive_Tar("tarname.tar"); $tar_object->setErrorHandling(PEAR_ERROR_PRINT); // Optional error handling $v_list[0]="file.txt"; $v_list[1]="data/"; (Optional '/' at the end) $v_list[2]="file.log"; $tar_object->create($v_list); Sample 2 : $tar_object = new Archive_Tar("tarname.tar"); $tar_object->setErrorHandling(PEAR_ERROR_PRINT); // Optional error handling $tar_object->create("file.txt data/ file.log"); How it works : Just calling the createModify() method with the right parameters. Method : createModify($p_filelist, $p_add_dir, $p_remove_dir = "") Description : This method creates the archive file and add the files / directories that are listed in $p_filelist. If the file already exists and is writable, it is replaced by the new tar. It is a create and not an add. If the file exists and is read-only or is a directory it is not replaced. The method return false and a PEAR error text. The $p_filelist parameter can be an array of string, each string representing a filename or a directory name with their path if needed. It can also be a single string with names separated by a single blank. The path indicated in $p_remove_dir will be removed from the memorized path of each file / directory listed when this path exists. By default nothing is removed (empty path "") The path indicated in $p_add_dir will be added at the beginning of the memorized path of each file / directory listed. However it can be set to empty "". The adding of a path is done after the removing of path. The path add/remove ability enables the user to prepare an archive for extraction in a different path than the origin files are. See also addModify() method for file adding properties. Arguments : $p_filelist : An array of filenames and directory names, or a single string with names separated by a single blank space. $p_add_dir : A string which contains a path to be added to the memorized path of each element in the list. $p_remove_dir : A string which contains a path to be removed from the memorized path of each element in the list, when relevant. Return value : true on success, false on error. Sample 1 : $tar_object = new Archive_Tar("tarname.tar"); $tar_object->setErrorHandling(PEAR_ERROR_PRINT); // Optional error handling $v_list[0]="file.txt"; $v_list[1]="data/"; (Optional '/' at the end) $v_list[2]="file.log"; $tar_object->createModify($v_list, "install"); // files are stored in the archive as : // install/file.txt // install/data // install/data/file1.txt // install/data/... all the files and sub-dirs of data/ // install/file.log Sample 2 : $tar_object = new Archive_Tar("tarname.tar"); $tar_object->setErrorHandling(PEAR_ERROR_PRINT); // Optional error handling $v_list[0]="dev/file.txt"; $v_list[1]="dev/data/"; (Optional '/' at the end) $v_list[2]="log/file.log"; $tar_object->createModify($v_list, "install", "dev"); // files are stored in the archive as : // install/file.txt // install/data // install/data/file1.txt // install/data/... all the files and sub-dirs of data/ // install/log/file.log How it works : Open the file in write mode (erasing the existing one if one), call the _addList() method for adding the files in an empty archive, add the tar footer (512 bytes block), close the tar file. Method : addModify($p_filelist, $p_add_dir, $p_remove_dir="") Description : This method add the files / directories listed in $p_filelist at the end of the existing archive. If the archive does not yet exists it is created. The $p_filelist parameter can be an array of string, each string representing a filename or a directory name with their path if needed. It can also be a single string with names separated by a single blank. The path indicated in $p_remove_dir will be removed from the memorized path of each file / directory listed when this path exists. By default nothing is removed (empty path "") The path indicated in $p_add_dir will be added at the beginning of the memorized path of each file / directory listed. However it can be set to empty "". The adding of a path is done after the removing of path. The path add/remove ability enables the user to prepare an archive for extraction in a different path than the origin files are. If a file/dir is already in the archive it will only be added at the end of the archive. There is no update of the existing archived file/dir. However while extracting the archive, the last file will replace the first one. This results in a none optimization of the archive size. If a file/dir does not exist the file/dir is ignored. However an error text is send to PEAR error. If a file/dir is not readable the file/dir is ignored. However an error text is send to PEAR error. If the resulting filename/dirname (after the add/remove option or not) string is greater than 99 char, the file/dir is ignored. However an error text is send to PEAR error. Arguments : $p_filelist : An array of filenames and directory names, or a single string with names separated by a single blank space. $p_add_dir : A string which contains a path to be added to the memorized path of each element in the list. $p_remove_dir : A string which contains a path to be removed from the memorized path of each element in the list, when relevant. Return value : true on success, false on error. Sample 1 : $tar_object = new Archive_Tar("tarname.tar"); [...] $v_list[0]="dev/file.txt"; $v_list[1]="dev/data/"; (Optional '/' at the end) $v_list[2]="log/file.log"; $tar_object->addModify($v_list, "install"); // files are stored in the archive as : // install/file.txt // install/data // install/data/file1.txt // install/data/... all the files and sub-dirs of data/ // install/file.log Sample 2 : $tar_object = new Archive_Tar("tarname.tar"); [...] $v_list[0]="dev/file.txt"; $v_list[1]="dev/data/"; (Optional '/' at the end) $v_list[2]="log/file.log"; $tar_object->addModify($v_list, "install", "dev"); // files are stored in the archive as : // install/file.txt // install/data // install/data/file1.txt // install/data/... all the files and sub-dirs of data/ // install/log/file.log How it works : If the archive does not exists it create it and add the files. If the archive does exists and is not compressed, it open it, jump before the last empty 512 bytes block (tar footer) and add the files at this point. If the archive does exists and is compressed, a temporary copy file is created. This temporary file is then 'gzip' read block by block until the last empty block. The new files are then added in the compressed file. The adding of files is done by going through the file/dir list, adding files per files, in a recursive way through the directory. Each time a path need to be added/removed it is done before writing the file header in the archive. Method : add($p_filelist) Description : This method add the files / directories listed in $p_filelist at the end of the existing archive. If the archive does not yet exists it is created. The $p_filelist parameter can be an array of string, each string representing a filename or a directory name with their path if needed. It can also be a single string with names separated by a single blank. See addModify() method for details and limitations. Arguments : $p_filelist : An array of filenames and directory names, or a single string with names separated by a single blank space. Return value : true on success, false on error. Sample 1 : $tar_object = new Archive_Tar("tarname.tar"); [...] $v_list[0]="dev/file.txt"; $v_list[1]="dev/data/"; (Optional '/' at the end) $v_list[2]="log/file.log"; $tar_object->add($v_list); Sample 2 : $tar_object = new Archive_Tar("tarname.tgz", true); [...] $v_list[0]="dev/file.txt"; $v_list[1]="dev/data/"; (Optional '/' at the end) $v_list[2]="log/file.log"; $tar_object->add($v_list); How it works : Simply call the addModify() method with the right parameters. Method : addString($p_filename, $p_string, $p_datetime, $p_params) Description : This method add a single string as a file at the end of the existing archive. If the archive does not yet exists it is created. Arguments : $p_filename : A string which contains the full filename path that will be associated with the string. $p_string : The content of the file added in the archive. $p_datetime : (Optional) Timestamp of the file (default = now) $p_params : (Optional) Various file metadata: stamp - As above, timestamp of the file mode - UNIX-style permissions (default 0600) type - Is this a regular file or link (see TAR format spec for how to create a hard/symlink) uid - UNIX-style user ID (default 0 = root) gid - UNIX-style group ID (default 0 = root) Return value : true on success, false on error. Sample 1 : $v_archive = & new Archive_Tar($p_filename); $v_archive->setErrorHandling(PEAR_ERROR_PRINT); $v_result = $v_archive->addString('data/test.txt', 'This is the text of the string'); $v_result = $v_archive->addString( 'data/test.sh', "#!/bin/sh\necho 'Hello'", time(), array( "mode" => 0755, "uid" => 34 ) ); Method : extract($p_path = "") Description : This method extract all the content of the archive in the directory indicated by $p_path.If $p_path is optional, if not set the archive is extracted in the current directory. While extracting a file, if the directory path does not exists it is created. See extractModify() for details and limitations. Arguments : $p_path : Optional path where the files/dir need to by extracted. Return value : true on success, false on error. Sample : $tar_object = new Archive_Tar("tarname.tar"); $tar_object->extract(); How it works : Simply call the extractModify() method with appropriate parameters. Method : extractModify($p_path, $p_remove_path) Description : This method extract all the content of the archive in the directory indicated by $p_path. When relevant the memorized path of the files/dir can be modified by removing the $p_remove_path path at the beginning of the file/dir path. While extracting a file, if the directory path does not exists it is created. While extracting a file, if the file already exists it is replaced without looking for last modification date. While extracting a file, if the file already exists and is write protected, the extraction is aborted. While extracting a file, if a directory with the same name already exists, the extraction is aborted. While extracting a directory, if a file with the same name already exists, the extraction is aborted. While extracting a file/directory if the destination directory exist and is write protected, or does not exist but can not be created, the extraction is aborted. If after extraction an extracted file does not show the correct stored file size, the extraction is aborted. When the extraction is aborted, a PEAR error text is set and false is returned. However the result can be a partial extraction that may need to be manually cleaned. Arguments : $p_path : The path of the directory where the files/dir need to by extracted. $p_remove_path : Part of the memorized path that can be removed if present at the beginning of the file/dir path. Return value : true on success, false on error. Sample : // Imagine tarname.tar with files : // dev/data/file.txt // dev/data/log.txt // readme.txt $tar_object = new Archive_Tar("tarname.tar"); $tar_object->extractModify("install", "dev"); // Files will be extracted there : // install/data/file.txt // install/data/log.txt // install/readme.txt How it works : Open the archive and call a more generic function that can extract only a part of the archive or all the archive. See extractList() method for more details. Method : extractInString($p_filename) Description : This method extract from the archive one file identified by $p_filename. The return value is a string with the file content, or NULL on error. Arguments : $p_filename : The path of the file to extract in a string. Return value : a string with the file content or NULL. Sample : // Imagine tarname.tar with files : // dev/data/file.txt // dev/data/log.txt // dev/readme.txt $v_archive = & new Archive_Tar('tarname.tar'); $v_archive->setErrorHandling(PEAR_ERROR_PRINT); $v_string = $v_archive->extractInString('dev/readme.txt'); echo $v_string; Method : listContent() Description : This method returns an array of arrays that describe each file/directory present in the archive. The array is not sorted, so it show the position of the file in the archive. The file informations are : $file[filename] : Name and path of the file/dir. $file[mode] : File permissions (result of fileperms()) $file[uid] : user id $file[gid] : group id $file[size] : filesize $file[mtime] : Last modification time (result of filemtime()) $file[typeflag] : "" for file, "5" for directory Arguments : Return value : An array of arrays or 0 on error. Sample : $tar_object = new Archive_Tar("tarname.tar"); if (($v_list = $tar_object->listContent()) != 0) for ($i=0; $i"; echo " .size :'".$v_list[$i][size]."'
"; echo " .mtime :'".$v_list[$i][mtime]."' (". date("l dS of F Y h:i:s A", $v_list[$i][mtime]).")
"; echo " .mode :'".$v_list[$i][mode]."'
"; echo " .uid :'".$v_list[$i][uid]."'
"; echo " .gid :'".$v_list[$i][gid]."'
"; echo " .typeflag :'".$v_list[$i][typeflag]."'
"; } How it works : Call the same function as an extract however with a flag to only go through the archive without extracting the files. Method : extractList($p_filelist, $p_path = "", $p_remove_path = "") Description : This method extract from the archive only the files indicated in the $p_filelist. These files are extracted in the current directory or in the directory indicated by the optional $p_path parameter. If indicated the $p_remove_path can be used in the same way as it is used in extractModify() method. Arguments : $p_filelist : An array of filenames and directory names, or a single string with names separated by a single blank space. $p_path : The path of the directory where the files/dir need to by extracted. $p_remove_path : Part of the memorized path that can be removed if present at the beginning of the file/dir path. Return value : true on success, false on error. Sample : // Imagine tarname.tar with files : // dev/data/file.txt // dev/data/log.txt // readme.txt $tar_object = new Archive_Tar("tarname.tar"); $tar_object->extractList("dev/data/file.txt readme.txt", "install", "dev"); // Files will be extracted there : // install/data/file.txt // install/readme.txt How it works : Go through the archive and extract only the files present in the list. PK=1]f'doc/pear/XML_Util/examples/example2.phpnu[ * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @category XML * @package XML_Util * @subpackage Examples * @author Stephan Schmidt * @copyright 2003-2008 Stephan Schmidt * @license http://opensource.org/licenses/bsd-license New BSD License * @version CVS: $Id$ * @link http://pear.php.net/package/XML_Util */ /** * set error level */ error_reporting(E_ALL); require_once 'XML/Util.php'; /** * creating a start element */ print 'creating a start element:
'; print htmlentities(XML_Util::createStartElement('myNs:myTag', array('foo' => 'bar'), 'http://www.w3c.org/myNs#')); print "\n

\n"; /** * creating a start element */ print 'creating a start element:
'; print htmlentities(XML_Util::createStartElement('myTag', array(), 'http://www.w3c.org/myNs#')); print "\n

\n"; /** * creating a start element */ print 'creating a start element:
'; print '
';
    print htmlentities(XML_Util::createStartElement('myTag', 
        array('foo' => 'bar', 'argh' => 'tomato'), 
        'http://www.w3c.org/myNs#', true));
    print '
'; print "\n

\n"; /** * creating an end element */ print 'creating an end element:
'; print htmlentities(XML_Util::createEndElement('myNs:myTag')); print "\n

\n"; /** * creating a CData section */ print 'creating a CData section:
'; print htmlentities(XML_Util::createCDataSection('I am content.')); print "\n

\n"; /** * creating a comment */ print 'creating a comment:
'; print htmlentities(XML_Util::createComment('I am a comment.')); print "\n

\n"; /** * creating an XML tag with multiline mode */ $tag = array( 'qname' => 'foo:bar', 'namespaceUri' => 'http://foo.com', 'attributes' => array('key' => 'value', 'argh' => 'fruit&vegetable'), 'content' => 'I\'m inside the tag & contain dangerous chars' ); print 'creating a tag with qualified name and namespaceUri:
'; print '
';
    print htmlentities(XML_Util::createTagFromArray($tag, 
        XML_UTIL_REPLACE_ENTITIES, true));
    print '
'; print "\n

\n"; /** * create an attribute string without replacing the entities */ $atts = array('series' => 'Starsky & Hutch', 'channel' => 'ABC'); print 'creating a attribute string, ' . 'entities in values already had been replaced:
'; print htmlentities(XML_Util::attributesToString($atts, true, false, false, false, XML_UTIL_ENTITIES_NONE)); print "\n

\n"; /** * using the array-syntax for attributesToString() */ $atts = array('series' => 'Starsky & Hutch', 'channel' => 'ABC'); print 'using the array-syntax for attributesToString()
'; print htmlentities(XML_Util::attributesToString($atts, array('entities' => XML_UTIL_ENTITIES_NONE))); print "\n

\n"; ?> PK=1]5k##&doc/pear/XML_Util/examples/example.phpnu[ * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @category XML * @package XML_Util * @subpackage Examples * @author Stephan Schmidt * @copyright 2003-2008 Stephan Schmidt * @license http://opensource.org/licenses/bsd-license New BSD License * @version CVS: $Id$ * @link http://pear.php.net/package/XML_Util */ /** * set error level */ error_reporting(E_ALL); require_once 'XML/Util.php'; /** * replacing XML entities */ print 'replace XML entities:
'; print XML_Util::replaceEntities('This string contains < & >.'); print "\n

\n"; /** * reversing XML entities */ print 'replace XML entities:
'; print XML_Util::reverseEntities('This string contains < & >.'); print "\n

\n"; /** * building XML declaration */ print 'building XML declaration:
'; print htmlspecialchars(XML_Util::getXMLDeclaration()); print "\n

\n"; print 'building XML declaration with additional attributes:
'; print htmlspecialchars(XML_Util::getXMLDeclaration('1.0', 'UTF-8', true)); print "\n

\n"; /** * building document type declaration */ print 'building DocType declaration:
'; print htmlspecialchars(XML_Util::getDocTypeDeclaration('package', 'http://pear.php.net/dtd/package-1.0')); print "\n

\n"; print 'building DocType declaration with public ID (does not exist):
'; print htmlspecialchars(XML_Util::getDocTypeDeclaration('package', array('uri' => 'http://pear.php.net/dtd/package-1.0', 'id' => '-//PHP//PEAR/DTD PACKAGE 0.1'))); print "\n

\n"; print 'building DocType declaration with internal DTD:
'; print '
';
    print htmlspecialchars(XML_Util::getDocTypeDeclaration('package', 
        'http://pear.php.net/dtd/package-1.0', 
        ''));
    print '
'; print "\n

\n"; /** * creating an attribute string */ $att = array( 'foo' => 'bar', 'argh' => 'tomato' ); print 'converting array to string:
'; print XML_Util::attributesToString($att); print "\n

\n"; /** * creating an attribute string with linebreaks */ $att = array( 'foo' => 'bar', 'argh' => 'tomato' ); print 'converting array to string (including line breaks):
'; print '
';
    print XML_Util::attributesToString($att, true, true);
    print '
'; print "\n

\n"; /** * splitting a qualified tag name */ print 'splitting qualified tag name:
'; print '
';
    print_r(XML_Util::splitQualifiedName('xslt:stylesheet'));
    print '
'; print "\n
\n"; /** * splitting a qualified tag name (no namespace) */ print 'splitting qualified tag name (no namespace):
'; print '
';
    print_r(XML_Util::splitQualifiedName('foo'));
    print '
'; print "\n
\n"; /** * splitting a qualified tag name (no namespace, but default namespace specified) */ print 'splitting qualified tag name ' . '(no namespace, but default namespace specified):
'; print '
';
    print_r(XML_Util::splitQualifiedName('foo', 'bar'));
    print '
'; print "\n
\n"; /** * verifying XML names */ print 'verifying \'My private tag\':
'; print '
';
    print_r(XML_Util::isValidname('My Private Tag'));
    print '
'; print "\n

\n"; print 'verifying \'-MyTag\':
'; print '
';
    print_r(XML_Util::isValidname('-MyTag'));
    print '
'; print "\n

\n"; /** * creating an XML tag */ $tag = array( 'namespace' => 'foo', 'localPart' => 'bar', 'attributes' => array('key' => 'value', 'argh' => 'fruit&vegetable'), 'content' => 'I\'m inside the tag' ); print 'creating a tag with namespace and local part:
'; print htmlentities(XML_Util::createTagFromArray($tag)); print "\n

\n"; /** * creating an XML tag */ $tag = array( 'qname' => 'foo:bar', 'namespaceUri' => 'http://foo.com', 'attributes' => array('key' => 'value', 'argh' => 'fruit&vegetable'), 'content' => 'I\'m inside the tag' ); print 'creating a tag with qualified name and namespaceUri:
'; print htmlentities(XML_Util::createTagFromArray($tag)); print "\n

\n"; /** * creating an XML tag */ $tag = array( 'qname' => 'bar', 'namespaceUri' => 'http://foo.com', 'attributes' => array('key' => 'value', 'argh' => 'fruit&vegetable') ); print 'creating an empty tag without namespace but namespace Uri:
'; print htmlentities(XML_Util::createTagFromArray($tag)); print "\n

\n"; /** * creating an XML tag with more namespaces */ $tag = array( 'namespace' => 'foo', 'localPart' => 'bar', 'attributes' => array('key' => 'value', 'argh' => 'fruit&vegetable'), 'content' => 'I\'m inside the tag', 'namespaces' => array( 'bar' => 'http://bar.com', 'pear' => 'http://pear.php.net', ) ); print 'creating an XML tag with more namespaces:
'; print htmlentities(XML_Util::createTagFromArray($tag)); print "\n

\n"; /** * creating an XML tag with a CData Section */ $tag = array( 'qname' => 'foo', 'attributes' => array('key' => 'value', 'argh' => 'fruit&vegetable'), 'content' => 'I\'m inside the tag' ); print 'creating a tag with CData section:
'; print htmlentities(XML_Util::createTagFromArray($tag, XML_UTIL_CDATA_SECTION)); print "\n

\n"; /** * creating an XML tag with a CData Section */ $tag = array( 'qname' => 'foo', 'attributes' => array('key' => 'value', 'argh' => 'tt'), 'content' => 'Also XHTML-tags can be created ' . 'and HTML entities can be replaced <>.' ); print 'creating a tag with HTML entities:
'; print htmlentities(XML_Util::createTagFromArray($tag, XML_UTIL_ENTITIES_HTML)); print "\n

\n"; /** * creating an XML tag with createTag */ print 'creating a tag with createTag:
'; print htmlentities(XML_Util::createTag('myNs:myTag', array('foo' => 'bar'), 'This is inside the tag', 'http://www.w3c.org/myNs#')); print "\n

\n"; /** * trying to create an XML tag with an array as content */ $tag = array( 'qname' => 'bar', 'content' => array('foo' => 'bar') ); print 'trying to create an XML tag with an array as content:
'; print '
';
    print_r(XML_Util::createTagFromArray($tag));
    print '
'; print "\n

\n"; /** * trying to create an XML tag without a name */ $tag = array( 'attributes' => array('foo' => 'bar'), ); print 'trying to create an XML tag without a name:
'; print '
';
    print_r(XML_Util::createTagFromArray($tag));
    print '
'; print "\n

\n"; ?> PK=1]x Ndoc/pear/Structures_Graph/docs/tutorials/Structures_Graph/Structures_Graph.pkgnu[ Structures_Graph Tutorial A first tour of graph datastructure manipulation Introduction Structures_Graph is a package for creating and manipulating graph datastructures. A graph is a set of objects, called nodes, connected by arcs. When used as a datastructure, usually nodes contain data, and arcs represent relationships between nodes. When arcs have a direction, and can be travelled only one way, graphs are said to be directed. When arcs have no direction, and can always be travelled both ways, graphs are said to be non directed. Structures_Graph provides an object oriented API to create and directly query a graph, as well as a set of Manipulator classes to extract information from the graph. Creating a Graph Creating a graph is done using the simple constructor: and passing the constructor a flag telling it whether the graph should be directed. A directed graph will always be directed during its lifetime. It's a permanent characteristic. To fill out the graph, we'll need to create some nodes, and then call Graph::addNode. addNode(&$nodeOne); $directedGraph->addNode(&$nodeTwo); $directedGraph->addNode(&$nodeThree); ]]> and then setup the arcs: connectTo($nodeTwo); $nodeOne->connectTo($nodeThree); ]]> Note that arcs can only be created after the nodes have been inserted into the graph. Associating Data Graphs are only useful as datastructures if they can hold data. Structure_Graph stores data in nodes. Each node contains a setter and a getter for its data. setData("Node One's Data is a String"); $nodeTwo->setData(1976); $nodeThree->setData('Some other string'); print("NodeTwo's Data is an integer: " . $nodeTwo->getData()); ]]> Structure_Graph nodes can also store metadata, alongside with the main data. Metadata differs from regular data just because it is stored under a key, making it possible to store more than one data reference per node. The metadata getter and setter need the key to perform the operation: setMetadata('example key', "Node One's Sample Metadata"); print("Metadata stored under key 'example key' in node one: " . $nodeOne->getMetadata('example key')); $nodeOne->unsetMetadata('example key'); ]]> Querying a Graph Structures_Graph provides for basic querying of the graph: inDegree()); print("NodeOne's outDegree: " . $nodeOne->outDegree()); // and naturally, nodes can report on their arcs $arcs = $nodeOne->getNeighbours(); for ($i=0;$igetData()); } ]]> PK=1]DGe!doc/pear/Structures_Graph/LICENSEnu[ GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. PK=1]E doc/pear/PEAR/README.rstnu[************************* PEAR - The PEAR Installer ************************* .. image:: https://travis-ci.org/pear/pear-core.svg?branch=stable :target: https://travis-ci.org/pear/pear-core ========================================= What is the PEAR Installer? What is PEAR? ========================================= PEAR is the PHP Extension and Application Repository, found at http://pear.php.net. The **PEAR Installer** is this software, which contains executable files and PHP code that is used to **download and install** PEAR code from pear.php.net. PEAR contains useful **software libraries and applications** such as MDB2 (database abstraction), HTML_QuickForm (HTML forms management), PhpDocumentor (auto-documentation generator), DB_DataObject (Data Access Abstraction), and many hundreds more. Browse all available packages at http://pear.php.net, the list is constantly growing and updating to reflect improvements in the PHP language. .. warning:: Do not run PEAR without installing it - if you downloaded this tarball manually, you MUST install it. Read the instructions in INSTALL prior to use. ============= Documentation ============= Documentation for PEAR can be found at http://pear.php.net/manual/. Installation documentation can be found in the INSTALL file included in this tarball. ===== Tests ===== Run the tests without installation as follows:: $ ./scripts/pear.sh run-tests -r tests You should have the ``Text_Diff`` package installed to get nicer error output. To run the tests with another PHP version, modify ``php_bin`` and set the ``PHP_PEAR_PHP_BIN`` environment variable:: $ pear config-set php_bin /usr/local/bin/php7 $ PHP_PEAR_PHP_BIN=/usr/local/bin/php7 ./scripts/pear.sh run-tests -r tests Happy PHPing, we hope PEAR will be a great tool for your development work! Test dependencies ================= * ``zlib`` ========= Releasing ========= Create a PEAR package, as well as phars for pear-less installation, simply run ``build-release.sh``). ``go-pear.phar`` contains the PEAR installer installer that asks where to install it. It is available from http://pear.php.net/go-pear.phar. ``install-pear-nozlib.phar`` installs PEAR automatically without asking anything. It is shipped with PHP itself. PK=1] xxdoc/pear/PEAR/INSTALLnu[PEAR - The PEAR Installer ========================= Installing the PEAR Installer. You should install PEAR on a local development machine first. Installing PEAR on a remote production machine should only be done after you are familiar with PEAR and have tested code using PEAR on your development machine. There are two methods of installing PEAR - PEAR bundled in PHP - go-pear We will first examine how to install PEAR that is bundled with PHP. Microsoft Windows ================= If you are running PHP 5.2.0 or newer, simply download and run the windows installer (.msi) and PEAR can be automatically installed. Otherwise, for older PHP versions, download the .zip of windows, there is a script included with your PHP distribution that is called "go-pear". You must open a command box in order to run it. Click "start" then click "Run..." and type "cmd.exe" to open a command box. Use "cd" to change directory to the location of PHP where you unzipped it, and run the go-pear command. Unix ==== When compiling PHP from source, you simply need to include the --with-pear directive on the "./configure" command. This is "on" by default in most PHP versions, but it doesn't hurt to list it explicitly. You should also consider enabling the zlib extension via --enable-zlib, so that the PEAR installer will be able to handle gzipped files (i.e. smaller package files for faster downloads). Later, when you run "make install" to install PHP itself, part of the process will be prompts that ask you where you want PEAR to be installed. go-pear ======= For users who cannot perform the above steps, or who wish to obtain the latest PEAR with a slightly higher risk of failure, use go-pear. go-pear is obtained by downloading http://pear.php.net/go-pear and saving it as go-pear.php. After downloading, simply run "php go-pear.php" or open it in a web browser (windows only) to download and install PEAR. You can always ask general installation questions on pear-general@lists.php.net, a public mailing list devoted to support for PEAR packages and installation- related issues. Happy PHPing, we hope PEAR will be a great tool for your development work! PK=1]udoc/pear/PEAR/LICENSEnu[Copyright (c) 1997-2009, Stig Bakken , Gregory Beaver , Helgi Þormar Þorbjörnsson , Tomas V.V.Cox , Martin Jansen . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PK=1]c??)doc/ea-php83-php-fpm/php-fpm.conf.defaultnu[;;;;;;;;;;;;;;;;;;;;; ; FPM Configuration ; ;;;;;;;;;;;;;;;;;;;;; ; All relative paths in this configuration file are relative to PHP's install ; prefix (/opt/cpanel/ea-php83/root/usr). This prefix can be dynamically changed by using the ; '-p' argument from the command line. ;;;;;;;;;;;;;;;;;; ; Global Options ; ;;;;;;;;;;;;;;;;;; [global] ; Pid file ; Note: the default prefix is /opt/cpanel/ea-php83/root/var ; Default Value: none ;pid = run/php-fpm.pid ; Error log file ; If it's set to "syslog", log is sent to syslogd instead of being written ; into a local file. ; Note: the default prefix is /opt/cpanel/ea-php83/root/var ; Default Value: log/php-fpm.log ;error_log = log/php-fpm.log ; syslog_facility is used to specify what type of program is logging the ; message. This lets syslogd specify that messages from different facilities ; will be handled differently. ; See syslog(3) for possible values (ex daemon equiv LOG_DAEMON) ; Default Value: daemon ;syslog.facility = daemon ; syslog_ident is prepended to every message. If you have multiple FPM ; instances running on the same server, you can change the default value ; which must suit common needs. ; Default Value: php-fpm ;syslog.ident = php-fpm ; Log level ; Possible Values: alert, error, warning, notice, debug ; Default Value: notice ;log_level = notice ; Log limit on number of characters in the single line (log entry). If the ; line is over the limit, it is wrapped on multiple lines. The limit is for ; all logged characters including message prefix and suffix if present. However ; the new line character does not count into it as it is present only when ; logging to a file descriptor. It means the new line character is not present ; when logging to syslog. ; Default Value: 1024 ;log_limit = 4096 ; Log buffering specifies if the log line is buffered which means that the ; line is written in a single write operation. If the value is false, then the ; data is written directly into the file descriptor. It is an experimental ; option that can potentially improve logging performance and memory usage ; for some heavy logging scenarios. This option is ignored if logging to syslog ; as it has to be always buffered. ; Default value: yes ;log_buffering = no ; If this number of child processes exit with SIGSEGV or SIGBUS within the time ; interval set by emergency_restart_interval then FPM will restart. A value ; of '0' means 'Off'. ; Default Value: 0 ;emergency_restart_threshold = 0 ; Interval of time used by emergency_restart_interval to determine when ; a graceful restart will be initiated. This can be useful to work around ; accidental corruptions in an accelerator's shared memory. ; Available Units: s(econds), m(inutes), h(ours), or d(ays) ; Default Unit: seconds ; Default Value: 0 ;emergency_restart_interval = 0 ; Time limit for child processes to wait for a reaction on signals from master. ; Available units: s(econds), m(inutes), h(ours), or d(ays) ; Default Unit: seconds ; Default Value: 0 ;process_control_timeout = 0 ; The maximum number of processes FPM will fork. This has been designed to control ; the global number of processes when using dynamic PM within a lot of pools. ; Use it with caution. ; Note: A value of 0 indicates no limit ; Default Value: 0 ; process.max = 128 ; Specify the nice(2) priority to apply to the master process (only if set) ; The value can vary from -19 (highest priority) to 20 (lowest priority) ; Note: - It will only work if the FPM master process is launched as root ; - The pool process will inherit the master process priority ; unless specified otherwise ; Default Value: no set ; process.priority = -19 ; Send FPM to background. Set to 'no' to keep FPM in foreground for debugging. ; Default Value: yes ;daemonize = yes ; Set open file descriptor rlimit for the master process. ; Default Value: system defined value ;rlimit_files = 1024 ; Set max core size rlimit for the master process. ; Possible Values: 'unlimited' or an integer greater or equal to 0 ; Default Value: system defined value ;rlimit_core = 0 ; Specify the event mechanism FPM will use. The following is available: ; - select (any POSIX os) ; - poll (any POSIX os) ; - epoll (linux >= 2.5.44) ; - kqueue (FreeBSD >= 4.1, OpenBSD >= 2.9, NetBSD >= 2.0) ; - /dev/poll (Solaris >= 7) ; - port (Solaris >= 10) ; Default Value: not set (auto detection) ;events.mechanism = epoll ; When FPM is built with systemd integration, specify the interval, ; in seconds, between health report notification to systemd. ; Set to 0 to disable. ; Available Units: s(econds), m(inutes), h(ours) ; Default Unit: seconds ; Default value: 10 ;systemd_interval = 10 ;;;;;;;;;;;;;;;;;;;; ; Pool Definitions ; ;;;;;;;;;;;;;;;;;;;; ; Multiple pools of child processes may be started with different listening ; ports and different management options. The name of the pool will be ; used in logs and stats. There is no limitation on the number of pools which ; FPM can handle. Your system will tell you anyway :) ; Include one or more files. If glob(3) exists, it is used to include a bunch of ; files from a glob(3) pattern. This directive can be used everywhere in the ; file. ; Relative path can also be used. They will be prefixed by: ; - the global prefix if it's been set (-p argument) ; - /opt/cpanel/ea-php83/root/usr otherwise include=/opt/cpanel/ea-php83/root/etc/php-fpm.d/*.conf PK=1]ũ  pear-data/PEAR/package.dtdnu[ PK=1]pear-data/PEAR/template.specnu[Summary: PEAR: @summary@ Name: @rpm_package@ Version: @version@ Release: 1 License: @release_license@ Group: Development/Libraries Source: http://@master_server@/get/@package@-%{version}.tgz BuildRoot: %{_tmppath}/%{name}-root URL: http://@master_server@/package/@package@ Prefix: %{_prefix} BuildArchitectures: @arch@ @extra_headers@ %description @description@ %prep rm -rf %{buildroot}/* %setup -c -T # XXX Source files location is missing here in pear cmd pear -v -c %{buildroot}/pearrc \ -d php_dir=%{_libdir}/php/pear \ -d doc_dir=/docs \ -d bin_dir=%{_bindir} \ -d data_dir=%{_libdir}/php/pear/data \ -d test_dir=%{_libdir}/php/pear/tests \ -d ext_dir=%{_libdir} \@extra_config@ -s %build echo BuildRoot=%{buildroot} %postun # if refcount = 0 then package has been removed (not upgraded) if [ "$1" -eq "0" ]; then pear uninstall --nodeps -r @possible_channel@@package@ rm @rpm_xml_dir@/@package@.xml fi %post # if refcount = 2 then package has been upgraded if [ "$1" -ge "2" ]; then pear upgrade --nodeps -r @rpm_xml_dir@/@package@.xml else pear install --nodeps -r @rpm_xml_dir@/@package@.xml fi %install pear -c %{buildroot}/pearrc install --nodeps -R %{buildroot} \ $RPM_SOURCE_DIR/@package@-%{version}.tgz rm %{buildroot}/pearrc rm %{buildroot}/%{_libdir}/php/pear/.filemap rm %{buildroot}/%{_libdir}/php/pear/.lock rm -rf %{buildroot}/%{_libdir}/php/pear/.registry if [ "@doc_files@" != "" ]; then mv %{buildroot}/docs/@package@/* . rm -rf %{buildroot}/docs fi mkdir -p %{buildroot}@rpm_xml_dir@ tar -xzf $RPM_SOURCE_DIR/@package@-%{version}.tgz package@package2xml@.xml cp -p package@package2xml@.xml %{buildroot}@rpm_xml_dir@/@package@.xml #rm -rf %{buildroot}/* #pear -q install -R %{buildroot} -n package@package2xml@.xml #mkdir -p %{buildroot}@rpm_xml_dir@ #cp -p package@package2xml@.xml %{buildroot}@rpm_xml_dir@/@package@.xml %files %defattr(-,root,root) %doc @doc_files@ / PK=1]G+tests/pear/XML_Util/tests/Bug21177Tests.phpnu['; return array( array('', ''), array('', ''), array('', ''), array('', ''), ); } /** * @dataProvider getTestCandidate() */ public function testCollapseEmptyTagsForBug21177($original, $expected) { $this->assertEquals($expected, XML_Util::collapseEmptyTags($original, XML_UTIL_COLLAPSE_ALL), "Failed bugcheck."); } } PK=1]GG5tests/pear/XML_Util/tests/SplitQualifiedNameTests.phpnu[ 'xslt', 'localPart' => 'stylesheet', ); $this->assertEquals($expected, XML_Util::splitQualifiedName($original)); } /** * @covers XML_Util::splitQualifiedName() */ public function testSplitQualifiedNameWithNamespace() { $original = "stylesheet"; $namespace = "myNs"; $expected = array( 'namespace' => 'myNs', 'localPart' => 'stylesheet', ); $this->assertEquals($expected, XML_Util::splitQualifiedName($original, $namespace)); } } PK=1]e>445tests/pear/XML_Util/tests/CreateTagFromArrayTests.phpnu[ "foo:bar", ); $expected = ""; $this->assertEquals($expected, XML_Util::createTagFromArray($original)); } /** * @covers XML_Util::createTagFromArray() */ public function testCreateTagFromArrayWithQnameAndNamespace() { $original = array( "qname" => "foo:bar", "namespaceUri" => "http://foo.com", ); $expected = ""; $this->assertEquals($expected, XML_Util::createTagFromArray($original)); } /** * @covers XML_Util::createTagFromArray() */ public function testCreateTagFromArrayWithQnameAndNamespaceAndAttributes() { $original = array( "qname" => "foo:bar", "namespaceUri" => "http://foo.com", "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ), ); $expected = ""; $this->assertEquals($expected, XML_Util::createTagFromArray($original)); } /** * @covers XML_Util::createTagFromArray() */ public function testCreateTagFromArrayWithQnameAndNamespaceAndAttributesAndContent() { $original = array( "qname" => "foo:bar", "namespaceUri" => "http://foo.com", "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ), "content" => "I'm inside the tag", ); $expected = "I'm inside the tag"; $this->assertEquals($expected, XML_Util::createTagFromArray($original)); } /** * @covers XML_Util::createTagFromArray() */ public function testCreateTagFromArrayWithQnameAndAttributesAndContent() { $original = array( "qname" => "foo:bar", "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ), "content" => "I'm inside the tag", ); $expected = "I'm inside the tag"; $this->assertEquals($expected, XML_Util::createTagFromArray($original)); } /** * @covers XML_Util::createTagFromArray() */ public function testCreateTagFromArrayWithQnameAndNamespaceAndContent() { $original = array( "qname" => "foo:bar", "namespaceUri" => "http://foo.com", "content" => "I'm inside the tag", ); $expected = "I'm inside the tag"; $this->assertEquals($expected, XML_Util::createTagFromArray($original)); } /** * @covers XML_Util::createTagFromArray() */ public function testCreateTagFromArrayWithQnameAndNamespaceAndAttributesAndContentWithEntitiesNone() { $original = array( "qname" => "foo:bar", "namespaceUri" => "http://foo.com", "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ), "content" => "I'm inside the tag", ); $expected = "I'm inside the tag"; $this->assertEquals($expected, XML_Util::createTagFromArray($original, XML_UTIL_ENTITIES_NONE)); } /** * @covers XML_Util::createTagFromArray() */ public function testCreateTagFromArrayWithQnameAndNamespaceAndAttributesAndContentWithReplaceEntities() { $original = array( "qname" => "foo:bar", "namespaceUri" => "http://foo.com", "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ), "content" => "I'm inside the tag", ); $expected = "I'm inside the tag"; $this->assertEquals($expected, XML_Util::createTagFromArray($original, XML_UTIL_REPLACE_ENTITIES)); } /** * @covers XML_Util::createTagFromArray() */ public function testCreateTagFromArrayWithQnameAndNamespaceAndAttributesAndContentWithReplaceEntitiesAndMultilineFalse() { $original = array( "qname" => "foo:bar", "namespaceUri" => "http://foo.com", "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ), "content" => "I'm inside the tag", ); $multiline = false; $expected = "I'm inside the tag"; $this->assertEquals($expected, XML_Util::createTagFromArray($original, XML_UTIL_REPLACE_ENTITIES, $multiline)); } /** * @covers XML_Util::createTagFromArray() */ public function testCreateTagFromArrayWithQnameAndNamespaceAndAttributesAndContentWithReplaceEntitiesAndMultilineTrue() { $original = array( "qname" => "foo:bar", "namespaceUri" => "http://foo.com", "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ), "content" => "I'm inside the tag", ); $multiline = true; $expected = <<< EOF I'm inside the tag EOF; $this->assertEquals($expected, XML_Util::createTagFromArray($original, XML_UTIL_REPLACE_ENTITIES, $multiline)); } /** * @covers XML_Util::createTagFromArray() */ public function testCreateTagFromArrayWithQnameAndNamespaceAndAttributesAndContentWithReplaceEntitiesAndMultilineTrueAndIndent() { $original = array( "qname" => "foo:bar", "namespaceUri" => "http://foo.com", "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ), "content" => "I'm inside the tag", ); $multiline = true; $indent = " "; $expected = <<< EOF I'm inside the tag EOF; $this->assertEquals($expected, XML_Util::createTagFromArray($original, XML_UTIL_REPLACE_ENTITIES, $multiline, $indent)); } /** * @covers XML_Util::createTagFromArray() */ public function testCreateTagFromArrayWithQnameAndNamespaceAndAttributesAndContentWithReplaceEntitiesAndMultilineTrueAndIndentAndLinebreak() { $original = array( "qname" => "foo:bar", "namespaceUri" => "http://foo.com", "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ), "content" => "I'm inside the tag", ); $multiline = true; $indent = " "; $linebreak = "^"; $expected = "I'm inside the tag"; $this->assertEquals($expected, XML_Util::createTagFromArray($original, XML_UTIL_REPLACE_ENTITIES, $multiline, $indent, $linebreak)); } /** * @covers XML_Util::createTagFromArray() */ public function testCreateTagFromArrayWithQnameAndNamespaceAndAttributesAndContentWithReplaceEntitiesAndMultilineTrueAndIndentAndLinebreakAndSortAttributesTrue() { $original = array( "qname" => "foo:bar", "namespaceUri" => "http://foo.com", "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ), "content" => "I'm inside the tag", ); $multiline = true; $indent = " "; $linebreak = "^"; $sortAttributes = true; $expected = "I'm inside the tag"; $this->assertEquals($expected, XML_Util::createTagFromArray($original, XML_UTIL_REPLACE_ENTITIES, $multiline, $indent, $linebreak, $sortAttributes)); } /** * @covers XML_Util::createTagFromArray() */ public function testCreateTagFromArrayWithQnameAndNamespaceAndAttributesAndContentWithReplaceEntitiesAndMultilineTrueAndIndentAndLinebreakAndSortAttributesFalse() { $original = array( "qname" => "foo:bar", "namespaceUri" => "http://foo.com", "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ), "content" => "I'm inside the tag", ); $multiline = true; $indent = " "; $linebreak = "^"; $sortAttributes = false; $expected = "I'm inside the tag"; $this->assertEquals($expected, XML_Util::createTagFromArray($original, XML_UTIL_REPLACE_ENTITIES, $multiline, $indent, $linebreak, $sortAttributes)); } /** * @covers XML_Util::createTagFromArray() */ public function testCreateTagFromArrayWithInvalidArray() { $badArray = array( "foo" => "bar", ); $expectedError = "You must either supply a qualified name (qname) or local tag name (localPart)."; $this->assertEquals($expectedError, XML_Util::createTagFromArray($badArray)); } /** * @covers XML_Util::createTagFromArray() */ public function testCreateTagFromArrayWithNamespaceAndAttributesAndContentButWithoutQname() { $original = array( "namespaceUri" => "http://foo.com", "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ), "content" => "I'm inside the tag", ); $expectedError = "You must either supply a qualified name (qname) or local tag name (localPart)."; $this->assertEquals($expectedError, XML_Util::createTagFromArray($original)); } /** * @covers XML_Util::createTagFromArray() */ public function testCreateTagFromArrayWithNonScalarContent() { $badArray = array( 'content' => array('foo', 'bar'), ); $expectedError = "Supplied non-scalar value as tag content"; $this->assertEquals($expectedError, XML_Util::createTagFromArray($badArray)); } /** * @covers XML_Util::createTagFromArray() */ public function testCreateTagFromArrayWithArrayOfNamespaces() { $original = array( 'qname' => 'foo:bar', 'namespaces' => array('ns1' => 'uri1', 'ns2' => 'uri2'), ); $expected = ""; $this->assertEquals($expected, XML_Util::createTagFromArray($original)); } /** * @covers XML_Util::createTagFromArray() */ public function testCreateTagFromArrayWithQnameDerivedFromNamespaceUriAndLocalPart() { $original = array( 'namespaceUri' => 'http://bar.org', 'localPart' => 'foo' ); $expected = ""; $this->assertEquals($expected, XML_Util::createTagFromArray($original)); } /** * @covers XML_Util::createTagFromArray() */ public function testCreateTagFromArrayWithQnameDerivedFromNamespaceAndLocalPart() { $original = array( 'namespace' => 'http://foo.org', 'localPart' => 'bar' ); $expected = ""; $this->assertEquals($expected, XML_Util::createTagFromArray($original)); } /** * @covers XML_Util::createTagFromArray() */ public function testCreateTagFromArrayWithQnameDerivedFromLocalPart() { $original = array( 'namespace' => '', 'localPart' => 'bar' ); $expected = ""; $this->assertEquals($expected, XML_Util::createTagFromArray($original)); } /** * @covers XML_Util::createTagFromArray() */ public function testCreateTagFromArrayWithImplicitlyEmptyContentAndCollapseNoneDoesNotCollapseTag() { $original = array('qname' => 'tag1'); $expected = ""; $actual = XML_Util::createTagFromArray( $original, XML_UTIL_REPLACE_ENTITIES, // default $replaceEntities false, // default $multiline '_auto', // default $indent "\n", // default $linebreak true, // default $sortAttributes XML_UTIL_COLLAPSE_NONE ); $this->assertEquals($expected, $actual); } /** * @covers XML_Util::createTagFromArray() */ public function testCreateTagFromArrayForCdataWithExplicitlyEmptyContentDoesNotCollapseTag() { $original = array('qname' => 'tag1', 'content' => ''); $expected = ""; $this->assertEquals($expected, XML_Util::createTagFromArray($original, XML_UTIL_CDATA_SECTION)); } } PK=1]Yq+tests/pear/XML_Util/tests/Bug21184Tests.phpnu[one'; $this->assertEquals($xml, XML_Util::collapseEmptyTags($xml, XML_UTIL_COLLAPSE_ALL)); } } PK=1]C2tests/pear/XML_Util/tests/ReplaceEntitiesTests.phpnu[.'; } protected function getUtf8Data() { return 'This data contains special chars like <, >, & and " as well as ä, ö, ß, à and ê'; } /** * @covers XML_Util::replaceEntities() */ public function testReplaceEntitiesForSimpleData() { $expected = "This string contains < & >."; $this->assertEquals($expected, XML_Util::replaceEntities($this->getSimpleData())); } /** * @covers XML_Util::replaceEntities() */ public function testReplaceEntitiesForSimpleDataWithInvalidOptionReturnsOriginalData() { $expected = "This string contains < & >."; $this->assertEquals($expected, XML_Util::replaceEntities($this->getSimpleData(), 'INVALID_OPTION')); } /** * @covers XML_Util::replaceEntities() */ public function testReplaceEntitiesForSimpleDataWithEntitiesXml() { $expected = "This string contains < & >."; $this->assertEquals($expected, XML_Util::replaceEntities($this->getSimpleData(), XML_UTIL_ENTITIES_XML)); } /** * @covers XML_Util::replaceEntities() */ public function testReplaceEntitiesForSimpleDataWithEntitiesXmlAndEncoding() { $encoding = "UTF-8"; $expected = "This string contains < & >."; $this->assertEquals($expected, XML_Util::replaceEntities($this->getSimpleData(), XML_UTIL_ENTITIES_XML, $encoding)); } /** * @covers XML_Util::replaceEntities() */ public function testReplaceEntitiesForUtf8DataWithEntitiesXmlAndEncoding() { $encoding = "UTF-8"; $expected = "This data contains special chars like <, >, & and " as well as ä, ö, ß, à and ê"; $this->assertEquals($expected, XML_Util::replaceEntities($this->getUtf8Data(), XML_UTIL_ENTITIES_XML, $encoding)); } /** * @covers XML_Util::replaceEntities() */ public function testReplaceEntitiesForSimpleDataWithEntitiesXmlRequired() { $expected = "This string contains < & >."; $this->assertEquals($expected, XML_Util::replaceEntities($this->getSimpleData(), XML_UTIL_ENTITIES_XML_REQUIRED)); } /** * @covers XML_Util::replaceEntities() */ public function testReplaceEntitiesForSimpleDataWithEntitiesXmlRequiredAndEncoding() { $encoding = "UTF-8"; $expected = "This string contains < & >."; $this->assertEquals($expected, XML_Util::replaceEntities($this->getSimpleData(), XML_UTIL_ENTITIES_XML_REQUIRED, $encoding)); } /** * @covers XML_Util::replaceEntities() */ public function testReplaceEntitiesForUtf8DataWithEntitiesXmlRequiredAndEncoding() { $encoding = "UTF-8"; $expected = "This data contains special chars like <, >, & and " as well as ä, ö, ß, à and ê"; $this->assertEquals($expected, XML_Util::replaceEntities($this->getUtf8Data(), XML_UTIL_ENTITIES_XML_REQUIRED, $encoding)); } /** * @covers XML_Util::replaceEntities() */ public function testReplaceEntitiesForSimpleDataWithEntitiesHtml() { $expected = "This string contains < & >."; $this->assertEquals($expected, XML_Util::replaceEntities($this->getSimpleData(), XML_UTIL_ENTITIES_HTML)); } /** * @covers XML_Util::replaceEntities() */ public function testReplaceEntitiesForSimpleDataWithEntitiesHtmlAndEncoding() { $encoding = "UTF-8"; $expected = "This string contains < & >."; $this->assertEquals($expected, XML_Util::replaceEntities($this->getSimpleData(), XML_UTIL_ENTITIES_HTML, $encoding)); } /** * @covers XML_Util::replaceEntities() */ public function testReplaceEntitiesForUtf8DataWithEntitiesHtmlAndEncoding() { $encoding = "UTF-8"; $expected = "This data contains special chars like <, >, & and " as well as ä, ö, ß, à and ê"; $this->assertEquals($expected, XML_Util::replaceEntities($this->getUtf8Data(), XML_UTIL_ENTITIES_HTML, $encoding)); } } PK=1]Zt5tests/pear/XML_Util/tests/AttributesToStringTests.phpnu[ 'bar','boo' => 'baz',); $expected = " boo=\"baz\" foo=\"bar\""; $this->assertEquals($expected, XML_Util::attributesToString($original)); } /** * @covers XML_Util::attributesToString() */ public function testAttributesToStringWithExplicitSortTrue() { $original = array('foo' => 'bar','boo' => 'baz',); $expected = " boo=\"baz\" foo=\"bar\""; $sort = true; $this->assertEquals($expected, XML_Util::attributesToString($original, $sort)); } /** * @covers XML_Util::attributesToString() */ public function testAttributesToStringWithExplicitSortFalse() { $original = array('foo' => 'bar','boo' => 'baz',); $expected = " foo=\"bar\" boo=\"baz\""; $sort = false; $this->assertEquals($expected, XML_Util::attributesToString($original, $sort)); } /** * @covers XML_Util::attributesToString() */ public function testAttributesToStringWithMultilineFalse() { $original = array('foo' => 'bar','boo' => 'baz',); $expected = " boo=\"baz\" foo=\"bar\""; $sort = true; $multiline = false; $this->assertEquals($expected, XML_Util::attributesToString($original, $sort, $multiline)); } /** * @covers XML_Util::attributesToString() */ public function testAttributesToStringWithMultilineTrue() { $original = array('foo' => 'bar','boo' => 'baz',); $expected = <<< EOF boo="baz" foo="bar" EOF; $sort = true; $multiline = true; $this->assertEquals($expected, XML_Util::attributesToString($original, $sort, $multiline)); } /** * @covers XML_Util::attributesToString() */ public function testAttributesToStringWithExplicitIndent() { $original = array('foo' => 'bar','boo' => 'baz',); $expected = " boo=\"baz\"\n foo=\"bar\""; $sort = true; $multiline = true; $indent = ' '; // 8 spaces $this->assertEquals($expected, XML_Util::attributesToString($original, $sort, $multiline, $indent)); } /** * @covers XML_Util::attributesToString() */ public function testAttributesToStringWithExplicitLinebreak() { $original = array('foo' => 'bar','boo' => 'baz',); $expected = " boo=\"baz\"\n^foo=\"bar\""; $sort = true; $multiline = true; $linebreak = '^'; // some dummy character $this->assertEquals($expected, XML_Util::attributesToString($original, $sort, $multiline, $linebreak)); } /** * @covers XML_Util::attributesToString() */ public function testAttributesToStringWithOptionsThatIncludesSort() { $original = array('foo' => 'bar','boo' => 'baz',); $options = array( 'multiline' => true, 'indent' => '----', 'linebreak' => "^", 'entities' => XML_UTIL_ENTITIES_XML, 'sort' => true, ); $expected = " boo=\"baz\"\n----foo=\"bar\""; $this->assertEquals($expected, XML_Util::attributesToString($original, $options)); } /** * @covers XML_Util::attributesToString() */ public function testAttributesToStringWithOptionsThatExcludesSort() { $original = array('foo' => 'bar','boo' => 'baz',); $options = array( 'multiline' => true, 'indent' => '----', 'linebreak' => "^", 'entities' => XML_UTIL_ENTITIES_XML, ); $expected = " boo=\"baz\"\n----foo=\"bar\""; $this->assertEquals($expected, XML_Util::attributesToString($original, $options)); } /** * @covers XML_Util::attributesToString() */ public function testAttributesToStringWithEntitiesNone() { $original = array("foo" => "b@&r", "boo" => "b>assertEquals($expected, XML_Util::attributesToString($original, $sort, $multiline, $linebreak, PHP_EOL, XML_UTIL_ENTITIES_NONE)); } /** * @covers XML_Util::attributesToString() */ public function testAttributesToStringWithEntitiesXml() { $original = array("foo" => "b@&r", "boo" => "b>assertEquals($expected, XML_Util::attributesToString($original, $sort, $multiline, $linebreak, PHP_EOL, XML_UTIL_ENTITIES_XML)); } /** * @covers XML_Util::attributesToString() */ public function testAttributesToStringWithEntitiesXmlRequired() { $original = array("foo" => "b@&r", "boo" => "b><z\" foo=\"b@&r\""; $sort = true; $multiline = false; $linebreak = ' '; $this->assertEquals($expected, XML_Util::attributesToString($original, $sort, $multiline, $linebreak, PHP_EOL, XML_UTIL_ENTITIES_XML_REQUIRED)); } /** * @covers XML_Util::attributesToString() */ public function testAttributesToStringWithEntitiesHtml() { $original = array("foo" => "b@&r", "boo" => "b>assertEquals($expected, XML_Util::attributesToString($original, $sort, $multiline, $linebreak, PHP_EOL, XML_UTIL_ENTITIES_HTML)); } /** * Tag attributes should not be treated as CDATA, * so the operation will instead quietly use XML_UTIL_ENTITIES_XML. * * @covers XML_Util::attributesToString() */ public function testAttributesToStringWithCDataSectionForSingleAttribute() { $original = array('foo' => 'bar'); // need exactly one attribute here $options = array( 'sort' => true, // doesn't matter for this testcase 'multiline' => false, // doesn't matter for this testcase 'indent' => null, // doesn't matter for this testcase 'linebreak' => null, // doesn't matter for this testcase 'entities' => XML_UTIL_CDATA_SECTION, // DOES matter for this testcase ); $expected = " foo=\"bar\""; $this->assertEquals($expected, XML_Util::attributesToString($original, $options)); } /** * Tag attributes should not be treated as CDATA, * so the operation will instead quietly use XML_UTIL_ENTITIES_XML. * * @covers XML_Util::attributesToString() */ public function testAttributesToStringWithCDataSectionForMultipleAttributesAndMultilineFalse() { $original = array('foo' => 'bar', 'boo' => 'baz'); // need more than one attribute here $options = array( 'sort' => true, // doesn't matter for this testcase 'multiline' => false, // DOES matter for this testcase, must be false 'indent' => null, // doesn't matter for this testcase 'linebreak' => null, // doesn't matter for this testcase 'entities' => XML_UTIL_CDATA_SECTION, // DOES matter for this testcase ); $expected = " boo=\"baz\" foo=\"bar\""; $this->assertEquals($expected, XML_Util::attributesToString($original, $options)); } } PK=1]rv-tests/pear/XML_Util/tests/RaiseErrorTests.phpnu[assertInstanceOf('PEAR_Error', $error); $this->assertEquals($message, $error->getMessage()); $this->assertEquals($code, $error->getCode()); } } PK=1]4ejj5tests/pear/XML_Util/tests/CreateCDataSectionTests.phpnu["; $this->assertEquals($expected, XML_Util::createCDataSection($original)); } } PK=1]TT0tests/pear/XML_Util/tests/CreateCommentTests.phpnu["; $this->assertEquals($expected, XML_Util::createComment($original)); } } PK=1]zt2tests/pear/XML_Util/tests/ReverseEntitiesTests.phpnu[."; $this->assertEquals($expected, XML_Util::reverseEntities($this->getSimpleData())); } /** * @covers XML_Util::reverseEntities() */ public function testReverseEntitiesForSimpleDataWithInvalidOptionReturnsOriginalData() { $expected = "This string contains < & >."; $this->assertEquals($expected, XML_Util::reverseEntities($this->getSimpleData(), 'INVALID_OPTION')); } /** * @covers XML_Util::reverseEntities() */ public function testReverseEntitiesForSimpleDataWithEntitiesXml() { $expected = "This string contains < & >."; $this->assertEquals($expected, XML_Util::reverseEntities($this->getSimpleData(), XML_UTIL_ENTITIES_XML)); } /** * @covers XML_Util::reverseEntities() */ public function testReverseEntitiesForSimpleDataWithEntitiesXmlAndEncoding() { $encoding = "UTF-8"; $expected = "This string contains < & >."; $this->assertEquals($expected, XML_Util::reverseEntities($this->getSimpleData(), XML_UTIL_ENTITIES_XML), $encoding); } /** * @covers XML_Util::reverseEntities() */ public function testReverseEntitiesForUtf8DataWithEntitiesXmlAndEncoding() { $encoding = "UTF-8"; $expected = "This data contains special chars like <, >, & and \" as well as ä, ö, ß, à and ê"; $this->assertEquals($expected, XML_Util::reverseEntities($this->getUtf8Data(), XML_UTIL_ENTITIES_XML), $encoding); } /** * @covers XML_Util::reverseEntities() */ public function testReverseEntitiesForSimpleDataWithEntitiesXmlRequired() { $expected = "This string contains < & >."; $this->assertEquals($expected, XML_Util::reverseEntities($this->getSimpleData(), XML_UTIL_ENTITIES_XML_REQUIRED)); } /** * @covers XML_Util::reverseEntities() */ public function testReverseEntitiesForSimpleDataWithEntitiesXmlRequiredAndEncoding() { $encoding = "UTF-8"; $expected = "This string contains < & >."; $this->assertEquals($expected, XML_Util::reverseEntities($this->getSimpleData(), XML_UTIL_ENTITIES_XML_REQUIRED, $encoding)); } /** * @covers XML_Util::reverseEntities() */ public function testReverseEntitiesForUtf8DataWithEntitiesXmlRequiredAndEncoding() { $encoding = "UTF-8"; $expected = "This data contains special chars like <, >, & and \" as well as ä, ö, ß, à and ê"; $this->assertEquals($expected, XML_Util::reverseEntities($this->getUtf8Data(), XML_UTIL_ENTITIES_XML_REQUIRED, $encoding)); } /** * @covers XML_Util::reverseEntities() */ public function testReverseEntitiesForSimpleDataWithEntitiesHtml() { $expected = "This string contains < & >."; $this->assertEquals($expected, XML_Util::reverseEntities($this->getSimpleData(), XML_UTIL_ENTITIES_HTML)); } /** * @covers XML_Util::reverseEntities() */ public function testReverseEntitiesForSimpleDataWithEntitiesHtmlAndEncoding() { $encoding = "UTF-8"; $expected = "This string contains < & >."; $this->assertEquals($expected, XML_Util::reverseEntities($this->getSimpleData(), XML_UTIL_ENTITIES_HTML, $encoding)); } /** * @covers XML_Util::reverseEntities() */ public function testReverseEntitiesForUtf8DataWithEntitiesHtmlAndEncoding() { $encoding = "UTF-8"; $expected = "This data contains special chars like <, >, & and \" as well as ä, ö, ß, à and ê"; $this->assertEquals($expected, XML_Util::reverseEntities($this->getUtf8Data(), XML_UTIL_ENTITIES_HTML, $encoding)); } } PK=1]!/tests/pear/XML_Util/tests/AbstractUnitTests.phpnu[assertEquals('1.4', XML_Util::apiVersion()); } }PK=1]۩+tests/pear/XML_Util/tests/Bug18343Tests.phpnu[ "install", "attributes" => array( "as" => "Horde/Feed/fixtures/lexicon/http-p.moreover.com-cgi-local-page%2Fo=rss&s=Newsweek", "name" => "test/Horde/Feed/fixtures/lexicon/http-p.moreover.com-cgi-local-page%2Fo=rss&s=Newsweek", ) ); public function getFlagsToTest() { new XML_Util(); // for constants to be declared return array( array('no flag', null), array('false', false), array('ENTITIES_NONE', XML_UTIL_ENTITIES_NONE), array('ENTITIES_XML', XML_UTIL_ENTITIES_XML), array('ENTITIES_XML_REQUIRED', XML_UTIL_ENTITIES_XML_REQUIRED), array('ENTITIES_HTML', XML_UTIL_ENTITIES_HTML), array('REPLACE_ENTITIES', XML_UTIL_REPLACE_ENTITIES), ); } /** * @dataProvider getFlagsToTest() */ public function testCreateTagFromArrayForBug18343($key, $flag) { // all flags for the candidate input should return the same result $expected = <<< EOF EOF; $this->assertEquals($expected, XML_Util::createTagFromArray($this->tagArray, $flag), "Failed bugcheck for $key."); } } PK=1]m4**,tests/pear/XML_Util/tests/CreateTagTests.phpnu[ "bar"); $expected = ""; $this->assertEquals($expected, XML_Util::createTag($originalTag, $originalAttributes)); } /** * @covers XML_Util::createTag() */ public function testCreateTagForTagWithAttributesAndContent() { $originalTag = "myNs:myTag"; $originalAttributes = array("foo" => "bar"); $originalContent = "This is inside the tag"; $expected = "This is inside the tag"; $this->assertEquals($expected, XML_Util::createTag($originalTag, $originalAttributes, $originalContent)); } /** * @covers XML_Util::createTag() */ public function testCreateTagForTagWithAttributesAndContentAndNamespace() { $originalTag = "myNs:myTag"; $originalAttributes = array("foo" => "bar"); $originalContent = "This is inside the tag"; $originalNamespace = "http://www.w3c.org/myNs#"; $expected = "This is inside the tag"; $this->assertEquals($expected, XML_Util::createTag($originalTag, $originalAttributes, $originalContent, $originalNamespace)); } /** * @covers XML_Util::createTag() */ public function testCreateTagForTagWithAttributesAndContentAndNamespaceWithCDataSection() { $originalTag = "myNs:myTag"; $originalAttributes = array("foo" => "bar"); $originalContent = "This is inside the tag and has < & @ > in it"; $originalNamespace = "http://www.w3c.org/myNs#"; $expected = " in it]]>"; $this->assertEquals($expected, XML_Util::createTag($originalTag, $originalAttributes, $originalContent, $originalNamespace, XML_UTIL_CDATA_SECTION)); } /** * @covers XML_Util::createTag() */ public function testCreateTagForTagWithAttributesAndContentAndNamespaceWithReplaceEntities() { $originalTag = "myNs:myTag"; $originalAttributes = array("foo" => "bar"); $originalContent = "This is inside the tag and has < & @ > in it"; $originalNamespace = "http://www.w3c.org/myNs#"; $expected = "This is inside the tag and has < & @ > in it"; $this->assertEquals($expected, XML_Util::createTag($originalTag, $originalAttributes, $originalContent, $originalNamespace, XML_UTIL_REPLACE_ENTITIES)); } /** * @covers XML_Util::createTag() */ public function testCreateTagForTagWithAttributesAndContentAndNamespaceWithReplaceEntitiesAndMultilineFalse() { $originalTag = "myNs:myTag"; $originalAttributes = array("foo" => "bar"); $originalContent = "This is inside the tag and has < & @ > in it"; $originalNamespace = "http://www.w3c.org/myNs#"; $multiline = false; $expected = "This is inside the tag and has < & @ > in it"; $this->assertEquals($expected, XML_Util::createTag($originalTag, $originalAttributes, $originalContent, $originalNamespace, XML_UTIL_REPLACE_ENTITIES, $multiline)); } /** * @covers XML_Util::createTag() */ public function testCreateTagForTagWithAttributesAndContentAndNamespaceWithReplaceEntitiesAndMultilineTrue() { $originalTag = "myNs:myTag"; $originalAttributes = array("foo" => "bar"); $originalContent = "This is inside the tag and has < & @ > in it"; $originalNamespace = "http://www.w3c.org/myNs#"; $multiline = true; $expected = <<< EOF This is inside the tag and has < & @ > in it EOF; $this->assertEquals($expected, XML_Util::createTag($originalTag, $originalAttributes, $originalContent, $originalNamespace, XML_UTIL_REPLACE_ENTITIES, $multiline)); } /** * @covers XML_Util::createTag() */ public function testCreateTagForTagWithAttributesAndContentAndNamespaceWithReplaceEntitiesAndMultilineTrueAndIndent() { $originalTag = "myNs:myTag"; $originalAttributes = array("foo" => "bar"); $originalContent = "This is inside the tag and has < & @ > in it"; $originalNamespace = "http://www.w3c.org/myNs#"; $multiline = true; $indent = " "; $expected = <<< EOF This is inside the tag and has < & @ > in it EOF; $this->assertEquals($expected, XML_Util::createTag($originalTag, $originalAttributes, $originalContent, $originalNamespace, XML_UTIL_REPLACE_ENTITIES, $multiline, $indent)); } /** * @covers XML_Util::createTag() */ public function testCreateTagForTagWithAttributesAndContentAndNamespaceWithReplaceEntitiesAndMultilineTrueAndIndentAndLinebreak() { $originalTag = "myNs:myTag"; $originalAttributes = array("foo" => "bar"); $originalContent = "This is inside the tag and has < & @ > in it"; $originalNamespace = "http://www.w3c.org/myNs#"; $multiline = true; $indent = " "; $linebreak = "^"; $expected = "This is inside the tag and has < & @ > in it"; $this->assertEquals($expected, XML_Util::createTag($originalTag, $originalAttributes, $originalContent, $originalNamespace, XML_UTIL_REPLACE_ENTITIES, $multiline, $indent, $linebreak)); } /** * @covers XML_Util::createTag() */ public function testCreateTagForTagWithAttributesAndContentAndNamespaceWithReplaceEntitiesAndMultilineTrueAndIndentAndLinebreakAndSortAttributesTrue() { $originalTag = "myNs:myTag"; $originalAttributes = array("foo" => "bar", "boo" => "baz"); $originalContent = "This is inside the tag and has < & @ > in it"; $originalNamespace = "http://www.w3c.org/myNs#"; $multiline = true; $indent = " "; $linebreak = "^"; $sortAttributes = true; $expected = "This is inside the tag and has < & @ > in it"; $this->assertEquals($expected, XML_Util::createTag($originalTag, $originalAttributes, $originalContent, $originalNamespace, XML_UTIL_REPLACE_ENTITIES, $multiline, $indent, $linebreak, $sortAttributes)); } /** * @covers XML_Util::createTag() */ public function testCreateTagForTagWithAttributesAndContentAndNamespaceWithReplaceEntitiesAndMultilineTrueAndIndentAndLinebreakAndSortAttributesFalse() { $originalTag = "myNs:myTag"; $originalAttributes = array("foo" => "bar", "boo" => "baz"); $originalContent = "This is inside the tag and has < & @ > in it"; $originalNamespace = "http://www.w3c.org/myNs#"; $multiline = true; $indent = " "; $linebreak = "^"; $sortAttributes = false; $expected = "This is inside the tag and has < & @ > in it"; $this->assertEquals($expected, XML_Util::createTag($originalTag, $originalAttributes, $originalContent, $originalNamespace, XML_UTIL_REPLACE_ENTITIES, $multiline, $indent, $linebreak, $sortAttributes)); } } PK=1]|\54tests/pear/XML_Util/tests/CollapseEmptyTagsTests.phpnu["; $expected = ""; $this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag)); } /** * @covers XML_Util::collapseEmptyTags() */ public function testCollapseEmptyTagsBasicUsageAlongsideNonemptyTag() { $emptyTag = ""; $otherTag = "baz"; $expected = "baz"; $this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag . $otherTag)); } /** * @covers XML_Util::collapseEmptyTags() */ public function testCollapseEmptyTagsOnOneEmptyTagWithCollapseAll() { $emptyTag = ""; $expected = ""; $this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag, XML_UTIL_COLLAPSE_ALL)); } /** * @covers XML_Util::collapseEmptyTags() */ public function testCollapseEmptyTagsOnOneEmptyTagAlongsideNonemptyTagWithCollapseAll() { $emptyTag = ""; $otherTag = "baz"; $expected = "baz"; $this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag . $otherTag, XML_UTIL_COLLAPSE_ALL)); } /** * @covers XML_Util::collapseEmptyTags() */ public function testCollapseEmptyTagsOnOneEmptyTagAlongsideNonemptyTagAlongsideEmptyTagWithCollapseAll() { $emptyTag = ""; $otherTag = "baz"; $expected = "baz"; $this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag . $otherTag . $emptyTag, XML_UTIL_COLLAPSE_ALL)); } /** * @covers XML_Util::collapseEmptyTags() */ public function testCollapseEmptyTagsOnOneEmptyPrefixedTagAlongsideNonemptyTagAlongsideEmptyPrefixedTagWithCollapseAll() { $emptyTag = ""; $otherTag = "baz"; $expected = "baz"; $this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag . $otherTag . $emptyTag, XML_UTIL_COLLAPSE_ALL)); } /** * @covers XML_Util::collapseEmptyTags() */ public function testCollapseEmptyTagsOnOneEmptyNsPrefixedTagAlongsideNonemptyTagAlongsideEmptyNsPrefixedTagWithCollapseAll() { $emptyTag = ""; $otherTag = "baz"; $expected = "baz"; $this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag . $otherTag . $emptyTag, XML_UTIL_COLLAPSE_ALL)); } /** * @covers XML_Util::collapseEmptyTags() */ public function testCollapseEmptyTagsOnOneEmptyTagWithCollapseXhtml() { $emptyTag = ""; $expected = ""; $this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag, XML_UTIL_COLLAPSE_XHTML_ONLY)); } /** * @covers XML_Util::collapseEmptyTags() */ public function testCollapseEmptyTagsOnOneEmptyTagAlongsideNonemptyTagWithCollapseXhtml() { $emptyTag = ""; $otherTag = "baz"; $xhtmlTag = "

"; $expected = "
baz"; $this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag . $xhtmlTag . $otherTag, XML_UTIL_COLLAPSE_XHTML_ONLY)); } /** * @covers XML_Util::collapseEmptyTags() */ public function testCollapseEmptyTagsOnOneEmptyTagWithCollapseNone() { $emptyTag = ""; $expected = ""; $this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag, XML_UTIL_COLLAPSE_NONE)); } /** * @covers XML_Util::collapseEmptyTags() */ public function testCollapseEmptyTagsOnOneEmptyTagAlongsideNonemptyTagWithCollapseNone() { $emptyTag = ""; $otherTag = "baz"; $expected = "baz"; $this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag . $otherTag, XML_UTIL_COLLAPSE_NONE)); } } PK=1]ݑh.tests/pear/XML_Util/tests/IsValidNameTests.phpnu[assertTrue($result); } /** * @covers XML_Util::isValidName() */ public function testIsValidNameForTagNameWithInvalidCharacter() { $tagName = "invalidTag?"; $result = XML_Util::isValidName($tagName); $this->assertInstanceOf('PEAR_Error', $result); $expectedError = "XML names may only contain alphanumeric chars, period, hyphen, colon and underscores"; $this->assertEquals($expectedError, $result->getMessage()); } /** * @covers XML_Util::isValidName() */ public function testIsValidNameForTagNameWithInvalidStartingCharacter() { $tagName = "1234five"; $result = XML_Util::isValidName($tagName); $this->assertInstanceOf('PEAR_Error', $result); $expectedError = "XML names may only start with letter or underscore"; $this->assertEquals($expectedError, $result->getMessage()); } /** * @covers XML_Util::isValidName() */ public function testIsValidNameForInt() { $tagName = 1; $result = XML_Util::isValidName($tagName); $this->assertInstanceOf('PEAR_Error', $result); $expectedError = "XML names may only start with letter or underscore"; $this->assertEquals($expectedError, $result->getMessage()); } /** * @covers XML_Util::isValidName() */ public function testIsValidNameForEmptyString() { $tagName = ''; $result = XML_Util::isValidName($tagName); $this->assertInstanceOf('PEAR_Error', $result); $expectedError = "XML names may only start with letter or underscore"; $this->assertEquals($expectedError, $result->getMessage()); } } PK=1]ܬ$8tests/pear/XML_Util/tests/GetDocTypeDeclarationTests.phpnu["; $this->assertEquals($expected, XML_Util::getDocTypeDeclaration("rootTag")); } /** * @covers XML_Util::getDocTypeDeclaration() */ public function testGetDocTypeDeclarationUsingRootAndStringUri() { $expected = ""; $this->assertEquals($expected, XML_Util::getDocTypeDeclaration("rootTag", "myDocType.dtd")); } /** * @covers XML_Util::getDocTypeDeclaration() */ public function testGetDocTypeDeclarationUsingRootAndArrayUri() { $uri = array( 'uri' => 'http://pear.php.net/dtd/package-1.0', 'id' => '-//PHP//PEAR/DTD PACKAGE 0.1' ); $expected = ""; $this->assertEquals($expected, XML_Util::getDocTypeDeclaration("rootTag", $uri)); } /** * @covers XML_Util::getDocTypeDeclaration() */ public function testGetDocTypeDeclarationUsingRootAndArrayUriAndInternalDtd() { $uri = array( 'uri' => 'http://pear.php.net/dtd/package-1.0', 'id' => '-//PHP//PEAR/DTD PACKAGE 0.1' ); $dtdEntry = ''; $expected = <<< EOF ]> EOF; $this->assertEquals($expected, XML_Util::getDocTypeDeclaration("rootTag", $uri, $dtdEntry)); } } PK=1]Df:4tests/pear/XML_Util/tests/GetXmlDeclarationTests.phpnu["; $this->assertEquals($expected, XML_Util::getXMLDeclaration($version)); } /** * @covers XML_Util::getXMLDeclaration() */ public function testGetXMLDeclarationUsingVersionAndEncodingAndStandalone() { $version = "1.0"; $encoding = "UTF-8"; $standalone = true; $expected = ""; $this->assertEquals($expected, XML_Util::getXMLDeclaration($version, $encoding, $standalone)); } /** * @covers XML_Util::getXMLDeclaration() */ public function testGetXMLDeclarationUsingVersionAndStandalone() { $version = "1.0"; $encoding = null; $standalone = true; $expected = ""; $this->assertEquals($expected, XML_Util::getXMLDeclaration($version, $encoding, $standalone)); } } PK=1]>M*tests/pear/XML_Util/tests/Bug4950Tests.phpnu[ here!"; $namespaceUrl = null; $expected = " here!]]>"; $result = XML_Util::createTag($qname, $attributes, $content, $namespaceUrl, XML_UTIL_CDATA_SECTION); $this->assertEquals($expected, $result, "Failed bugcheck."); } } PK=1]Gs*tests/pear/XML_Util/tests/Bug5392Tests.phpnu[, & and " as well as ä, ö, ß, à and ê'; $replacedResult = XML_Util::replaceEntities($original, XML_UTIL_ENTITIES_HTML, "UTF-8"); $reversedResult = XML_Util::reverseEntities($replacedResult, XML_UTIL_ENTITIES_HTML, "UTF-8"); $this->assertEquals($original, $reversedResult, "Failed bugcheck."); } } PK=1]~$[ee3tests/pear/XML_Util/tests/CreateEndElementTests.phpnu["; $this->assertEquals($expected, XML_Util::createEndElement($original)); } /** * @covers XML_Util::createEndElement() */ public function testCreateEndElementWithNamespacedTag() { $original = "myNs:myTag"; $expected = ""; $this->assertEquals($expected, XML_Util::createEndElement($original)); } } PK=1]YR""5tests/pear/XML_Util/tests/CreateStartElementTests.phpnu["; $this->assertEquals($expected, XML_Util::createStartElement($original)); } /** * @covers XML_Util::createStartElement() */ public function testCreateStartElementForTagWithAttributes() { $originalTag = "myNs:myTag"; $originalAttributes = array("foo" => "bar"); $expected = ""; $this->assertEquals($expected, XML_Util::createStartElement($originalTag, $originalAttributes)); } /** * @covers XML_Util::createStartElement() */ public function testCreateStartElementForTagWithEmptyAttributes() { $originalTag = "myNs:myTag"; $originalAttributes = ""; $expected = ""; $this->assertEquals($expected, XML_Util::createStartElement($originalTag, $originalAttributes)); } /** * @covers XML_Util::createStartElement() */ public function testCreateStartElementForTagWithAttributesAndNamespace() { $originalTag = "myNs:myTag"; $originalAttributes = array("foo" => "bar"); $originalNamespace = "http://www.w3c.org/myNs#"; $expected = ""; $this->assertEquals($expected, XML_Util::createStartElement($originalTag, $originalAttributes, $originalNamespace)); } /** * @covers XML_Util::createStartElement() */ public function testCreateStartElementForTagWithEmptyAttributesAndNonUriNamespace() { $originalTag = "myTag"; $originalAttributes = ""; $originalNamespace = "foo"; $expected = ""; $this->assertEquals($expected, XML_Util::createStartElement($originalTag, $originalAttributes, $originalNamespace)); } /** * @covers XML_Util::createStartElement() */ public function testCreateStartElementForTagWithAttributesAndNamespaceWithMultiline() { $originalTag = "myNs:myTag"; $originalAttributes = array("foo" => "bar"); $originalNamespace = "http://www.w3c.org/myNs#"; $expected = <<< EOF EOF; $multiline = true; $this->assertEquals($expected, XML_Util::createStartElement($originalTag, $originalAttributes, $originalNamespace, $multiline)); } /** * @covers XML_Util::createStartElement() */ public function testCreateStartElementForTagWithAttributesAndNamespaceWithMultilineAndIndent() { $originalTag = "myNs:myTag"; $originalAttributes = array("foo" => "bar"); $originalNamespace = "http://www.w3c.org/myNs#"; $expected = <<< EOF EOF; $multiline = true; $indent = " "; $this->assertEquals($expected, XML_Util::createStartElement($originalTag, $originalAttributes, $originalNamespace, $multiline, $indent)); } /** * @covers XML_Util::createStartElement() */ public function testCreateStartElementForTagWithAttributesAndNamespaceWithMultilineAndIndentAndLinebreak() { $originalTag = "myNs:myTag"; $originalAttributes = array("foo" => "bar"); $originalNamespace = "http://www.w3c.org/myNs#"; $expected = ""; $multiline = true; $indent = " "; $linebreak = "^"; $this->assertEquals($expected, XML_Util::createStartElement($originalTag, $originalAttributes, $originalNamespace, $multiline, $indent, $linebreak)); } /** * @covers XML_Util::createStartElement() */ public function testCreateStartElementForTagWithAttributesAndNamespaceWithMultilineAndIndentAndLinebreakAndSortAttributesIsTrue() { $originalTag = "myNs:myTag"; $originalAttributes = array("foo" => "bar", "boo" => "baz"); $originalNamespace = "http://www.w3c.org/myNs#"; $expected = ""; $multiline = true; $indent = " "; $linebreak = "^"; $sortAttributes = true; $this->assertEquals($expected, XML_Util::createStartElement($originalTag, $originalAttributes, $originalNamespace, $multiline, $indent, $linebreak, $sortAttributes)); } /** * @covers XML_Util::createStartElement() */ public function testCreateStartElementForTagWithAttributesAndNamespaceWithMultilineAndIndentAndLinebreakAndSortAttributesIsFalse() { $originalTag = "myNs:myTag"; $originalAttributes = array("foo" => "bar", "boo" => "baz"); $originalNamespace = "http://www.w3c.org/myNs#"; $expected = ""; $multiline = true; $indent = " "; $linebreak = "^"; $sortAttributes = false; $this->assertEquals($expected, XML_Util::createStartElement($originalTag, $originalAttributes, $originalNamespace, $multiline, $indent, $linebreak, $sortAttributes)); } } PK=1]e5tests/pear/Structures_Graph/tests/AcyclicTestTest.phpnu[addNode($node1); $node2 = new Structures_Graph_Node(); $graph->addNode($node2); $node1->connectTo($node2); $node3 = new Structures_Graph_Node(); $graph->addNode($node3); $node2->connectTo($node3); $node3->connectTo($node1); $this->assertFalse( Structures_Graph_Manipulator_AcyclicTest::isAcyclic($graph), 'Graph is cyclic' ); } public function testIsAcyclicTrue() { $graph = new Structures_Graph(); $node1 = new Structures_Graph_Node(); $graph->addNode($node1); $node2 = new Structures_Graph_Node(); $graph->addNode($node2); $node1->connectTo($node2); $node3 = new Structures_Graph_Node(); $graph->addNode($node3); $node2->connectTo($node3); $this->assertTrue( Structures_Graph_Manipulator_AcyclicTest::isAcyclic($graph), 'Graph is acyclic' ); } } ?> PK=1] ;VV;tests/pear/Structures_Graph/tests/TopologicalSorterTest.phpnu[setData($name1); $graph->addNode($node1); $name11 = 'node11'; $node11 = new Structures_Graph_Node(); $node11->setData($name11); $graph->addNode($node11); $node1->connectTo($node11); $name12 = 'node12'; $node12 = new Structures_Graph_Node(); $node12->setData($name12); $graph->addNode($node12); $node1->connectTo($node12); $name121 = 'node121'; $node121 = new Structures_Graph_Node(); $node121->setData($name121); $graph->addNode($node121); $node12->connectTo($node121); $name2 = 'node2'; $node2 = new Structures_Graph_Node(); $node2->setData($name2); $graph->addNode($node2); $name21 = 'node21'; $node21 = new Structures_Graph_Node(); $node21->setData($name21); $graph->addNode($node21); $node2->connectTo($node21); $nodes = Structures_Graph_Manipulator_TopologicalSorter::sort($graph); $this->assertCount(2, $nodes[0]); $this->assertEquals('node1', $nodes[0][0]->getData()); $this->assertEquals('node2', $nodes[0][1]->getData()); $this->assertCount(3, $nodes[1]); $this->assertEquals('node11', $nodes[1][0]->getData()); $this->assertEquals('node12', $nodes[1][1]->getData()); $this->assertEquals('node21', $nodes[1][2]->getData()); $this->assertCount(1, $nodes[2]); $this->assertEquals('node121', $nodes[2][0]->getData()); } } ?> PK=1]'-*tt,tests/pear/Structures_Graph/tests/helper.incnu[ | // +-----------------------------------------------------------------------------+ // require_once dirname(__FILE__) . '/helper.inc'; /** * @access private */ class BasicGraph extends \PHPUnit\Framework\TestCase { var $_graph = null; function test_create_graph() { $this->_graph = new Structures_Graph(); $this->assertTrue(is_a($this->_graph, 'Structures_Graph')); } function test_add_node() { $this->_graph = new Structures_Graph(); $data = 1; $node = new Structures_Graph_Node($data); $this->_graph->addNode($node); $node = new Structures_Graph_Node($data); $this->_graph->addNode($node); $node = new Structures_Graph_Node($data); $this->_graph->addNode($node); $this->assertTrue(true); } function test_connect_node() { $this->_graph = new Structures_Graph(); $data = 1; $node1 = new Structures_Graph_Node($data); $node2 = new Structures_Graph_Node($data); $this->_graph->addNode($node1); $this->_graph->addNode($node2); $node1->connectTo($node2); $node =& $this->_graph->getNodes(); $node =& $node[0]; $node = $node->getNeighbours(); $node =& $node[0]; /* ZE1 == and === operators fail on $node,$node2 because of the recursion introduced by the _graph field in the Node object. So, we'll use the stupid method for reference testing */ $node = true; $this->assertTrue($node2); $node = false; $this->assertFalse($node2); } function test_data_references() { $this->_graph = new Structures_Graph(); $data = 1; $node = new Structures_Graph_Node(); $node->setData($data); $this->_graph->addNode($node); $data = 2; $dataInNode =& $this->_graph->getNodes(); $dataInNode =& $dataInNode[0]; $dataInNode =& $dataInNode->getData(); $this->assertEquals($data, $dataInNode); } function test_metadata_references() { $this->_graph = new Structures_Graph(); $data = 1; $node = new Structures_Graph_Node(); $node->setMetadata('5', $data); $data = 2; $dataInNode =& $node->getMetadata('5'); $this->assertEquals($data, $dataInNode); } function test_metadata_key_exists() { $this->_graph = new Structures_Graph(); $data = 1; $node = new Structures_Graph_Node(); $node->setMetadata('5', $data); $this->assertTrue($node->metadataKeyExists('5')); $this->assertFalse($node->metadataKeyExists('1')); } function test_directed_degree() { $this->_graph = new Structures_Graph(true); $node = array(); $node[] = new Structures_Graph_Node(); $node[] = new Structures_Graph_Node(); $node[] = new Structures_Graph_Node(); $this->_graph->addNode($node[0]); $this->_graph->addNode($node[1]); $this->_graph->addNode($node[2]); $this->assertEquals(0, $node[0]->inDegree(), 'inDegree test failed for node 0 with 0 arcs'); $this->assertEquals(0, $node[1]->inDegree(), 'inDegree test failed for node 1 with 0 arcs'); $this->assertEquals(0, $node[2]->inDegree(), 'inDegree test failed for node 2 with 0 arcs'); $this->assertEquals(0, $node[0]->outDegree(), 'outDegree test failed for node 0 with 0 arcs'); $this->assertEquals(0, $node[1]->outDegree(), 'outDegree test failed for node 1 with 0 arcs'); $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 0 arcs'); $node[0]->connectTo($node[1]); $this->assertEquals(0, $node[0]->inDegree(), 'inDegree test failed for node 0 with 1 arc'); $this->assertEquals(1, $node[1]->inDegree(), 'inDegree test failed for node 1 with 1 arc'); $this->assertEquals(0, $node[2]->inDegree(), 'inDegree test failed for node 2 with 1 arc'); $this->assertEquals(1, $node[0]->outDegree(), 'outDegree test failed for node 0 with 1 arc'); $this->assertEquals(0, $node[1]->outDegree(), 'outDegree test failed for node 1 with 1 arc'); $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 1 arc'); $node[0]->connectTo($node[2]); $this->assertEquals(0, $node[0]->inDegree(), 'inDegree test failed for node 0 with 2 arcs'); $this->assertEquals(1, $node[1]->inDegree(), 'inDegree test failed for node 1 with 2 arcs'); $this->assertEquals(1, $node[2]->inDegree(), 'inDegree test failed for node 2 with 2 arcs'); $this->assertEquals(2, $node[0]->outDegree(), 'outDegree test failed for node 0 with 2 arcs'); $this->assertEquals(0, $node[1]->outDegree(), 'outDegree test failed for node 1 with 2 arcs'); $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 2 arcs'); } function test_undirected_degree() { $this->_graph = new Structures_Graph(false); $node = array(); $node[] = new Structures_Graph_Node(); $node[] = new Structures_Graph_Node(); $node[] = new Structures_Graph_Node(); $this->_graph->addNode($node[0]); $this->_graph->addNode($node[1]); $this->_graph->addNode($node[2]); $this->assertEquals(0, $node[0]->inDegree(), 'inDegree test failed for node 0 with 0 arcs'); $this->assertEquals(0, $node[1]->inDegree(), 'inDegree test failed for node 1 with 0 arcs'); $this->assertEquals(0, $node[2]->inDegree(), 'inDegree test failed for node 2 with 0 arcs'); $this->assertEquals(0, $node[0]->outDegree(), 'outDegree test failed for node 0 with 0 arcs'); $this->assertEquals(0, $node[1]->outDegree(), 'outDegree test failed for node 1 with 0 arcs'); $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 0 arcs'); $node[0]->connectTo($node[1]); $this->assertEquals(1, $node[0]->inDegree(), 'inDegree test failed for node 0 with 1 arc'); $this->assertEquals(1, $node[1]->inDegree(), 'inDegree test failed for node 1 with 1 arc'); $this->assertEquals(0, $node[2]->inDegree(), 'inDegree test failed for node 2 with 1 arc'); $this->assertEquals(1, $node[0]->outDegree(), 'outDegree test failed for node 0 with 1 arc'); $this->assertEquals(1, $node[1]->outDegree(), 'outDegree test failed for node 1 with 1 arc'); $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 1 arc'); $node[0]->connectTo($node[2]); $this->assertEquals(2, $node[0]->inDegree(), 'inDegree test failed for node 0 with 2 arcs'); $this->assertEquals(1, $node[1]->inDegree(), 'inDegree test failed for node 1 with 2 arcs'); $this->assertEquals(1, $node[2]->inDegree(), 'inDegree test failed for node 2 with 2 arcs'); $this->assertEquals(2, $node[0]->outDegree(), 'outDegree test failed for node 0 with 2 arcs'); $this->assertEquals(1, $node[1]->outDegree(), 'outDegree test failed for node 1 with 2 arcs'); $this->assertEquals(1, $node[2]->outDegree(), 'outDegree test failed for node 2 with 2 arcs'); } } ?> PK=1]|EE-tests/pear/Console_Getopt/tests/bug13140.phptnu[--TEST-- Console_Getopt [bug 13140] --SKIPIF-- --FILE-- getopt2($cg->readPHPArgv(), 't', array('test'), true)); print_r($cg->getopt2($cg->readPHPArgv(), 'bar', array('foo'), true)); ?> --EXPECT-- Array ( [0] => Array ( [0] => Array ( [0] => --test [1] => ) ) [1] => Array ( [0] => thisshouldbehere ) ) Array ( [0] => Array ( [0] => Array ( [0] => --foo [1] => ) [1] => Array ( [0] => b [1] => ) [2] => Array ( [0] => a [1] => ) [3] => Array ( [0] => r [1] => ) [4] => Array ( [0] => r [1] => ) ) [1] => Array ( [0] => thisshouldbehere ) ) PK=1]-tests/pear/Console_Getopt/tests/bug10557.phptnu[--TEST-- Console_Getopt [bug 10557] --SKIPIF-- --FILE-- getMessage()."\n"; echo 'FATAL'; exit; } print_r($ret); ?> --EXPECT-- Console_Getopt: option requires an argument --to FATALPK=1]-tests/pear/Console_Getopt/tests/bug11068.phptnu[--TEST-- Console_Getopt [bug 11068] --SKIPIF-- --FILE-- getMessage()."\n"; echo 'FATAL'; exit; } print_r($ret); ?> --EXPECT-- Array ( [0] => Array ( [0] => Array ( [0] => f [1] => jjohnston@mail.com ) [1] => Array ( [0] => --to [1] => hi ) ) [1] => Array ( [0] => - ) )PK=1]/tests/pear/Console_Getopt/tests/001-getopt.phptnu[--TEST-- Console_Getopt --FILE-- $d) { if ($i++ > 0) { print ", "; } print $d[0] . '=' . $d[1]; } print "\n"; print "params: " . implode(", ", $non_opts) . "\n"; print "\n"; } test("-abc", "abc"); test("-abc foo", "abc"); test("-abc foo", "abc:"); test("-abc foo bar gazonk", "abc"); test("-abc foo bar gazonk", "abc:"); test("-a -b -c", "abc"); test("-a -b -c", "abc:"); test("-abc", "ab:c"); test("-abc foo -bar gazonk", "abc"); ?> --EXPECT-- options: a=, b=, c= params: options: a=, b=, c= params: foo options: a=, b=, c=foo params: options: a=, b=, c= params: foo, bar, gazonk options: a=, b=, c=foo params: bar, gazonk options: a=, b=, c= params: Console_Getopt: option requires an argument --c options: a=, b=c params: options: a=, b=, c= params: foo, -bar, gazonk PK=1],,pear/OS/Guess.phpnu[ * @author Gregory Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since PEAR 0.1 */ // {{{ uname examples // php_uname() without args returns the same as 'uname -a', or a PHP-custom // string for Windows. // PHP versions prior to 4.3 return the uname of the host where PHP was built, // as of 4.3 it returns the uname of the host running the PHP code. // // PC RedHat Linux 7.1: // Linux host.example.com 2.4.2-2 #1 Sun Apr 8 20:41:30 EDT 2001 i686 unknown // // PC Debian Potato: // Linux host 2.4.17 #2 SMP Tue Feb 12 15:10:04 CET 2002 i686 unknown // // PC FreeBSD 3.3: // FreeBSD host.example.com 3.3-STABLE FreeBSD 3.3-STABLE #0: Mon Feb 21 00:42:31 CET 2000 root@example.com:/usr/src/sys/compile/CONFIG i386 // // PC FreeBSD 4.3: // FreeBSD host.example.com 4.3-RELEASE FreeBSD 4.3-RELEASE #1: Mon Jun 25 11:19:43 EDT 2001 root@example.com:/usr/src/sys/compile/CONFIG i386 // // PC FreeBSD 4.5: // FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb 6 23:59:23 CET 2002 root@example.com:/usr/src/sys/compile/CONFIG i386 // // PC FreeBSD 4.5 w/uname from GNU shellutils: // FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb i386 unknown // // HP 9000/712 HP-UX 10: // HP-UX iq B.10.10 A 9000/712 2008429113 two-user license // // HP 9000/712 HP-UX 10 w/uname from GNU shellutils: // HP-UX host B.10.10 A 9000/712 unknown // // IBM RS6000/550 AIX 4.3: // AIX host 3 4 000003531C00 // // AIX 4.3 w/uname from GNU shellutils: // AIX host 3 4 000003531C00 unknown // // SGI Onyx IRIX 6.5 w/uname from GNU shellutils: // IRIX64 host 6.5 01091820 IP19 mips // // SGI Onyx IRIX 6.5: // IRIX64 host 6.5 01091820 IP19 // // SparcStation 20 Solaris 8 w/uname from GNU shellutils: // SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc // // SparcStation 20 Solaris 8: // SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc SUNW,SPARCstation-20 // // Mac OS X (Darwin) // Darwin home-eden.local 7.5.0 Darwin Kernel Version 7.5.0: Thu Aug 5 19:26:16 PDT 2004; root:xnu/xnu-517.7.21.obj~3/RELEASE_PPC Power Macintosh // // Mac OS X early versions // // }}} /* TODO: * - define endianness, to allow matchSignature("bigend") etc. */ /** * Retrieves information about the current operating system * * This class uses php_uname() to grok information about the current OS * * @category pear * @package PEAR * @author Stig Bakken * @author Gregory Beaver * @copyright 1997-2020 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ class OS_Guess { var $sysname; var $nodename; var $cpu; var $release; var $extra; function __construct($uname = null) { list($this->sysname, $this->release, $this->cpu, $this->extra, $this->nodename) = $this->parseSignature($uname); } function parseSignature($uname = null) { static $sysmap = array( 'HP-UX' => 'hpux', 'IRIX64' => 'irix', ); static $cpumap = array( 'i586' => 'i386', 'i686' => 'i386', 'ppc' => 'powerpc', ); if ($uname === null) { $uname = php_uname(); } $parts = preg_split('/\s+/', trim($uname)); $n = count($parts); $release = $machine = $cpu = ''; $sysname = $parts[0]; $nodename = $parts[1]; $cpu = $parts[$n-1]; $extra = ''; if ($cpu == 'unknown') { $cpu = $parts[$n - 2]; } switch ($sysname) { case 'AIX' : $release = "$parts[3].$parts[2]"; break; case 'Windows' : $release = $parts[1]; if ($release == '95/98') { $release = '9x'; } $cpu = 'i386'; break; case 'Linux' : $extra = $this->_detectGlibcVersion(); // use only the first two digits from the kernel version $release = preg_replace('/^([0-9]+\.[0-9]+).*/', '\1', $parts[2]); break; case 'Mac' : $sysname = 'darwin'; $nodename = $parts[2]; $release = $parts[3]; $cpu = $this->_determineIfPowerpc($cpu, $parts); break; case 'Darwin' : $cpu = $this->_determineIfPowerpc($cpu, $parts); $release = preg_replace('/^([0-9]+\.[0-9]+).*/', '\1', $parts[2]); break; default: $release = preg_replace('/-.*/', '', $parts[2]); break; } if (isset($sysmap[$sysname])) { $sysname = $sysmap[$sysname]; } else { $sysname = strtolower($sysname); } if (isset($cpumap[$cpu])) { $cpu = $cpumap[$cpu]; } return array($sysname, $release, $cpu, $extra, $nodename); } function _determineIfPowerpc($cpu, $parts) { $n = count($parts); if ($cpu == 'Macintosh' && $parts[$n - 2] == 'Power') { $cpu = 'powerpc'; } return $cpu; } function _detectGlibcVersion() { static $glibc = false; if ($glibc !== false) { return $glibc; // no need to run this multiple times } $major = $minor = 0; include_once "System.php"; // Let's try reading possible libc.so.6 symlinks $libcs = array( '/lib64/libc.so.6', '/lib/libc.so.6', '/lib/i386-linux-gnu/libc.so.6' ); $versions = array(); foreach ($libcs as $file) { $versions = $this->_readGlibCVersionFromSymlink($file); if ($versions != []) { list($major, $minor) = $versions; break; } } // Use glibc's header file to // get major and minor version number: if (!($major && $minor)) { $versions = $this->_readGlibCVersionFromFeaturesHeaderFile(); } if (is_array($versions) && $versions != []) { list($major, $minor) = $versions; } if (!($major && $minor)) { return $glibc = ''; } return $glibc = "glibc{$major}.{$minor}"; } function _readGlibCVersionFromSymlink($file) { $versions = array(); if (@is_link($file) && (preg_match('/^libc-(.*)\.so$/', basename(readlink($file)), $matches)) ) { $versions = explode('.', $matches[1]); } return $versions; } function _readGlibCVersionFromFeaturesHeaderFile() { $features_header_file = '/usr/include/features.h'; if (!(@file_exists($features_header_file) && @is_readable($features_header_file)) ) { return array(); } if (!@file_exists('/usr/bin/cpp') || !@is_executable('/usr/bin/cpp')) { return $this->_parseFeaturesHeaderFile($features_header_file); } // no cpp return $this->_fromGlibCTest(); } function _parseFeaturesHeaderFile($features_header_file) { $features_file = fopen($features_header_file, 'rb'); while (!feof($features_file)) { $line = fgets($features_file, 8192); if (!$this->_IsADefinition($line)) { continue; } if (strpos($line, '__GLIBC__')) { // major version number #define __GLIBC__ version $line = preg_split('/\s+/', $line); $glibc_major = trim($line[2]); if (isset($glibc_minor)) { break; } continue; } if (strpos($line, '__GLIBC_MINOR__')) { // got the minor version number // #define __GLIBC_MINOR__ version $line = preg_split('/\s+/', $line); $glibc_minor = trim($line[2]); if (isset($glibc_major)) { break; } } } fclose($features_file); if (!isset($glibc_major) || !isset($glibc_minor)) { return array(); } return array(trim($glibc_major), trim($glibc_minor)); } function _IsADefinition($line) { if ($line === false) { return false; } return strpos(trim($line), '#define') !== false; } function _fromGlibCTest() { $major = null; $minor = null; $tmpfile = System::mktemp("glibctest"); $fp = fopen($tmpfile, "w"); fwrite($fp, "#include \n__GLIBC__ __GLIBC_MINOR__\n"); fclose($fp); $cpp = popen("/usr/bin/cpp $tmpfile", "r"); while ($line = fgets($cpp, 1024)) { if ($line[0] == '#' || trim($line) == '') { continue; } if (list($major, $minor) = explode(' ', trim($line))) { break; } } pclose($cpp); unlink($tmpfile); if ($major !== null && $minor !== null) { return [$major, $minor]; } } function getSignature() { if (empty($this->extra)) { return "{$this->sysname}-{$this->release}-{$this->cpu}"; } return "{$this->sysname}-{$this->release}-{$this->cpu}-{$this->extra}"; } function getSysname() { return $this->sysname; } function getNodename() { return $this->nodename; } function getCpu() { return $this->cpu; } function getRelease() { return $this->release; } function getExtra() { return $this->extra; } function matchSignature($match) { $fragments = is_array($match) ? $match : explode('-', $match); $n = count($fragments); $matches = 0; if ($n > 0) { $matches += $this->_matchFragment($fragments[0], $this->sysname); } if ($n > 1) { $matches += $this->_matchFragment($fragments[1], $this->release); } if ($n > 2) { $matches += $this->_matchFragment($fragments[2], $this->cpu); } if ($n > 3) { $matches += $this->_matchFragment($fragments[3], $this->extra); } return ($matches == $n); } function _matchFragment($fragment, $value) { if (strcspn($fragment, '*?') < strlen($fragment)) { $expression = str_replace( array('*', '?', '/'), array('.*', '.', '\\/'), $fragment ); $reg = '/^' . $expression . '\\z/'; return preg_match($reg, $value); } return ($fragment == '*' || !strcasecmp($fragment, $value)); } } /* * Local Variables: * indent-tabs-mode: nil * c-basic-offset: 4 * End: */ PK=1]c}}pear/XML/Util.phpnu[ * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @category XML * @package XML_Util * @author Stephan Schmidt * @copyright 2003-2008 Stephan Schmidt * @license http://opensource.org/licenses/bsd-license New BSD License * @version CVS: $Id$ * @link http://pear.php.net/package/XML_Util */ /** * Error code for invalid chars in XML name */ define('XML_UTIL_ERROR_INVALID_CHARS', 51); /** * Error code for invalid chars in XML name */ define('XML_UTIL_ERROR_INVALID_START', 52); /** * Error code for non-scalar tag content */ define('XML_UTIL_ERROR_NON_SCALAR_CONTENT', 60); /** * Error code for missing tag name */ define('XML_UTIL_ERROR_NO_TAG_NAME', 61); /** * Replace XML entities */ define('XML_UTIL_REPLACE_ENTITIES', 1); /** * Embedd content in a CData Section */ define('XML_UTIL_CDATA_SECTION', 5); /** * Do not replace entitites */ define('XML_UTIL_ENTITIES_NONE', 0); /** * Replace all XML entitites * This setting will replace <, >, ", ' and & */ define('XML_UTIL_ENTITIES_XML', 1); /** * Replace only required XML entitites * This setting will replace <, " and & */ define('XML_UTIL_ENTITIES_XML_REQUIRED', 2); /** * Replace HTML entitites * @link http://www.php.net/htmlentities */ define('XML_UTIL_ENTITIES_HTML', 3); /** * Do not collapse any empty tags. */ define('XML_UTIL_COLLAPSE_NONE', 0); /** * Collapse all empty tags. */ define('XML_UTIL_COLLAPSE_ALL', 1); /** * Collapse only empty XHTML tags that have no end tag. */ define('XML_UTIL_COLLAPSE_XHTML_ONLY', 2); /** * Utility class for working with XML documents * * @category XML * @package XML_Util * @author Stephan Schmidt * @copyright 2003-2008 Stephan Schmidt * @license http://opensource.org/licenses/bsd-license New BSD License * @version Release: 1.4.5 * @link http://pear.php.net/package/XML_Util */ class XML_Util { /** * Return API version * * @return string $version API version */ public static function apiVersion() { return '1.4'; } /** * Replace XML entities * * With the optional second parameter, you may select, which * entities should be replaced. * * * require_once 'XML/Util.php'; * * // replace XML entites: * $string = XML_Util::replaceEntities('This string contains < & >.'); * * * With the optional third parameter, you may pass the character encoding * * require_once 'XML/Util.php'; * * // replace XML entites in UTF-8: * $string = XML_Util::replaceEntities( * 'This string contains < & > as well as ä, ö, ß, à and ê', * XML_UTIL_ENTITIES_HTML, * 'UTF-8' * ); * * * @param string $string string where XML special chars * should be replaced * @param int $replaceEntities setting for entities in attribute values * (one of XML_UTIL_ENTITIES_XML, * XML_UTIL_ENTITIES_XML_REQUIRED, * XML_UTIL_ENTITIES_HTML) * @param string $encoding encoding value (if any)... * must be a valid encoding as determined * by the htmlentities() function * * @return string string with replaced chars * @see reverseEntities() */ public static function replaceEntities( $string, $replaceEntities = XML_UTIL_ENTITIES_XML, $encoding = 'ISO-8859-1' ) { switch ($replaceEntities) { case XML_UTIL_ENTITIES_XML: return strtr( $string, array( '&' => '&', '>' => '>', '<' => '<', '"' => '"', '\'' => ''' ) ); break; case XML_UTIL_ENTITIES_XML_REQUIRED: return strtr( $string, array( '&' => '&', '<' => '<', '"' => '"' ) ); break; case XML_UTIL_ENTITIES_HTML: return htmlentities($string, ENT_COMPAT, $encoding); break; } return $string; } /** * Reverse XML entities * * With the optional second parameter, you may select, which * entities should be reversed. * * * require_once 'XML/Util.php'; * * // reverse XML entites: * $string = XML_Util::reverseEntities('This string contains < & >.'); * * * With the optional third parameter, you may pass the character encoding * * require_once 'XML/Util.php'; * * // reverse XML entites in UTF-8: * $string = XML_Util::reverseEntities( * 'This string contains < & > as well as' * . ' ä, ö, ß, à and ê', * XML_UTIL_ENTITIES_HTML, * 'UTF-8' * ); * * * @param string $string string where XML special chars * should be replaced * @param int $replaceEntities setting for entities in attribute values * (one of XML_UTIL_ENTITIES_XML, * XML_UTIL_ENTITIES_XML_REQUIRED, * XML_UTIL_ENTITIES_HTML) * @param string $encoding encoding value (if any)... * must be a valid encoding as determined * by the html_entity_decode() function * * @return string string with replaced chars * @see replaceEntities() */ public static function reverseEntities( $string, $replaceEntities = XML_UTIL_ENTITIES_XML, $encoding = 'ISO-8859-1' ) { switch ($replaceEntities) { case XML_UTIL_ENTITIES_XML: return strtr( $string, array( '&' => '&', '>' => '>', '<' => '<', '"' => '"', ''' => '\'' ) ); break; case XML_UTIL_ENTITIES_XML_REQUIRED: return strtr( $string, array( '&' => '&', '<' => '<', '"' => '"' ) ); break; case XML_UTIL_ENTITIES_HTML: return html_entity_decode($string, ENT_COMPAT, $encoding); break; } return $string; } /** * Build an xml declaration * * * require_once 'XML/Util.php'; * * // get an XML declaration: * $xmlDecl = XML_Util::getXMLDeclaration('1.0', 'UTF-8', true); * * * @param string $version xml version * @param string $encoding character encoding * @param bool $standalone document is standalone (or not) * * @return string xml declaration * @uses attributesToString() to serialize the attributes of the * XML declaration */ public static function getXMLDeclaration( $version = '1.0', $encoding = null, $standalone = null ) { $attributes = array( 'version' => $version, ); // add encoding if ($encoding !== null) { $attributes['encoding'] = $encoding; } // add standalone, if specified if ($standalone !== null) { $attributes['standalone'] = $standalone ? 'yes' : 'no'; } return sprintf( '', XML_Util::attributesToString($attributes, false) ); } /** * Build a document type declaration * * * require_once 'XML/Util.php'; * * // get a doctype declaration: * $xmlDecl = XML_Util::getDocTypeDeclaration('rootTag','myDocType.dtd'); * * * @param string $root name of the root tag * @param string $uri uri of the doctype definition * (or array with uri and public id) * @param string $internalDtd internal dtd entries * * @return string doctype declaration * @since 0.2 */ public static function getDocTypeDeclaration( $root, $uri = null, $internalDtd = null ) { if (is_array($uri)) { $ref = sprintf(' PUBLIC "%s" "%s"', $uri['id'], $uri['uri']); } elseif (!empty($uri)) { $ref = sprintf(' SYSTEM "%s"', $uri); } else { $ref = ''; } if (empty($internalDtd)) { return sprintf('', $root, $ref); } else { return sprintf("", $root, $ref, $internalDtd); } } /** * Create string representation of an attribute list * * * require_once 'XML/Util.php'; * * // build an attribute string * $att = array( * 'foo' => 'bar', * 'argh' => 'tomato' * ); * * $attList = XML_Util::attributesToString($att); * * * @param array $attributes attribute array * @param bool|array $sort sort attribute list alphabetically, * may also be an assoc array containing * the keys 'sort', 'multiline', 'indent', * 'linebreak' and 'entities' * @param bool $multiline use linebreaks, if more than * one attribute is given * @param string $indent string used for indentation of * multiline attributes * @param string $linebreak string used for linebreaks of * multiline attributes * @param int $entities setting for entities in attribute values * (one of XML_UTIL_ENTITIES_NONE, * XML_UTIL_ENTITIES_XML, * XML_UTIL_ENTITIES_XML_REQUIRED, * XML_UTIL_ENTITIES_HTML) * * @return string string representation of the attributes * @uses replaceEntities() to replace XML entities in attribute values * @todo allow sort also to be an options array */ public static function attributesToString( $attributes, $sort = true, $multiline = false, $indent = ' ', $linebreak = "\n", $entities = XML_UTIL_ENTITIES_XML ) { /* * second parameter may be an array */ if (is_array($sort)) { if (isset($sort['multiline'])) { $multiline = $sort['multiline']; } if (isset($sort['indent'])) { $indent = $sort['indent']; } if (isset($sort['linebreak'])) { $multiline = $sort['linebreak']; } if (isset($sort['entities'])) { $entities = $sort['entities']; } if (isset($sort['sort'])) { $sort = $sort['sort']; } else { $sort = true; } } $string = ''; if (is_array($attributes) && !empty($attributes)) { if ($sort) { ksort($attributes); } if (!$multiline || count($attributes) == 1) { foreach ($attributes as $key => $value) { if ($entities != XML_UTIL_ENTITIES_NONE) { if ($entities === XML_UTIL_CDATA_SECTION) { $entities = XML_UTIL_ENTITIES_XML; } $value = XML_Util::replaceEntities($value, $entities); } $string .= ' ' . $key . '="' . $value . '"'; } } else { $first = true; foreach ($attributes as $key => $value) { if ($entities != XML_UTIL_ENTITIES_NONE) { $value = XML_Util::replaceEntities($value, $entities); } if ($first) { $string .= ' ' . $key . '="' . $value . '"'; $first = false; } else { $string .= $linebreak . $indent . $key . '="' . $value . '"'; } } } } return $string; } /** * Collapses empty tags. * * @param string $xml XML * @param int $mode Whether to collapse all empty tags (XML_UTIL_COLLAPSE_ALL) * or only XHTML (XML_UTIL_COLLAPSE_XHTML_ONLY) ones. * * @return string XML */ public static function collapseEmptyTags($xml, $mode = XML_UTIL_COLLAPSE_ALL) { if (preg_match('~<([^>])+/>~s', $xml, $matches)) { // it's already an empty tag return $xml; } switch ($mode) { case XML_UTIL_COLLAPSE_ALL: $preg1 = '~<' . '(?:' . '(https?://[^:\s]+:\w+)' . // ]*)' . // attributes ($4) '>' . '<\/(\1|\2|\3)>' . // 1, 2, or 3 again ($5) '~s' ; $preg2 = '<' . '${1}${2}${3}' . // tag (only one should have been populated) '${4}' . // attributes ' />' ; return (preg_replace($preg1, $preg2, $xml)?:$xml); break; case XML_UTIL_COLLAPSE_XHTML_ONLY: return ( preg_replace( '/<(area|base(?:font)?|br|col|frame|hr|img|input|isindex|link|meta|' . 'param)([^>]*)><\/\\1>/s', '<\\1\\2 />', $xml ) ?: $xml ); break; case XML_UTIL_COLLAPSE_NONE: // fall thru default: return $xml; } } /** * Create a tag * * This method will call XML_Util::createTagFromArray(), which * is more flexible. * * * require_once 'XML/Util.php'; * * // create an XML tag: * $tag = XML_Util::createTag('myNs:myTag', * array('foo' => 'bar'), * 'This is inside the tag', * 'http://www.w3c.org/myNs#'); * * * @param string $qname qualified tagname (including namespace) * @param array $attributes array containg attributes * @param mixed $content the content * @param string $namespaceUri URI of the namespace * @param int $replaceEntities whether to replace XML special chars in * content, embedd it in a CData section * or none of both * @param bool $multiline whether to create a multiline tag where * each attribute gets written to a single line * @param string $indent string used to indent attributes * (_auto indents attributes so they start * at the same column) * @param string $linebreak string used for linebreaks * @param bool $sortAttributes Whether to sort the attributes or not * @param int $collapseTagMode How to handle a content-less, and thus collapseable, tag * * @return string XML tag * @see createTagFromArray() * @uses createTagFromArray() to create the tag */ public static function createTag( $qname, $attributes = array(), $content = null, $namespaceUri = null, $replaceEntities = XML_UTIL_REPLACE_ENTITIES, $multiline = false, $indent = '_auto', $linebreak = "\n", $sortAttributes = true, $collapseTagMode = XML_UTIL_COLLAPSE_ALL ) { $tag = array( 'qname' => $qname, 'attributes' => $attributes ); // add tag content if ($content !== null) { $tag['content'] = $content; } // add namespace Uri if ($namespaceUri !== null) { $tag['namespaceUri'] = $namespaceUri; } return XML_Util::createTagFromArray( $tag, $replaceEntities, $multiline, $indent, $linebreak, $sortAttributes, $collapseTagMode ); } /** * Create a tag from an array. * This method awaits an array in the following format *
     * array(
     *     // qualified name of the tag
     *     'qname' => $qname
     *
     *     // namespace prefix (optional, if qname is specified or no namespace)
     *     'namespace' => $namespace
     *
     *     // local part of the tagname (optional, if qname is specified)
     *     'localpart' => $localpart,
     *
     *     // array containing all attributes (optional)
     *     'attributes' => array(),
     *
     *     // tag content (optional)
     *     'content' => $content,
     *
     *     // namespaceUri for the given namespace (optional)
     *     'namespaceUri' => $namespaceUri
     * )
     * 
* * * require_once 'XML/Util.php'; * * $tag = array( * 'qname' => 'foo:bar', * 'namespaceUri' => 'http://foo.com', * 'attributes' => array('key' => 'value', 'argh' => 'fruit&vegetable'), * 'content' => 'I\'m inside the tag', * ); * // creating a tag with qualified name and namespaceUri * $string = XML_Util::createTagFromArray($tag); * * * @param array $tag tag definition * @param int $replaceEntities whether to replace XML special chars in * content, embedd it in a CData section * or none of both * @param bool $multiline whether to create a multiline tag where each * attribute gets written to a single line * @param string $indent string used to indent attributes * (_auto indents attributes so they start * at the same column) * @param string $linebreak string used for linebreaks * @param bool $sortAttributes Whether to sort the attributes or not * @param int $collapseTagMode How to handle a content-less, and thus collapseable, tag * * @return string XML tag * * @see createTag() * @uses attributesToString() to serialize the attributes of the tag * @uses splitQualifiedName() to get local part and namespace of a qualified name * @uses createCDataSection() * @uses collapseEmptyTags() * @uses raiseError() */ public static function createTagFromArray( $tag, $replaceEntities = XML_UTIL_REPLACE_ENTITIES, $multiline = false, $indent = '_auto', $linebreak = "\n", $sortAttributes = true, $collapseTagMode = XML_UTIL_COLLAPSE_ALL ) { if (isset($tag['content']) && !is_scalar($tag['content'])) { return XML_Util::raiseError( 'Supplied non-scalar value as tag content', XML_UTIL_ERROR_NON_SCALAR_CONTENT ); } if (!isset($tag['qname']) && !isset($tag['localPart'])) { return XML_Util::raiseError( 'You must either supply a qualified name ' . '(qname) or local tag name (localPart).', XML_UTIL_ERROR_NO_TAG_NAME ); } // if no attributes hav been set, use empty attributes if (!isset($tag['attributes']) || !is_array($tag['attributes'])) { $tag['attributes'] = array(); } if (isset($tag['namespaces'])) { foreach ($tag['namespaces'] as $ns => $uri) { $tag['attributes']['xmlns:' . $ns] = $uri; } } if (!isset($tag['qname'])) { // qualified name is not given // check for namespace if (isset($tag['namespace']) && !empty($tag['namespace'])) { $tag['qname'] = $tag['namespace'] . ':' . $tag['localPart']; } else { $tag['qname'] = $tag['localPart']; } } elseif (isset($tag['namespaceUri']) && !isset($tag['namespace'])) { // namespace URI is set, but no namespace $parts = XML_Util::splitQualifiedName($tag['qname']); $tag['localPart'] = $parts['localPart']; if (isset($parts['namespace'])) { $tag['namespace'] = $parts['namespace']; } } if (isset($tag['namespaceUri']) && !empty($tag['namespaceUri'])) { // is a namespace given if (isset($tag['namespace']) && !empty($tag['namespace'])) { $tag['attributes']['xmlns:' . $tag['namespace']] = $tag['namespaceUri']; } else { // define this Uri as the default namespace $tag['attributes']['xmlns'] = $tag['namespaceUri']; } } if (!array_key_exists('content', $tag)) { $tag['content'] = ''; } // check for multiline attributes if ($multiline === true) { if ($indent === '_auto') { $indent = str_repeat(' ', (strlen($tag['qname'])+2)); } } // create attribute list $attList = XML_Util::attributesToString( $tag['attributes'], $sortAttributes, $multiline, $indent, $linebreak ); switch ($replaceEntities) { case XML_UTIL_ENTITIES_NONE: break; case XML_UTIL_CDATA_SECTION: $tag['content'] = XML_Util::createCDataSection($tag['content']); break; default: $tag['content'] = XML_Util::replaceEntities( $tag['content'], $replaceEntities ); break; } $tag = sprintf( '<%s%s>%s', $tag['qname'], $attList, $tag['content'], $tag['qname'] ); return self::collapseEmptyTags($tag, $collapseTagMode); } /** * Create a start element * * * require_once 'XML/Util.php'; * * // create an XML start element: * $tag = XML_Util::createStartElement('myNs:myTag', * array('foo' => 'bar') ,'http://www.w3c.org/myNs#'); * * * @param string $qname qualified tagname (including namespace) * @param array $attributes array containg attributes * @param string $namespaceUri URI of the namespace * @param bool $multiline whether to create a multiline tag where each * attribute gets written to a single line * @param string $indent string used to indent attributes (_auto indents * attributes so they start at the same column) * @param string $linebreak string used for linebreaks * @param bool $sortAttributes Whether to sort the attributes or not * * @return string XML start element * @see createEndElement(), createTag() */ public static function createStartElement( $qname, $attributes = array(), $namespaceUri = null, $multiline = false, $indent = '_auto', $linebreak = "\n", $sortAttributes = true ) { // if no attributes hav been set, use empty attributes if (!isset($attributes) || !is_array($attributes)) { $attributes = array(); } if ($namespaceUri != null) { $parts = XML_Util::splitQualifiedName($qname); } // check for multiline attributes if ($multiline === true) { if ($indent === '_auto') { $indent = str_repeat(' ', (strlen($qname)+2)); } } if ($namespaceUri != null) { // is a namespace given if (isset($parts['namespace']) && !empty($parts['namespace'])) { $attributes['xmlns:' . $parts['namespace']] = $namespaceUri; } else { // define this Uri as the default namespace $attributes['xmlns'] = $namespaceUri; } } // create attribute list $attList = XML_Util::attributesToString( $attributes, $sortAttributes, $multiline, $indent, $linebreak ); $element = sprintf('<%s%s>', $qname, $attList); return $element; } /** * Create an end element * * * require_once 'XML/Util.php'; * * // create an XML start element: * $tag = XML_Util::createEndElement('myNs:myTag'); * * * @param string $qname qualified tagname (including namespace) * * @return string XML end element * @see createStartElement(), createTag() */ public static function createEndElement($qname) { $element = sprintf('', $qname); return $element; } /** * Create an XML comment * * * require_once 'XML/Util.php'; * * // create an XML start element: * $tag = XML_Util::createComment('I am a comment'); * * * @param string $content content of the comment * * @return string XML comment */ public static function createComment($content) { $comment = sprintf('', $content); return $comment; } /** * Create a CData section * * * require_once 'XML/Util.php'; * * // create a CData section * $tag = XML_Util::createCDataSection('I am content.'); * * * @param string $data data of the CData section * * @return string CData section with content */ public static function createCDataSection($data) { return sprintf( '', preg_replace('/\]\]>/', ']]]]>', strval($data)) ); } /** * Split qualified name and return namespace and local part * * * require_once 'XML/Util.php'; * * // split qualified tag * $parts = XML_Util::splitQualifiedName('xslt:stylesheet'); * * the returned array will contain two elements: *
     * array(
     *     'namespace' => 'xslt',
     *     'localPart' => 'stylesheet'
     * );
     * 
* * @param string $qname qualified tag name * @param string $defaultNs default namespace (optional) * * @return array array containing namespace and local part */ public static function splitQualifiedName($qname, $defaultNs = null) { if (strstr($qname, ':')) { $tmp = explode(':', $qname); return array( 'namespace' => $tmp[0], 'localPart' => $tmp[1] ); } return array( 'namespace' => $defaultNs, 'localPart' => $qname ); } /** * Check, whether string is valid XML name * *

XML names are used for tagname, attribute names and various * other, lesser known entities.

*

An XML name may only consist of alphanumeric characters, * dashes, undescores and periods, and has to start with a letter * or an underscore.

* * * require_once 'XML/Util.php'; * * // verify tag name * $result = XML_Util::isValidName('invalidTag?'); * if (is_a($result, 'PEAR_Error')) { * print 'Invalid XML name: ' . $result->getMessage(); * } * * * @param string $string string that should be checked * * @return mixed true, if string is a valid XML name, PEAR error otherwise * * @todo support for other charsets * @todo PEAR CS - unable to avoid 85-char limit on second preg_match */ public static function isValidName($string) { // check for invalid chars if (!is_string($string) || !strlen($string) || !preg_match('/^[[:alpha:]_]\\z/', $string[0])) { return XML_Util::raiseError( 'XML names may only start with letter or underscore', XML_UTIL_ERROR_INVALID_START ); } // check for invalid chars $match = preg_match( '/^([[:alpha:]_]([[:alnum:]\-\.]*)?:)?' . '[[:alpha:]_]([[:alnum:]\_\-\.]+)?\\z/', $string ); if (!$match) { return XML_Util::raiseError( 'XML names may only contain alphanumeric ' . 'chars, period, hyphen, colon and underscores', XML_UTIL_ERROR_INVALID_CHARS ); } // XML name is valid return true; } /** * Replacement for XML_Util::raiseError * * Avoids the necessity to always require * PEAR.php * * @param string $msg error message * @param int $code error code * * @return PEAR_Error * @todo PEAR CS - should this use include_once instead? */ public static function raiseError($msg, $code) { include_once 'PEAR.php'; return PEAR::raiseError($msg, $code); } } ?> PK=1]tOoPPpear/System.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ /** * base class */ require_once 'PEAR.php'; require_once 'Console/Getopt.php'; $GLOBALS['_System_temp_files'] = array(); /** * System offers cross platform compatible system functions * * Static functions for different operations. Should work under * Unix and Windows. The names and usage has been taken from its respectively * GNU commands. The functions will return (bool) false on error and will * trigger the error with the PHP trigger_error() function (you can silence * the error by prefixing a '@' sign after the function call, but this * is not recommended practice. Instead use an error handler with * {@link set_error_handler()}). * * Documentation on this class you can find in: * http://pear.php.net/manual/ * * Example usage: * if (!@System::rm('-r file1 dir1')) { * print "could not delete file1 or dir1"; * } * * In case you need to to pass file names with spaces, * pass the params as an array: * * System::rm(array('-r', $file1, $dir1)); * * @category pear * @package System * @author Tomas V.V. Cox * @copyright 1997-2006 The PHP Group * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 * @static */ class System { /** * returns the commandline arguments of a function * * @param string $argv the commandline * @param string $short_options the allowed option short-tags * @param string $long_options the allowed option long-tags * @return array the given options and there values */ public static function _parseArgs($argv, $short_options, $long_options = null) { if (!is_array($argv) && $argv !== null) { /* // Quote all items that are a short option $av = preg_split('/(\A| )--?[a-z0-9]+[ =]?((? $a) { if (empty($a)) { continue; } $argv[$k] = trim($a) ; } } return Console_Getopt::getopt2($argv, $short_options, $long_options); } /** * Output errors with PHP trigger_error(). You can silence the errors * with prefixing a "@" sign to the function call: @System::mkdir(..); * * @param mixed $error a PEAR error or a string with the error message * @return bool false */ protected static function raiseError($error) { if (PEAR::isError($error)) { $error = $error->getMessage(); } trigger_error($error, E_USER_WARNING); return false; } /** * Creates a nested array representing the structure of a directory * * System::_dirToStruct('dir1', 0) => * Array * ( * [dirs] => Array * ( * [0] => dir1 * ) * * [files] => Array * ( * [0] => dir1/file2 * [1] => dir1/file3 * ) * ) * @param string $sPath Name of the directory * @param integer $maxinst max. deep of the lookup * @param integer $aktinst starting deep of the lookup * @param bool $silent if true, do not emit errors. * @return array the structure of the dir */ protected static function _dirToStruct($sPath, $maxinst, $aktinst = 0, $silent = false) { $struct = array('dirs' => array(), 'files' => array()); if (($dir = @opendir($sPath)) === false) { if (!$silent) { System::raiseError("Could not open dir $sPath"); } return $struct; // XXX could not open error } $struct['dirs'][] = $sPath = realpath($sPath); // XXX don't add if '.' or '..' ? $list = array(); while (false !== ($file = readdir($dir))) { if ($file != '.' && $file != '..') { $list[] = $file; } } closedir($dir); natsort($list); if ($aktinst < $maxinst || $maxinst == 0) { foreach ($list as $val) { $path = $sPath . DIRECTORY_SEPARATOR . $val; if (is_dir($path) && !is_link($path)) { $tmp = System::_dirToStruct($path, $maxinst, $aktinst+1, $silent); $struct = array_merge_recursive($struct, $tmp); } else { $struct['files'][] = $path; } } } return $struct; } /** * Creates a nested array representing the structure of a directory and files * * @param array $files Array listing files and dirs * @return array * @static * @see System::_dirToStruct() */ protected static function _multipleToStruct($files) { $struct = array('dirs' => array(), 'files' => array()); settype($files, 'array'); foreach ($files as $file) { if (is_dir($file) && !is_link($file)) { $tmp = System::_dirToStruct($file, 0); $struct = array_merge_recursive($tmp, $struct); } else { if (!in_array($file, $struct['files'])) { $struct['files'][] = $file; } } } return $struct; } /** * The rm command for removing files. * Supports multiple files and dirs and also recursive deletes * * @param string $args the arguments for rm * @return mixed PEAR_Error or true for success * @static * @access public */ public static function rm($args) { $opts = System::_parseArgs($args, 'rf'); // "f" does nothing but I like it :-) if (PEAR::isError($opts)) { return System::raiseError($opts); } foreach ($opts[0] as $opt) { if ($opt[0] == 'r') { $do_recursive = true; } } $ret = true; if (isset($do_recursive)) { $struct = System::_multipleToStruct($opts[1]); foreach ($struct['files'] as $file) { if (!@unlink($file)) { $ret = false; } } rsort($struct['dirs']); foreach ($struct['dirs'] as $dir) { if (!@rmdir($dir)) { $ret = false; } } } else { foreach ($opts[1] as $file) { $delete = (is_dir($file)) ? 'rmdir' : 'unlink'; if (!@$delete($file)) { $ret = false; } } } return $ret; } /** * Make directories. * * The -p option will create parent directories * @param string $args the name of the director(y|ies) to create * @return bool True for success */ public static function mkDir($args) { $opts = System::_parseArgs($args, 'pm:'); if (PEAR::isError($opts)) { return System::raiseError($opts); } $mode = 0777; // default mode foreach ($opts[0] as $opt) { if ($opt[0] == 'p') { $create_parents = true; } elseif ($opt[0] == 'm') { // if the mode is clearly an octal number (starts with 0) // convert it to decimal if (strlen($opt[1]) && $opt[1][0] == '0') { $opt[1] = octdec($opt[1]); } else { // convert to int $opt[1] += 0; } $mode = $opt[1]; } } $ret = true; if (isset($create_parents)) { foreach ($opts[1] as $dir) { $dirstack = array(); while ((!file_exists($dir) || !is_dir($dir)) && $dir != DIRECTORY_SEPARATOR) { array_unshift($dirstack, $dir); $dir = dirname($dir); } while ($newdir = array_shift($dirstack)) { if (!is_writeable(dirname($newdir))) { $ret = false; break; } if (!mkdir($newdir, $mode)) { $ret = false; } } } } else { foreach($opts[1] as $dir) { if ((@file_exists($dir) || !is_dir($dir)) && !mkdir($dir, $mode)) { $ret = false; } } } return $ret; } /** * Concatenate files * * Usage: * 1) $var = System::cat('sample.txt test.txt'); * 2) System::cat('sample.txt test.txt > final.txt'); * 3) System::cat('sample.txt test.txt >> final.txt'); * * Note: as the class use fopen, urls should work also * * @param string $args the arguments * @return boolean true on success */ public static function &cat($args) { $ret = null; $files = array(); if (!is_array($args)) { $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY); } $count_args = count($args); for ($i = 0; $i < $count_args; $i++) { if ($args[$i] == '>') { $mode = 'wb'; $outputfile = $args[$i+1]; break; } elseif ($args[$i] == '>>') { $mode = 'ab+'; $outputfile = $args[$i+1]; break; } else { $files[] = $args[$i]; } } $outputfd = false; if (isset($mode)) { if (!$outputfd = fopen($outputfile, $mode)) { $err = System::raiseError("Could not open $outputfile"); return $err; } $ret = true; } foreach ($files as $file) { if (!$fd = fopen($file, 'r')) { System::raiseError("Could not open $file"); continue; } while ($cont = fread($fd, 2048)) { if (is_resource($outputfd)) { fwrite($outputfd, $cont); } else { $ret .= $cont; } } fclose($fd); } if (is_resource($outputfd)) { fclose($outputfd); } return $ret; } /** * Creates temporary files or directories. This function will remove * the created files when the scripts finish its execution. * * Usage: * 1) $tempfile = System::mktemp("prefix"); * 2) $tempdir = System::mktemp("-d prefix"); * 3) $tempfile = System::mktemp(); * 4) $tempfile = System::mktemp("-t /var/tmp prefix"); * * prefix -> The string that will be prepended to the temp name * (defaults to "tmp"). * -d -> A temporary dir will be created instead of a file. * -t -> The target dir where the temporary (file|dir) will be created. If * this param is missing by default the env vars TMP on Windows or * TMPDIR in Unix will be used. If these vars are also missing * c:\windows\temp or /tmp will be used. * * @param string $args The arguments * @return mixed the full path of the created (file|dir) or false * @see System::tmpdir() */ public static function mktemp($args = null) { static $first_time = true; $opts = System::_parseArgs($args, 't:d'); if (PEAR::isError($opts)) { return System::raiseError($opts); } foreach ($opts[0] as $opt) { if ($opt[0] == 'd') { $tmp_is_dir = true; } elseif ($opt[0] == 't') { $tmpdir = $opt[1]; } } $prefix = (isset($opts[1][0])) ? $opts[1][0] : 'tmp'; if (!isset($tmpdir)) { $tmpdir = System::tmpdir(); } if (!System::mkDir(array('-p', $tmpdir))) { return false; } $tmp = tempnam($tmpdir, $prefix); if (isset($tmp_is_dir)) { unlink($tmp); // be careful possible race condition here if (!mkdir($tmp, 0700)) { return System::raiseError("Unable to create temporary directory $tmpdir"); } } $GLOBALS['_System_temp_files'][] = $tmp; if (isset($tmp_is_dir)) { //$GLOBALS['_System_temp_files'][] = dirname($tmp); } if ($first_time) { PEAR::registerShutdownFunc(array('System', '_removeTmpFiles')); $first_time = false; } return $tmp; } /** * Remove temporary files created my mkTemp. This function is executed * at script shutdown time */ public static function _removeTmpFiles() { if (count($GLOBALS['_System_temp_files'])) { $delete = $GLOBALS['_System_temp_files']; array_unshift($delete, '-r'); System::rm($delete); $GLOBALS['_System_temp_files'] = array(); } } /** * Get the path of the temporal directory set in the system * by looking in its environments variables. * Note: php.ini-recommended removes the "E" from the variables_order setting, * making unavaible the $_ENV array, that s why we do tests with _ENV * * @return string The temporary directory on the system */ public static function tmpdir() { if (OS_WINDOWS) { if ($var = isset($_ENV['TMP']) ? $_ENV['TMP'] : getenv('TMP')) { return $var; } if ($var = isset($_ENV['TEMP']) ? $_ENV['TEMP'] : getenv('TEMP')) { return $var; } if ($var = isset($_ENV['USERPROFILE']) ? $_ENV['USERPROFILE'] : getenv('USERPROFILE')) { return $var; } if ($var = isset($_ENV['windir']) ? $_ENV['windir'] : getenv('windir')) { return $var; } return getenv('SystemRoot') . '\temp'; } if ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR')) { return $var; } return realpath(function_exists('sys_get_temp_dir') ? sys_get_temp_dir() : '/tmp'); } /** * The "which" command (show the full path of a command) * * @param string $program The command to search for * @param mixed $fallback Value to return if $program is not found * * @return mixed A string with the full path or false if not found * @author Stig Bakken */ public static function which($program, $fallback = false) { // enforce API if (!is_string($program) || '' == $program) { return $fallback; } // full path given if (basename($program) != $program) { $path_elements[] = dirname($program); $program = basename($program); } else { $path = getenv('PATH'); if (!$path) { $path = getenv('Path'); // some OSes are just stupid enough to do this } $path_elements = explode(PATH_SEPARATOR, $path); } if (OS_WINDOWS) { $exe_suffixes = getenv('PATHEXT') ? explode(PATH_SEPARATOR, getenv('PATHEXT')) : array('.exe','.bat','.cmd','.com'); // allow passing a command.exe param if (strpos($program, '.') !== false) { array_unshift($exe_suffixes, ''); } } else { $exe_suffixes = array(''); } foreach ($exe_suffixes as $suff) { foreach ($path_elements as $dir) { $file = $dir . DIRECTORY_SEPARATOR . $program . $suff; // It's possible to run a .bat on Windows that is_executable // would return false for. The is_executable check is meaningless... if (OS_WINDOWS) { if (file_exists($file)) { return $file; } } else { if (is_executable($file)) { return $file; } } } } return $fallback; } /** * The "find" command * * Usage: * * System::find($dir); * System::find("$dir -type d"); * System::find("$dir -type f"); * System::find("$dir -name *.php"); * System::find("$dir -name *.php -name *.htm*"); * System::find("$dir -maxdepth 1"); * * Params implemented: * $dir -> Start the search at this directory * -type d -> return only directories * -type f -> return only files * -maxdepth -> max depth of recursion * -name -> search pattern (bash style). Multiple -name param allowed * * @param mixed Either array or string with the command line * @return array Array of found files */ public static function find($args) { if (!is_array($args)) { $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY); } $dir = realpath(array_shift($args)); if (!$dir) { return array(); } $patterns = array(); $depth = 0; $do_files = $do_dirs = true; $args_count = count($args); for ($i = 0; $i < $args_count; $i++) { switch ($args[$i]) { case '-type': if (in_array($args[$i+1], array('d', 'f'))) { if ($args[$i+1] == 'd') { $do_files = false; } else { $do_dirs = false; } } $i++; break; case '-name': $name = preg_quote($args[$i+1], '#'); // our magic characters ? and * have just been escaped, // so now we change the escaped versions to PCRE operators $name = strtr($name, array('\?' => '.', '\*' => '.*')); $patterns[] = '('.$name.')'; $i++; break; case '-maxdepth': $depth = $args[$i+1]; break; } } $path = System::_dirToStruct($dir, $depth, 0, true); if ($do_files && $do_dirs) { $files = array_merge($path['files'], $path['dirs']); } elseif ($do_dirs) { $files = $path['dirs']; } else { $files = $path['files']; } if (count($patterns)) { $dsq = preg_quote(DIRECTORY_SEPARATOR, '#'); $pattern = '#(^|'.$dsq.')'.implode('|', $patterns).'($|'.$dsq.')#'; $ret = array(); $files_count = count($files); for ($i = 0; $i < $files_count; $i++) { // only search in the part of the file below the current directory $filepart = basename($files[$i]); if (preg_match($pattern, $filepart)) { $ret[] = $files[$i]; } } return $ret; } return $files; } } PK=1]v::pear/pearcmd.phpnu[ * @author Tomas V.V.Cox * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR */ @ob_end_clean(); if (!defined('PEAR_RUNTYPE')) { // this is defined in peclcmd.php as 'pecl' define('PEAR_RUNTYPE', 'pear'); } define('PEAR_IGNORE_BACKTRACE', 1); /** * @nodep Gtk */ //the space is needed for windows include paths with trailing backslash // http://pear.php.net/bugs/bug.php?id=19482 if ('/opt/cpanel/ea-php83/root/usr/share/pear ' != '@'.'include_path'.'@ ') { ini_set('include_path', trim('/opt/cpanel/ea-php83/root/usr/share/pear '). PATH_SEPARATOR . get_include_path()); $raw = false; } else { // this is a raw, uninstalled pear, either a cvs checkout, or php distro ini_set('include_path', dirname(__DIR__) . PATH_SEPARATOR . get_include_path()); $raw = true; } @ini_set('allow_url_fopen', true); @set_time_limit(0); ob_implicit_flush(true); @ini_set('track_errors', true); @ini_set('html_errors', false); $_PEAR_PHPDIR = '#$%^&*'; set_error_handler('error_handler'); $pear_package_version = "1.10.18"; require_once 'PEAR.php'; require_once 'PEAR/Frontend.php'; require_once 'PEAR/Config.php'; require_once 'PEAR/Command.php'; require_once 'Console/Getopt.php'; PEAR_Command::setFrontendType('CLI'); $all_commands = PEAR_Command::getCommands(); $argv = Console_Getopt::readPHPArgv(); // fix CGI sapi oddity - the -- in pear.bat/pear is not removed if (php_sapi_name() != 'cli' && isset($argv[1]) && $argv[1] == '--') { unset($argv[1]); $argv = array_values($argv); } $progname = PEAR_RUNTYPE; array_shift($argv); $options = Console_Getopt::getopt2($argv, "c:C:d:D:Gh?sSqu:vV"); if (PEAR::isError($options)) { usage($options); } $opts = $options[0]; $fetype = 'CLI'; if ($progname == 'gpear' || $progname == 'pear-gtk') { $fetype = 'Gtk2'; } else { foreach ($opts as $opt) { if ($opt[0] == 'G') { $fetype = 'Gtk2'; } } } $pear_user_config = ''; $pear_system_config = ''; $store_user_config = false; $store_system_config = false; $verbose = 1; foreach ($opts as $opt) { switch ($opt[0]) { case 'c': $pear_user_config = $opt[1]; break; case 'C': $pear_system_config = $opt[1]; break; } } PEAR_Command::setFrontendType($fetype); $ui = &PEAR_Command::getFrontendObject(); $config = &PEAR_Config::singleton($pear_user_config, $pear_system_config); if (PEAR::isError($config)) { $_file = ''; if ($pear_user_config !== false) { $_file .= $pear_user_config; } if ($pear_system_config !== false) { $_file .= '/' . $pear_system_config; } if ($_file == '/') { $_file = 'The default config file'; } $config->getMessage(); $ui->outputData("ERROR: $_file is not a valid config file or is corrupted."); // We stop, we have no idea where we are :) exit(1); } // this is used in the error handler to retrieve a relative path $_PEAR_PHPDIR = $config->get('php_dir'); $ui->setConfig($config); PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, array($ui, "displayFatalError")); $verbose = $config->get("verbose"); $cmdopts = array(); if ($raw) { if (!$config->isDefinedLayer('user') && !$config->isDefinedLayer('system')) { $found = false; foreach ($opts as $opt) { if ($opt[0] == 'd' || $opt[0] == 'D') { // the user knows what they are doing, and are setting config values $found = true; } } if (!$found) { // no prior runs, try to install PEAR $parent = dirname(__FILE__); if (strpos($parent, 'scripts')) { $grandparent = dirname($parent); $packagexml = $grandparent . DIRECTORY_SEPARATOR . 'package2.xml'; $pearbase = $grandparent; } else { $packagexml = $parent . DIRECTORY_SEPARATOR . 'package2.xml'; $pearbase = $parent; } if (file_exists($packagexml)) { $options[1] = array( 'install', $packagexml ); $config->set('php_dir', $pearbase . DIRECTORY_SEPARATOR . 'php'); $config->set('data_dir', $pearbase . DIRECTORY_SEPARATOR . 'data'); $config->set('doc_dir', $pearbase . DIRECTORY_SEPARATOR . 'docs'); $config->set('test_dir', $pearbase . DIRECTORY_SEPARATOR . 'tests'); $config->set( 'ext_dir', $pearbase . DIRECTORY_SEPARATOR . 'extensions' ); $config->set('bin_dir', $pearbase); $config->mergeConfigFile($pearbase . 'pear.ini', false); $config->store(); $config->set('auto_discover', 1); } } } } foreach ($opts as $opt) { $param = !empty($opt[1]) ? $opt[1] : true; switch ($opt[0]) { case 'd': if ($param === true) { die( 'Invalid usage of "-d" option, expected -d config_value=value, ' . 'received "-d"' . "\n" ); } $possible = explode('=', $param); if (count($possible) != 2) { die( 'Invalid usage of "-d" option, expected ' . '-d config_value=value, received "' . $param . '"' . "\n" ); } list($key, $value) = explode('=', $param); $config->set($key, $value, 'user'); break; case 'D': if ($param === true) { die( 'Invalid usage of "-d" option, expected ' . '-d config_value=value, received "-d"' . "\n" ); } $possible = explode('=', $param); if (count($possible) != 2) { die( 'Invalid usage of "-d" option, expected ' . '-d config_value=value, received "' . $param . '"' . "\n" ); } list($key, $value) = explode('=', $param); $config->set($key, $value, 'system'); break; case 's': $store_user_config = true; break; case 'S': $store_system_config = true; break; case 'u': $config->remove($param, 'user'); break; case 'v': $config->set('verbose', $config->get('verbose') + 1); break; case 'q': $config->set('verbose', $config->get('verbose') - 1); break; case 'V': usage(null, 'version'); case 'c': case 'C': break; default: // all non pear params goes to the command $cmdopts[$opt[0]] = $param; break; } } if ($store_system_config) { $config->store('system'); } if ($store_user_config) { $config->store('user'); } $command = (isset($options[1][0])) ? $options[1][0] : null; if (empty($command) && ($store_user_config || $store_system_config)) { exit; } if ($fetype == 'Gtk2') { if (!$config->validConfiguration()) { PEAR::raiseError( "CRITICAL ERROR: no existing valid configuration files found in " . "files '$pear_user_config' or '$pear_system_config', " . "please copy an existing configuration file to one of these " . "locations, or use the -c and -s options to create one" ); } Gtk::main(); } else { do { if ($command == 'help') { usage(null, isset($options[1][1]) ? $options[1][1] : null); } if (!$config->validConfiguration()) { PEAR::raiseError( "CRITICAL ERROR: no existing valid configuration files found " . "in files '$pear_user_config' or '$pear_system_config', " . "please copy an existing configuration file to one of " . "these locations, or use the -c and -s options to create one" ); } PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $cmd = PEAR_Command::factory($command, $config); PEAR::popErrorHandling(); if (PEAR::isError($cmd)) { usage(null, isset($options[1][0]) ? $options[1][0] : null); } $short_args = $long_args = null; PEAR_Command::getGetoptArgs($command, $short_args, $long_args); array_shift($options[1]); $tmp = Console_Getopt::getopt2($options[1], $short_args, $long_args); if (PEAR::isError($tmp)) { break; } list($tmpopt, $params) = $tmp; $opts = array(); foreach ($tmpopt as $foo => $tmp2) { list($opt, $value) = $tmp2; if ($value === null) { $value = true; // options without args } if (strlen($opt) == 1) { $cmdoptions = $cmd->getOptions($command); foreach ($cmdoptions as $o => $d) { if (isset($d['shortopt']) && $d['shortopt'] == $opt) { $opts[$o] = $value; } } } else { if (substr($opt, 0, 2) == '--') { $opts[substr($opt, 2)] = $value; } } } $ok = $cmd->run($command, $opts, $params); if ($ok === false) { PEAR::raiseError("unknown command `$command'"); } if (PEAR::isError($ok)) { PEAR::setErrorHandling( PEAR_ERROR_CALLBACK, array($ui, "displayFatalError") ); PEAR::raiseError($ok); } } while (false); } // {{{ usage() /** * Display usage information * * @param mixed $error Optional error message * @param mixed $helpsubject Optional subject/command to display help for * * @return void */ function usage($error = null, $helpsubject = null) { global $progname, $all_commands; $stdout = fopen('php://stdout', 'w'); if (PEAR::isError($error)) { fputs($stdout, $error->getMessage() . "\n"); } elseif ($error !== null) { fputs($stdout, "$error\n"); } if ($helpsubject != null) { $put = cmdHelp($helpsubject); } else { $put = "Commands:\n"; $maxlen = max(array_map("strlen", $all_commands)); $formatstr = "%-{$maxlen}s %s\n"; ksort($all_commands); foreach ($all_commands as $cmd => $class) { $put .= sprintf($formatstr, $cmd, PEAR_Command::getDescription($cmd)); } $put .= "Usage: $progname [options] command [command-options] \n". "Type \"$progname help options\" to list all options.\n". "Type \"$progname help shortcuts\" to list all command shortcuts.\n". "Type \"$progname help version\" or ". "\"$progname version\" to list version information.\n". "Type \"$progname help \" to get the help ". "for the specified command."; } fputs($stdout, "$put\n"); fclose($stdout); if ($error === null) { exit(0); } exit(1); } /** * Return help string for specified command * * @param string $command Command to return help for * * @return void */ function cmdHelp($command) { global $progname, $all_commands, $config; if ($command == "options") { return "Options:\n". " -v increase verbosity level (default 1)\n". " -q be quiet, decrease verbosity level\n". " -c file find user configuration in `file'\n". " -C file find system configuration in `file'\n". " -d foo=bar set user config variable `foo' to `bar'\n". " -D foo=bar set system config variable `foo' to `bar'\n". " -G start in graphical (Gtk) mode\n". " -s store user configuration\n". " -S store system configuration\n". " -u foo unset `foo' in the user configuration\n". " -h, -? display help/usage (this message)\n". " -V version information\n"; } elseif ($command == "shortcuts") { $sc = PEAR_Command::getShortcuts(); $ret = "Shortcuts:\n"; foreach ($sc as $s => $c) { $ret .= sprintf(" %-8s %s\n", $s, $c); } return $ret; } elseif ($command == "version") { return "PEAR Version: ".$GLOBALS['pear_package_version']. "\nPHP Version: ".phpversion(). "\nZend Engine Version: ".zend_version(). "\nRunning on: ".php_uname(); } elseif ($help = PEAR_Command::getHelp($command)) { if (is_string($help)) { return "$progname $command [options] $help\n"; } if ($help[1] === null) { return "$progname $command $help[0]"; } return "$progname $command [options] $help[0]\n$help[1]"; } return "Command '$command' is not valid, try '$progname help'"; } // }}} /** * error_handler * * @param mixed $errno Error number * @param mixed $errmsg Message * @param mixed $file Filename * @param mixed $line Line number * * @access public * @return boolean */ function error_handler($errno, $errmsg, $file, $line) { if ((!defined('PHP_VERSION_ID') || PHP_VERSION_ID < 80400) && ($errno & E_STRICT)) { return; // E_STRICT } if ($errno & E_DEPRECATED) { return; // E_DEPRECATED } if (!(error_reporting() & $errno) && isset($GLOBALS['config']) && $GLOBALS['config']->get('verbose') < 4 ) { return false; // @silenced error, show all if debug is high enough } $errortype = array ( E_DEPRECATED => 'Deprecated Warning', E_ERROR => "Error", E_WARNING => "Warning", E_PARSE => "Parsing Error", E_NOTICE => "Notice", E_CORE_ERROR => "Core Error", E_CORE_WARNING => "Core Warning", E_COMPILE_ERROR => "Compile Error", E_COMPILE_WARNING => "Compile Warning", E_USER_ERROR => "User Error", E_USER_WARNING => "User Warning", E_USER_NOTICE => "User Notice" ); if (!defined('PHP_VERSION_ID') || PHP_VERSION_ID < 80400) { $errortype[E_STRICT] = 'Strict Warning'; } $prefix = $errortype[$errno]; global $_PEAR_PHPDIR; if (stristr($file, $_PEAR_PHPDIR)) { $file = substr($file, strlen($_PEAR_PHPDIR) + 1); } else { $file = basename($file); } print "\n$prefix: $errmsg in $file on line $line\n"; return false; } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * indent-tabs-mode: nil * mode: php * End: */ // vim600:syn=php PK=1]91pear/Structures/Graph/Manipulator/AcyclicTest.phpnu[ | // +-----------------------------------------------------------------------------+ // /** * This file contains the definition of the Structures_Graph_Manipulator_AcyclicTest graph manipulator. * * @see Structures_Graph_Manipulator_AcyclicTest * @package Structures_Graph */ /* dependencies {{{ */ /** */ require_once 'PEAR.php'; /** */ require_once 'Structures/Graph.php'; /** */ require_once 'Structures/Graph/Node.php'; /* }}} */ /* class Structures_Graph_Manipulator_AcyclicTest {{{ */ /** * The Structures_Graph_Manipulator_AcyclicTest is a graph manipulator * which tests whether a graph contains a cycle. * * The definition of an acyclic graph used in this manipulator is that of a * DAG. The graph must be directed, or else it is considered cyclic, even when * there are no arcs. * * @author Srgio Carvalho * @copyright (c) 2004 by Srgio Carvalho * @package Structures_Graph */ class Structures_Graph_Manipulator_AcyclicTest { /* _nonVisitedInDegree {{{ */ /** * * This is a variant of Structures_Graph::inDegree which does * not count nodes marked as visited. * * @return integer Number of non-visited nodes that link to this one */ protected static function _nonVisitedInDegree(&$node) { $result = 0; $graphNodes =& $node->_graph->getNodes(); foreach (array_keys($graphNodes) as $key) { if ((!$graphNodes[$key]->getMetadata('acyclic-test-visited')) && $graphNodes[$key]->connectsTo($node)) $result++; } return $result; } /* }}} */ /* _isAcyclic {{{ */ /** * Check if the graph is acyclic */ protected static function _isAcyclic(&$graph) { // Mark every node as not visited $nodes =& $graph->getNodes(); $nodeKeys = array_keys($nodes); $refGenerator = array(); foreach($nodeKeys as $key) { $refGenerator[] = false; $nodes[$key]->setMetadata('acyclic-test-visited', $refGenerator[sizeof($refGenerator) - 1]); } // Iteratively peel off leaf nodes do { // Find out which nodes are leafs (excluding visited nodes) $leafNodes = array(); foreach($nodeKeys as $key) { if ((!$nodes[$key]->getMetadata('acyclic-test-visited')) && Structures_Graph_Manipulator_AcyclicTest::_nonVisitedInDegree($nodes[$key]) == 0) { $leafNodes[] =& $nodes[$key]; } } // Mark leafs as visited for ($i=sizeof($leafNodes) - 1; $i>=0; $i--) { $visited =& $leafNodes[$i]->getMetadata('acyclic-test-visited'); $visited = true; $leafNodes[$i]->setMetadata('acyclic-test-visited', $visited); } } while (sizeof($leafNodes) > 0); // If graph is a DAG, there should be no non-visited nodes. Let's try to prove otherwise $result = true; foreach($nodeKeys as $key) if (!$nodes[$key]->getMetadata('acyclic-test-visited')) $result = false; // Cleanup visited marks foreach($nodeKeys as $key) $nodes[$key]->unsetMetadata('acyclic-test-visited'); return $result; } /* }}} */ /* isAcyclic {{{ */ /** * * isAcyclic returns true if a graph contains no cycles, false otherwise. * * @return boolean true iff graph is acyclic */ public static function isAcyclic(&$graph) { // We only test graphs if (!is_a($graph, 'Structures_Graph')) return Pear::raiseError('Structures_Graph_Manipulator_AcyclicTest::isAcyclic received an object that is not a Structures_Graph', STRUCTURES_GRAPH_ERROR_GENERIC); if (!$graph->isDirected()) return false; // Only directed graphs may be acyclic return Structures_Graph_Manipulator_AcyclicTest::_isAcyclic($graph); } /* }}} */ } /* }}} */ ?> PK=1]~(htt7pear/Structures/Graph/Manipulator/TopologicalSorter.phpnu[ | // +-----------------------------------------------------------------------------+ // /** * This file contains the definition of the Structures_Graph_Manipulator_TopologicalSorter class. * * @package Structures_Graph */ require_once 'PEAR.php'; require_once 'Structures/Graph.php'; require_once 'Structures/Graph/Node.php'; require_once 'Structures/Graph/Manipulator/AcyclicTest.php'; /** * The Structures_Graph_Manipulator_TopologicalSorter is a manipulator * which is able to return the set of nodes in a graph, sorted by topological * order. * * A graph may only be sorted topologically iff it's a DAG. You can test it * with the Structures_Graph_Manipulator_AcyclicTest. * * @author Srgio Carvalho * @copyright (c) 2004 by Srgio Carvalho * @see Structures_Graph_Manipulator_AcyclicTest * @package Structures_Graph */ class Structures_Graph_Manipulator_TopologicalSorter { /** * This is a variant of Structures_Graph::inDegree which does * not count nodes marked as visited. * * @param object $node Node to check * * @return integer Number of non-visited nodes that link to this one */ protected static function _nonVisitedInDegree(&$node) { $result = 0; $graphNodes =& $node->_graph->getNodes(); foreach (array_keys($graphNodes) as $key) { if ((!$graphNodes[$key]->getMetadata('topological-sort-visited')) && $graphNodes[$key]->connectsTo($node) ) { $result++; } } return $result; } /** * Sort implementation * * @param object $graph Graph to sort * * @return void */ protected static function _sort(&$graph) { // Mark every node as not visited $nodes =& $graph->getNodes(); $nodeKeys = array_keys($nodes); $refGenerator = array(); foreach ($nodeKeys as $key) { $refGenerator[] = false; $nodes[$key]->setMetadata( 'topological-sort-visited', $refGenerator[sizeof($refGenerator) - 1] ); } // Iteratively peel off leaf nodes $topologicalLevel = 0; do { // Find out which nodes are leafs (excluding visited nodes) $leafNodes = array(); foreach ($nodeKeys as $key) { if ((!$nodes[$key]->getMetadata('topological-sort-visited')) && static::_nonVisitedInDegree($nodes[$key]) == 0 ) { $leafNodes[] =& $nodes[$key]; } } // Mark leafs as visited $refGenerator[] = $topologicalLevel; for ($i = sizeof($leafNodes) - 1; $i>=0; $i--) { $visited =& $leafNodes[$i]->getMetadata('topological-sort-visited'); $visited = true; $leafNodes[$i]->setMetadata('topological-sort-visited', $visited); $leafNodes[$i]->setMetadata( 'topological-sort-level', $refGenerator[sizeof($refGenerator) - 1] ); } $topologicalLevel++; } while (sizeof($leafNodes) > 0); // Cleanup visited marks foreach ($nodeKeys as $key) { $nodes[$key]->unsetMetadata('topological-sort-visited'); } } /** * Sort returns the graph's nodes, sorted by topological order. * * The result is an array with as many entries as topological levels. * Each entry in this array is an array of nodes within * the given topological level. * * @param object $graph Graph to sort * * @return array The graph's nodes, sorted by topological order. */ public static function sort(&$graph) { // We only sort graphs if (!is_a($graph, 'Structures_Graph')) { return Pear::raiseError( 'Structures_Graph_Manipulator_TopologicalSorter::sort received' . ' an object that is not a Structures_Graph', STRUCTURES_GRAPH_ERROR_GENERIC ); } if (!Structures_Graph_Manipulator_AcyclicTest::isAcyclic($graph)) { return Pear::raiseError( 'Structures_Graph_Manipulator_TopologicalSorter::sort' . ' received an graph that has cycles', STRUCTURES_GRAPH_ERROR_GENERIC ); } Structures_Graph_Manipulator_TopologicalSorter::_sort($graph); $result = array(); // Fill out result array $nodes =& $graph->getNodes(); $nodeKeys = array_keys($nodes); foreach ($nodeKeys as $key) { if (!array_key_exists($nodes[$key]->getMetadata('topological-sort-level'), $result)) { $result[$nodes[$key]->getMetadata('topological-sort-level')] = array(); } $result[$nodes[$key]->getMetadata('topological-sort-level')][] =& $nodes[$key]; $nodes[$key]->unsetMetadata('topological-sort-level'); } return $result; } } ?> PK=1]C + +pear/Structures/Graph/Node.phpnu[ | // +-----------------------------------------------------------------------------+ // /** * This file contains the definition of the Structures_Graph_Node class * * @see Structures_Graph_Node * @package Structures_Graph */ /* dependencies {{{ */ /** */ require_once 'PEAR.php'; /** */ require_once 'Structures/Graph.php'; /* }}} */ /* class Structures_Graph_Node {{{ */ /** * The Structures_Graph_Node class represents a Node that can be member of a * graph node set. * * A graph node can contain data. Under this API, the node contains default data, * and key index data. It behaves, thus, both as a regular data node, and as a * dictionary (or associative array) node. * * Regular data is accessed via getData and setData. Key indexed data is accessed * via getMetadata and setMetadata. * * @author Srgio Carvalho * @copyright (c) 2004 by Srgio Carvalho * @package Structures_Graph */ /* }}} */ class Structures_Graph_Node { /* fields {{{ */ /** * @access private */ var $_data = null; /** @access private */ var $_metadata = array(); /** @access private */ var $_arcs = array(); /** @access private */ var $_graph = null; /* }}} */ /* Constructor {{{ */ /** * * Constructor * * @access public */ function __construct() { } /* }}} */ /* getGraph {{{ */ /** * * Node graph getter * * @return Structures_Graph Graph where node is stored * @access public */ function &getGraph() { return $this->_graph; } /* }}} */ /* setGraph {{{ */ /** * * Node graph setter. This method should not be called directly. Use Graph::addNode instead. * * @param Structures_Graph Set the graph for this node. * @see Structures_Graph::addNode() * @access public */ function setGraph(&$graph) { $this->_graph =& $graph; } /* }}} */ /* getData {{{ */ /** * * Node data getter. * * Each graph node can contain a reference to one variable. This is the getter for that reference. * * @return mixed Data stored in node * @access public */ function &getData() { return $this->_data; } /* }}} */ /* setData {{{ */ /** * * Node data setter * * Each graph node can contain a reference to one variable. This is the setter for that reference. * * @return mixed Data to store in node * @access public */ function setData(&$data) { $this->_data =& $data; } /* }}} */ /* metadataKeyExists {{{ */ /** * * Test for existence of metadata under a given key. * * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an * associative array or in a dictionary. This method tests whether a given metadata key exists for this node. * * @param string Key to test * @return boolean * @access public */ function metadataKeyExists($key) { return array_key_exists($key, $this->_metadata); } /* }}} */ /* getMetadata {{{ */ /** * * Node metadata getter * * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an * associative array or in a dictionary. This method gets the data under the given key. If the key does * not exist, an error will be thrown, so testing using metadataKeyExists might be needed. * * @param string Key * @param boolean nullIfNonexistent (defaults to false). * @return mixed Metadata Data stored in node under given key * @see metadataKeyExists * @access public */ function &getMetadata($key, $nullIfNonexistent = false) { if (array_key_exists($key, $this->_metadata)) { return $this->_metadata[$key]; } else { if ($nullIfNonexistent) { $a = null; return $a; } else { $a = Pear::raiseError('Structures_Graph_Node::getMetadata: Requested key does not exist', STRUCTURES_GRAPH_ERROR_GENERIC); return $a; } } } /* }}} */ /* unsetMetadata {{{ */ /** * * Delete metadata by key * * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an * associative array or in a dictionary. This method removes any data that might be stored under the provided key. * If the key does not exist, no error is thrown, so it is safe using this method without testing for key existence. * * @param string Key * @access public */ function unsetMetadata($key) { if (array_key_exists($key, $this->_metadata)) unset($this->_metadata[$key]); } /* }}} */ /* setMetadata {{{ */ /** * * Node metadata setter * * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an * associative array or in a dictionary. This method stores data under the given key. If the key already exists, * previously stored data is discarded. * * @param string Key * @param mixed Data * @access public */ function setMetadata($key, &$data) { $this->_metadata[$key] =& $data; } /* }}} */ /* _connectTo {{{ */ /** @access private */ function _connectTo(&$destinationNode) { $this->_arcs[] =& $destinationNode; } /* }}} */ /* connectTo {{{ */ /** * * Connect this node to another one. * * If the graph is not directed, the reverse arc, connecting $destinationNode to $this is also created. * * @param Structures_Graph_Node Node to connect to * @access public */ function connectTo(&$destinationNode) { // We only connect to nodes if (!is_a($destinationNode, 'Structures_Graph_Node')) return Pear::raiseError('Structures_Graph_Node::connectTo received an object that is not a Structures_Graph_Node', STRUCTURES_GRAPH_ERROR_GENERIC); // Nodes must already be in graphs to be connected if ($this->_graph == null) return Pear::raiseError('Structures_Graph_Node::connectTo Tried to connect a node that is not in a graph', STRUCTURES_GRAPH_ERROR_GENERIC); if ($destinationNode->getGraph() == null) return Pear::raiseError('Structures_Graph_Node::connectTo Tried to connect to a node that is not in a graph', STRUCTURES_GRAPH_ERROR_GENERIC); // Connect here $this->_connectTo($destinationNode); // If graph is undirected, connect back if (!$this->_graph->isDirected()) { $destinationNode->_connectTo($this); } } /* }}} */ /* getNeighbours {{{ */ /** * * Return nodes connected to this one. * * @return array Array of nodes * @access public */ function getNeighbours() { return $this->_arcs; } /* }}} */ /* connectsTo {{{ */ /** * * Test wether this node has an arc to the target node * * @return boolean True if the two nodes are connected * @access public */ function connectsTo(&$target) { if (version_compare(PHP_VERSION, '5.0.0') >= 0) { return in_array($target, $this->getNeighbours(), true); } $copy = $target; $arcKeys = array_keys($this->_arcs); foreach($arcKeys as $key) { /* ZE1 chokes on this expression: if ($target === $arc) return true; so, we'll use more convoluted stuff */ $arc =& $this->_arcs[$key]; $target = true; if ($arc === true) { $target = false; if ($arc === false) { $target = $copy; return true; } } } $target = $copy; return false; } /* }}} */ /* inDegree {{{ */ /** * * Calculate the in degree of the node. * * The indegree for a node is the number of arcs entering the node. For non directed graphs, * the indegree is equal to the outdegree. * * @return integer In degree of the node * @access public */ function inDegree() { if ($this->_graph == null) return 0; if (!$this->_graph->isDirected()) return $this->outDegree(); $result = 0; $graphNodes =& $this->_graph->getNodes(); foreach (array_keys($graphNodes) as $key) { if ($graphNodes[$key]->connectsTo($this)) $result++; } return $result; } /* }}} */ /* outDegree {{{ */ /** * * Calculate the out degree of the node. * * The outdegree for a node is the number of arcs exiting the node. For non directed graphs, * the outdegree is always equal to the indegree. * * @return integer Out degree of the node * @access public */ function outDegree() { if ($this->_graph == null) return 0; return sizeof($this->_arcs); } /* }}} */ } ?> PK=1]Uyvpear/Structures/Graph.phpnu[ | // +-----------------------------------------------------------------------------+ // /** * The Graph.php file contains the definition of the Structures_Graph class * * @package Structures_Graph */ /* dependencies {{{ */ require_once 'PEAR.php'; require_once 'Structures/Graph/Node.php'; /* }}} */ define('STRUCTURES_GRAPH_ERROR_GENERIC', 100); /* class Structures_Graph {{{ */ /** * The Structures_Graph class represents a graph data structure. * * A Graph is a data structure composed by a set of nodes, connected by arcs. * Graphs may either be directed or undirected. In a directed graph, arcs are * directional, and can be traveled only one way. In an undirected graph, arcs * are bidirectional, and can be traveled both ways. * * @author Srgio Carvalho * @copyright (c) 2004 by Srgio Carvalho * @package Structures_Graph */ /* }}} */ class Structures_Graph { /** * List of node objects in this graph * @access private */ var $_nodes = array(); /** * If the graph is directed or not * @access private */ var $_directed = false; /** * Constructor * * @param boolean $directed Set to true if the graph is directed. * Set to false if it is not directed. */ public function __construct($directed = true) { $this->_directed = $directed; } /** * Old constructor (PHP4-style; kept for BC with extending classes) * * @param boolean $directed Set to true if the graph is directed. * Set to false if it is not directed. * * @return void */ public function Structures_Graph($directed = true) { $this->__construct($directed); } /** * Return true if a graph is directed * * @return boolean true if the graph is directed */ public function isDirected() { return (boolean) $this->_directed; } /** * Add a Node to the Graph * * @param Structures_Graph_Node $newNode The node to be added. * * @return void */ public function addNode(&$newNode) { // We only add nodes if (!is_a($newNode, 'Structures_Graph_Node')) { return Pear::raiseError( 'Structures_Graph::addNode received an object that is not' . ' a Structures_Graph_Node', STRUCTURES_GRAPH_ERROR_GENERIC ); } //Graphs are node *sets*, so duplicates are forbidden. // We allow nodes that are exactly equal, but disallow equal references. foreach ($this->_nodes as $key => $node) { /* ZE1 equality operators choke on the recursive cycle introduced by the _graph field in the Node object. So, we'll check references the hard way (change $this->_nodes[$key] and check if the change reflects in $node) */ $savedData = $this->_nodes[$key]; $referenceIsEqualFlag = false; $this->_nodes[$key] = true; if ($node === true) { $this->_nodes[$key] = false; if ($node === false) { $referenceIsEqualFlag = true; } } $this->_nodes[$key] = $savedData; if ($referenceIsEqualFlag) { return Pear::raiseError( 'Structures_Graph::addNode received an object that is' . ' a duplicate for this dataset', STRUCTURES_GRAPH_ERROR_GENERIC ); } } $this->_nodes[] =& $newNode; $newNode->setGraph($this); } /** * Remove a Node from the Graph * * @param Structures_Graph_Node $node The node to be removed from the graph * * @return void * @todo This is unimplemented */ public function removeNode(&$node) { } /** * Return the node set, in no particular order. * For ordered node sets, use a Graph Manipulator insted. * * @return array The set of nodes in this graph * @see Structures_Graph_Manipulator_TopologicalSorter */ public function &getNodes() { return $this->_nodes; } } ?> PK=1] pear/PEAR.phpnu[ * @author Stig Bakken * @author Tomas V.V.Cox * @author Greg Beaver * @copyright 1997-2010 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ /**#@+ * ERROR constants */ define('PEAR_ERROR_RETURN', 1); define('PEAR_ERROR_PRINT', 2); define('PEAR_ERROR_TRIGGER', 4); define('PEAR_ERROR_DIE', 8); define('PEAR_ERROR_CALLBACK', 16); /** * WARNING: obsolete * @deprecated */ define('PEAR_ERROR_EXCEPTION', 32); /**#@-*/ if (substr(PHP_OS, 0, 3) == 'WIN') { define('OS_WINDOWS', true); define('OS_UNIX', false); define('PEAR_OS', 'Windows'); } else { define('OS_WINDOWS', false); define('OS_UNIX', true); define('PEAR_OS', 'Unix'); // blatant assumption } $GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN; $GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE; $GLOBALS['_PEAR_destructor_object_list'] = array(); $GLOBALS['_PEAR_shutdown_funcs'] = array(); $GLOBALS['_PEAR_error_handler_stack'] = array(); if(function_exists('ini_set')) { @ini_set('track_errors', true); } /** * Base class for other PEAR classes. Provides rudimentary * emulation of destructors. * * If you want a destructor in your class, inherit PEAR and make a * destructor method called _yourclassname (same name as the * constructor, but with a "_" prefix). Also, in your constructor you * have to call the PEAR constructor: $this->PEAR();. * The destructor method will be called without parameters. Note that * at in some SAPI implementations (such as Apache), any output during * the request shutdown (in which destructors are called) seems to be * discarded. If you need to get any debug information from your * destructor, use error_log(), syslog() or something similar. * * IMPORTANT! To use the emulated destructors you need to create the * objects by reference: $obj =& new PEAR_child; * * @category pear * @package PEAR * @author Stig Bakken * @author Tomas V.V. Cox * @author Greg Beaver * @copyright 1997-2006 The PHP Group * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @see PEAR_Error * @since Class available since PHP 4.0.2 * @link http://pear.php.net/manual/en/core.pear.php#core.pear.pear */ class PEAR { /** * Whether to enable internal debug messages. * * @var bool * @access private */ var $_debug = false; /** * Default error mode for this object. * * @var int * @access private */ var $_default_error_mode = null; /** * Default error options used for this object when error mode * is PEAR_ERROR_TRIGGER. * * @var int * @access private */ var $_default_error_options = null; /** * Default error handler (callback) for this object, if error mode is * PEAR_ERROR_CALLBACK. * * @var string * @access private */ var $_default_error_handler = ''; /** * Which class to use for error objects. * * @var string * @access private */ var $_error_class = 'PEAR_Error'; /** * An array of expected errors. * * @var array * @access private */ var $_expected_errors = array(); /** * List of methods that can be called both statically and non-statically. * @var array */ protected static $bivalentMethods = array( 'setErrorHandling' => true, 'raiseError' => true, 'throwError' => true, 'pushErrorHandling' => true, 'popErrorHandling' => true, ); /** * Constructor. Registers this object in * $_PEAR_destructor_object_list for destructor emulation if a * destructor object exists. * * @param string $error_class (optional) which class to use for * error objects, defaults to PEAR_Error. * @access public * @return void */ function __construct($error_class = null) { $classname = strtolower(get_class($this)); if ($this->_debug) { print "PEAR constructor called, class=$classname\n"; } if ($error_class !== null) { $this->_error_class = $error_class; } while ($classname && strcasecmp($classname, "pear")) { $destructor = "_$classname"; if (method_exists($this, $destructor)) { global $_PEAR_destructor_object_list; $_PEAR_destructor_object_list[] = $this; if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) { register_shutdown_function("_PEAR_call_destructors"); $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true; } break; } else { $classname = get_parent_class($classname); } } } /** * Only here for backwards compatibility. * E.g. Archive_Tar calls $this->PEAR() in its constructor. * * @param string $error_class Which class to use for error objects, * defaults to PEAR_Error. */ public function PEAR($error_class = null) { self::__construct($error_class); } /** * Destructor (the emulated type of...). Does nothing right now, * but is included for forward compatibility, so subclass * destructors should always call it. * * See the note in the class desciption about output from * destructors. * * @access public * @return void */ function _PEAR() { if ($this->_debug) { printf("PEAR destructor called, class=%s\n", strtolower(get_class($this))); } } public function __call($method, $arguments) { if (!isset(self::$bivalentMethods[$method])) { if (PHP_VERSION_ID < 70000) { trigger_error( 'Call to undefined method PEAR::' . $method . '()', E_USER_ERROR ); } else { throw new Error('Call to undefined method PEAR::' . $method . '()'); } } return call_user_func_array( array(__CLASS__, '_' . $method), array_merge(array($this), $arguments) ); } public static function __callStatic($method, $arguments) { if (!isset(self::$bivalentMethods[$method])) { if (PHP_VERSION_ID < 70000) { trigger_error( 'Call to undefined method PEAR::' . $method . '()', E_USER_ERROR ); } else { throw new Error('Call to undefined method PEAR::' . $method . '()'); } } return call_user_func_array( array(__CLASS__, '_' . $method), array_merge(array(null), $arguments) ); } /** * If you have a class that's mostly/entirely static, and you need static * properties, you can use this method to simulate them. Eg. in your method(s) * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar'); * You MUST use a reference, or they will not persist! * * @param string $class The calling classname, to prevent clashes * @param string $var The variable to retrieve. * @return mixed A reference to the variable. If not set it will be * auto initialised to NULL. */ public static function &getStaticProperty($class, $var) { static $properties; if (!isset($properties[$class])) { $properties[$class] = array(); } if (!array_key_exists($var, $properties[$class])) { $properties[$class][$var] = null; } return $properties[$class][$var]; } /** * Use this function to register a shutdown method for static * classes. * * @param mixed $func The function name (or array of class/method) to call * @param mixed $args The arguments to pass to the function * * @return void */ public static function registerShutdownFunc($func, $args = array()) { // if we are called statically, there is a potential // that no shutdown func is registered. Bug #6445 if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) { register_shutdown_function("_PEAR_call_destructors"); $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true; } $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args); } /** * Tell whether a value is a PEAR error. * * @param mixed $data the value to test * @param int $code if $data is an error object, return true * only if $code is a string and * $obj->getMessage() == $code or * $code is an integer and $obj->getCode() == $code * * @return bool true if parameter is an error */ public static function isError($data, $code = null) { if (!is_a($data, 'PEAR_Error')) { return false; } if (is_null($code)) { return true; } elseif (is_string($code)) { return $data->getMessage() == $code; } return $data->getCode() == $code; } /** * Sets how errors generated by this object should be handled. * Can be invoked both in objects and statically. If called * statically, setErrorHandling sets the default behaviour for all * PEAR objects. If called in an object, setErrorHandling sets * the default behaviour for that object. * * @param object $object * Object the method was called on (non-static mode) * * @param int $mode * One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT, * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE, * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION. * * @param mixed $options * When $mode is PEAR_ERROR_TRIGGER, this is the error level (one * of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). * * When $mode is PEAR_ERROR_CALLBACK, this parameter is expected * to be the callback function or method. A callback * function is a string with the name of the function, a * callback method is an array of two elements: the element * at index 0 is the object, and the element at index 1 is * the name of the method to call in the object. * * When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is * a printf format string used when printing the error * message. * * @access public * @return void * @see PEAR_ERROR_RETURN * @see PEAR_ERROR_PRINT * @see PEAR_ERROR_TRIGGER * @see PEAR_ERROR_DIE * @see PEAR_ERROR_CALLBACK * @see PEAR_ERROR_EXCEPTION * * @since PHP 4.0.5 */ protected static function _setErrorHandling( $object, $mode = null, $options = null ) { if ($object !== null) { $setmode = &$object->_default_error_mode; $setoptions = &$object->_default_error_options; } else { $setmode = &$GLOBALS['_PEAR_default_error_mode']; $setoptions = &$GLOBALS['_PEAR_default_error_options']; } switch ($mode) { case PEAR_ERROR_EXCEPTION: case PEAR_ERROR_RETURN: case PEAR_ERROR_PRINT: case PEAR_ERROR_TRIGGER: case PEAR_ERROR_DIE: case null: $setmode = $mode; $setoptions = $options; break; case PEAR_ERROR_CALLBACK: $setmode = $mode; // class/object method callback if (is_callable($options)) { $setoptions = $options; } else { trigger_error("invalid error callback", E_USER_WARNING); } break; default: trigger_error("invalid error mode", E_USER_WARNING); break; } } /** * This method is used to tell which errors you expect to get. * Expected errors are always returned with error mode * PEAR_ERROR_RETURN. Expected error codes are stored in a stack, * and this method pushes a new element onto it. The list of * expected errors are in effect until they are popped off the * stack with the popExpect() method. * * Note that this method can not be called statically * * @param mixed $code a single error code or an array of error codes to expect * * @return int the new depth of the "expected errors" stack * @access public */ function expectError($code = '*') { if (is_array($code)) { array_push($this->_expected_errors, $code); } else { array_push($this->_expected_errors, array($code)); } return count($this->_expected_errors); } /** * This method pops one element off the expected error codes * stack. * * @return array the list of error codes that were popped */ function popExpect() { return array_pop($this->_expected_errors); } /** * This method checks unsets an error code if available * * @param mixed error code * @return bool true if the error code was unset, false otherwise * @access private * @since PHP 4.3.0 */ function _checkDelExpect($error_code) { $deleted = false; foreach ($this->_expected_errors as $key => $error_array) { if (in_array($error_code, $error_array)) { unset($this->_expected_errors[$key][array_search($error_code, $error_array)]); $deleted = true; } // clean up empty arrays if (0 == count($this->_expected_errors[$key])) { unset($this->_expected_errors[$key]); } } return $deleted; } /** * This method deletes all occurrences of the specified element from * the expected error codes stack. * * @param mixed $error_code error code that should be deleted * @return mixed list of error codes that were deleted or error * @access public * @since PHP 4.3.0 */ function delExpect($error_code) { $deleted = false; if ((is_array($error_code) && (0 != count($error_code)))) { // $error_code is a non-empty array here; we walk through it trying // to unset all values foreach ($error_code as $key => $error) { $deleted = $this->_checkDelExpect($error) ? true : false; } return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME } elseif (!empty($error_code)) { // $error_code comes alone, trying to unset it if ($this->_checkDelExpect($error_code)) { return true; } return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME } // $error_code is empty return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME } /** * This method is a wrapper that returns an instance of the * configured error class with this object's default error * handling applied. If the $mode and $options parameters are not * specified, the object's defaults are used. * * @param mixed $message a text error message or a PEAR error object * * @param int $code a numeric error code (it is up to your class * to define these if you want to use codes) * * @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT, * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE, * PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION. * * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter * specifies the PHP-internal error level (one of * E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). * If $mode is PEAR_ERROR_CALLBACK, this * parameter specifies the callback function or * method. In other error modes this parameter * is ignored. * * @param string $userinfo If you need to pass along for example debug * information, this parameter is meant for that. * * @param string $error_class The returned error object will be * instantiated from this class, if specified. * * @param bool $skipmsg If true, raiseError will only pass error codes, * the error message parameter will be dropped. * * @return object a PEAR error object * @see PEAR::setErrorHandling * @since PHP 4.0.5 */ protected static function _raiseError($object, $message = null, $code = null, $mode = null, $options = null, $userinfo = null, $error_class = null, $skipmsg = false) { // The error is yet a PEAR error object if (is_object($message)) { $code = $message->getCode(); $userinfo = $message->getUserInfo(); $error_class = $message->getType(); $message->error_message_prefix = ''; $message = $message->getMessage(); } if ( $object !== null && isset($object->_expected_errors) && count($object->_expected_errors) > 0 && count($exp = end($object->_expected_errors)) ) { if ($exp[0] === "*" || (is_int(reset($exp)) && in_array($code, $exp)) || (is_string(reset($exp)) && in_array($message, $exp)) ) { $mode = PEAR_ERROR_RETURN; } } // No mode given, try global ones if ($mode === null) { // Class error handler if ($object !== null && isset($object->_default_error_mode)) { $mode = $object->_default_error_mode; $options = $object->_default_error_options; // Global error handler } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) { $mode = $GLOBALS['_PEAR_default_error_mode']; $options = $GLOBALS['_PEAR_default_error_options']; } } if ($error_class !== null) { $ec = $error_class; } elseif ($object !== null && isset($object->_error_class)) { $ec = $object->_error_class; } else { $ec = 'PEAR_Error'; } if ($skipmsg) { $a = new $ec($code, $mode, $options, $userinfo); } else { $a = new $ec($message, $code, $mode, $options, $userinfo); } return $a; } /** * Simpler form of raiseError with fewer options. In most cases * message, code and userinfo are enough. * * @param mixed $message a text error message or a PEAR error object * * @param int $code a numeric error code (it is up to your class * to define these if you want to use codes) * * @param string $userinfo If you need to pass along for example debug * information, this parameter is meant for that. * * @return object a PEAR error object * @see PEAR::raiseError */ protected static function _throwError($object, $message = null, $code = null, $userinfo = null) { if ($object !== null) { $a = $object->raiseError($message, $code, null, null, $userinfo); return $a; } $a = PEAR::raiseError($message, $code, null, null, $userinfo); return $a; } public static function staticPushErrorHandling($mode, $options = null) { $stack = &$GLOBALS['_PEAR_error_handler_stack']; $def_mode = &$GLOBALS['_PEAR_default_error_mode']; $def_options = &$GLOBALS['_PEAR_default_error_options']; $stack[] = array($def_mode, $def_options); switch ($mode) { case PEAR_ERROR_EXCEPTION: case PEAR_ERROR_RETURN: case PEAR_ERROR_PRINT: case PEAR_ERROR_TRIGGER: case PEAR_ERROR_DIE: case null: $def_mode = $mode; $def_options = $options; break; case PEAR_ERROR_CALLBACK: $def_mode = $mode; // class/object method callback if (is_callable($options)) { $def_options = $options; } else { trigger_error("invalid error callback", E_USER_WARNING); } break; default: trigger_error("invalid error mode", E_USER_WARNING); break; } $stack[] = array($mode, $options); return true; } public static function staticPopErrorHandling() { $stack = &$GLOBALS['_PEAR_error_handler_stack']; $setmode = &$GLOBALS['_PEAR_default_error_mode']; $setoptions = &$GLOBALS['_PEAR_default_error_options']; array_pop($stack); list($mode, $options) = $stack[sizeof($stack) - 1]; array_pop($stack); switch ($mode) { case PEAR_ERROR_EXCEPTION: case PEAR_ERROR_RETURN: case PEAR_ERROR_PRINT: case PEAR_ERROR_TRIGGER: case PEAR_ERROR_DIE: case null: $setmode = $mode; $setoptions = $options; break; case PEAR_ERROR_CALLBACK: $setmode = $mode; // class/object method callback if (is_callable($options)) { $setoptions = $options; } else { trigger_error("invalid error callback", E_USER_WARNING); } break; default: trigger_error("invalid error mode", E_USER_WARNING); break; } return true; } /** * Push a new error handler on top of the error handler options stack. With this * you can easily override the actual error handler for some code and restore * it later with popErrorHandling. * * @param mixed $mode (same as setErrorHandling) * @param mixed $options (same as setErrorHandling) * * @return bool Always true * * @see PEAR::setErrorHandling */ protected static function _pushErrorHandling($object, $mode, $options = null) { $stack = &$GLOBALS['_PEAR_error_handler_stack']; if ($object !== null) { $def_mode = &$object->_default_error_mode; $def_options = &$object->_default_error_options; } else { $def_mode = &$GLOBALS['_PEAR_default_error_mode']; $def_options = &$GLOBALS['_PEAR_default_error_options']; } $stack[] = array($def_mode, $def_options); if ($object !== null) { $object->setErrorHandling($mode, $options); } else { PEAR::setErrorHandling($mode, $options); } $stack[] = array($mode, $options); return true; } /** * Pop the last error handler used * * @return bool Always true * * @see PEAR::pushErrorHandling */ protected static function _popErrorHandling($object) { $stack = &$GLOBALS['_PEAR_error_handler_stack']; array_pop($stack); list($mode, $options) = $stack[sizeof($stack) - 1]; array_pop($stack); if ($object !== null) { $object->setErrorHandling($mode, $options); } else { PEAR::setErrorHandling($mode, $options); } return true; } /** * OS independent PHP extension load. Remember to take care * on the correct extension name for case sensitive OSes. * * @param string $ext The extension name * @return bool Success or not on the dl() call */ public static function loadExtension($ext) { if (extension_loaded($ext)) { return true; } // if either returns true dl() will produce a FATAL error, stop that if ( function_exists('dl') === false || ini_get('enable_dl') != 1 ) { return false; } if (OS_WINDOWS) { $suffix = '.dll'; } elseif (PHP_OS == 'HP-UX') { $suffix = '.sl'; } elseif (PHP_OS == 'AIX') { $suffix = '.a'; } elseif (PHP_OS == 'OSX') { $suffix = '.bundle'; } else { $suffix = '.so'; } return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix); } /** * Get SOURCE_DATE_EPOCH environment variable * See https://reproducible-builds.org/specs/source-date-epoch/ * * @return int * @access public */ static function getSourceDateEpoch() { if ($source_date_epoch = getenv('SOURCE_DATE_EPOCH')) { if (preg_match('/^\d+$/', $source_date_epoch)) { return (int) $source_date_epoch; } else { // "If the value is malformed, the build process SHOULD exit with a non-zero error code." self::raiseError("Invalid SOURCE_DATE_EPOCH: $source_date_epoch"); exit(1); } } else { return time(); } } } function _PEAR_call_destructors() { global $_PEAR_destructor_object_list; if (is_array($_PEAR_destructor_object_list) && sizeof($_PEAR_destructor_object_list)) { reset($_PEAR_destructor_object_list); $destructLifoExists = PEAR::getStaticProperty('PEAR', 'destructlifo'); if ($destructLifoExists) { $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list); } foreach ($_PEAR_destructor_object_list as $k => $objref) { $classname = get_class($objref); while ($classname) { $destructor = "_$classname"; if (method_exists($objref, $destructor)) { $objref->$destructor(); break; } else { $classname = get_parent_class($classname); } } } // Empty the object list to ensure that destructors are // not called more than once. $_PEAR_destructor_object_list = array(); } // Now call the shutdown functions if ( isset($GLOBALS['_PEAR_shutdown_funcs']) && is_array($GLOBALS['_PEAR_shutdown_funcs']) && !empty($GLOBALS['_PEAR_shutdown_funcs']) ) { foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) { call_user_func_array($value[0], $value[1]); } } } /** * Standard PEAR error class for PHP 4 * * This class is supserseded by {@link PEAR_Exception} in PHP 5 * * @category pear * @package PEAR * @author Stig Bakken * @author Tomas V.V. Cox * @author Gregory Beaver * @copyright 1997-2006 The PHP Group * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/manual/en/core.pear.pear-error.php * @see PEAR::raiseError(), PEAR::throwError() * @since Class available since PHP 4.0.2 */ class PEAR_Error { var $error_message_prefix = ''; var $mode = PEAR_ERROR_RETURN; var $level = E_USER_NOTICE; var $code = -1; var $message = ''; var $userinfo = ''; var $backtrace = null; var $callback = null; /** * PEAR_Error constructor * * @param string $message message * * @param int $code (optional) error code * * @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN, * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER, * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION * * @param mixed $options (optional) error level, _OR_ in the case of * PEAR_ERROR_CALLBACK, the callback function or object/method * tuple. * * @param string $userinfo (optional) additional user/debug info * * @access public * */ function __construct($message = 'unknown error', $code = null, $mode = null, $options = null, $userinfo = null) { if ($mode === null) { $mode = PEAR_ERROR_RETURN; } $this->message = $message; $this->code = $code; $this->mode = $mode; $this->userinfo = $userinfo; $skiptrace = PEAR::getStaticProperty('PEAR_Error', 'skiptrace'); if (!$skiptrace) { $this->backtrace = debug_backtrace(); if (isset($this->backtrace[0]) && isset($this->backtrace[0]['object'])) { unset($this->backtrace[0]['object']); } } if ($mode & PEAR_ERROR_CALLBACK) { $this->level = E_USER_NOTICE; $this->callback = $options; } else { if ($options === null) { $options = E_USER_NOTICE; } $this->level = $options; $this->callback = null; } if ($this->mode & PEAR_ERROR_PRINT) { if (is_null($options) || is_int($options)) { $format = "%s"; } else { $format = $options; } printf($format, $this->getMessage()); } if ($this->mode & PEAR_ERROR_TRIGGER) { trigger_error($this->getMessage(), $this->level); } if ($this->mode & PEAR_ERROR_DIE) { $msg = $this->getMessage(); if (is_null($options) || is_int($options)) { $format = "%s"; if (substr($msg, -1) != "\n") { $msg .= "\n"; } } else { $format = $options; } printf($format, $msg); exit($code); } if ($this->mode & PEAR_ERROR_CALLBACK && is_callable($this->callback)) { call_user_func($this->callback, $this); } if ($this->mode & PEAR_ERROR_EXCEPTION) { trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING); eval('$e = new Exception($this->message, $this->code);throw($e);'); } } /** * Only here for backwards compatibility. * * Class "Cache_Error" still uses it, among others. * * @param string $message Message * @param int $code Error code * @param int $mode Error mode * @param mixed $options See __construct() * @param string $userinfo Additional user/debug info */ public function PEAR_Error( $message = 'unknown error', $code = null, $mode = null, $options = null, $userinfo = null ) { self::__construct($message, $code, $mode, $options, $userinfo); } /** * Get the error mode from an error object. * * @return int error mode * @access public */ function getMode() { return $this->mode; } /** * Get the callback function/method from an error object. * * @return mixed callback function or object/method array * @access public */ function getCallback() { return $this->callback; } /** * Get the error message from an error object. * * @return string full error message * @access public */ function getMessage() { return ($this->error_message_prefix . $this->message); } /** * Get error code from an error object * * @return int error code * @access public */ function getCode() { return $this->code; } /** * Get the name of this error/exception. * * @return string error/exception name (type) * @access public */ function getType() { return get_class($this); } /** * Get additional user-supplied information. * * @return string user-supplied information * @access public */ function getUserInfo() { return $this->userinfo; } /** * Get additional debug information supplied by the application. * * @return string debug information * @access public */ function getDebugInfo() { return $this->getUserInfo(); } /** * Get the call backtrace from where the error was generated. * Supported with PHP 4.3.0 or newer. * * @param int $frame (optional) what frame to fetch * @return array Backtrace, or NULL if not available. * @access public */ function getBacktrace($frame = null) { if (defined('PEAR_IGNORE_BACKTRACE')) { return null; } if ($frame === null) { return $this->backtrace; } return $this->backtrace[$frame]; } function addUserInfo($info) { if (empty($this->userinfo)) { $this->userinfo = $info; } else { $this->userinfo .= " ** $info"; } } function __toString() { return $this->getMessage(); } /** * Make a string representation of this object. * * @return string a string with an object summary * @access public */ function toString() { $modes = array(); $levels = array(E_USER_NOTICE => 'notice', E_USER_WARNING => 'warning', E_USER_ERROR => 'error'); if ($this->mode & PEAR_ERROR_CALLBACK) { if (is_array($this->callback)) { $callback = (is_object($this->callback[0]) ? strtolower(get_class($this->callback[0])) : $this->callback[0]) . '::' . $this->callback[1]; } else { $callback = $this->callback; } return sprintf('[%s: message="%s" code=%d mode=callback '. 'callback=%s prefix="%s" info="%s"]', strtolower(get_class($this)), $this->message, $this->code, $callback, $this->error_message_prefix, $this->userinfo); } if ($this->mode & PEAR_ERROR_PRINT) { $modes[] = 'print'; } if ($this->mode & PEAR_ERROR_TRIGGER) { $modes[] = 'trigger'; } if ($this->mode & PEAR_ERROR_DIE) { $modes[] = 'die'; } if ($this->mode & PEAR_ERROR_RETURN) { $modes[] = 'return'; } return sprintf('[%s: message="%s" code=%d mode=%s level=%s '. 'prefix="%s" info="%s"]', strtolower(get_class($this)), $this->message, $this->code, implode("|", $modes), $levels[$this->level], $this->error_message_prefix, $this->userinfo); } } /* * Local Variables: * mode: php * tab-width: 4 * c-basic-offset: 4 * End: */ PK=1]0AC5C5pear/Console/Getopt.phpnu[ * @license http://opensource.org/licenses/bsd-license.php BSD-2-Clause * @version CVS: $Id$ * @link http://pear.php.net/package/Console_Getopt */ require_once 'PEAR.php'; /** * Command-line options parsing class. * * @category Console * @package Console_Getopt * @author Andrei Zmievski * @license http://opensource.org/licenses/bsd-license.php BSD-2-Clause * @link http://pear.php.net/package/Console_Getopt */ class Console_Getopt { /** * Parses the command-line options. * * The first parameter to this function should be the list of command-line * arguments without the leading reference to the running program. * * The second parameter is a string of allowed short options. Each of the * option letters can be followed by a colon ':' to specify that the option * requires an argument, or a double colon '::' to specify that the option * takes an optional argument. * * The third argument is an optional array of allowed long options. The * leading '--' should not be included in the option name. Options that * require an argument should be followed by '=', and options that take an * option argument should be followed by '=='. * * The return value is an array of two elements: the list of parsed * options and the list of non-option command-line arguments. Each entry in * the list of parsed options is a pair of elements - the first one * specifies the option, and the second one specifies the option argument, * if there was one. * * Long and short options can be mixed. * * Most of the semantics of this function are based on GNU getopt_long(). * * @param array $args an array of command-line arguments * @param string $short_options specifies the list of allowed short options * @param array $long_options specifies the list of allowed long options * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option * * @return array two-element array containing the list of parsed options and * the non-option arguments */ public static function getopt2($args, $short_options, $long_options = null, $skip_unknown = false) { return Console_Getopt::doGetopt(2, $args, $short_options, $long_options, $skip_unknown); } /** * This function expects $args to start with the script name (POSIX-style). * Preserved for backwards compatibility. * * @param array $args an array of command-line arguments * @param string $short_options specifies the list of allowed short options * @param array $long_options specifies the list of allowed long options * * @see getopt2() * @return array two-element array containing the list of parsed options and * the non-option arguments */ public static function getopt($args, $short_options, $long_options = null, $skip_unknown = false) { return Console_Getopt::doGetopt(1, $args, $short_options, $long_options, $skip_unknown); } /** * The actual implementation of the argument parsing code. * * @param int $version Version to use * @param array $args an array of command-line arguments * @param string $short_options specifies the list of allowed short options * @param array $long_options specifies the list of allowed long options * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option * * @return array */ public static function doGetopt($version, $args, $short_options, $long_options = null, $skip_unknown = false) { // in case you pass directly readPHPArgv() as the first arg if (PEAR::isError($args)) { return $args; } if (empty($args)) { return array(array(), array()); } $non_opts = $opts = array(); settype($args, 'array'); if ($long_options) { sort($long_options); } /* * Preserve backwards compatibility with callers that relied on * erroneous POSIX fix. */ if ($version < 2) { if (isset($args[0][0]) && $args[0][0] != '-') { array_shift($args); } } for ($i = 0; $i < count($args); $i++) { $arg = $args[$i]; /* The special element '--' means explicit end of options. Treat the rest of the arguments as non-options and end the loop. */ if ($arg == '--') { $non_opts = array_merge($non_opts, array_slice($args, $i + 1)); break; } if ($arg[0] != '-' || (strlen($arg) > 1 && $arg[1] == '-' && !$long_options)) { $non_opts = array_merge($non_opts, array_slice($args, $i)); break; } elseif (strlen($arg) > 1 && $arg[1] == '-') { $error = Console_Getopt::_parseLongOption(substr($arg, 2), $long_options, $opts, $i, $args, $skip_unknown); if (PEAR::isError($error)) { return $error; } } elseif ($arg == '-') { // - is stdin $non_opts = array_merge($non_opts, array_slice($args, $i)); break; } else { $error = Console_Getopt::_parseShortOption(substr($arg, 1), $short_options, $opts, $i, $args, $skip_unknown); if (PEAR::isError($error)) { return $error; } } } return array($opts, $non_opts); } /** * Parse short option * * @param string $arg Argument * @param string[] $short_options Available short options * @param string[][] &$opts * @param int &$argIdx * @param string[] $args * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option * * @return void */ protected static function _parseShortOption($arg, $short_options, &$opts, &$argIdx, $args, $skip_unknown) { for ($i = 0; $i < strlen($arg); $i++) { $opt = $arg[$i]; $opt_arg = null; /* Try to find the short option in the specifier string. */ if (($spec = strstr($short_options, $opt)) === false || $arg[$i] == ':') { if ($skip_unknown === true) { break; } $msg = "Console_Getopt: unrecognized option -- $opt"; return PEAR::raiseError($msg); } if (strlen($spec) > 1 && $spec[1] == ':') { if (strlen($spec) > 2 && $spec[2] == ':') { if ($i + 1 < strlen($arg)) { /* Option takes an optional argument. Use the remainder of the arg string if there is anything left. */ $opts[] = array($opt, substr($arg, $i + 1)); break; } } else { /* Option requires an argument. Use the remainder of the arg string if there is anything left. */ if ($i + 1 < strlen($arg)) { $opts[] = array($opt, substr($arg, $i + 1)); break; } else if (isset($args[++$argIdx])) { $opt_arg = $args[$argIdx]; /* Else use the next argument. */; if (Console_Getopt::_isShortOpt($opt_arg) || Console_Getopt::_isLongOpt($opt_arg)) { $msg = "option requires an argument --$opt"; return PEAR::raiseError("Console_Getopt: " . $msg); } } else { $msg = "option requires an argument --$opt"; return PEAR::raiseError("Console_Getopt: " . $msg); } } } $opts[] = array($opt, $opt_arg); } } /** * Checks if an argument is a short option * * @param string $arg Argument to check * * @return bool */ protected static function _isShortOpt($arg) { return strlen($arg) == 2 && $arg[0] == '-' && preg_match('/[a-zA-Z]/', $arg[1]); } /** * Checks if an argument is a long option * * @param string $arg Argument to check * * @return bool */ protected static function _isLongOpt($arg) { return strlen($arg) > 2 && $arg[0] == '-' && $arg[1] == '-' && preg_match('/[a-zA-Z]+$/', substr($arg, 2)); } /** * Parse long option * * @param string $arg Argument * @param string[] $long_options Available long options * @param string[][] &$opts * @param int &$argIdx * @param string[] $args * * @return void|PEAR_Error */ protected static function _parseLongOption($arg, $long_options, &$opts, &$argIdx, $args, $skip_unknown) { @list($opt, $opt_arg) = explode('=', $arg, 2); $opt_len = strlen($opt); for ($i = 0; $i < count($long_options); $i++) { $long_opt = $long_options[$i]; $opt_start = substr($long_opt, 0, $opt_len); $long_opt_name = str_replace('=', '', $long_opt); /* Option doesn't match. Go on to the next one. */ if ($long_opt_name != $opt) { continue; } $opt_rest = substr($long_opt, $opt_len); /* Check that the options uniquely matches one of the allowed options. */ if ($i + 1 < count($long_options)) { $next_option_rest = substr($long_options[$i + 1], $opt_len); } else { $next_option_rest = ''; } if ($opt_rest != '' && $opt[0] != '=' && $i + 1 < count($long_options) && $opt == substr($long_options[$i+1], 0, $opt_len) && $next_option_rest != '' && $next_option_rest[0] != '=') { $msg = "Console_Getopt: option --$opt is ambiguous"; return PEAR::raiseError($msg); } if (substr($long_opt, -1) == '=') { if (substr($long_opt, -2) != '==') { /* Long option requires an argument. Take the next argument if one wasn't specified. */; if (!strlen($opt_arg)) { if (!isset($args[++$argIdx])) { $msg = "Console_Getopt: option requires an argument --$opt"; return PEAR::raiseError($msg); } $opt_arg = $args[$argIdx]; } if (Console_Getopt::_isShortOpt($opt_arg) || Console_Getopt::_isLongOpt($opt_arg)) { $msg = "Console_Getopt: option requires an argument --$opt"; return PEAR::raiseError($msg); } } } else if ($opt_arg) { $msg = "Console_Getopt: option --$opt doesn't allow an argument"; return PEAR::raiseError($msg); } $opts[] = array('--' . $opt, $opt_arg); return; } if ($skip_unknown === true) { return; } return PEAR::raiseError("Console_Getopt: unrecognized option --$opt"); } /** * Safely read the $argv PHP array across different PHP configurations. * Will take care on register_globals and register_argc_argv ini directives * * @return mixed the $argv PHP array or PEAR error if not registered */ public static function readPHPArgv() { global $argv; if (!is_array($argv)) { if (!@is_array($_SERVER['argv'])) { if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) { $msg = "Could not read cmd args (register_argc_argv=Off?)"; return PEAR::raiseError("Console_Getopt: " . $msg); } return $GLOBALS['HTTP_SERVER_VARS']['argv']; } return $_SERVER['argv']; } return $argv; } } PK=1]9 ^yypear/peclcmd.phpnu[ * @author Tomas V.V.Cox * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR */ /** * @nodep Gtk */ //the space is needed for windows include paths with trailing backslash // http://pear.php.net/bugs/bug.php?id=19482 if ('/opt/cpanel/ea-php83/root/usr/share/pear ' != '@'.'include_path'.'@ ') { ini_set('include_path', trim('/opt/cpanel/ea-php83/root/usr/share/pear '). PATH_SEPARATOR . get_include_path()); $raw = false; } else { // this is a raw, uninstalled pear, either a cvs checkout, or php distro ini_set('include_path', __DIR__ . PATH_SEPARATOR . get_include_path()); $raw = true; } define('PEAR_RUNTYPE', 'pecl'); require_once 'pearcmd.php'; /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * indent-tabs-mode: nil * mode: php * End: */ // vim600:syn=php ?> PK=1]1%xcUcUpear/Archive/Tar.phpnu[ * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @category File_Formats * @package Archive_Tar * @author Vincent Blavet * @copyright 1997-2010 The Authors * @license http://www.opensource.org/licenses/bsd-license.php New BSD License * @version CVS: $Id$ * @link http://pear.php.net/package/Archive_Tar */ // If the PEAR class cannot be loaded via the autoloader, // then try to require_once it from the PHP include path. if (!class_exists('PEAR')) { require_once 'PEAR.php'; } define('ARCHIVE_TAR_ATT_SEPARATOR', 90001); define('ARCHIVE_TAR_END_BLOCK', pack("a512", '')); /** * Creates a (compressed) Tar archive * * @package Archive_Tar * @author Vincent Blavet * @license http://www.opensource.org/licenses/bsd-license.php New BSD License * @version $Revision$ */ class Archive_Tar extends PEAR { /** * @var string Name of the Tar */ public $_tarname = ''; /** * @var boolean if true, the Tar file will be gzipped */ public $_compress = false; /** * @var string Type of compression : 'none', 'gz', 'bz2' or 'lzma2' */ public $_compress_type = 'none'; /** * @var string Explode separator */ public $_separator = ' '; /** * @var file descriptor */ public $_file = 0; /** * @var string Local Tar name of a remote Tar (http:// or ftp://) */ public $_temp_tarname = ''; /** * @var string regular expression for ignoring files or directories */ public $_ignore_regexp = ''; /** * @var object PEAR_Error object */ public $error_object = null; /** * Format for data extraction * * @var string */ public $_fmt = ''; /** * @var int Length of the read buffer in bytes */ protected $buffer_length; /** * Archive_Tar Class constructor. This flavour of the constructor only * declare a new Archive_Tar object, identifying it by the name of the * tar file. * If the compress argument is set the tar will be read or created as a * gzip or bz2 compressed TAR file. * * @param string $p_tarname The name of the tar archive to create * @param string $p_compress can be null, 'gz', 'bz2' or 'lzma2'. This * parameter indicates if gzip, bz2 or lzma2 compression * is required. For compatibility reason the * boolean value 'true' means 'gz'. * @param int $buffer_length Length of the read buffer in bytes * * @return bool */ public function __construct($p_tarname, $p_compress = null, $buffer_length = 512) { parent::__construct(); $this->_compress = false; $this->_compress_type = 'none'; if (($p_compress === null) || ($p_compress == '')) { if (@file_exists($p_tarname)) { if ($fp = @fopen($p_tarname, "rb")) { // look for gzip magic cookie $data = fread($fp, 2); fclose($fp); if ($data == "\37\213") { $this->_compress = true; $this->_compress_type = 'gz'; // No sure it's enought for a magic code .... } elseif ($data == "BZ") { $this->_compress = true; $this->_compress_type = 'bz2'; } elseif (file_get_contents($p_tarname, false, null, 1, 4) == '7zXZ') { $this->_compress = true; $this->_compress_type = 'lzma2'; } } } else { // probably a remote file or some file accessible // through a stream interface if (substr($p_tarname, -2) == 'gz') { $this->_compress = true; $this->_compress_type = 'gz'; } elseif ((substr($p_tarname, -3) == 'bz2') || (substr($p_tarname, -2) == 'bz') ) { $this->_compress = true; $this->_compress_type = 'bz2'; } else { if (substr($p_tarname, -2) == 'xz') { $this->_compress = true; $this->_compress_type = 'lzma2'; } } } } else { if (($p_compress === true) || ($p_compress == 'gz')) { $this->_compress = true; $this->_compress_type = 'gz'; } else { if ($p_compress == 'bz2') { $this->_compress = true; $this->_compress_type = 'bz2'; } else { if ($p_compress == 'lzma2') { $this->_compress = true; $this->_compress_type = 'lzma2'; } else { $this->_error( "Unsupported compression type '$p_compress'\n" . "Supported types are 'gz', 'bz2' and 'lzma2'.\n" ); return false; } } } } $this->_tarname = $p_tarname; if ($this->_compress) { // assert zlib or bz2 or xz extension support if ($this->_compress_type == 'gz') { $extname = 'zlib'; } else { if ($this->_compress_type == 'bz2') { $extname = 'bz2'; } else { if ($this->_compress_type == 'lzma2') { $extname = 'xz'; } } } if (!extension_loaded($extname)) { PEAR::loadExtension($extname); } if (!extension_loaded($extname)) { $this->_error( "The extension '$extname' couldn't be found.\n" . "Please make sure your version of PHP was built " . "with '$extname' support.\n" ); return false; } } if (version_compare(PHP_VERSION, "5.5.0-dev") < 0) { $this->_fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" . "a8checksum/a1typeflag/a100link/a6magic/a2version/" . "a32uname/a32gname/a8devmajor/a8devminor/a131prefix"; } else { $this->_fmt = "Z100filename/Z8mode/Z8uid/Z8gid/a12size/Z12mtime/" . "Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" . "Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix"; } $this->buffer_length = $buffer_length; } public function __destruct() { $this->_close(); // ----- Look for a local copy to delete if ($this->_temp_tarname != '' && (bool) preg_match('/^tar[[:alnum:]]*\.tmp$/', $this->_temp_tarname)) { @unlink($this->_temp_tarname); } } /** * This method creates the archive file and add the files / directories * that are listed in $p_filelist. * If a file with the same name exist and is writable, it is replaced * by the new tar. * The method return false and a PEAR error text. * The $p_filelist parameter can be an array of string, each string * representing a filename or a directory name with their path if * needed. It can also be a single string with names separated by a * single blank. * For each directory added in the archive, the files and * sub-directories are also added. * See also createModify() method for more details. * * @param array $p_filelist An array of filenames and directory names, or a * single string with names separated by a single * blank space. * * @return bool true on success, false on error. * @see createModify() */ public function create($p_filelist) { return $this->createModify($p_filelist, '', ''); } /** * This method add the files / directories that are listed in $p_filelist in * the archive. If the archive does not exist it is created. * The method return false and a PEAR error text. * The files and directories listed are only added at the end of the archive, * even if a file with the same name is already archived. * See also createModify() method for more details. * * @param array $p_filelist An array of filenames and directory names, or a * single string with names separated by a single * blank space. * * @return bool true on success, false on error. * @see createModify() * @access public */ public function add($p_filelist) { return $this->addModify($p_filelist, '', ''); } /** * @param string $p_path * @param bool $p_preserve * @param bool $p_symlinks * @return bool */ public function extract($p_path = '', $p_preserve = false, $p_symlinks = true) { return $this->extractModify($p_path, '', $p_preserve, $p_symlinks); } /** * @return array|int */ public function listContent() { $v_list_detail = array(); if ($this->_openRead()) { if (!$this->_extractList('', $v_list_detail, "list", '', '')) { unset($v_list_detail); $v_list_detail = 0; } $this->_close(); } return $v_list_detail; } /** * This method creates the archive file and add the files / directories * that are listed in $p_filelist. * If the file already exists and is writable, it is replaced by the * new tar. It is a create and not an add. If the file exists and is * read-only or is a directory it is not replaced. The method return * false and a PEAR error text. * The $p_filelist parameter can be an array of string, each string * representing a filename or a directory name with their path if * needed. It can also be a single string with names separated by a * single blank. * The path indicated in $p_remove_dir will be removed from the * memorized path of each file / directory listed when this path * exists. By default nothing is removed (empty path '') * The path indicated in $p_add_dir will be added at the beginning of * the memorized path of each file / directory listed. However it can * be set to empty ''. The adding of a path is done after the removing * of path. * The path add/remove ability enables the user to prepare an archive * for extraction in a different path than the origin files are. * See also addModify() method for file adding properties. * * @param array $p_filelist An array of filenames and directory names, * or a single string with names separated by * a single blank space. * @param string $p_add_dir A string which contains a path to be added * to the memorized path of each element in * the list. * @param string $p_remove_dir A string which contains a path to be * removed from the memorized path of each * element in the list, when relevant. * * @return boolean true on success, false on error. * @see addModify() */ public function createModify($p_filelist, $p_add_dir, $p_remove_dir = '') { $v_result = true; if (!$this->_openWrite()) { return false; } if ($p_filelist != '') { if (is_array($p_filelist)) { $v_list = $p_filelist; } elseif (is_string($p_filelist)) { $v_list = explode($this->_separator, $p_filelist); } else { $this->_cleanFile(); $this->_error('Invalid file list'); return false; } $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir); } if ($v_result) { $this->_writeFooter(); $this->_close(); } else { $this->_cleanFile(); } return $v_result; } /** * This method add the files / directories listed in $p_filelist at the * end of the existing archive. If the archive does not yet exists it * is created. * The $p_filelist parameter can be an array of string, each string * representing a filename or a directory name with their path if * needed. It can also be a single string with names separated by a * single blank. * The path indicated in $p_remove_dir will be removed from the * memorized path of each file / directory listed when this path * exists. By default nothing is removed (empty path '') * The path indicated in $p_add_dir will be added at the beginning of * the memorized path of each file / directory listed. However it can * be set to empty ''. The adding of a path is done after the removing * of path. * The path add/remove ability enables the user to prepare an archive * for extraction in a different path than the origin files are. * If a file/dir is already in the archive it will only be added at the * end of the archive. There is no update of the existing archived * file/dir. However while extracting the archive, the last file will * replace the first one. This results in a none optimization of the * archive size. * If a file/dir does not exist the file/dir is ignored. However an * error text is send to PEAR error. * If a file/dir is not readable the file/dir is ignored. However an * error text is send to PEAR error. * * @param array $p_filelist An array of filenames and directory * names, or a single string with names * separated by a single blank space. * @param string $p_add_dir A string which contains a path to be * added to the memorized path of each * element in the list. * @param string $p_remove_dir A string which contains a path to be * removed from the memorized path of * each element in the list, when * relevant. * * @return bool true on success, false on error. */ public function addModify($p_filelist, $p_add_dir, $p_remove_dir = '') { $v_result = true; if (!$this->_isArchive()) { $v_result = $this->createModify( $p_filelist, $p_add_dir, $p_remove_dir ); } else { if (is_array($p_filelist)) { $v_list = $p_filelist; } elseif (is_string($p_filelist)) { $v_list = explode($this->_separator, $p_filelist); } else { $this->_error('Invalid file list'); return false; } $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir); } return $v_result; } /** * This method add a single string as a file at the * end of the existing archive. If the archive does not yet exists it * is created. * * @param string $p_filename A string which contains the full * filename path that will be associated * with the string. * @param string $p_string The content of the file added in * the archive. * @param bool|int $p_datetime A custom date/time (unix timestamp) * for the file (optional). * @param array $p_params An array of optional params: * stamp => the datetime (replaces * datetime above if it exists) * mode => the permissions on the * file (600 by default) * type => is this a link? See the * tar specification for details. * (default = regular file) * uid => the user ID of the file * (default = 0 = root) * gid => the group ID of the file * (default = 0 = root) * * @return bool true on success, false on error. */ public function addString($p_filename, $p_string, $p_datetime = false, $p_params = array()) { $p_stamp = @$p_params["stamp"] ? $p_params["stamp"] : ($p_datetime ? $p_datetime : time()); $p_mode = @$p_params["mode"] ? $p_params["mode"] : 0600; $p_type = @$p_params["type"] ? $p_params["type"] : ""; $p_uid = @$p_params["uid"] ? $p_params["uid"] : ""; $p_gid = @$p_params["gid"] ? $p_params["gid"] : ""; $v_result = true; if (!$this->_isArchive()) { if (!$this->_openWrite()) { return false; } $this->_close(); } if (!$this->_openAppend()) { return false; } // Need to check the get back to the temporary file ? .... $v_result = $this->_addString($p_filename, $p_string, $p_datetime, $p_params); $this->_writeFooter(); $this->_close(); return $v_result; } /** * This method extract all the content of the archive in the directory * indicated by $p_path. When relevant the memorized path of the * files/dir can be modified by removing the $p_remove_path path at the * beginning of the file/dir path. * While extracting a file, if the directory path does not exists it is * created. * While extracting a file, if the file already exists it is replaced * without looking for last modification date. * While extracting a file, if the file already exists and is write * protected, the extraction is aborted. * While extracting a file, if a directory with the same name already * exists, the extraction is aborted. * While extracting a directory, if a file with the same name already * exists, the extraction is aborted. * While extracting a file/directory if the destination directory exist * and is write protected, or does not exist but can not be created, * the extraction is aborted. * If after extraction an extracted file does not show the correct * stored file size, the extraction is aborted. * When the extraction is aborted, a PEAR error text is set and false * is returned. However the result can be a partial extraction that may * need to be manually cleaned. * * @param string $p_path The path of the directory where the * files/dir need to by extracted. * @param string $p_remove_path Part of the memorized path that can be * removed if present at the beginning of * the file/dir path. * @param boolean $p_preserve Preserve user/group ownership of files * @param boolean $p_symlinks Allow symlinks. * * @return boolean true on success, false on error. * @see extractList() */ public function extractModify($p_path, $p_remove_path, $p_preserve = false, $p_symlinks = true) { $v_result = true; $v_list_detail = array(); if ($v_result = $this->_openRead()) { $v_result = $this->_extractList( $p_path, $v_list_detail, "complete", 0, $p_remove_path, $p_preserve, $p_symlinks ); $this->_close(); } return $v_result; } /** * This method extract from the archive one file identified by $p_filename. * The return value is a string with the file content, or NULL on error. * * @param string $p_filename The path of the file to extract in a string. * * @return a string with the file content or NULL. */ public function extractInString($p_filename) { if ($this->_openRead()) { $v_result = $this->_extractInString($p_filename); $this->_close(); } else { $v_result = null; } return $v_result; } /** * This method extract from the archive only the files indicated in the * $p_filelist. These files are extracted in the current directory or * in the directory indicated by the optional $p_path parameter. * If indicated the $p_remove_path can be used in the same way as it is * used in extractModify() method. * * @param array $p_filelist An array of filenames and directory names, * or a single string with names separated * by a single blank space. * @param string $p_path The path of the directory where the * files/dir need to by extracted. * @param string $p_remove_path Part of the memorized path that can be * removed if present at the beginning of * the file/dir path. * @param boolean $p_preserve Preserve user/group ownership of files * @param boolean $p_symlinks Allow symlinks. * * @return bool true on success, false on error. * @see extractModify() */ public function extractList($p_filelist, $p_path = '', $p_remove_path = '', $p_preserve = false, $p_symlinks = true) { $v_result = true; $v_list_detail = array(); if (is_array($p_filelist)) { $v_list = $p_filelist; } elseif (is_string($p_filelist)) { $v_list = explode($this->_separator, $p_filelist); } else { $this->_error('Invalid string list'); return false; } if ($v_result = $this->_openRead()) { $v_result = $this->_extractList( $p_path, $v_list_detail, "partial", $v_list, $p_remove_path, $p_preserve, $p_symlinks ); $this->_close(); } return $v_result; } /** * This method set specific attributes of the archive. It uses a variable * list of parameters, in the format attribute code + attribute values : * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ','); * * @return bool true on success, false on error. */ public function setAttribute() { $v_result = true; // ----- Get the number of variable list of arguments if (($v_size = func_num_args()) == 0) { return true; } // ----- Get the arguments $v_att_list = func_get_args(); // ----- Read the attributes $i = 0; while ($i < $v_size) { // ----- Look for next option switch ($v_att_list[$i]) { // ----- Look for options that request a string value case ARCHIVE_TAR_ATT_SEPARATOR : // ----- Check the number of parameters if (($i + 1) >= $v_size) { $this->_error( 'Invalid number of parameters for ' . 'attribute ARCHIVE_TAR_ATT_SEPARATOR' ); return false; } // ----- Get the value $this->_separator = $v_att_list[$i + 1]; $i++; break; default : $this->_error('Unknown attribute code ' . $v_att_list[$i] . ''); return false; } // ----- Next attribute $i++; } return $v_result; } /** * This method sets the regular expression for ignoring files and directories * at import, for example: * $arch->setIgnoreRegexp("#CVS|\.svn#"); * * @param string $regexp regular expression defining which files or directories to ignore */ public function setIgnoreRegexp($regexp) { $this->_ignore_regexp = $regexp; } /** * This method sets the regular expression for ignoring all files and directories * matching the filenames in the array list at import, for example: * $arch->setIgnoreList(array('CVS', '.svn', 'bin/tool')); * * @param array $list a list of file or directory names to ignore * * @access public */ public function setIgnoreList($list) { $list = str_replace(array('#', '.', '^', '$'), array('\#', '\.', '\^', '\$'), $list); $regexp = '#/' . join('$|/', $list) . '#'; $this->setIgnoreRegexp($regexp); } /** * @param string $p_message */ public function _error($p_message) { $this->error_object = $this->raiseError($p_message); } /** * @param string $p_message */ public function _warning($p_message) { $this->error_object = $this->raiseError($p_message); } /** * @param string $p_filename * @return bool */ public function _isArchive($p_filename = null) { if ($p_filename == null) { $p_filename = $this->_tarname; } clearstatcache(); return @is_file($p_filename) && !@is_link($p_filename); } /** * @return bool */ public function _openWrite() { if ($this->_compress_type == 'gz' && function_exists('gzopen')) { $this->_file = @gzopen($this->_tarname, "wb9"); } else { if ($this->_compress_type == 'bz2' && function_exists('bzopen')) { $this->_file = @bzopen($this->_tarname, "w"); } else { if ($this->_compress_type == 'lzma2' && function_exists('xzopen')) { $this->_file = @xzopen($this->_tarname, 'w'); } else { if ($this->_compress_type == 'none') { $this->_file = @fopen($this->_tarname, "wb"); } else { $this->_error( 'Unknown or missing compression type (' . $this->_compress_type . ')' ); return false; } } } } if ($this->_file == 0) { $this->_error( 'Unable to open in write mode \'' . $this->_tarname . '\'' ); return false; } return true; } /** * @return bool */ public function _openRead() { if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') { // ----- Look if a local copy need to be done if ($this->_temp_tarname == '') { $this->_temp_tarname = uniqid('tar') . '.tmp'; if (!$v_file_from = @fopen($this->_tarname, 'rb')) { $this->_error( 'Unable to open in read mode \'' . $this->_tarname . '\'' ); $this->_temp_tarname = ''; return false; } if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) { $this->_error( 'Unable to open in write mode \'' . $this->_temp_tarname . '\'' ); $this->_temp_tarname = ''; return false; } while ($v_data = @fread($v_file_from, 1024)) { @fwrite($v_file_to, $v_data); } @fclose($v_file_from); @fclose($v_file_to); } // ----- File to open if the local copy $v_filename = $this->_temp_tarname; } else { // ----- File to open if the normal Tar file $v_filename = $this->_tarname; } if ($this->_compress_type == 'gz' && function_exists('gzopen')) { $this->_file = @gzopen($v_filename, "rb"); } else { if ($this->_compress_type == 'bz2' && function_exists('bzopen')) { $this->_file = @bzopen($v_filename, "r"); } else { if ($this->_compress_type == 'lzma2' && function_exists('xzopen')) { $this->_file = @xzopen($v_filename, "r"); } else { if ($this->_compress_type == 'none') { $this->_file = @fopen($v_filename, "rb"); } else { $this->_error( 'Unknown or missing compression type (' . $this->_compress_type . ')' ); return false; } } } } if ($this->_file == 0) { $this->_error('Unable to open in read mode \'' . $v_filename . '\''); return false; } return true; } /** * @return bool */ public function _openReadWrite() { if ($this->_compress_type == 'gz') { $this->_file = @gzopen($this->_tarname, "r+b"); } else { if ($this->_compress_type == 'bz2') { $this->_error( 'Unable to open bz2 in read/write mode \'' . $this->_tarname . '\' (limitation of bz2 extension)' ); return false; } else { if ($this->_compress_type == 'lzma2') { $this->_error( 'Unable to open lzma2 in read/write mode \'' . $this->_tarname . '\' (limitation of lzma2 extension)' ); return false; } else { if ($this->_compress_type == 'none') { $this->_file = @fopen($this->_tarname, "r+b"); } else { $this->_error( 'Unknown or missing compression type (' . $this->_compress_type . ')' ); return false; } } } } if ($this->_file == 0) { $this->_error( 'Unable to open in read/write mode \'' . $this->_tarname . '\'' ); return false; } return true; } /** * @return bool */ public function _close() { //if (isset($this->_file)) { if (is_resource($this->_file)) { if ($this->_compress_type == 'gz') { @gzclose($this->_file); } else { if ($this->_compress_type == 'bz2') { @bzclose($this->_file); } else { if ($this->_compress_type == 'lzma2') { @xzclose($this->_file); } else { if ($this->_compress_type == 'none') { @fclose($this->_file); } else { $this->_error( 'Unknown or missing compression type (' . $this->_compress_type . ')' ); } } } } $this->_file = 0; } // ----- Look if a local copy need to be erase // Note that it might be interesting to keep the url for a time : ToDo if ($this->_temp_tarname != '') { @unlink($this->_temp_tarname); $this->_temp_tarname = ''; } return true; } /** * @return bool */ public function _cleanFile() { $this->_close(); // ----- Look for a local copy if ($this->_temp_tarname != '') { // ----- Remove the local copy but not the remote tarname @unlink($this->_temp_tarname); $this->_temp_tarname = ''; } else { // ----- Remove the local tarname file @unlink($this->_tarname); } $this->_tarname = ''; return true; } /** * @param mixed $p_binary_data * @param integer $p_len * @return bool */ public function _writeBlock($p_binary_data, $p_len = null) { if (is_resource($this->_file)) { if ($p_len === null) { switch ($this->_compress_type) { case 'gz': $bytes = @gzwrite($this->_file, $p_binary_data); break; case 'bz2': $bytes = @bzwrite($this->_file, $p_binary_data); break; case 'lzma2': $bytes = @xzwrite($this->_file, $p_binary_data); break; case 'none': $bytes = @fwrite($this->_file, $p_binary_data); break; default: $this->_error('Unknown or missing compression type (' . $this->_compress_type . ')'); return false; } } else { switch ($this->_compress_type) { case 'gz': $bytes = @gzwrite($this->_file, $p_binary_data, $p_len); break; case 'bz2': $bytes = @bzwrite($this->_file, $p_binary_data, $p_len); break; case 'lzma2': $bytes = @xzwrite($this->_file, $p_binary_data, $p_len); break; case 'none': $bytes = @fwrite($this->_file, $p_binary_data, $p_len); break; default: $this->_error('Unknown or missing compression type (' . $this->_compress_type . ')'); return false; } } return $bytes !== false; } return true; } /** * @return null|string */ public function _readBlock() { $v_block = null; if (is_resource($this->_file)) { if ($this->_compress_type == 'gz') { $v_block = @gzread($this->_file, 512); } else { if ($this->_compress_type == 'bz2') { $v_block = @bzread($this->_file, 512); } else { if ($this->_compress_type == 'lzma2') { $v_block = @xzread($this->_file, 512); } else { if ($this->_compress_type == 'none') { $v_block = @fread($this->_file, 512); } else { $this->_error( 'Unknown or missing compression type (' . $this->_compress_type . ')' ); } } } } } return $v_block; } /** * @param null $p_len * @return bool */ public function _jumpBlock($p_len = null) { if (is_resource($this->_file)) { if ($p_len === null) { $p_len = 1; } if ($this->_compress_type == 'gz') { @gzseek($this->_file, gztell($this->_file) + ($p_len * 512)); } else { if ($this->_compress_type == 'bz2') { // ----- Replace missing bztell() and bzseek() for ($i = 0; $i < $p_len; $i++) { $this->_readBlock(); } } else { if ($this->_compress_type == 'lzma2') { // ----- Replace missing xztell() and xzseek() for ($i = 0; $i < $p_len; $i++) { $this->_readBlock(); } } else { if ($this->_compress_type == 'none') { @fseek($this->_file, $p_len * 512, SEEK_CUR); } else { $this->_error( 'Unknown or missing compression type (' . $this->_compress_type . ')' ); } } } } } return true; } /** * @return bool */ public function _writeFooter() { if (is_resource($this->_file)) { // ----- Write the last 0 filled block for end of archive $v_binary_data = pack('a1024', ''); return $this->_writeBlock($v_binary_data); } return true; } /** * @param array $p_list * @param string $p_add_dir * @param string $p_remove_dir * @return bool */ public function _addList($p_list, $p_add_dir, $p_remove_dir) { $v_result = true; $v_header = array(); // ----- Remove potential windows directory separator $p_add_dir = $this->_translateWinPath($p_add_dir); $p_remove_dir = $this->_translateWinPath($p_remove_dir, false); if (!$this->_file) { $this->_error('Invalid file descriptor'); return false; } if (sizeof($p_list) == 0) { return true; } foreach ($p_list as $v_filename) { if (!$v_result) { break; } // ----- Skip the current tar name if ($v_filename == $this->_tarname) { continue; } if ($v_filename == '') { continue; } // ----- ignore files and directories matching the ignore regular expression if ($this->_ignore_regexp && preg_match($this->_ignore_regexp, '/' . $v_filename)) { $this->_warning("File '$v_filename' ignored"); continue; } if (!file_exists($v_filename) && !is_link($v_filename)) { $this->_warning("File '$v_filename' does not exist"); continue; } // ----- Add the file or directory header if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir)) { return false; } if (@is_dir($v_filename) && !@is_link($v_filename)) { if (!($p_hdir = opendir($v_filename))) { $this->_warning("Directory '$v_filename' can not be read"); continue; } while (false !== ($p_hitem = readdir($p_hdir))) { if (($p_hitem != '.') && ($p_hitem != '..')) { if ($v_filename != ".") { $p_temp_list[0] = $v_filename . '/' . $p_hitem; } else { $p_temp_list[0] = $p_hitem; } $v_result = $this->_addList( $p_temp_list, $p_add_dir, $p_remove_dir ); } } unset($p_temp_list); unset($p_hdir); unset($p_hitem); } } return $v_result; } /** * @param string $p_filename * @param mixed $p_header * @param string $p_add_dir * @param string $p_remove_dir * @param null $v_stored_filename * @return bool */ public function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir, $v_stored_filename = null) { if (!$this->_file) { $this->_error('Invalid file descriptor'); return false; } if ($p_filename == '') { $this->_error('Invalid file name'); return false; } if (is_null($v_stored_filename)) { // ----- Calculate the stored filename $p_filename = $this->_translateWinPath($p_filename, false); $v_stored_filename = $p_filename; if (strcmp($p_filename, $p_remove_dir) == 0) { return true; } if ($p_remove_dir != '') { if (substr($p_remove_dir, -1) != '/') { $p_remove_dir .= '/'; } if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir) { $v_stored_filename = substr($p_filename, strlen($p_remove_dir)); } } $v_stored_filename = $this->_translateWinPath($v_stored_filename); if ($p_add_dir != '') { if (substr($p_add_dir, -1) == '/') { $v_stored_filename = $p_add_dir . $v_stored_filename; } else { $v_stored_filename = $p_add_dir . '/' . $v_stored_filename; } } $v_stored_filename = $this->_pathReduction($v_stored_filename); } if ($this->_isArchive($p_filename)) { if (($v_file = @fopen($p_filename, "rb")) == 0) { $this->_warning( "Unable to open file '" . $p_filename . "' in binary read mode" ); return true; } if (!$this->_writeHeader($p_filename, $v_stored_filename)) { return false; } while (($v_buffer = fread($v_file, $this->buffer_length)) != '') { $buffer_length = strlen("$v_buffer"); if ($buffer_length != $this->buffer_length) { $pack_size = ((int)($buffer_length / 512) + ($buffer_length % 512 !== 0 ? 1 : 0)) * 512; $pack_format = sprintf('a%d', $pack_size); } else { $pack_format = sprintf('a%d', $this->buffer_length); } $v_binary_data = pack($pack_format, "$v_buffer"); if(!$this->_writeBlock($v_binary_data)) { return false; } } fclose($v_file); } else { // ----- Only header for dir if (!$this->_writeHeader($p_filename, $v_stored_filename)) { return false; } } return true; } /** * @param string $p_filename * @param string $p_string * @param bool $p_datetime * @param array $p_params * @return bool */ public function _addString($p_filename, $p_string, $p_datetime = false, $p_params = array()) { $p_stamp = @$p_params["stamp"] ? $p_params["stamp"] : ($p_datetime ? $p_datetime : time()); $p_mode = @$p_params["mode"] ? $p_params["mode"] : 0600; $p_type = @$p_params["type"] ? $p_params["type"] : ""; $p_uid = @$p_params["uid"] ? $p_params["uid"] : 0; $p_gid = @$p_params["gid"] ? $p_params["gid"] : 0; if (!$this->_file) { $this->_error('Invalid file descriptor'); return false; } if ($p_filename == '') { $this->_error('Invalid file name'); return false; } // ----- Calculate the stored filename $p_filename = $this->_translateWinPath($p_filename, false); // ----- If datetime is not specified, set current time if ($p_datetime === false) { $p_datetime = time(); } if (!$this->_writeHeaderBlock( $p_filename, strlen($p_string), $p_stamp, $p_mode, $p_type, $p_uid, $p_gid ) ) { return false; } $i = 0; while (($v_buffer = substr($p_string, (($i++) * 512), 512)) != '') { $v_binary_data = pack("a512", $v_buffer); if (!$this->_writeBlock($v_binary_data)) { return false; } } return true; } /** * @param string $p_filename * @param string $p_stored_filename * @return bool */ public function _writeHeader($p_filename, $p_stored_filename) { if ($p_stored_filename == '') { $p_stored_filename = $p_filename; } $v_reduced_filename = $this->_pathReduction($p_stored_filename); if (strlen($v_reduced_filename) > 99) { if (!$this->_writeLongHeader($v_reduced_filename, false)) { return false; } } $v_linkname = ''; if (@is_link($p_filename)) { $v_linkname = readlink($p_filename); } if (strlen($v_linkname) > 99) { if (!$this->_writeLongHeader($v_linkname, true)) { return false; } } $v_info = lstat($p_filename); $v_uid = sprintf("%07s", DecOct($v_info[4])); $v_gid = sprintf("%07s", DecOct($v_info[5])); $v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777)); $v_mtime = sprintf("%011s", DecOct($v_info['mtime'])); if (@is_link($p_filename)) { $v_typeflag = '2'; $v_size = sprintf("%011s", DecOct(0)); } elseif (@is_dir($p_filename)) { $v_typeflag = "5"; $v_size = sprintf("%011s", DecOct(0)); } else { $v_typeflag = '0'; clearstatcache(); $v_size = sprintf("%011s", DecOct($v_info['size'])); } $v_magic = 'ustar '; $v_version = ' '; $v_uname = ''; $v_gname = ''; if (function_exists('posix_getpwuid')) { $userinfo = posix_getpwuid($v_info[4]); $groupinfo = posix_getgrgid($v_info[5]); if (isset($userinfo['name'])) { $v_uname = $userinfo['name']; } if (isset($groupinfo['name'])) { $v_gname = $groupinfo['name']; } } $v_devmajor = ''; $v_devminor = ''; $v_prefix = ''; $v_binary_data_first = pack( "a100a8a8a8a12a12", $v_reduced_filename, $v_perms, $v_uid, $v_gid, $v_size, $v_mtime ); $v_binary_data_last = pack( "a1a100a6a2a32a32a8a8a155a12", $v_typeflag, $v_linkname, $v_magic, $v_version, $v_uname, $v_gname, $v_devmajor, $v_devminor, $v_prefix, '' ); // ----- Calculate the checksum $v_checksum = 0; // ..... First part of the header for ($i = 0; $i < 148; $i++) { $v_checksum += ord(substr($v_binary_data_first, $i, 1)); } // ..... Ignore the checksum value and replace it by ' ' (space) for ($i = 148; $i < 156; $i++) { $v_checksum += ord(' '); } // ..... Last part of the header for ($i = 156, $j = 0; $i < 512; $i++, $j++) { $v_checksum += ord(substr($v_binary_data_last, $j, 1)); } // ----- Write the first 148 bytes of the header in the archive $this->_writeBlock($v_binary_data_first, 148); // ----- Write the calculated checksum $v_checksum = sprintf("%06s\0 ", DecOct($v_checksum)); $v_binary_data = pack("a8", $v_checksum); $this->_writeBlock($v_binary_data, 8); // ----- Write the last 356 bytes of the header in the archive $this->_writeBlock($v_binary_data_last, 356); return true; } /** * @param string $p_filename * @param int $p_size * @param int $p_mtime * @param int $p_perms * @param string $p_type * @param int $p_uid * @param int $p_gid * @return bool */ public function _writeHeaderBlock( $p_filename, $p_size, $p_mtime = 0, $p_perms = 0, $p_type = '', $p_uid = 0, $p_gid = 0 ) { $p_filename = $this->_pathReduction($p_filename); if (strlen($p_filename) > 99) { if (!$this->_writeLongHeader($p_filename, false)) { return false; } } if ($p_type == "5") { $v_size = sprintf("%011s", DecOct(0)); } else { $v_size = sprintf("%011s", DecOct($p_size)); } $v_uid = sprintf("%07s", DecOct($p_uid)); $v_gid = sprintf("%07s", DecOct($p_gid)); $v_perms = sprintf("%07s", DecOct($p_perms & 000777)); $v_mtime = sprintf("%11s", DecOct($p_mtime)); $v_linkname = ''; $v_magic = 'ustar '; $v_version = ' '; if (function_exists('posix_getpwuid')) { $userinfo = posix_getpwuid($p_uid); $groupinfo = posix_getgrgid($p_gid); if ($userinfo === false || $groupinfo === false) { $v_uname = ''; $v_gname = ''; } else { $v_uname = $userinfo['name']; $v_gname = $groupinfo['name']; } } else { $v_uname = ''; $v_gname = ''; } $v_devmajor = ''; $v_devminor = ''; $v_prefix = ''; $v_binary_data_first = pack( "a100a8a8a8a12A12", $p_filename, $v_perms, $v_uid, $v_gid, $v_size, $v_mtime ); $v_binary_data_last = pack( "a1a100a6a2a32a32a8a8a155a12", $p_type, $v_linkname, $v_magic, $v_version, $v_uname, $v_gname, $v_devmajor, $v_devminor, $v_prefix, '' ); // ----- Calculate the checksum $v_checksum = 0; // ..... First part of the header for ($i = 0; $i < 148; $i++) { $v_checksum += ord(substr($v_binary_data_first, $i, 1)); } // ..... Ignore the checksum value and replace it by ' ' (space) for ($i = 148; $i < 156; $i++) { $v_checksum += ord(' '); } // ..... Last part of the header for ($i = 156, $j = 0; $i < 512; $i++, $j++) { $v_checksum += ord(substr($v_binary_data_last, $j, 1)); } // ----- Write the first 148 bytes of the header in the archive $this->_writeBlock($v_binary_data_first, 148); // ----- Write the calculated checksum $v_checksum = sprintf("%06s ", DecOct($v_checksum)); $v_binary_data = pack("a8", $v_checksum); $this->_writeBlock($v_binary_data, 8); // ----- Write the last 356 bytes of the header in the archive $this->_writeBlock($v_binary_data_last, 356); return true; } /** * @param string $p_filename * @return bool */ public function _writeLongHeader($p_filename, $is_link = false) { $v_uid = sprintf("%07s", 0); $v_gid = sprintf("%07s", 0); $v_perms = sprintf("%07s", 0); $v_size = sprintf("%'011s", DecOct(strlen($p_filename))); $v_mtime = sprintf("%011s", 0); $v_typeflag = ($is_link ? 'K' : 'L'); $v_linkname = ''; $v_magic = 'ustar '; $v_version = ' '; $v_uname = ''; $v_gname = ''; $v_devmajor = ''; $v_devminor = ''; $v_prefix = ''; $v_binary_data_first = pack( "a100a8a8a8a12a12", '././@LongLink', $v_perms, $v_uid, $v_gid, $v_size, $v_mtime ); $v_binary_data_last = pack( "a1a100a6a2a32a32a8a8a155a12", $v_typeflag, $v_linkname, $v_magic, $v_version, $v_uname, $v_gname, $v_devmajor, $v_devminor, $v_prefix, '' ); // ----- Calculate the checksum $v_checksum = 0; // ..... First part of the header for ($i = 0; $i < 148; $i++) { $v_checksum += ord(substr($v_binary_data_first, $i, 1)); } // ..... Ignore the checksum value and replace it by ' ' (space) for ($i = 148; $i < 156; $i++) { $v_checksum += ord(' '); } // ..... Last part of the header for ($i = 156, $j = 0; $i < 512; $i++, $j++) { $v_checksum += ord(substr($v_binary_data_last, $j, 1)); } // ----- Write the first 148 bytes of the header in the archive $this->_writeBlock($v_binary_data_first, 148); // ----- Write the calculated checksum $v_checksum = sprintf("%06s\0 ", DecOct($v_checksum)); $v_binary_data = pack("a8", $v_checksum); $this->_writeBlock($v_binary_data, 8); // ----- Write the last 356 bytes of the header in the archive $this->_writeBlock($v_binary_data_last, 356); // ----- Write the filename as content of the block $i = 0; while (($v_buffer = substr($p_filename, (($i++) * 512), 512)) != '') { $v_binary_data = pack("a512", "$v_buffer"); $this->_writeBlock($v_binary_data); } return true; } /** * @param mixed $v_binary_data * @param mixed $v_header * @return bool */ public function _readHeader($v_binary_data, &$v_header) { if (strlen($v_binary_data) == 0) { $v_header['filename'] = ''; return true; } if (strlen($v_binary_data) != 512) { $v_header['filename'] = ''; $this->_error('Invalid block size : ' . strlen($v_binary_data)); return false; } if (!is_array($v_header)) { $v_header = array(); } // ----- Calculate the checksum $v_checksum = 0; // ..... First part of the header $v_binary_split = str_split($v_binary_data); $v_checksum += array_sum(array_map('ord', array_slice($v_binary_split, 0, 148))); $v_checksum += array_sum(array_map('ord', array(' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',))); $v_checksum += array_sum(array_map('ord', array_slice($v_binary_split, 156, 512))); $v_data = unpack($this->_fmt, $v_binary_data); if (strlen($v_data["prefix"]) > 0) { $v_data["filename"] = "$v_data[prefix]/$v_data[filename]"; } // ----- Extract the checksum $v_data_checksum = trim($v_data['checksum']); if (!preg_match('/^[0-7]*$/', $v_data_checksum)) { $this->_error( 'Invalid checksum for file "' . $v_data['filename'] . '" : ' . $v_data_checksum . ' extracted' ); return false; } $v_header['checksum'] = OctDec($v_data_checksum); if ($v_header['checksum'] != $v_checksum) { $v_header['filename'] = ''; // ----- Look for last block (empty block) if (($v_checksum == 256) && ($v_header['checksum'] == 0)) { return true; } $this->_error( 'Invalid checksum for file "' . $v_data['filename'] . '" : ' . $v_checksum . ' calculated, ' . $v_header['checksum'] . ' expected' ); return false; } // ----- Extract the properties $v_header['filename'] = rtrim($v_data['filename'], "\0"); if ($this->_isMaliciousFilename($v_header['filename'])) { $this->_error( 'Malicious .tar detected, file "' . $v_header['filename'] . '" will not install in desired directory tree' ); return false; } $v_header['mode'] = OctDec(trim($v_data['mode'])); $v_header['uid'] = OctDec(trim($v_data['uid'])); $v_header['gid'] = OctDec(trim($v_data['gid'])); $v_header['size'] = $this->_tarRecToSize($v_data['size']); $v_header['mtime'] = OctDec(trim($v_data['mtime'])); if (($v_header['typeflag'] = $v_data['typeflag']) == "5") { $v_header['size'] = 0; } $v_header['link'] = trim($v_data['link']); /* ----- All these fields are removed form the header because they do not carry interesting info $v_header[magic] = trim($v_data[magic]); $v_header[version] = trim($v_data[version]); $v_header[uname] = trim($v_data[uname]); $v_header[gname] = trim($v_data[gname]); $v_header[devmajor] = trim($v_data[devmajor]); $v_header[devminor] = trim($v_data[devminor]); */ return true; } /** * Convert Tar record size to actual size * * @param string $tar_size * @return size of tar record in bytes */ private function _tarRecToSize($tar_size) { /* * First byte of size has a special meaning if bit 7 is set. * * Bit 7 indicates base-256 encoding if set. * Bit 6 is the sign bit. * Bits 5:0 are most significant value bits. */ $ch = ord($tar_size[0]); if ($ch & 0x80) { // Full 12-bytes record is required. $rec_str = $tar_size . "\x00"; $size = ($ch & 0x40) ? -1 : 0; $size = ($size << 6) | ($ch & 0x3f); for ($num_ch = 1; $num_ch < 12; ++$num_ch) { $size = ($size * 256) + ord($rec_str[$num_ch]); } return $size; } else { return OctDec(trim($tar_size)); } } /** * Detect and report a malicious file name * * @param string $file * * @return bool */ private function _isMaliciousFilename($file) { if (strpos($file, '://') !== false) { return true; } if (strpos($file, '../') !== false || strpos($file, '..\\') !== false) { return true; } return false; } /** * @param $v_header * @return bool */ public function _readLongHeader(&$v_header) { $v_filename = ''; $v_filesize = $v_header['size']; $n = floor($v_header['size'] / 512); for ($i = 0; $i < $n; $i++) { $v_content = $this->_readBlock(); $v_filename .= $v_content; } if (($v_header['size'] % 512) != 0) { $v_content = $this->_readBlock(); $v_filename .= $v_content; } // ----- Read the next header $v_binary_data = $this->_readBlock(); if (!$this->_readHeader($v_binary_data, $v_header)) { return false; } $v_filename = rtrim(substr($v_filename, 0, $v_filesize), "\0"); $v_header['filename'] = $v_filename; if ($this->_isMaliciousFilename($v_filename)) { $this->_error( 'Malicious .tar detected, file "' . $v_filename . '" will not install in desired directory tree' ); return false; } return true; } /** * This method extract from the archive one file identified by $p_filename. * The return value is a string with the file content, or null on error. * * @param string $p_filename The path of the file to extract in a string. * * @return a string with the file content or null. */ private function _extractInString($p_filename) { $v_result_str = ""; while (strlen($v_binary_data = $this->_readBlock()) != 0) { if (!$this->_readHeader($v_binary_data, $v_header)) { return null; } if ($v_header['filename'] == '') { continue; } switch ($v_header['typeflag']) { case 'L': { if (!$this->_readLongHeader($v_header)) { return null; } } break; case 'K': { $v_link_header = $v_header; if (!$this->_readLongHeader($v_link_header)) { return null; } $v_header['link'] = $v_link_header['filename']; } break; } if ($v_header['filename'] == $p_filename) { if ($v_header['typeflag'] == "5") { $this->_error( 'Unable to extract in string a directory ' . 'entry {' . $v_header['filename'] . '}' ); return null; } else { $n = floor($v_header['size'] / 512); for ($i = 0; $i < $n; $i++) { $v_result_str .= $this->_readBlock(); } if (($v_header['size'] % 512) != 0) { $v_content = $this->_readBlock(); $v_result_str .= substr( $v_content, 0, ($v_header['size'] % 512) ); } return $v_result_str; } } else { $this->_jumpBlock(ceil(($v_header['size'] / 512))); } } return null; } /** * @param string $p_path * @param string $p_list_detail * @param string $p_mode * @param string $p_file_list * @param string $p_remove_path * @param bool $p_preserve * @param bool $p_symlinks * @return bool */ public function _extractList( $p_path, &$p_list_detail, $p_mode, $p_file_list, $p_remove_path, $p_preserve = false, $p_symlinks = true ) { $v_result = true; $v_nb = 0; $v_extract_all = true; $v_listing = false; $p_path = $this->_translateWinPath($p_path, false); if ($p_path == '' || (substr($p_path, 0, 1) != '/' && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':')) ) { $p_path = "./" . $p_path; } $p_remove_path = $this->_translateWinPath($p_remove_path); // ----- Look for path to remove format (should end by /) if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/')) { $p_remove_path .= '/'; } $p_remove_path_size = strlen($p_remove_path); switch ($p_mode) { case "complete" : $v_extract_all = true; $v_listing = false; break; case "partial" : $v_extract_all = false; $v_listing = false; break; case "list" : $v_extract_all = false; $v_listing = true; break; default : $this->_error('Invalid extract mode (' . $p_mode . ')'); return false; } clearstatcache(); while (strlen($v_binary_data = $this->_readBlock()) != 0) { $v_extract_file = false; $v_extraction_stopped = 0; if (!$this->_readHeader($v_binary_data, $v_header)) { return false; } if ($v_header['filename'] == '') { continue; } switch ($v_header['typeflag']) { case 'L': { if (!$this->_readLongHeader($v_header)) { return null; } } break; case 'K': { $v_link_header = $v_header; if (!$this->_readLongHeader($v_link_header)) { return null; } $v_header['link'] = $v_link_header['filename']; } break; } // ignore extended / pax headers if ($v_header['typeflag'] == 'x' || $v_header['typeflag'] == 'g') { $this->_jumpBlock(ceil(($v_header['size'] / 512))); continue; } if ((!$v_extract_all) && (is_array($p_file_list))) { // ----- By default no unzip if the file is not found $v_extract_file = false; for ($i = 0; $i < sizeof($p_file_list); $i++) { // ----- Look if it is a directory if (substr($p_file_list[$i], -1) == '/') { // ----- Look if the directory is in the filename path if ((strlen($v_header['filename']) > strlen($p_file_list[$i])) && (substr($v_header['filename'], 0, strlen($p_file_list[$i])) == $p_file_list[$i]) ) { $v_extract_file = true; break; } } // ----- It is a file, so compare the file names elseif ($p_file_list[$i] == $v_header['filename']) { $v_extract_file = true; break; } } } else { $v_extract_file = true; } // ----- Look if this file need to be extracted if (($v_extract_file) && (!$v_listing)) { if (($p_remove_path != '') && (substr($v_header['filename'] . '/', 0, $p_remove_path_size) == $p_remove_path) ) { $v_header['filename'] = substr( $v_header['filename'], $p_remove_path_size ); if ($v_header['filename'] == '') { continue; } } if (($p_path != './') && ($p_path != '/')) { while (substr($p_path, -1) == '/') { $p_path = substr($p_path, 0, strlen($p_path) - 1); } if (substr($v_header['filename'], 0, 1) == '/') { $v_header['filename'] = $p_path . $v_header['filename']; } else { $v_header['filename'] = $p_path . '/' . $v_header['filename']; } } if (file_exists($v_header['filename'])) { if ((@is_dir($v_header['filename'])) && ($v_header['typeflag'] == '') ) { $this->_error( 'File ' . $v_header['filename'] . ' already exists as a directory' ); return false; } if (($this->_isArchive($v_header['filename'])) && ($v_header['typeflag'] == "5") ) { $this->_error( 'Directory ' . $v_header['filename'] . ' already exists as a file' ); return false; } if (!is_writeable($v_header['filename'])) { $this->_error( 'File ' . $v_header['filename'] . ' already exists and is write protected' ); return false; } if (filemtime($v_header['filename']) > $v_header['mtime']) { // To be completed : An error or silent no replace ? } } // ----- Check the directory availability and create it if necessary elseif (($v_result = $this->_dirCheck( ($v_header['typeflag'] == "5" ? $v_header['filename'] : dirname($v_header['filename'])) )) != 1 ) { $this->_error('Unable to create path for ' . $v_header['filename']); return false; } if ($v_extract_file) { if ($v_header['typeflag'] == "5") { if (!@file_exists($v_header['filename'])) { if (!@mkdir($v_header['filename'], 0775)) { $this->_error( 'Unable to create directory {' . $v_header['filename'] . '}' ); return false; } } } elseif ($v_header['typeflag'] == "2") { if (!$p_symlinks) { $this->_warning('Symbolic links are not allowed. ' . 'Unable to extract {' . $v_header['filename'] . '}' ); return false; } $absolute_link = FALSE; $link_depth = 0; if (strpos($v_header['link'], "/") === 0 || strpos($v_header['link'], ':') !== FALSE) { $absolute_link = TRUE; } else { $s_filename = preg_replace('@^' . preg_quote($p_path) . '@', "", $v_header['filename']); $s_linkname = str_replace('\\', '/', $v_header['link']); foreach (explode("/", $s_filename) as $dir) { if ($dir === "..") { $link_depth--; } elseif ($dir !== "" && $dir !== "." ) { $link_depth++; } } foreach (explode("/", $s_linkname) as $dir){ if ($link_depth <= 0) { break; } if ($dir === "..") { $link_depth--; } elseif ($dir !== "" && $dir !== ".") { $link_depth++; } } } if ($absolute_link || $link_depth <= 0) { $this->_error( 'Out-of-path file extraction {' . $v_header['filename'] . ' --> ' . $v_header['link'] . '}' ); return false; } if (@file_exists($v_header['filename'])) { @unlink($v_header['filename']); } if (!@symlink($v_header['link'], $v_header['filename'])) { $this->_error( 'Unable to extract symbolic link {' . $v_header['filename'] . '}' ); return false; } } else { if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) { $this->_error( 'Error while opening {' . $v_header['filename'] . '} in write binary mode' ); return false; } else { $n = floor($v_header['size'] / 512); for ($i = 0; $i < $n; $i++) { $v_content = $this->_readBlock(); fwrite($v_dest_file, $v_content, 512); } if (($v_header['size'] % 512) != 0) { $v_content = $this->_readBlock(); fwrite($v_dest_file, $v_content, ($v_header['size'] % 512)); } @fclose($v_dest_file); if ($p_preserve) { @chown($v_header['filename'], $v_header['uid']); @chgrp($v_header['filename'], $v_header['gid']); } // ----- Change the file mode, mtime @touch($v_header['filename'], $v_header['mtime']); if ($v_header['mode'] & 0111) { // make file executable, obey umask $mode = fileperms($v_header['filename']) | (~umask() & 0111); @chmod($v_header['filename'], $mode); } } // ----- Check the file size clearstatcache(); if (!is_file($v_header['filename'])) { $this->_error( 'Extracted file ' . $v_header['filename'] . 'does not exist. Archive may be corrupted.' ); return false; } $filesize = filesize($v_header['filename']); if ($filesize != $v_header['size']) { $this->_error( 'Extracted file ' . $v_header['filename'] . ' does not have the correct file size \'' . $filesize . '\' (' . $v_header['size'] . ' expected). Archive may be corrupted.' ); return false; } } } else { $this->_jumpBlock(ceil(($v_header['size'] / 512))); } } else { $this->_jumpBlock(ceil(($v_header['size'] / 512))); } /* TBC : Seems to be unused ... if ($this->_compress) $v_end_of_file = @gzeof($this->_file); else $v_end_of_file = @feof($this->_file); */ if ($v_listing || $v_extract_file || $v_extraction_stopped) { // ----- Log extracted files if (($v_file_dir = dirname($v_header['filename'])) == $v_header['filename'] ) { $v_file_dir = ''; } if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == '')) { $v_file_dir = '/'; } $p_list_detail[$v_nb++] = $v_header; if (is_array($p_file_list) && (count($p_list_detail) == count($p_file_list))) { return true; } } } return true; } /** * @return bool */ public function _openAppend() { if (filesize($this->_tarname) == 0) { return $this->_openWrite(); } if ($this->_compress) { $this->_close(); if (!@rename($this->_tarname, $this->_tarname . ".tmp")) { $this->_error( 'Error while renaming \'' . $this->_tarname . '\' to temporary file \'' . $this->_tarname . '.tmp\'' ); return false; } if ($this->_compress_type == 'gz') { $v_temp_tar = @gzopen($this->_tarname . ".tmp", "rb"); } elseif ($this->_compress_type == 'bz2') { $v_temp_tar = @bzopen($this->_tarname . ".tmp", "r"); } elseif ($this->_compress_type == 'lzma2') { $v_temp_tar = @xzopen($this->_tarname . ".tmp", "r"); } if ($v_temp_tar == 0) { $this->_error( 'Unable to open file \'' . $this->_tarname . '.tmp\' in binary read mode' ); @rename($this->_tarname . ".tmp", $this->_tarname); return false; } if (!$this->_openWrite()) { @rename($this->_tarname . ".tmp", $this->_tarname); return false; } if ($this->_compress_type == 'gz') { $end_blocks = 0; while (!@gzeof($v_temp_tar)) { $v_buffer = @gzread($v_temp_tar, 512); if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) { $end_blocks++; // do not copy end blocks, we will re-make them // after appending continue; } elseif ($end_blocks > 0) { for ($i = 0; $i < $end_blocks; $i++) { $this->_writeBlock(ARCHIVE_TAR_END_BLOCK); } $end_blocks = 0; } $v_binary_data = pack("a512", $v_buffer); $this->_writeBlock($v_binary_data); } @gzclose($v_temp_tar); } elseif ($this->_compress_type == 'bz2') { $end_blocks = 0; while (strlen($v_buffer = @bzread($v_temp_tar, 512)) > 0) { if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) { $end_blocks++; // do not copy end blocks, we will re-make them // after appending continue; } elseif ($end_blocks > 0) { for ($i = 0; $i < $end_blocks; $i++) { $this->_writeBlock(ARCHIVE_TAR_END_BLOCK); } $end_blocks = 0; } $v_binary_data = pack("a512", $v_buffer); $this->_writeBlock($v_binary_data); } @bzclose($v_temp_tar); } elseif ($this->_compress_type == 'lzma2') { $end_blocks = 0; while (strlen($v_buffer = @xzread($v_temp_tar, 512)) > 0) { if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) { $end_blocks++; // do not copy end blocks, we will re-make them // after appending continue; } elseif ($end_blocks > 0) { for ($i = 0; $i < $end_blocks; $i++) { $this->_writeBlock(ARCHIVE_TAR_END_BLOCK); } $end_blocks = 0; } $v_binary_data = pack("a512", $v_buffer); $this->_writeBlock($v_binary_data); } @xzclose($v_temp_tar); } if (!@unlink($this->_tarname . ".tmp")) { $this->_error( 'Error while deleting temporary file \'' . $this->_tarname . '.tmp\'' ); } } else { // ----- For not compressed tar, just add files before the last // one or two 512 bytes block if (!$this->_openReadWrite()) { return false; } clearstatcache(); $v_size = filesize($this->_tarname); // We might have zero, one or two end blocks. // The standard is two, but we should try to handle // other cases. fseek($this->_file, $v_size - 1024); if (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) { fseek($this->_file, $v_size - 1024); } elseif (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) { fseek($this->_file, $v_size - 512); } } return true; } /** * @param $p_filelist * @param string $p_add_dir * @param string $p_remove_dir * @return bool */ public function _append($p_filelist, $p_add_dir = '', $p_remove_dir = '') { if (!$this->_openAppend()) { return false; } if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir)) { $this->_writeFooter(); } $this->_close(); return true; } /** * Check if a directory exists and create it (including parent * dirs) if not. * * @param string $p_dir directory to check * * @return bool true if the directory exists or was created */ public function _dirCheck($p_dir) { clearstatcache(); if ((@is_dir($p_dir)) || ($p_dir == '')) { return true; } $p_parent_dir = dirname($p_dir); if (($p_parent_dir != $p_dir) && ($p_parent_dir != '') && (!$this->_dirCheck($p_parent_dir)) ) { return false; } if (!@mkdir($p_dir, 0775)) { $this->_error("Unable to create directory '$p_dir'"); return false; } return true; } /** * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar", * rand emove double slashes. * * @param string $p_dir path to reduce * * @return string reduced path */ private function _pathReduction($p_dir) { $v_result = ''; // ----- Look for not empty path if ($p_dir != '') { // ----- Explode path by directory names $v_list = explode('/', $p_dir); // ----- Study directories from last to first for ($i = sizeof($v_list) - 1; $i >= 0; $i--) { // ----- Look for current path if ($v_list[$i] == ".") { // ----- Ignore this directory // Should be the first $i=0, but no check is done } else { if ($v_list[$i] == "..") { // ----- Ignore it and ignore the $i-1 $i--; } else { if (($v_list[$i] == '') && ($i != (sizeof($v_list) - 1)) && ($i != 0) ) { // ----- Ignore only the double '//' in path, // but not the first and last / } else { $v_result = $v_list[$i] . ($i != (sizeof($v_list) - 1) ? '/' . $v_result : ''); } } } } } if (defined('OS_WINDOWS') && OS_WINDOWS) { $v_result = strtr($v_result, '\\', '/'); } return $v_result; } /** * @param $p_path * @param bool $p_remove_disk_letter * @return string */ public function _translateWinPath($p_path, $p_remove_disk_letter = true) { if (defined('OS_WINDOWS') && OS_WINDOWS) { // ----- Look for potential disk letter if (($p_remove_disk_letter) && (($v_position = strpos($p_path, ':')) != false) ) { $p_path = substr($p_path, $v_position + 1); } // ----- Change potential windows directory separator if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0, 1) == '\\')) { $p_path = strtr($p_path, '\\', '/'); } } return $p_path; } } PK=1]fS==pear/PEAR/PackageFile.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * needed for PEAR_VALIDATE_* constants */ require_once 'PEAR/Validate.php'; /** * Error code if the package.xml tag does not contain a valid version */ define('PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION', 1); /** * Error code if the package.xml tag version is not supported (version 1.0 and 1.1 are the only supported versions, * currently */ define('PEAR_PACKAGEFILE_ERROR_INVALID_PACKAGEVERSION', 2); /** * Abstraction for the package.xml package description file * * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_PackageFile { /** * @var PEAR_Config */ var $_config; var $_debug; var $_logger = false; /** * @var boolean */ var $_rawReturn = false; /** * helper for extracting Archive_Tar errors * @var array * @access private */ var $_extractErrors = array(); /** * * @param PEAR_Config $config * @param ? $debug * @param string @tmpdir Optional temporary directory for uncompressing * files */ function __construct(&$config, $debug = false) { $this->_config = $config; $this->_debug = $debug; } /** * Turn off validation - return a parsed package.xml without checking it * * This is used by the package-validate command */ function rawReturn() { $this->_rawReturn = true; } function setLogger(&$l) { $this->_logger = &$l; } /** * Create a PEAR_PackageFile_Parser_v* of a given version. * @param int $version * @return PEAR_PackageFile_Parser_v1|PEAR_PackageFile_Parser_v1 */ function &parserFactory($version) { if (!in_array($version[0], array('1', '2'))) { $a = false; return $a; } include_once 'PEAR/PackageFile/Parser/v' . $version[0] . '.php'; $version = $version[0]; $class = "PEAR_PackageFile_Parser_v$version"; $a = new $class; return $a; } /** * For simpler unit-testing * @return string */ function getClassPrefix() { return 'PEAR_PackageFile_v'; } /** * Create a PEAR_PackageFile_v* of a given version. * @param int $version * @return PEAR_PackageFile_v1|PEAR_PackageFile_v1 */ function &factory($version) { if (!in_array($version[0], array('1', '2'))) { $a = false; return $a; } include_once 'PEAR/PackageFile/v' . $version[0] . '.php'; $version = $version[0]; $class = $this->getClassPrefix() . $version; $a = new $class; return $a; } /** * Create a PEAR_PackageFile_v* from its toArray() method * * WARNING: no validation is performed, the array is assumed to be valid, * always parse from xml if you want validation. * @param array $arr * @return PEAR_PackageFileManager_v1|PEAR_PackageFileManager_v2 * @uses factory() to construct the returned object. */ function &fromArray($arr) { if (isset($arr['xsdversion'])) { $obj = &$this->factory($arr['xsdversion']); if ($this->_logger) { $obj->setLogger($this->_logger); } $obj->setConfig($this->_config); $obj->fromArray($arr); return $obj; } if (isset($arr['package']['attribs']['version'])) { $obj = &$this->factory($arr['package']['attribs']['version']); } else { $obj = &$this->factory('1.0'); } if ($this->_logger) { $obj->setLogger($this->_logger); } $obj->setConfig($this->_config); $obj->fromArray($arr); return $obj; } /** * Create a PEAR_PackageFile_v* from an XML string. * @access public * @param string $data contents of package.xml file * @param int $state package state (one of PEAR_VALIDATE_* constants) * @param string $file full path to the package.xml file (and the files * it references) * @param string $archive optional name of the archive that the XML was * extracted from, if any * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2 * @uses parserFactory() to construct a parser to load the package. */ function &fromXmlString($data, $state, $file, $archive = false) { if (preg_match('/]+version=[\'"]([0-9]+\.[0-9]+)[\'"]/', $data, $packageversion)) { if (!in_array($packageversion[1], array('1.0', '2.0', '2.1'))) { return PEAR::raiseError('package.xml version "' . $packageversion[1] . '" is not supported, only 1.0, 2.0, and 2.1 are supported.'); } $object = &$this->parserFactory($packageversion[1]); if ($this->_logger) { $object->setLogger($this->_logger); } $object->setConfig($this->_config); $pf = $object->parse($data, $file, $archive); if (PEAR::isError($pf)) { return $pf; } if ($this->_rawReturn) { return $pf; } if (!$pf->validate($state)) {; if ($this->_config->get('verbose') > 0 && $this->_logger && $pf->getValidationWarnings(false) ) { foreach ($pf->getValidationWarnings(false) as $warning) { $this->_logger->log(0, 'ERROR: ' . $warning['message']); } } $a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed', 2, null, null, $pf->getValidationWarnings()); return $a; } if ($this->_logger && $pf->getValidationWarnings(false)) { foreach ($pf->getValidationWarnings() as $warning) { $this->_logger->log(0, 'WARNING: ' . $warning['message']); } } if (method_exists($pf, 'flattenFilelist')) { $pf->flattenFilelist(); // for v2 } return $pf; } elseif (preg_match('/]+version=[\'"]([^"\']+)[\'"]/', $data, $packageversion)) { $a = PEAR::raiseError('package.xml file "' . $file . '" has unsupported package.xml version "' . $packageversion[1] . '"'); return $a; } else { if (!class_exists('PEAR_ErrorStack')) { require_once 'PEAR/ErrorStack.php'; } PEAR_ErrorStack::staticPush('PEAR_PackageFile', PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION, 'warning', array('xml' => $data), 'package.xml "' . $file . '" has no package.xml version'); $object = &$this->parserFactory('1.0'); $object->setConfig($this->_config); $pf = $object->parse($data, $file, $archive); if (PEAR::isError($pf)) { return $pf; } if ($this->_rawReturn) { return $pf; } if (!$pf->validate($state)) { $a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed', 2, null, null, $pf->getValidationWarnings()); return $a; } if ($this->_logger && $pf->getValidationWarnings(false)) { foreach ($pf->getValidationWarnings() as $warning) { $this->_logger->log(0, 'WARNING: ' . $warning['message']); } } if (method_exists($pf, 'flattenFilelist')) { $pf->flattenFilelist(); // for v2 } return $pf; } } /** * Register a temporary file or directory. When the destructor is * executed, all registered temporary files and directories are * removed. * * @param string $file name of file or directory * @return void */ static function addTempFile($file) { $GLOBALS['_PEAR_Common_tempfiles'][] = $file; } /** * Create a PEAR_PackageFile_v* from a compressed Tar or Tgz file. * @access public * @param string contents of package.xml file * @param int package state (one of PEAR_VALIDATE_* constants) * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2 * @using Archive_Tar to extract the files * @using fromPackageFile() to load the package after the package.xml * file is extracted. */ function &fromTgzFile($file, $state) { if (!class_exists('Archive_Tar')) { require_once 'Archive/Tar.php'; } $tar = new Archive_Tar($file); if ($this->_debug <= 1) { $tar->pushErrorHandling(PEAR_ERROR_RETURN); } $content = $tar->listContent(); if ($this->_debug <= 1) { $tar->popErrorHandling(); } if (!is_array($content)) { if (is_string($file) && strlen($file) < 255 && (!file_exists($file) || !@is_file($file))) { $ret = PEAR::raiseError("could not open file \"$file\""); return $ret; } $file = realpath($file); $ret = PEAR::raiseError("Could not get contents of package \"$file\"". '. Invalid tgz file.'); return $ret; } if (!count($content) && !@is_file($file)) { $ret = PEAR::raiseError("could not open file \"$file\""); return $ret; } $xml = null; $origfile = $file; foreach ($content as $file) { $name = $file['filename']; if ($name == 'package2.xml') { // allow a .tgz to distribute both versions $xml = $name; break; } if ($name == 'package.xml') { $xml = $name; break; } elseif (preg_match('/package.xml$/', $name, $match)) { $xml = $name; break; } } $tmpdir = System::mktemp('-t "' . $this->_config->get('temp_dir') . '" -d pear'); if ($tmpdir === false) { $ret = PEAR::raiseError("there was a problem with getting the configured temp directory"); return $ret; } PEAR_PackageFile::addTempFile($tmpdir); $this->_extractErrors(); PEAR::staticPushErrorHandling(PEAR_ERROR_CALLBACK, array($this, '_extractErrors')); if (!$xml || !$tar->extractList(array($xml), $tmpdir)) { $extra = implode("\n", $this->_extractErrors()); if ($extra) { $extra = ' ' . $extra; } PEAR::staticPopErrorHandling(); $ret = PEAR::raiseError('could not extract the package.xml file from "' . $origfile . '"' . $extra); return $ret; } PEAR::staticPopErrorHandling(); $ret = &PEAR_PackageFile::fromPackageFile("$tmpdir/$xml", $state, $origfile); return $ret; } /** * helper callback for extracting Archive_Tar errors * * @param PEAR_Error|null $err * @return array * @access private */ function _extractErrors($err = null) { static $errors = array(); if ($err === null) { $e = $errors; $errors = array(); return $e; } $errors[] = $err->getMessage(); } /** * Create a PEAR_PackageFile_v* from a package.xml file. * * @access public * @param string $descfile name of package xml file * @param int $state package state (one of PEAR_VALIDATE_* constants) * @param string|false $archive name of the archive this package.xml came * from, if any * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2 * @uses PEAR_PackageFile::fromXmlString to create the oject after the * XML is loaded from the package.xml file. */ function &fromPackageFile($descfile, $state, $archive = false) { $fp = false; if (is_string($descfile) && strlen($descfile) < 255 && ( !file_exists($descfile) || !is_file($descfile) || !is_readable($descfile) || (!$fp = @fopen($descfile, 'r')) ) ) { $a = PEAR::raiseError("Unable to open $descfile"); return $a; } // read the whole thing so we only get one cdata callback // for each block of cdata fclose($fp); $data = file_get_contents($descfile); $ret = &PEAR_PackageFile::fromXmlString($data, $state, $descfile, $archive); return $ret; } /** * Create a PEAR_PackageFile_v* from a .tgz archive or package.xml file. * * This method is able to extract information about a package from a .tgz * archive or from a XML package definition file. * * @access public * @param string $info file name * @param int $state package state (one of PEAR_VALIDATE_* constants) * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2 * @uses fromPackageFile() if the file appears to be XML * @uses fromTgzFile() to load all non-XML files */ function &fromAnyFile($info, $state) { if (is_dir($info)) { $dir_name = realpath($info); if (file_exists($dir_name . '/package.xml')) { $info = PEAR_PackageFile::fromPackageFile($dir_name . '/package.xml', $state); } elseif (file_exists($dir_name . '/package2.xml')) { $info = PEAR_PackageFile::fromPackageFile($dir_name . '/package2.xml', $state); } else { $info = PEAR::raiseError("No package definition found in '$info' directory"); } return $info; } $fp = false; if (is_string($info) && strlen($info) < 255 && (file_exists($info) || ($fp = @fopen($info, 'r'))) ) { if ($fp) { fclose($fp); } $tmp = substr($info, -4); if ($tmp == '.xml') { $info = &PEAR_PackageFile::fromPackageFile($info, $state); } elseif ($tmp == '.tar' || $tmp == '.tgz') { $info = &PEAR_PackageFile::fromTgzFile($info, $state); } else { $fp = fopen($info, 'r'); $test = fread($fp, 5); fclose($fp); if ($test == ' * @author Tomas V. V. Cox * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ /** * base class */ require_once 'PEAR/Common.php'; require_once 'PEAR/PackageFile.php'; require_once 'System.php'; /** * Administration class used to make a PEAR release tarball. * * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ class PEAR_Packager extends PEAR_Common { /** * @var PEAR_Registry */ var $_registry; function package($pkgfile = null, $compress = true, $pkg2 = null) { // {{{ validate supplied package.xml file if (empty($pkgfile)) { $pkgfile = 'package.xml'; } PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $pkg = new PEAR_PackageFile($this->config, $this->debug); $pf = &$pkg->fromPackageFile($pkgfile, PEAR_VALIDATE_NORMAL); $main = &$pf; PEAR::staticPopErrorHandling(); if (PEAR::isError($pf)) { if (is_array($pf->getUserInfo())) { foreach ($pf->getUserInfo() as $error) { $this->log(0, 'Error: ' . $error['message']); } } $this->log(0, $pf->getMessage()); return $this->raiseError("Cannot package, errors in package file"); } foreach ($pf->getValidationWarnings() as $warning) { $this->log(1, 'Warning: ' . $warning['message']); } // }}} if ($pkg2) { $this->log(0, 'Attempting to process the second package file'); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $pf2 = &$pkg->fromPackageFile($pkg2, PEAR_VALIDATE_NORMAL); PEAR::staticPopErrorHandling(); if (PEAR::isError($pf2)) { if (is_array($pf2->getUserInfo())) { foreach ($pf2->getUserInfo() as $error) { $this->log(0, 'Error: ' . $error['message']); } } $this->log(0, $pf2->getMessage()); return $this->raiseError("Cannot package, errors in second package file"); } foreach ($pf2->getValidationWarnings() as $warning) { $this->log(1, 'Warning: ' . $warning['message']); } if ($pf2->getPackagexmlVersion() == '2.0' || $pf2->getPackagexmlVersion() == '2.1' ) { $main = &$pf2; $other = &$pf; } else { $main = &$pf; $other = &$pf2; } if ($main->getPackagexmlVersion() != '2.0' && $main->getPackagexmlVersion() != '2.1') { return PEAR::raiseError('Error: cannot package two package.xml version 1.0, can ' . 'only package together a package.xml 1.0 and package.xml 2.0'); } if ($other->getPackagexmlVersion() != '1.0') { return PEAR::raiseError('Error: cannot package two package.xml version 2.0, can ' . 'only package together a package.xml 1.0 and package.xml 2.0'); } } $main->setLogger($this); if (!$main->validate(PEAR_VALIDATE_PACKAGING)) { foreach ($main->getValidationWarnings() as $warning) { $this->log(0, 'Error: ' . $warning['message']); } return $this->raiseError("Cannot package, errors in package"); } foreach ($main->getValidationWarnings() as $warning) { $this->log(1, 'Warning: ' . $warning['message']); } if ($pkg2) { $other->setLogger($this); $a = false; if (!$other->validate(PEAR_VALIDATE_NORMAL) || $a = !$main->isEquivalent($other)) { foreach ($other->getValidationWarnings() as $warning) { $this->log(0, 'Error: ' . $warning['message']); } foreach ($main->getValidationWarnings() as $warning) { $this->log(0, 'Error: ' . $warning['message']); } if ($a) { return $this->raiseError('The two package.xml files are not equivalent!'); } return $this->raiseError("Cannot package, errors in package"); } foreach ($other->getValidationWarnings() as $warning) { $this->log(1, 'Warning: ' . $warning['message']); } $gen = &$main->getDefaultGenerator(); $tgzfile = $gen->toTgz2($this, $other, $compress); if (PEAR::isError($tgzfile)) { return $tgzfile; } $dest_package = basename($tgzfile); $pkgdir = dirname($pkgfile); // TAR the Package ------------------------------------------- $this->log(1, "Package $dest_package done"); if (file_exists("$pkgdir/CVS/Root")) { $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $pf->getVersion()); $cvstag = "RELEASE_$cvsversion"; $this->log(1, 'Tag the released code with "pear cvstag ' . $main->getPackageFile() . '"'); $this->log(1, "(or set the CVS tag $cvstag by hand)"); } elseif (file_exists("$pkgdir/.svn")) { $svnversion = preg_replace('/[^a-z0-9]/i', '.', $pf->getVersion()); $svntag = $pf->getName() . "-$svnversion"; $this->log(1, 'Tag the released code with "pear svntag ' . $main->getPackageFile() . '"'); $this->log(1, "(or set the SVN tag $svntag by hand)"); } } else { // this branch is executed for single packagefile packaging $gen = &$pf->getDefaultGenerator(); $tgzfile = $gen->toTgz($this, $compress); if (PEAR::isError($tgzfile)) { $this->log(0, $tgzfile->getMessage()); return $this->raiseError("Cannot package, errors in package"); } $dest_package = basename($tgzfile); $pkgdir = dirname($pkgfile); // TAR the Package ------------------------------------------- $this->log(1, "Package $dest_package done"); if (file_exists("$pkgdir/CVS/Root")) { $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $pf->getVersion()); $cvstag = "RELEASE_$cvsversion"; $this->log(1, "Tag the released code with `pear cvstag $pkgfile'"); $this->log(1, "(or set the CVS tag $cvstag by hand)"); } elseif (file_exists("$pkgdir/.svn")) { $svnversion = preg_replace('/[^a-z0-9]/i', '.', $pf->getVersion()); $svntag = $pf->getName() . "-$svnversion"; $this->log(1, "Tag the released code with `pear svntag $pkgfile'"); $this->log(1, "(or set the SVN tag $svntag by hand)"); } } return $dest_package; } }PK=1](Pzpear/PEAR/Proxy.phpnu[config = $config; $this->_parseProxyInfo(); } /** * @access private */ function _parseProxyInfo() { $this->proxy_host = $this->proxy_port = $this->proxy_user = $this->proxy_pass = ''; if ($this->config->get('http_proxy')&& $proxy = parse_url($this->config->get('http_proxy')) ) { $this->proxy_host = isset($proxy['host']) ? $proxy['host'] : null; $this->proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080; $this->proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null; $this->proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null; $this->proxy_schema = (isset($proxy['scheme']) && $proxy['scheme'] == 'https') ? 'https' : 'http'; } } /** * @access private */ function _httpConnect($fp, $host, $port) { fwrite($fp, "CONNECT $host:$port HTTP/1.1\r\n"); fwrite($fp, "Host: $host:$port\r\n"); if ($this->getProxyAuth()) { fwrite($fp, 'Proxy-Authorization: Basic ' . $this->getProxyAuth() . "\r\n"); } fwrite($fp, "\r\n"); while ($line = trim(fgets($fp, 1024))) { if (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) { $code = (int)$matches[1]; /* as per RFC 2817 */ if ($code < 200 || $code >= 300) { return PEAR::raiseError("Establishing a CONNECT tunnel through proxy failed with response code $code"); } } } // connection was successful -- establish SSL through // the tunnel $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT; if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) { $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; } // set the correct hostname for working hostname // verification stream_context_set_option($fp, 'ssl', 'peer_name', $host); // blocking socket needed for // stream_socket_enable_crypto() // see // stream_set_blocking ($fp, true); $crypto_res = stream_socket_enable_crypto($fp, true, $crypto_method); if (!$crypto_res) { return PEAR::raiseError("Could not establish SSL connection through proxy: $crypto_res"); } return true; } /** * get the authorization information for the proxy, encoded to be * passed in the Proxy-Authentication HTTP header. * @return null|string the encoded authentication information if a * proxy and authentication is configured, null * otherwise. */ function getProxyAuth() { if ($this->isProxyConfigured() && $this->proxy_user != '') { return base64_encode($this->proxy_user . ':' . $this->proxy_pass); } return null; } function getProxyUser() { return $this->proxy_user; } /** * Check if we are configured to use a proxy. * * @return boolean true if we are configured to use a proxy, false * otherwise. * @access public */ function isProxyConfigured() { return $this->proxy_host != ''; } /** * Open a socket to a remote server, possibly involving a HTTP * proxy. * * If an HTTP proxy has been configured (http_proxy PEAR_Config * setting), the proxy will be used. * * @param string $host the host to connect to * @param string $port the port to connect to * @param boolean $secure if true, establish a secure connection * using TLS. * @access public */ function openSocket($host, $port, $secure = false) { if ($this->isProxyConfigured()) { $fp = @fsockopen( $this->proxy_host, $this->proxy_port, $errno, $errstr, 15 ); if (!$fp) { return PEAR::raiseError("Connection to the proxy failed: $errstr", -9276); } /* HTTPS is to be used and we have a proxy, use CONNECT verb */ if ($secure) { $res = $this->_httpConnect($fp, $host, $port); if (PEAR::isError($res)) { return $res; } } } else { if ($secure) { $host = 'ssl://' . $host; } $fp = @fsockopen($host, $port, $errno, $errstr); if (!$fp) { return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno); } } return $fp; } } PK=1]h:3Npear/PEAR/Dependency2.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * Required for the PEAR_VALIDATE_* constants */ require_once 'PEAR/Validate.php'; /** * Dependency check for PEAR packages * * This class handles both version 1.0 and 2.0 dependencies * WARNING: *any* changes to this class must be duplicated in the * test_PEAR_Dependency2 class found in tests/PEAR_Dependency2/setup.php.inc, * or unit tests will not actually validate the changes * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_Dependency2 { /** * One of the PEAR_VALIDATE_* states * @see PEAR_VALIDATE_NORMAL * @var integer */ var $_state; /** * Command-line options to install/upgrade/uninstall commands * @param array */ var $_options; /** * @var OS_Guess */ var $_os; /** * @var PEAR_Registry */ var $_registry; /** * @var PEAR_Config */ var $_config; /** * @var PEAR_DependencyDB */ var $_dependencydb; /** * Output of PEAR_Registry::parsedPackageName() * @var array */ var $_currentPackage; /** * @param PEAR_Config * @param array installation options * @param array format of PEAR_Registry::parsedPackageName() * @param int installation state (one of PEAR_VALIDATE_*) */ function __construct(&$config, $installoptions, $package, $state = PEAR_VALIDATE_INSTALLING) { $this->_config = &$config; if (!class_exists('PEAR_DependencyDB')) { require_once 'PEAR/DependencyDB.php'; } if (isset($installoptions['packagingroot'])) { // make sure depdb is in the right location $config->setInstallRoot($installoptions['packagingroot']); } $this->_registry = &$config->getRegistry(); $this->_dependencydb = &PEAR_DependencyDB::singleton($config); if (isset($installoptions['packagingroot'])) { $config->setInstallRoot(false); } $this->_options = $installoptions; $this->_state = $state; if (!class_exists('OS_Guess')) { require_once 'OS/Guess.php'; } $this->_os = new OS_Guess; $this->_currentPackage = $package; } static function _getExtraString($dep) { $extra = ' ('; if (isset($dep['uri'])) { return ''; } if (isset($dep['recommended'])) { $extra .= 'recommended version ' . $dep['recommended']; } else { if (isset($dep['min'])) { $extra .= 'version >= ' . $dep['min']; } if (isset($dep['max'])) { if ($extra != ' (') { $extra .= ', '; } $extra .= 'version <= ' . $dep['max']; } if (isset($dep['exclude'])) { if (!is_array($dep['exclude'])) { $dep['exclude'] = array($dep['exclude']); } if ($extra != ' (') { $extra .= ', '; } $extra .= 'excluded versions: '; foreach ($dep['exclude'] as $i => $exclude) { if ($i) { $extra .= ', '; } $extra .= $exclude; } } } $extra .= ')'; if ($extra == ' ()') { $extra = ''; } return $extra; } /** * This makes unit-testing a heck of a lot easier */ function getPHP_OS() { return PHP_OS; } /** * This makes unit-testing a heck of a lot easier */ function getsysname() { return $this->_os->getSysname(); } /** * Specify a dependency on an OS. Use arch for detailed os/processor information * * There are two generic OS dependencies that will be the most common, unix and windows. * Other options are linux, freebsd, darwin (OS X), sunos, irix, hpux, aix */ function validateOsDependency($dep) { if ($this->_state != PEAR_VALIDATE_INSTALLING && $this->_state != PEAR_VALIDATE_DOWNLOADING) { return true; } if ($dep['name'] == '*') { return true; } $not = isset($dep['conflicts']) ? true : false; switch (strtolower($dep['name'])) { case 'windows' : if ($not) { if (strtolower(substr($this->getPHP_OS(), 0, 3)) == 'win') { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError("Cannot install %s on Windows"); } return $this->warning("warning: Cannot install %s on Windows"); } } else { if (strtolower(substr($this->getPHP_OS(), 0, 3)) != 'win') { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError("Can only install %s on Windows"); } return $this->warning("warning: Can only install %s on Windows"); } } break; case 'unix' : $unices = array('linux', 'freebsd', 'darwin', 'sunos', 'irix', 'hpux', 'aix'); if ($not) { if (in_array($this->getSysname(), $unices)) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError("Cannot install %s on any Unix system"); } return $this->warning( "warning: Cannot install %s on any Unix system"); } } else { if (!in_array($this->getSysname(), $unices)) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError("Can only install %s on a Unix system"); } return $this->warning("warning: Can only install %s on a Unix system"); } } break; default : if ($not) { if (strtolower($dep['name']) == strtolower($this->getSysname())) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('Cannot install %s on ' . $dep['name'] . ' operating system'); } return $this->warning('warning: Cannot install %s on ' . $dep['name'] . ' operating system'); } } else { if (strtolower($dep['name']) != strtolower($this->getSysname())) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('Cannot install %s on ' . $this->getSysname() . ' operating system, can only install on ' . $dep['name']); } return $this->warning('warning: Cannot install %s on ' . $this->getSysname() . ' operating system, can only install on ' . $dep['name']); } } } return true; } /** * This makes unit-testing a heck of a lot easier */ function matchSignature($pattern) { return $this->_os->matchSignature($pattern); } /** * Specify a complex dependency on an OS/processor/kernel version, * Use OS for simple operating system dependency. * * This is the only dependency that accepts an eregable pattern. The pattern * will be matched against the php_uname() output parsed by OS_Guess */ function validateArchDependency($dep) { if ($this->_state != PEAR_VALIDATE_INSTALLING) { return true; } $not = isset($dep['conflicts']) ? true : false; if (!$this->matchSignature($dep['pattern'])) { if (!$not) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s Architecture dependency failed, does not ' . 'match "' . $dep['pattern'] . '"'); } return $this->warning('warning: %s Architecture dependency failed, does ' . 'not match "' . $dep['pattern'] . '"'); } return true; } if ($not) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s Architecture dependency failed, required "' . $dep['pattern'] . '"'); } return $this->warning('warning: %s Architecture dependency failed, ' . 'required "' . $dep['pattern'] . '"'); } return true; } /** * This makes unit-testing a heck of a lot easier */ function extension_loaded($name) { return extension_loaded($name); } /** * This makes unit-testing a heck of a lot easier */ function phpversion($name = null) { if ($name !== null) { return phpversion($name); } return phpversion(); } function validateExtensionDependency($dep, $required = true) { if ($this->_state != PEAR_VALIDATE_INSTALLING && $this->_state != PEAR_VALIDATE_DOWNLOADING) { return true; } $loaded = $this->extension_loaded($dep['name']); $extra = self::_getExtraString($dep); if (isset($dep['exclude'])) { if (!is_array($dep['exclude'])) { $dep['exclude'] = array($dep['exclude']); } } if (!isset($dep['min']) && !isset($dep['max']) && !isset($dep['recommended']) && !isset($dep['exclude']) ) { if ($loaded) { if (isset($dep['conflicts'])) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s conflicts with PHP extension "' . $dep['name'] . '"' . $extra); } return $this->warning('warning: %s conflicts with PHP extension "' . $dep['name'] . '"' . $extra); } return true; } if (isset($dep['conflicts'])) { return true; } if ($required) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s requires PHP extension "' . $dep['name'] . '"' . $extra); } return $this->warning('warning: %s requires PHP extension "' . $dep['name'] . '"' . $extra); } return $this->warning('%s can optionally use PHP extension "' . $dep['name'] . '"' . $extra); } if (!$loaded) { if (isset($dep['conflicts'])) { return true; } if (!$required) { return $this->warning('%s can optionally use PHP extension "' . $dep['name'] . '"' . $extra); } if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s requires PHP extension "' . $dep['name'] . '"' . $extra); } return $this->warning('warning: %s requires PHP extension "' . $dep['name'] . '"' . $extra); } $version = (string) $this->phpversion($dep['name']); if (empty($version)) { $version = '0'; } $fail = false; if (isset($dep['min']) && !version_compare($version, $dep['min'], '>=')) { $fail = true; } if (isset($dep['max']) && !version_compare($version, $dep['max'], '<=')) { $fail = true; } if ($fail && !isset($dep['conflicts'])) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s requires PHP extension "' . $dep['name'] . '"' . $extra . ', installed version is ' . $version); } return $this->warning('warning: %s requires PHP extension "' . $dep['name'] . '"' . $extra . ', installed version is ' . $version); } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail && isset($dep['conflicts'])) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s conflicts with PHP extension "' . $dep['name'] . '"' . $extra . ', installed version is ' . $version); } return $this->warning('warning: %s conflicts with PHP extension "' . $dep['name'] . '"' . $extra . ', installed version is ' . $version); } if (isset($dep['exclude'])) { foreach ($dep['exclude'] as $exclude) { if (version_compare($version, $exclude, '==')) { if (isset($dep['conflicts'])) { continue; } if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s is not compatible with PHP extension "' . $dep['name'] . '" version ' . $exclude); } return $this->warning('warning: %s is not compatible with PHP extension "' . $dep['name'] . '" version ' . $exclude); } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s conflicts with PHP extension "' . $dep['name'] . '"' . $extra . ', installed version is ' . $version); } return $this->warning('warning: %s conflicts with PHP extension "' . $dep['name'] . '"' . $extra . ', installed version is ' . $version); } } } if (isset($dep['recommended'])) { if (version_compare($version, $dep['recommended'], '==')) { return true; } if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s dependency: PHP extension ' . $dep['name'] . ' version "' . $version . '"' . ' is not the recommended version "' . $dep['recommended'] . '", but may be compatible, use --force to install'); } return $this->warning('warning: %s dependency: PHP extension ' . $dep['name'] . ' version "' . $version . '"' . ' is not the recommended version "' . $dep['recommended'].'"'); } return true; } function validatePhpDependency($dep) { if ($this->_state != PEAR_VALIDATE_INSTALLING && $this->_state != PEAR_VALIDATE_DOWNLOADING) { return true; } $version = $this->phpversion(); $extra = self::_getExtraString($dep); if (isset($dep['exclude'])) { if (!is_array($dep['exclude'])) { $dep['exclude'] = array($dep['exclude']); } } if (isset($dep['min'])) { if (!version_compare($version, $dep['min'], '>=')) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s requires PHP' . $extra . ', installed version is ' . $version); } return $this->warning('warning: %s requires PHP' . $extra . ', installed version is ' . $version); } } if (isset($dep['max'])) { if (!version_compare($version, $dep['max'], '<=')) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s requires PHP' . $extra . ', installed version is ' . $version); } return $this->warning('warning: %s requires PHP' . $extra . ', installed version is ' . $version); } } if (isset($dep['exclude'])) { foreach ($dep['exclude'] as $exclude) { if (version_compare($version, $exclude, '==')) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s is not compatible with PHP version ' . $exclude); } return $this->warning( 'warning: %s is not compatible with PHP version ' . $exclude); } } } return true; } /** * This makes unit-testing a heck of a lot easier */ function getPEARVersion() { return '1.10.18'; } function validatePearinstallerDependency($dep) { $pearversion = $this->getPEARVersion(); $extra = self::_getExtraString($dep); if (isset($dep['exclude'])) { if (!is_array($dep['exclude'])) { $dep['exclude'] = array($dep['exclude']); } } if (version_compare($pearversion, $dep['min'], '<')) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s requires PEAR Installer' . $extra . ', installed version is ' . $pearversion); } return $this->warning('warning: %s requires PEAR Installer' . $extra . ', installed version is ' . $pearversion); } if (isset($dep['max'])) { if (version_compare($pearversion, $dep['max'], '>')) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s requires PEAR Installer' . $extra . ', installed version is ' . $pearversion); } return $this->warning('warning: %s requires PEAR Installer' . $extra . ', installed version is ' . $pearversion); } } if (isset($dep['exclude'])) { if (!isset($dep['exclude'][0])) { $dep['exclude'] = array($dep['exclude']); } foreach ($dep['exclude'] as $exclude) { if (version_compare($exclude, $pearversion, '==')) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s is not compatible with PEAR Installer ' . 'version ' . $exclude); } return $this->warning('warning: %s is not compatible with PEAR ' . 'Installer version ' . $exclude); } } } return true; } function validateSubpackageDependency($dep, $required, $params) { return $this->validatePackageDependency($dep, $required, $params); } /** * @param array dependency information (2.0 format) * @param boolean whether this is a required dependency * @param array a list of downloaded packages to be installed, if any * @param boolean if true, then deps on pear.php.net that fail will also check * against pecl.php.net packages to accommodate extensions that have * moved to pecl.php.net from pear.php.net */ function validatePackageDependency($dep, $required, $params, $depv1 = false) { if ($this->_state != PEAR_VALIDATE_INSTALLING && $this->_state != PEAR_VALIDATE_DOWNLOADING) { return true; } if (isset($dep['providesextension'])) { if ($this->extension_loaded($dep['providesextension'])) { $save = $dep; $subdep = $dep; $subdep['name'] = $subdep['providesextension']; PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $ret = $this->validateExtensionDependency($subdep, $required); PEAR::popErrorHandling(); if (!PEAR::isError($ret)) { return true; } } } if ($this->_state == PEAR_VALIDATE_INSTALLING) { return $this->_validatePackageInstall($dep, $required, $depv1); } if ($this->_state == PEAR_VALIDATE_DOWNLOADING) { return $this->_validatePackageDownload($dep, $required, $params, $depv1); } } function _validatePackageDownload($dep, $required, $params, $depv1 = false) { $dep['package'] = $dep['name']; if (isset($dep['uri'])) { $dep['channel'] = '__uri'; } $depname = $this->_registry->parsedPackageNameToString($dep, true); $found = false; foreach ($params as $param) { if ($param->isEqual( array('package' => $dep['name'], 'channel' => $dep['channel']))) { $found = true; break; } if ($depv1 && $dep['channel'] == 'pear.php.net') { if ($param->isEqual( array('package' => $dep['name'], 'channel' => 'pecl.php.net'))) { $found = true; break; } } } if (!$found && isset($dep['providesextension'])) { foreach ($params as $param) { if ($param->isExtension($dep['providesextension'])) { $found = true; break; } } } if ($found) { $version = $param->getVersion(); $installed = false; $downloaded = true; } else { if ($this->_registry->packageExists($dep['name'], $dep['channel'])) { $installed = true; $downloaded = false; $version = $this->_registry->packageinfo($dep['name'], 'version', $dep['channel']); } else { if ($dep['channel'] == 'pecl.php.net' && $this->_registry->packageExists($dep['name'], 'pear.php.net')) { $installed = true; $downloaded = false; $version = $this->_registry->packageinfo($dep['name'], 'version', 'pear.php.net'); } else { $version = 'not installed or downloaded'; $installed = false; $downloaded = false; } } } $extra = self::_getExtraString($dep); if (isset($dep['exclude']) && !is_array($dep['exclude'])) { $dep['exclude'] = array($dep['exclude']); } if (!isset($dep['min']) && !isset($dep['max']) && !isset($dep['recommended']) && !isset($dep['exclude']) ) { if ($installed || $downloaded) { $installed = $installed ? 'installed' : 'downloaded'; if (isset($dep['conflicts'])) { $rest = ''; if ($version) { $rest = ", $installed version is " . $version; } if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra . $rest); } return $this->warning('warning: %s conflicts with package "' . $depname . '"' . $extra . $rest); } return true; } if (isset($dep['conflicts'])) { return true; } if ($required) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s requires package "' . $depname . '"' . $extra); } return $this->warning('warning: %s requires package "' . $depname . '"' . $extra); } return $this->warning('%s can optionally use package "' . $depname . '"' . $extra); } if (!$installed && !$downloaded) { if (isset($dep['conflicts'])) { return true; } if ($required) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s requires package "' . $depname . '"' . $extra); } return $this->warning('warning: %s requires package "' . $depname . '"' . $extra); } return $this->warning('%s can optionally use package "' . $depname . '"' . $extra); } $fail = false; if (isset($dep['min']) && version_compare($version, $dep['min'], '<')) { $fail = true; } if (isset($dep['max']) && version_compare($version, $dep['max'], '>')) { $fail = true; } if ($fail && !isset($dep['conflicts'])) { $installed = $installed ? 'installed' : 'downloaded'; $dep['package'] = $dep['name']; $dep = $this->_registry->parsedPackageNameToString($dep, true); if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s requires package "' . $depname . '"' . $extra . ", $installed version is " . $version); } return $this->warning('warning: %s requires package "' . $depname . '"' . $extra . ", $installed version is " . $version); } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail && isset($dep['conflicts']) && !isset($dep['exclude'])) { $installed = $installed ? 'installed' : 'downloaded'; if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra . ", $installed version is " . $version); } return $this->warning('warning: %s conflicts with package "' . $depname . '"' . $extra . ", $installed version is " . $version); } if (isset($dep['exclude'])) { $installed = $installed ? 'installed' : 'downloaded'; foreach ($dep['exclude'] as $exclude) { if (version_compare($version, $exclude, '==') && !isset($dep['conflicts'])) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force']) ) { return $this->raiseError('%s is not compatible with ' . $installed . ' package "' . $depname . '" version ' . $exclude); } return $this->warning('warning: %s is not compatible with ' . $installed . ' package "' . $depname . '" version ' . $exclude); } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) { $installed = $installed ? 'installed' : 'downloaded'; if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra . ", $installed version is " . $version); } return $this->warning('warning: %s conflicts with package "' . $depname . '"' . $extra . ", $installed version is " . $version); } } } if (isset($dep['recommended'])) { $installed = $installed ? 'installed' : 'downloaded'; if (version_compare($version, $dep['recommended'], '==')) { return true; } if (!$found && $installed) { $param = $this->_registry->getPackage($dep['name'], $dep['channel']); } if ($param) { $found = false; foreach ($params as $parent) { if ($parent->isEqual($this->_currentPackage)) { $found = true; break; } } if ($found) { if ($param->isCompatible($parent)) { return true; } } else { // this is for validPackage() calls $parent = $this->_registry->getPackage($this->_currentPackage['package'], $this->_currentPackage['channel']); if ($parent !== null && $param->isCompatible($parent)) { return true; } } } if (!isset($this->_options['nodeps']) && !isset($this->_options['force']) && !isset($this->_options['loose']) ) { return $this->raiseError('%s dependency package "' . $depname . '" ' . $installed . ' version ' . $version . ' is not the recommended version ' . $dep['recommended'] . ', but may be compatible, use --force to install'); } return $this->warning('warning: %s dependency package "' . $depname . '" ' . $installed . ' version ' . $version . ' is not the recommended version ' . $dep['recommended']); } return true; } function _validatePackageInstall($dep, $required, $depv1 = false) { return $this->_validatePackageDownload($dep, $required, array(), $depv1); } /** * Verify that uninstalling packages passed in to command line is OK. * * @param PEAR_Installer $dl * @return PEAR_Error|true */ function validatePackageUninstall(&$dl) { if (PEAR::isError($this->_dependencydb)) { return $this->_dependencydb; } $params = array(); // construct an array of "downloaded" packages to fool the package dependency checker // into using these to validate uninstalls of circular dependencies $downloaded = &$dl->getUninstallPackages(); foreach ($downloaded as $i => $pf) { if (!class_exists('PEAR_Downloader_Package')) { require_once 'PEAR/Downloader/Package.php'; } $dp = new PEAR_Downloader_Package($dl); $dp->setPackageFile($downloaded[$i]); $params[$i] = $dp; } // check cache $memyselfandI = strtolower($this->_currentPackage['channel']) . '/' . strtolower($this->_currentPackage['package']); if (isset($dl->___uninstall_package_cache)) { $badpackages = $dl->___uninstall_package_cache; if (isset($badpackages[$memyselfandI]['warnings'])) { foreach ($badpackages[$memyselfandI]['warnings'] as $warning) { $dl->log(0, $warning[0]); } } if (isset($badpackages[$memyselfandI]['errors'])) { foreach ($badpackages[$memyselfandI]['errors'] as $error) { if (is_array($error)) { $dl->log(0, $error[0]); } else { $dl->log(0, $error->getMessage()); } } if (isset($this->_options['nodeps']) || isset($this->_options['force'])) { return $this->warning( 'warning: %s should not be uninstalled, other installed packages depend ' . 'on this package'); } return $this->raiseError( '%s cannot be uninstalled, other installed packages depend on this package'); } return true; } // first, list the immediate parents of each package to be uninstalled $perpackagelist = array(); $allparents = array(); foreach ($params as $i => $param) { $a = array( 'channel' => strtolower($param->getChannel()), 'package' => strtolower($param->getPackage()) ); $deps = $this->_dependencydb->getDependentPackages($a); if ($deps) { foreach ($deps as $d) { $pardeps = $this->_dependencydb->getDependencies($d); foreach ($pardeps as $dep) { if (strtolower($dep['dep']['channel']) == $a['channel'] && strtolower($dep['dep']['name']) == $a['package']) { if (!isset($perpackagelist[$a['channel'] . '/' . $a['package']])) { $perpackagelist[$a['channel'] . '/' . $a['package']] = array(); } $perpackagelist[$a['channel'] . '/' . $a['package']][] = array($d['channel'] . '/' . $d['package'], $dep); if (!isset($allparents[$d['channel'] . '/' . $d['package']])) { $allparents[$d['channel'] . '/' . $d['package']] = array(); } if (!isset($allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']])) { $allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']] = array(); } $allparents[$d['channel'] . '/' . $d['package']] [$a['channel'] . '/' . $a['package']][] = array($d, $dep); } } } } } // next, remove any packages from the parents list that are not installed $remove = array(); foreach ($allparents as $parent => $d1) { foreach ($d1 as $d) { if ($this->_registry->packageExists($d[0][0]['package'], $d[0][0]['channel'])) { continue; } $remove[$parent] = true; } } // next remove any packages from the parents list that are not passed in for // uninstallation foreach ($allparents as $parent => $d1) { foreach ($d1 as $d) { foreach ($params as $param) { if (strtolower($param->getChannel()) == $d[0][0]['channel'] && strtolower($param->getPackage()) == $d[0][0]['package']) { // found it continue 3; } } $remove[$parent] = true; } } // remove all packages whose dependencies fail // save which ones failed for error reporting $badchildren = array(); do { $fail = false; foreach ($remove as $package => $unused) { if (!isset($allparents[$package])) { continue; } foreach ($allparents[$package] as $kid => $d1) { foreach ($d1 as $depinfo) { if ($depinfo[1]['type'] != 'optional') { if (isset($badchildren[$kid])) { continue; } $badchildren[$kid] = true; $remove[$kid] = true; $fail = true; continue 2; } } } if ($fail) { // start over, we removed some children continue 2; } } } while ($fail); // next, construct the list of packages that can't be uninstalled $badpackages = array(); $save = $this->_currentPackage; foreach ($perpackagelist as $package => $packagedeps) { foreach ($packagedeps as $parent) { if (!isset($remove[$parent[0]])) { continue; } $packagename = $this->_registry->parsePackageName($parent[0]); $packagename['channel'] = $this->_registry->channelAlias($packagename['channel']); $pa = $this->_registry->getPackage($packagename['package'], $packagename['channel']); $packagename['package'] = $pa->getPackage(); $this->_currentPackage = $packagename; // parent is not present in uninstall list, make sure we can actually // uninstall it (parent dep is optional) $parentname['channel'] = $this->_registry->channelAlias($parent[1]['dep']['channel']); $pa = $this->_registry->getPackage($parent[1]['dep']['name'], $parent[1]['dep']['channel']); $parentname['package'] = $pa->getPackage(); $parent[1]['dep']['package'] = $parentname['package']; $parent[1]['dep']['channel'] = $parentname['channel']; if ($parent[1]['type'] == 'optional') { $test = $this->_validatePackageUninstall($parent[1]['dep'], false, $dl); if ($test !== true) { $badpackages[$package]['warnings'][] = $test; } } else { $test = $this->_validatePackageUninstall($parent[1]['dep'], true, $dl); if ($test !== true) { $badpackages[$package]['errors'][] = $test; } } } } $this->_currentPackage = $save; $dl->___uninstall_package_cache = $badpackages; if (isset($badpackages[$memyselfandI])) { if (isset($badpackages[$memyselfandI]['warnings'])) { foreach ($badpackages[$memyselfandI]['warnings'] as $warning) { $dl->log(0, $warning[0]); } } if (isset($badpackages[$memyselfandI]['errors'])) { foreach ($badpackages[$memyselfandI]['errors'] as $error) { if (is_array($error)) { $dl->log(0, $error[0]); } else { $dl->log(0, $error->getMessage()); } } if (isset($this->_options['nodeps']) || isset($this->_options['force'])) { return $this->warning( 'warning: %s should not be uninstalled, other installed packages depend ' . 'on this package'); } return $this->raiseError( '%s cannot be uninstalled, other installed packages depend on this package'); } } return true; } function _validatePackageUninstall($dep, $required, $dl) { $depname = $this->_registry->parsedPackageNameToString($dep, true); $version = $this->_registry->packageinfo($dep['package'], 'version', $dep['channel']); if (!$version) { return true; } $extra = self::_getExtraString($dep); if (isset($dep['exclude']) && !is_array($dep['exclude'])) { $dep['exclude'] = array($dep['exclude']); } if (isset($dep['conflicts'])) { return true; // uninstall OK - these packages conflict (probably installed with --force) } if (!isset($dep['min']) && !isset($dep['max'])) { if (!$required) { return $this->warning('"' . $depname . '" can be optionally used by ' . 'installed package %s' . $extra); } if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('"' . $depname . '" is required by ' . 'installed package %s' . $extra); } return $this->warning('warning: "' . $depname . '" is required by ' . 'installed package %s' . $extra); } $fail = false; if (isset($dep['min']) && version_compare($version, $dep['min'], '>=')) { $fail = true; } if (isset($dep['max']) && version_compare($version, $dep['max'], '<=')) { $fail = true; } // we re-use this variable, preserve the original value $saverequired = $required; if (!$required) { return $this->warning($depname . $extra . ' can be optionally used by installed package' . ' "%s"'); } if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError($depname . $extra . ' is required by installed package' . ' "%s"'); } return $this->raiseError('warning: ' . $depname . $extra . ' is required by installed package "%s"'); } /** * validate a downloaded package against installed packages * * As of PEAR 1.4.3, this will only validate * * @param array|PEAR_Downloader_Package|PEAR_PackageFile_v1|PEAR_PackageFile_v2 * $pkg package identifier (either * array('package' => blah, 'channel' => blah) or an array with * index 'info' referencing an object) * @param PEAR_Downloader $dl * @param array $params full list of packages to install * @return true|PEAR_Error */ function validatePackage($pkg, &$dl, $params = array()) { if (is_array($pkg) && isset($pkg['info'])) { $deps = $this->_dependencydb->getDependentPackageDependencies($pkg['info']); } else { $deps = $this->_dependencydb->getDependentPackageDependencies($pkg); } $fail = false; if ($deps) { if (!class_exists('PEAR_Downloader_Package')) { require_once 'PEAR/Downloader/Package.php'; } $dp = new PEAR_Downloader_Package($dl); if (is_object($pkg)) { $dp->setPackageFile($pkg); } else { $dp->setDownloadURL($pkg); } PEAR::pushErrorHandling(PEAR_ERROR_RETURN); foreach ($deps as $channel => $info) { foreach ($info as $package => $ds) { foreach ($params as $packd) { if (strtolower($packd->getPackage()) == strtolower($package) && $packd->getChannel() == $channel) { $dl->log(3, 'skipping installed package check of "' . $this->_registry->parsedPackageNameToString( array('channel' => $channel, 'package' => $package), true) . '", version "' . $packd->getVersion() . '" will be ' . 'downloaded and installed'); continue 2; // jump to next package } } foreach ($ds as $d) { $checker = new PEAR_Dependency2($this->_config, $this->_options, array('channel' => $channel, 'package' => $package), $this->_state); $dep = $d['dep']; $required = $d['type'] == 'required'; $ret = $checker->_validatePackageDownload($dep, $required, array(&$dp)); if (is_array($ret)) { $dl->log(0, $ret[0]); } elseif (PEAR::isError($ret)) { $dl->log(0, $ret->getMessage()); $fail = true; } } } } PEAR::popErrorHandling(); } if ($fail) { return $this->raiseError( '%s cannot be installed, conflicts with installed packages'); } return true; } /** * validate a package.xml 1.0 dependency */ function validateDependency1($dep, $params = array()) { if (!isset($dep['optional'])) { $dep['optional'] = 'no'; } list($newdep, $type) = self::normalizeDep($dep); if (!$newdep) { return $this->raiseError("Invalid Dependency"); } if (method_exists($this, "validate{$type}Dependency")) { return $this->{"validate{$type}Dependency"}($newdep, $dep['optional'] == 'no', $params, true); } } /** * Convert a 1.0 dep into a 2.0 dep */ static function normalizeDep($dep) { $types = array( 'pkg' => 'Package', 'ext' => 'Extension', 'os' => 'Os', 'php' => 'Php' ); if (!isset($types[$dep['type']])) { return array(false, false); } $type = $types[$dep['type']]; $newdep = array(); switch ($type) { case 'Package' : $newdep['channel'] = 'pear.php.net'; case 'Extension' : case 'Os' : $newdep['name'] = $dep['name']; break; } $dep['rel'] = PEAR_Dependency2::signOperator($dep['rel']); switch ($dep['rel']) { case 'has' : return array($newdep, $type); break; case 'not' : $newdep['conflicts'] = true; break; case '>=' : case '>' : $newdep['min'] = $dep['version']; if ($dep['rel'] == '>') { $newdep['exclude'] = $dep['version']; } break; case '<=' : case '<' : $newdep['max'] = $dep['version']; if ($dep['rel'] == '<') { $newdep['exclude'] = $dep['version']; } break; case 'ne' : case '!=' : $newdep['min'] = '0'; $newdep['max'] = '100000'; $newdep['exclude'] = $dep['version']; break; case '==' : $newdep['min'] = $dep['version']; $newdep['max'] = $dep['version']; break; } if ($type == 'Php') { if (!isset($newdep['min'])) { $newdep['min'] = '4.4.0'; } if (!isset($newdep['max'])) { $newdep['max'] = '6.0.0'; } } return array($newdep, $type); } /** * Converts text comparing operators to them sign equivalents * * Example: 'ge' to '>=' * * @access public * @param string Operator * @return string Sign equivalent */ static function signOperator($operator) { switch($operator) { case 'lt': return '<'; case 'le': return '<='; case 'gt': return '>'; case 'ge': return '>='; case 'eq': return '=='; case 'ne': return '!='; default: return $operator; } } function raiseError($msg) { if (isset($this->_options['ignore-errors'])) { return $this->warning($msg); } return PEAR::raiseError(sprintf($msg, $this->_registry->parsedPackageNameToString( $this->_currentPackage, true))); } function warning($msg) { return array(sprintf($msg, $this->_registry->parsedPackageNameToString( $this->_currentPackage, true))); } } PK=1]_=@@#pear/PEAR/PackageFile/Parser/v1.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * package.xml abstraction class */ require_once 'PEAR/PackageFile/v1.php'; /** * Parser for package.xml version 1.0 * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: @PEAR-VER@ * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_PackageFile_Parser_v1 { var $_registry; var $_config; var $_logger; /** * BC hack to allow PEAR_Common::infoFromString() to sort of * work with the version 2.0 format - there's no filelist though * @param PEAR_PackageFile_v2 */ function fromV2($packagefile) { $info = $packagefile->getArray(true); $ret = new PEAR_PackageFile_v1; $ret->fromArray($info['old']); } function setConfig(&$c) { $this->_config = &$c; $this->_registry = &$c->getRegistry(); } function setLogger(&$l) { $this->_logger = &$l; } /** * @param string contents of package.xml file, version 1.0 * @return bool success of parsing */ function &parse($data, $file, $archive = false) { if (!extension_loaded('xml')) { return PEAR::raiseError('Cannot create xml parser for parsing package.xml, no xml extension'); } $xp = xml_parser_create(); if (!$xp) { $a = &PEAR::raiseError('Cannot create xml parser for parsing package.xml'); return $a; } xml_set_object($xp, $this); xml_set_element_handler($xp, '_element_start_1_0', '_element_end_1_0'); xml_set_character_data_handler($xp, '_pkginfo_cdata_1_0'); xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, false); $this->element_stack = array(); $this->_packageInfo = array('provides' => array()); $this->current_element = false; unset($this->dir_install); $this->_packageInfo['filelist'] = array(); $this->filelist =& $this->_packageInfo['filelist']; $this->dir_names = array(); $this->in_changelog = false; $this->d_i = 0; $this->cdata = ''; $this->_isValid = true; if (!xml_parse($xp, $data, 1)) { $code = xml_get_error_code($xp); $line = xml_get_current_line_number($xp); xml_parser_free($xp); $a = PEAR::raiseError(sprintf("XML error: %s at line %d", $str = xml_error_string($code), $line), 2); return $a; } xml_parser_free($xp); $pf = new PEAR_PackageFile_v1; $pf->setConfig($this->_config); if (isset($this->_logger)) { $pf->setLogger($this->_logger); } $pf->setPackagefile($file, $archive); $pf->fromArray($this->_packageInfo); return $pf; } // {{{ _unIndent() /** * Unindent given string * * @param string $str The string that has to be unindented. * @return string * @access private */ function _unIndent($str) { // remove leading newlines $str = preg_replace('/^[\r\n]+/', '', $str); // find whitespace at the beginning of the first line $indent_len = strspn($str, " \t"); $indent = substr($str, 0, $indent_len); $data = ''; // remove the same amount of whitespace from following lines foreach (explode("\n", $str) as $line) { if (substr($line, 0, $indent_len) == $indent) { $data .= substr($line, $indent_len) . "\n"; } elseif (trim(substr($line, 0, $indent_len))) { $data .= ltrim($line); } } return $data; } // Support for package DTD v1.0: // {{{ _element_start_1_0() /** * XML parser callback for ending elements. Used for version 1.0 * packages. * * @param resource $xp XML parser resource * @param string $name name of ending element * * @return void * * @access private */ function _element_start_1_0($xp, $name, $attribs) { array_push($this->element_stack, $name); $this->current_element = $name; $spos = sizeof($this->element_stack) - 2; $this->prev_element = ($spos >= 0) ? $this->element_stack[$spos] : ''; $this->current_attributes = $attribs; $this->cdata = ''; switch ($name) { case 'dir': if ($this->in_changelog) { break; } if (array_key_exists('name', $attribs) && $attribs['name'] != '/') { $attribs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $attribs['name']); if (strrpos($attribs['name'], '/') === strlen($attribs['name']) - 1) { $attribs['name'] = substr($attribs['name'], 0, strlen($attribs['name']) - 1); } if (strpos($attribs['name'], '/') === 0) { $attribs['name'] = substr($attribs['name'], 1); } $this->dir_names[] = $attribs['name']; } if (isset($attribs['baseinstalldir'])) { $this->dir_install = $attribs['baseinstalldir']; } if (isset($attribs['role'])) { $this->dir_role = $attribs['role']; } break; case 'file': if ($this->in_changelog) { break; } if (isset($attribs['name'])) { $path = ''; if (count($this->dir_names)) { foreach ($this->dir_names as $dir) { $path .= $dir . '/'; } } $path .= preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $attribs['name']); unset($attribs['name']); $this->current_path = $path; $this->filelist[$path] = $attribs; // Set the baseinstalldir only if the file don't have this attrib if (!isset($this->filelist[$path]['baseinstalldir']) && isset($this->dir_install)) { $this->filelist[$path]['baseinstalldir'] = $this->dir_install; } // Set the Role if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) { $this->filelist[$path]['role'] = $this->dir_role; } } break; case 'replace': if (!$this->in_changelog) { $this->filelist[$this->current_path]['replacements'][] = $attribs; } break; case 'maintainers': $this->_packageInfo['maintainers'] = array(); $this->m_i = 0; // maintainers array index break; case 'maintainer': // compatibility check if (!isset($this->_packageInfo['maintainers'])) { $this->_packageInfo['maintainers'] = array(); $this->m_i = 0; } $this->_packageInfo['maintainers'][$this->m_i] = array(); $this->current_maintainer =& $this->_packageInfo['maintainers'][$this->m_i]; break; case 'changelog': $this->_packageInfo['changelog'] = array(); $this->c_i = 0; // changelog array index $this->in_changelog = true; break; case 'release': if ($this->in_changelog) { $this->_packageInfo['changelog'][$this->c_i] = array(); $this->current_release = &$this->_packageInfo['changelog'][$this->c_i]; } else { $this->current_release = &$this->_packageInfo; } break; case 'deps': if (!$this->in_changelog) { $this->_packageInfo['release_deps'] = array(); } break; case 'dep': // dependencies array index if (!$this->in_changelog) { $this->d_i++; isset($attribs['type']) ? ($attribs['type'] = strtolower($attribs['type'])) : false; $this->_packageInfo['release_deps'][$this->d_i] = $attribs; } break; case 'configureoptions': if (!$this->in_changelog) { $this->_packageInfo['configure_options'] = array(); } break; case 'configureoption': if (!$this->in_changelog) { $this->_packageInfo['configure_options'][] = $attribs; } break; case 'provides': if (empty($attribs['type']) || empty($attribs['name'])) { break; } $attribs['explicit'] = true; $this->_packageInfo['provides']["$attribs[type];$attribs[name]"] = $attribs; break; case 'package' : if (isset($attribs['version'])) { $this->_packageInfo['xsdversion'] = trim($attribs['version']); } else { $this->_packageInfo['xsdversion'] = '1.0'; } if (isset($attribs['packagerversion'])) { $this->_packageInfo['packagerversion'] = $attribs['packagerversion']; } break; } } // }}} // {{{ _element_end_1_0() /** * XML parser callback for ending elements. Used for version 1.0 * packages. * * @param resource $xp XML parser resource * @param string $name name of ending element * * @return void * * @access private */ function _element_end_1_0($xp, $name) { $data = trim($this->cdata); switch ($name) { case 'name': switch ($this->prev_element) { case 'package': $this->_packageInfo['package'] = $data; break; case 'maintainer': $this->current_maintainer['name'] = $data; break; } break; case 'extends' : $this->_packageInfo['extends'] = $data; break; case 'summary': $this->_packageInfo['summary'] = $data; break; case 'description': $data = $this->_unIndent($this->cdata); $this->_packageInfo['description'] = $data; break; case 'user': $this->current_maintainer['handle'] = $data; break; case 'email': $this->current_maintainer['email'] = $data; break; case 'role': $this->current_maintainer['role'] = $data; break; case 'version': if ($this->in_changelog) { $this->current_release['version'] = $data; } else { $this->_packageInfo['version'] = $data; } break; case 'date': if ($this->in_changelog) { $this->current_release['release_date'] = $data; } else { $this->_packageInfo['release_date'] = $data; } break; case 'notes': // try to "de-indent" release notes in case someone // has been over-indenting their xml ;-) // Trim only on the right side $data = rtrim($this->_unIndent($this->cdata)); if ($this->in_changelog) { $this->current_release['release_notes'] = $data; } else { $this->_packageInfo['release_notes'] = $data; } break; case 'warnings': if ($this->in_changelog) { $this->current_release['release_warnings'] = $data; } else { $this->_packageInfo['release_warnings'] = $data; } break; case 'state': if ($this->in_changelog) { $this->current_release['release_state'] = $data; } else { $this->_packageInfo['release_state'] = $data; } break; case 'license': if ($this->in_changelog) { $this->current_release['release_license'] = $data; } else { $this->_packageInfo['release_license'] = $data; } break; case 'dep': if ($data && !$this->in_changelog) { $this->_packageInfo['release_deps'][$this->d_i]['name'] = $data; } break; case 'dir': if ($this->in_changelog) { break; } array_pop($this->dir_names); break; case 'file': if ($this->in_changelog) { break; } if ($data) { $path = ''; if (count($this->dir_names)) { foreach ($this->dir_names as $dir) { $path .= $dir . '/'; } } $path .= $data; $this->filelist[$path] = $this->current_attributes; // Set the baseinstalldir only if the file don't have this attrib if (!isset($this->filelist[$path]['baseinstalldir']) && isset($this->dir_install)) { $this->filelist[$path]['baseinstalldir'] = $this->dir_install; } // Set the Role if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) { $this->filelist[$path]['role'] = $this->dir_role; } } break; case 'maintainer': if (empty($this->_packageInfo['maintainers'][$this->m_i]['role'])) { $this->_packageInfo['maintainers'][$this->m_i]['role'] = 'lead'; } $this->m_i++; break; case 'release': if ($this->in_changelog) { $this->c_i++; } break; case 'changelog': $this->in_changelog = false; break; } array_pop($this->element_stack); $spos = sizeof($this->element_stack) - 1; $this->current_element = ($spos > 0) ? $this->element_stack[$spos] : ''; $this->cdata = ''; } // }}} // {{{ _pkginfo_cdata_1_0() /** * XML parser callback for character data. Used for version 1.0 * packages. * * @param resource $xp XML parser resource * @param string $name character data * * @return void * * @access private */ function _pkginfo_cdata_1_0($xp, $data) { if (isset($this->cdata)) { $this->cdata .= $data; } } // }}} } ?>PK=1]OWH H #pear/PEAR/PackageFile/Parser/v2.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * base xml parser class */ require_once 'PEAR/XMLParser.php'; require_once 'PEAR/PackageFile/v2.php'; /** * Parser for package.xml version 2.0 * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: @PEAR-VER@ * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_PackageFile_Parser_v2 extends PEAR_XMLParser { var $_config; var $_logger; var $_registry; function setConfig(&$c) { $this->_config = &$c; $this->_registry = &$c->getRegistry(); } function setLogger(&$l) { $this->_logger = &$l; } /** * Unindent given string * * @param string $str The string that has to be unindented. * @return string * @access private */ function _unIndent($str) { // remove leading newlines $str = preg_replace('/^[\r\n]+/', '', $str); // find whitespace at the beginning of the first line $indent_len = strspn($str, " \t"); $indent = substr($str, 0, $indent_len); $data = ''; // remove the same amount of whitespace from following lines foreach (explode("\n", $str) as $line) { if (substr($line, 0, $indent_len) == $indent) { $data .= substr($line, $indent_len) . "\n"; } else { $data .= $line . "\n"; } } return $data; } /** * post-process data * * @param string $data * @param string $element element name */ function postProcess($data, $element) { if ($element == 'notes') { return trim($this->_unIndent($data)); } return trim($data); } /** * @param string * @param string file name of the package.xml * @param string|false name of the archive this package.xml came from, if any * @param string class name to instantiate and return. This must be PEAR_PackageFile_v2 or * a subclass * @return PEAR_PackageFile_v2 */ function parse($data, $file = null, $archive = false, $class = 'PEAR_PackageFile_v2') { if (PEAR::isError($err = parent::parse($data))) { return $err; } $ret = new $class; $ret->encoding = $this->encoding; $ret->setConfig($this->_config); if (isset($this->_logger)) { $ret->setLogger($this->_logger); } $ret->fromArray($this->_unserializedData); $ret->setPackagefile($file, $archive); return $ret; } }PK=1]׳pear/PEAR/PackageFile/v1.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * For error handling */ require_once 'PEAR/ErrorStack.php'; /** * Error code if parsing is attempted with no xml extension */ define('PEAR_PACKAGEFILE_ERROR_NO_XML_EXT', 3); /** * Error code if creating the xml parser resource fails */ define('PEAR_PACKAGEFILE_ERROR_CANT_MAKE_PARSER', 4); /** * Error code used for all sax xml parsing errors */ define('PEAR_PACKAGEFILE_ERROR_PARSER_ERROR', 5); /** * Error code used when there is no name */ define('PEAR_PACKAGEFILE_ERROR_NO_NAME', 6); /** * Error code when a package name is not valid */ define('PEAR_PACKAGEFILE_ERROR_INVALID_NAME', 7); /** * Error code used when no summary is parsed */ define('PEAR_PACKAGEFILE_ERROR_NO_SUMMARY', 8); /** * Error code for summaries that are more than 1 line */ define('PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY', 9); /** * Error code used when no description is present */ define('PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION', 10); /** * Error code used when no license is present */ define('PEAR_PACKAGEFILE_ERROR_NO_LICENSE', 11); /** * Error code used when a version number is not present */ define('PEAR_PACKAGEFILE_ERROR_NO_VERSION', 12); /** * Error code used when a version number is invalid */ define('PEAR_PACKAGEFILE_ERROR_INVALID_VERSION', 13); /** * Error code when release state is missing */ define('PEAR_PACKAGEFILE_ERROR_NO_STATE', 14); /** * Error code when release state is invalid */ define('PEAR_PACKAGEFILE_ERROR_INVALID_STATE', 15); /** * Error code when release state is missing */ define('PEAR_PACKAGEFILE_ERROR_NO_DATE', 16); /** * Error code when release state is invalid */ define('PEAR_PACKAGEFILE_ERROR_INVALID_DATE', 17); /** * Error code when no release notes are found */ define('PEAR_PACKAGEFILE_ERROR_NO_NOTES', 18); /** * Error code when no maintainers are found */ define('PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS', 19); /** * Error code when a maintainer has no handle */ define('PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE', 20); /** * Error code when a maintainer has no handle */ define('PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE', 21); /** * Error code when a maintainer has no name */ define('PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME', 22); /** * Error code when a maintainer has no email */ define('PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL', 23); /** * Error code when a maintainer has no handle */ define('PEAR_PACKAGEFILE_ERROR_INVALID_MAINTROLE', 24); /** * Error code when a dependency is not a PHP dependency, but has no name */ define('PEAR_PACKAGEFILE_ERROR_NO_DEPNAME', 25); /** * Error code when a dependency has no type (pkg, php, etc.) */ define('PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE', 26); /** * Error code when a dependency has no relation (lt, ge, has, etc.) */ define('PEAR_PACKAGEFILE_ERROR_NO_DEPREL', 27); /** * Error code when a dependency is not a 'has' relation, but has no version */ define('PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION', 28); /** * Error code when a dependency has an invalid relation */ define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPREL', 29); /** * Error code when a dependency has an invalid type */ define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPTYPE', 30); /** * Error code when a dependency has an invalid optional option */ define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL', 31); /** * Error code when a dependency is a pkg dependency, and has an invalid package name */ define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPNAME', 32); /** * Error code when a dependency has a channel="foo" attribute, and foo is not a registered channel */ define('PEAR_PACKAGEFILE_ERROR_UNKNOWN_DEPCHANNEL', 33); /** * Error code when rel="has" and version attribute is present. */ define('PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED', 34); /** * Error code when type="php" and dependency name is present */ define('PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED', 35); /** * Error code when a configure option has no name */ define('PEAR_PACKAGEFILE_ERROR_NO_CONFNAME', 36); /** * Error code when a configure option has no name */ define('PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT', 37); /** * Error code when a file in the filelist has an invalid role */ define('PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE', 38); /** * Error code when a file in the filelist has no role */ define('PEAR_PACKAGEFILE_ERROR_NO_FILEROLE', 39); /** * Error code when analyzing a php source file that has parse errors */ define('PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE', 40); /** * Error code when analyzing a php source file reveals a source element * without a package name prefix */ define('PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX', 41); /** * Error code when an unknown channel is specified */ define('PEAR_PACKAGEFILE_ERROR_UNKNOWN_CHANNEL', 42); /** * Error code when no files are found in the filelist */ define('PEAR_PACKAGEFILE_ERROR_NO_FILES', 43); /** * Error code when a file is not valid php according to _analyzeSourceCode() */ define('PEAR_PACKAGEFILE_ERROR_INVALID_FILE', 44); /** * Error code when the channel validator returns an error or warning */ define('PEAR_PACKAGEFILE_ERROR_CHANNELVAL', 45); /** * Error code when a php5 package is packaged in php4 (analysis doesn't work) */ define('PEAR_PACKAGEFILE_ERROR_PHP5', 46); /** * Error code when a file is listed in package.xml but does not exist */ define('PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND', 47); /** * Error code when a * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_PackageFile_v1 { /** * @access private * @var PEAR_ErrorStack * @access private */ var $_stack; /** * A registry object, used to access the package name validation regex for non-standard channels * @var PEAR_Registry * @access private */ var $_registry; /** * An object that contains a log method that matches PEAR_Common::log's signature * @var object * @access private */ var $_logger; /** * Parsed package information * @var array * @access private */ var $_packageInfo; /** * path to package.xml * @var string * @access private */ var $_packageFile; /** * path to package .tgz or false if this is a local/extracted package.xml * @var string * @access private */ var $_archiveFile; /** * @var int * @access private */ var $_isValid = 0; /** * Determines whether this packagefile was initialized only with partial package info * * If this package file was constructed via parsing REST, it will only contain * * - package name * - channel name * - dependencies * @var boolean * @access private */ var $_incomplete = true; /** * @param bool determines whether to return a PEAR_Error object, or use the PEAR_ErrorStack * @param string Name of Error Stack class to use. */ function __construct() { $this->_stack = new PEAR_ErrorStack('PEAR_PackageFile_v1'); $this->_stack->setErrorMessageTemplate($this->_getErrorMessage()); $this->_isValid = 0; } function installBinary($installer) { return false; } function isExtension($name) { return false; } function setConfig(&$config) { $this->_config = &$config; $this->_registry = &$config->getRegistry(); } function setRequestedGroup() { // placeholder } /** * For saving in the registry. * * Set the last version that was installed * @param string */ function setLastInstalledVersion($version) { $this->_packageInfo['_lastversion'] = $version; } /** * @return string|false */ function getLastInstalledVersion() { if (isset($this->_packageInfo['_lastversion'])) { return $this->_packageInfo['_lastversion']; } return false; } function getInstalledBinary() { return false; } function listPostinstallScripts() { return false; } function initPostinstallScripts() { return false; } function setLogger(&$logger) { if ($logger && (!is_object($logger) || !method_exists($logger, 'log'))) { return PEAR::raiseError('Logger must be compatible with PEAR_Common::log'); } $this->_logger = &$logger; } function setPackagefile($file, $archive = false) { $this->_packageFile = $file; $this->_archiveFile = $archive ? $archive : $file; } function getPackageFile() { return isset($this->_packageFile) ? $this->_packageFile : false; } function getPackageType() { return 'php'; } function getArchiveFile() { return $this->_archiveFile; } function packageInfo($field) { if (!is_string($field) || empty($field) || !isset($this->_packageInfo[$field])) { return false; } return $this->_packageInfo[$field]; } function setDirtree($path) { if (!isset($this->_packageInfo['dirtree'])) { $this->_packageInfo['dirtree'] = array(); } $this->_packageInfo['dirtree'][$path] = true; } function getDirtree() { if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) { return $this->_packageInfo['dirtree']; } return false; } function resetDirtree() { unset($this->_packageInfo['dirtree']); } function fromArray($pinfo) { $this->_incomplete = false; $this->_packageInfo = $pinfo; } function isIncomplete() { return $this->_incomplete; } function getChannel() { return 'pear.php.net'; } function getUri() { return false; } function getTime() { return false; } function getExtends() { if (isset($this->_packageInfo['extends'])) { return $this->_packageInfo['extends']; } return false; } /** * @return array */ function toArray() { if (!$this->validate(PEAR_VALIDATE_NORMAL)) { return false; } return $this->getArray(); } function getArray() { return $this->_packageInfo; } function getName() { return $this->getPackage(); } function getPackage() { if (isset($this->_packageInfo['package'])) { return $this->_packageInfo['package']; } return false; } /** * WARNING - don't use this unless you know what you are doing */ function setRawPackage($package) { $this->_packageInfo['package'] = $package; } function setPackage($package) { $this->_packageInfo['package'] = $package; $this->_isValid = false; } function getVersion() { if (isset($this->_packageInfo['version'])) { return $this->_packageInfo['version']; } return false; } function setVersion($version) { $this->_packageInfo['version'] = $version; $this->_isValid = false; } function clearMaintainers() { unset($this->_packageInfo['maintainers']); } function getMaintainers() { if (isset($this->_packageInfo['maintainers'])) { return $this->_packageInfo['maintainers']; } return false; } /** * Adds a new maintainer - no checking of duplicates is performed, use * updatemaintainer for that purpose. */ function addMaintainer($role, $handle, $name, $email) { $this->_packageInfo['maintainers'][] = array('handle' => $handle, 'role' => $role, 'email' => $email, 'name' => $name); $this->_isValid = false; } function updateMaintainer($role, $handle, $name, $email) { $found = false; if (!isset($this->_packageInfo['maintainers']) || !is_array($this->_packageInfo['maintainers'])) { return $this->addMaintainer($role, $handle, $name, $email); } foreach ($this->_packageInfo['maintainers'] as $i => $maintainer) { if ($maintainer['handle'] == $handle) { $found = $i; break; } } if ($found !== false) { unset($this->_packageInfo['maintainers'][$found]); $this->_packageInfo['maintainers'] = array_values($this->_packageInfo['maintainers']); } $this->addMaintainer($role, $handle, $name, $email); } function deleteMaintainer($handle) { $found = false; foreach ($this->_packageInfo['maintainers'] as $i => $maintainer) { if ($maintainer['handle'] == $handle) { $found = $i; break; } } if ($found !== false) { unset($this->_packageInfo['maintainers'][$found]); $this->_packageInfo['maintainers'] = array_values($this->_packageInfo['maintainers']); return true; } return false; } function getState() { if (isset($this->_packageInfo['release_state'])) { return $this->_packageInfo['release_state']; } return false; } function setRawState($state) { $this->_packageInfo['release_state'] = $state; } function setState($state) { $this->_packageInfo['release_state'] = $state; $this->_isValid = false; } function getDate() { if (isset($this->_packageInfo['release_date'])) { return $this->_packageInfo['release_date']; } return false; } function setDate($date) { $this->_packageInfo['release_date'] = $date; $this->_isValid = false; } function getLicense() { if (isset($this->_packageInfo['release_license'])) { return $this->_packageInfo['release_license']; } return false; } function setLicense($date) { $this->_packageInfo['release_license'] = $date; $this->_isValid = false; } function getSummary() { if (isset($this->_packageInfo['summary'])) { return $this->_packageInfo['summary']; } return false; } function setSummary($summary) { $this->_packageInfo['summary'] = $summary; $this->_isValid = false; } function getDescription() { if (isset($this->_packageInfo['description'])) { return $this->_packageInfo['description']; } return false; } function setDescription($desc) { $this->_packageInfo['description'] = $desc; $this->_isValid = false; } function getNotes() { if (isset($this->_packageInfo['release_notes'])) { return $this->_packageInfo['release_notes']; } return false; } function setNotes($notes) { $this->_packageInfo['release_notes'] = $notes; $this->_isValid = false; } function getDeps() { if (isset($this->_packageInfo['release_deps'])) { return $this->_packageInfo['release_deps']; } return false; } /** * Reset dependencies prior to adding new ones */ function clearDeps() { unset($this->_packageInfo['release_deps']); } function addPhpDep($version, $rel) { $this->_isValid = false; $this->_packageInfo['release_deps'][] = array('type' => 'php', 'rel' => $rel, 'version' => $version); } function addPackageDep($name, $version, $rel, $optional = 'no') { $this->_isValid = false; $dep = array('type' => 'pkg', 'name' => $name, 'rel' => $rel, 'optional' => $optional); if ($rel != 'has' && $rel != 'not') { $dep['version'] = $version; } $this->_packageInfo['release_deps'][] = $dep; } function addExtensionDep($name, $version, $rel, $optional = 'no') { $this->_isValid = false; $this->_packageInfo['release_deps'][] = array('type' => 'ext', 'name' => $name, 'rel' => $rel, 'version' => $version, 'optional' => $optional); } /** * WARNING - do not use this function directly unless you know what you're doing */ function setDeps($deps) { $this->_packageInfo['release_deps'] = $deps; } function hasDeps() { return isset($this->_packageInfo['release_deps']) && count($this->_packageInfo['release_deps']); } function getDependencyGroup($group) { return false; } function isCompatible($pf) { return false; } function isSubpackageOf($p) { return $p->isSubpackage($this); } function isSubpackage($p) { return false; } function dependsOn($package, $channel) { if (strtolower($channel) != 'pear.php.net') { return false; } if (!($deps = $this->getDeps())) { return false; } foreach ($deps as $dep) { if ($dep['type'] != 'pkg') { continue; } if (strtolower($dep['name']) == strtolower($package)) { return true; } } return false; } function getConfigureOptions() { if (isset($this->_packageInfo['configure_options'])) { return $this->_packageInfo['configure_options']; } return false; } function hasConfigureOptions() { return isset($this->_packageInfo['configure_options']) && count($this->_packageInfo['configure_options']); } function addConfigureOption($name, $prompt, $default = false) { $o = array('name' => $name, 'prompt' => $prompt); if ($default !== false) { $o['default'] = $default; } if (!isset($this->_packageInfo['configure_options'])) { $this->_packageInfo['configure_options'] = array(); } $this->_packageInfo['configure_options'][] = $o; } function clearConfigureOptions() { unset($this->_packageInfo['configure_options']); } function getProvides() { if (isset($this->_packageInfo['provides'])) { return $this->_packageInfo['provides']; } return false; } function getProvidesExtension() { return false; } function addFile($dir, $file, $attrs) { $dir = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $dir); if ($dir == '/' || $dir == '') { $dir = ''; } else { $dir .= '/'; } $file = $dir . $file; $file = preg_replace('![\\/]+!', '/', $file); $this->_packageInfo['filelist'][$file] = $attrs; } function getInstallationFilelist() { return $this->getFilelist(); } function getFilelist() { if (isset($this->_packageInfo['filelist'])) { return $this->_packageInfo['filelist']; } return false; } function setFileAttribute($file, $attr, $value) { $this->_packageInfo['filelist'][$file][$attr] = $value; } function resetFilelist() { $this->_packageInfo['filelist'] = array(); } function setInstalledAs($file, $path) { if ($path) { return $this->_packageInfo['filelist'][$file]['installed_as'] = $path; } unset($this->_packageInfo['filelist'][$file]['installed_as']); } function installedFile($file, $atts) { if (isset($this->_packageInfo['filelist'][$file])) { $this->_packageInfo['filelist'][$file] = array_merge($this->_packageInfo['filelist'][$file], $atts); } else { $this->_packageInfo['filelist'][$file] = $atts; } } function getChangelog() { if (isset($this->_packageInfo['changelog'])) { return $this->_packageInfo['changelog']; } return false; } function getPackagexmlVersion() { return '1.0'; } /** * Wrapper to {@link PEAR_ErrorStack::getErrors()} * @param boolean determines whether to purge the error stack after retrieving * @return array */ function getValidationWarnings($purge = true) { return $this->_stack->getErrors($purge); } // }}} /** * Validation error. Also marks the object contents as invalid * @param error code * @param array error information * @access private */ function _validateError($code, $params = array()) { $this->_stack->push($code, 'error', $params, false, false, debug_backtrace()); $this->_isValid = false; } /** * Validation warning. Does not mark the object contents invalid. * @param error code * @param array error information * @access private */ function _validateWarning($code, $params = array()) { $this->_stack->push($code, 'warning', $params, false, false, debug_backtrace()); } /** * @param integer error code * @access protected */ function _getErrorMessage() { return array( PEAR_PACKAGEFILE_ERROR_NO_NAME => 'Missing Package Name', PEAR_PACKAGEFILE_ERROR_NO_SUMMARY => 'No summary found', PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY => 'Summary should be on one line', PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION => 'Missing description', PEAR_PACKAGEFILE_ERROR_NO_LICENSE => 'Missing license', PEAR_PACKAGEFILE_ERROR_NO_VERSION => 'No release version found', PEAR_PACKAGEFILE_ERROR_NO_STATE => 'No release state found', PEAR_PACKAGEFILE_ERROR_NO_DATE => 'No release date found', PEAR_PACKAGEFILE_ERROR_NO_NOTES => 'No release notes found', PEAR_PACKAGEFILE_ERROR_NO_LEAD => 'Package must have at least one lead maintainer', PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS => 'No maintainers found, at least one must be defined', PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE => 'Maintainer %index% has no handle (user ID at channel server)', PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE => 'Maintainer %index% has no role', PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME => 'Maintainer %index% has no name', PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL => 'Maintainer %index% has no email', PEAR_PACKAGEFILE_ERROR_NO_DEPNAME => 'Dependency %index% is not a php dependency, and has no name', PEAR_PACKAGEFILE_ERROR_NO_DEPREL => 'Dependency %index% has no relation (rel)', PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE => 'Dependency %index% has no type', PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED => 'PHP Dependency %index% has a name attribute of "%name%" which will be' . ' ignored!', PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION => 'Dependency %index% is not a rel="has" or rel="not" dependency, ' . 'and has no version', PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION => 'Dependency %index% is a type="php" dependency, ' . 'and has no version', PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED => 'Dependency %index% is a rel="%rel%" dependency, versioning is ignored', PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL => 'Dependency %index% has invalid optional value "%opt%", should be yes or no', PEAR_PACKAGEFILE_PHP_NO_NOT => 'Dependency %index%: php dependencies cannot use "not" rel, use "ne"' . ' to exclude specific versions', PEAR_PACKAGEFILE_ERROR_NO_CONFNAME => 'Configure Option %index% has no name', PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT => 'Configure Option %index% has no prompt', PEAR_PACKAGEFILE_ERROR_NO_FILES => 'No files in section of package.xml', PEAR_PACKAGEFILE_ERROR_NO_FILEROLE => 'File "%file%" has no role, expecting one of "%roles%"', PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE => 'File "%file%" has invalid role "%role%", expecting one of "%roles%"', PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME => 'File "%file%" cannot start with ".", cannot package or install', PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE => 'Parser error: invalid PHP found in file "%file%"', PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX => 'in %file%: %type% "%name%" not prefixed with package name "%package%"', PEAR_PACKAGEFILE_ERROR_INVALID_FILE => 'Parser error: invalid PHP file "%file%"', PEAR_PACKAGEFILE_ERROR_CHANNELVAL => 'Channel validator error: field "%field%" - %reason%', PEAR_PACKAGEFILE_ERROR_PHP5 => 'Error, PHP5 token encountered in %file%, analysis should be in PHP5', PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND => 'File "%file%" in package.xml does not exist', PEAR_PACKAGEFILE_ERROR_NON_ISO_CHARS => 'Package.xml contains non-ISO-8859-1 characters, and may not validate', ); } /** * Validate XML package definition file. * * @access public * @return boolean */ function validate($state = PEAR_VALIDATE_NORMAL, $nofilechecking = false) { if (($this->_isValid & $state) == $state) { return true; } $this->_isValid = true; $info = $this->_packageInfo; if (empty($info['package'])) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_NAME); $this->_packageName = $pn = 'unknown'; } else { $this->_packageName = $pn = $info['package']; } if (empty($info['summary'])) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_SUMMARY); } elseif (strpos(trim($info['summary']), "\n") !== false) { $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY, array('summary' => $info['summary'])); } if (empty($info['description'])) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION); } if (empty($info['release_license'])) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_LICENSE); } if (empty($info['version'])) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_VERSION); } if (empty($info['release_state'])) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_STATE); } if (empty($info['release_date'])) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DATE); } if (empty($info['release_notes'])) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_NOTES); } if (empty($info['maintainers'])) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS); } else { $haslead = false; $i = 1; foreach ($info['maintainers'] as $m) { if (empty($m['handle'])) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE, array('index' => $i)); } if (empty($m['role'])) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE, array('index' => $i, 'roles' => PEAR_Common::getUserRoles())); } elseif ($m['role'] == 'lead') { $haslead = true; } if (empty($m['name'])) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME, array('index' => $i)); } if (empty($m['email'])) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL, array('index' => $i)); } $i++; } if (!$haslead) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_LEAD); } } if (!empty($info['release_deps'])) { $i = 1; foreach ($info['release_deps'] as $d) { if (!isset($d['type']) || empty($d['type'])) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE, array('index' => $i, 'types' => PEAR_Common::getDependencyTypes())); continue; } if (!isset($d['rel']) || empty($d['rel'])) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPREL, array('index' => $i, 'rels' => PEAR_Common::getDependencyRelations())); continue; } if (!empty($d['optional'])) { if (!in_array($d['optional'], array('yes', 'no'))) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL, array('index' => $i, 'opt' => $d['optional'])); } } if ($d['rel'] != 'has' && $d['rel'] != 'not' && empty($d['version'])) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION, array('index' => $i)); } elseif (($d['rel'] == 'has' || $d['rel'] == 'not') && !empty($d['version'])) { $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED, array('index' => $i, 'rel' => $d['rel'])); } if ($d['type'] == 'php' && !empty($d['name'])) { $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED, array('index' => $i, 'name' => $d['name'])); } elseif ($d['type'] != 'php' && empty($d['name'])) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPNAME, array('index' => $i)); } if ($d['type'] == 'php' && empty($d['version'])) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION, array('index' => $i)); } if (($d['rel'] == 'not') && ($d['type'] == 'php')) { $this->_validateError(PEAR_PACKAGEFILE_PHP_NO_NOT, array('index' => $i)); } $i++; } } if (!empty($info['configure_options'])) { $i = 1; foreach ($info['configure_options'] as $c) { if (empty($c['name'])) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_CONFNAME, array('index' => $i)); } if (empty($c['prompt'])) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT, array('index' => $i)); } $i++; } } if (empty($info['filelist'])) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_FILES); $errors[] = 'no files'; } else { foreach ($info['filelist'] as $file => $fa) { if (empty($fa['role'])) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_FILEROLE, array('file' => $file, 'roles' => PEAR_Common::getFileRoles())); continue; } elseif (!in_array($fa['role'], PEAR_Common::getFileRoles())) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE, array('file' => $file, 'role' => $fa['role'], 'roles' => PEAR_Common::getFileRoles())); } if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~', str_replace('\\', '/', $file))) { // file contains .. parent directory or . cur directory references $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME, array('file' => $file)); } if (isset($fa['install-as']) && preg_match('~/\.\.?(/|\\z)|^\.\.?/~', str_replace('\\', '/', $fa['install-as']))) { // install-as contains .. parent directory or . cur directory references $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME, array('file' => $file . ' [installed as ' . $fa['install-as'] . ']')); } if (isset($fa['baseinstalldir']) && preg_match('~/\.\.?(/|\\z)|^\.\.?/~', str_replace('\\', '/', $fa['baseinstalldir']))) { // install-as contains .. parent directory or . cur directory references $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME, array('file' => $file . ' [baseinstalldir ' . $fa['baseinstalldir'] . ']')); } } } if (isset($this->_registry) && $this->_isValid) { $chan = $this->_registry->getChannel('pear.php.net'); if (PEAR::isError($chan)) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $chan->getMessage()); return $this->_isValid = 0; } $validator = $chan->getValidationObject(); $validator->setPackageFile($this); $validator->validate($state); $failures = $validator->getFailures(); foreach ($failures['errors'] as $error) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $error); } foreach ($failures['warnings'] as $warning) { $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $warning); } } if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$nofilechecking) { if ($this->_analyzePhpFiles()) { $this->_isValid = true; } } if ($this->_isValid) { return $this->_isValid = $state; } return $this->_isValid = 0; } function _analyzePhpFiles() { if (!$this->_isValid) { return false; } if (!isset($this->_packageFile)) { return false; } $dir_prefix = dirname($this->_packageFile); $common = new PEAR_Common; $log = isset($this->_logger) ? array(&$this->_logger, 'log') : array($common, 'log'); $info = $this->getFilelist(); foreach ($info as $file => $fa) { if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $file)) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND, array('file' => realpath($dir_prefix) . DIRECTORY_SEPARATOR . $file)); continue; } if ($fa['role'] == 'php' && $dir_prefix) { call_user_func_array($log, array(1, "Analyzing $file")); $srcinfo = $this->_analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file); if ($srcinfo) { $this->_buildProvidesArray($srcinfo); } } } $this->_packageName = $pn = $this->getPackage(); $pnl = strlen($pn); if (isset($this->_packageInfo['provides'])) { foreach ((array) $this->_packageInfo['provides'] as $key => $what) { if (isset($what['explicit'])) { // skip conformance checks if the provides entry is // specified in the package.xml file continue; } extract($what); if ($type == 'class') { if (!strncasecmp($name, $pn, $pnl)) { continue; } $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX, array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn)); } elseif ($type == 'function') { if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) { continue; } $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX, array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn)); } } } return $this->_isValid; } /** * Get the default xml generator object * * @return PEAR_PackageFile_Generator_v1 */ function &getDefaultGenerator() { if (!class_exists('PEAR_PackageFile_Generator_v1')) { require_once 'PEAR/PackageFile/Generator/v1.php'; } $a = new PEAR_PackageFile_Generator_v1($this); return $a; } /** * Get the contents of a file listed within the package.xml * @param string * @return string */ function getFileContents($file) { if ($this->_archiveFile == $this->_packageFile) { // unpacked $dir = dirname($this->_packageFile); $file = $dir . DIRECTORY_SEPARATOR . $file; $file = str_replace(array('/', '\\'), array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file); if (file_exists($file) && is_readable($file)) { return implode('', file($file)); } } else { // tgz if (!class_exists('Archive_Tar')) { require_once 'Archive/Tar.php'; } $tar = new Archive_Tar($this->_archiveFile); $tar->pushErrorHandling(PEAR_ERROR_RETURN); if ($file != 'package.xml' && $file != 'package2.xml') { $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file; } $file = $tar->extractInString($file); $tar->popErrorHandling(); if (PEAR::isError($file)) { return PEAR::raiseError("Cannot locate file '$file' in archive"); } return $file; } } // {{{ analyzeSourceCode() /** * Analyze the source code of the given PHP file * * @param string Filename of the PHP file * @return mixed * @access private */ function _analyzeSourceCode($file) { if (!function_exists("token_get_all")) { return false; } if (!defined('T_DOC_COMMENT')) { define('T_DOC_COMMENT', T_COMMENT); } if (!defined('T_INTERFACE')) { define('T_INTERFACE', -1); } if (!defined('T_IMPLEMENTS')) { define('T_IMPLEMENTS', -1); } if (!$fp = @fopen($file, "r")) { return false; } fclose($fp); $contents = file_get_contents($file); $tokens = token_get_all($contents); /* for ($i = 0; $i < sizeof($tokens); $i++) { @list($token, $data) = $tokens[$i]; if (is_string($token)) { var_dump($token); } else { print token_name($token) . ' '; var_dump(rtrim($data)); } } */ $look_for = 0; $paren_level = 0; $bracket_level = 0; $brace_level = 0; $lastphpdoc = ''; $current_class = ''; $current_interface = ''; $current_class_level = -1; $current_function = ''; $current_function_level = -1; $declared_classes = array(); $declared_interfaces = array(); $declared_functions = array(); $declared_methods = array(); $used_classes = array(); $used_functions = array(); $extends = array(); $implements = array(); $nodeps = array(); $inquote = false; $interface = false; for ($i = 0; $i < sizeof($tokens); $i++) { if (is_array($tokens[$i])) { list($token, $data) = $tokens[$i]; } else { $token = $tokens[$i]; $data = ''; } if ($inquote) { if ($token != '"' && $token != T_END_HEREDOC) { continue; } else { $inquote = false; continue; } } switch ($token) { case T_WHITESPACE: break; case ';': if ($interface) { $current_function = ''; $current_function_level = -1; } break; case '"': case T_START_HEREDOC: $inquote = true; break; case T_CURLY_OPEN: case T_DOLLAR_OPEN_CURLY_BRACES: case '{': $brace_level++; continue 2; case '}': $brace_level--; if ($current_class_level == $brace_level) { $current_class = ''; $current_class_level = -1; } if ($current_function_level == $brace_level) { $current_function = ''; $current_function_level = -1; } continue 2; case '[': $bracket_level++; continue 2; case ']': $bracket_level--; continue 2; case '(': $paren_level++; continue 2; case ')': $paren_level--; continue 2; case T_INTERFACE: $interface = true; case T_CLASS: if (($current_class_level != -1) || ($current_function_level != -1)) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE, array('file' => $file)); return false; } case T_FUNCTION: case T_NEW: case T_EXTENDS: case T_IMPLEMENTS: $look_for = $token; continue 2; case T_STRING: if ($look_for == T_CLASS) { $current_class = $data; $current_class_level = $brace_level; $declared_classes[] = $current_class; } elseif ($look_for == T_INTERFACE) { $current_interface = $data; $current_class_level = $brace_level; $declared_interfaces[] = $current_interface; } elseif ($look_for == T_IMPLEMENTS) { $implements[$current_class] = $data; } elseif ($look_for == T_EXTENDS) { $extends[$current_class] = $data; } elseif ($look_for == T_FUNCTION) { if ($current_class) { $current_function = "$current_class::$data"; $declared_methods[$current_class][] = $data; } elseif ($current_interface) { $current_function = "$current_interface::$data"; $declared_methods[$current_interface][] = $data; } else { $current_function = $data; $declared_functions[] = $current_function; } $current_function_level = $brace_level; $m = array(); } elseif ($look_for == T_NEW) { $used_classes[$data] = true; } $look_for = 0; continue 2; case T_VARIABLE: $look_for = 0; continue 2; case T_DOC_COMMENT: case T_COMMENT: if (preg_match('!^/\*\*\s!', $data)) { $lastphpdoc = $data; if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) { $nodeps = array_merge($nodeps, $m[1]); } } continue 2; case T_DOUBLE_COLON: if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) { $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE, array('file' => $file)); return false; } $class = $tokens[$i - 1][1]; if (strtolower($class) != 'parent') { $used_classes[$class] = true; } continue 2; } } return array( "source_file" => $file, "declared_classes" => $declared_classes, "declared_interfaces" => $declared_interfaces, "declared_methods" => $declared_methods, "declared_functions" => $declared_functions, "used_classes" => array_diff(array_keys($used_classes), $nodeps), "inheritance" => $extends, "implements" => $implements, ); } /** * Build a "provides" array from data returned by * analyzeSourceCode(). The format of the built array is like * this: * * array( * 'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'), * ... * ) * * * @param array $srcinfo array with information about a source file * as returned by the analyzeSourceCode() method. * * @return void * * @access private * */ function _buildProvidesArray($srcinfo) { if (!$this->_isValid) { return false; } $file = basename($srcinfo['source_file']); $pn = $this->getPackage(); $pnl = strlen($pn); foreach ($srcinfo['declared_classes'] as $class) { $key = "class;$class"; if (isset($this->_packageInfo['provides'][$key])) { continue; } $this->_packageInfo['provides'][$key] = array('file'=> $file, 'type' => 'class', 'name' => $class); if (isset($srcinfo['inheritance'][$class])) { $this->_packageInfo['provides'][$key]['extends'] = $srcinfo['inheritance'][$class]; } } foreach ($srcinfo['declared_methods'] as $class => $methods) { foreach ($methods as $method) { $function = "$class::$method"; $key = "function;$function"; if ($method[0] == '_' || !strcasecmp($method, $class) || isset($this->_packageInfo['provides'][$key])) { continue; } $this->_packageInfo['provides'][$key] = array('file'=> $file, 'type' => 'function', 'name' => $function); } } foreach ($srcinfo['declared_functions'] as $function) { $key = "function;$function"; if ($function[0] == '_' || isset($this->_packageInfo['provides'][$key])) { continue; } if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) { $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\""; } $this->_packageInfo['provides'][$key] = array('file'=> $file, 'type' => 'function', 'name' => $function); } } // }}} } ?> PK=1]ݰI&pear/PEAR/PackageFile/Generator/v1.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * needed for PEAR_VALIDATE_* constants */ require_once 'PEAR/Validate.php'; require_once 'System.php'; require_once 'PEAR/PackageFile/v2.php'; /** * This class converts a PEAR_PackageFile_v1 object into any output format. * * Supported output formats include array, XML string, and a PEAR_PackageFile_v2 * object, for converting package.xml 1.0 into package.xml 2.0 with no sweat. * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_PackageFile_Generator_v1 { /** * @var PEAR_PackageFile_v1 */ var $_packagefile; function __construct(&$packagefile) { $this->_packagefile = &$packagefile; } function getPackagerVersion() { return '1.10.18'; } /** * @param PEAR_Packager * @param bool if true, a .tgz is written, otherwise a .tar is written * @param string|null directory in which to save the .tgz * @return string|PEAR_Error location of package or error object */ function toTgz(&$packager, $compress = true, $where = null) { require_once 'Archive/Tar.php'; if ($where === null) { if (!($where = System::mktemp(array('-d')))) { return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: mktemp failed'); } } elseif (!@System::mkDir(array('-p', $where))) { return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: "' . $where . '" could' . ' not be created'); } if (file_exists($where . DIRECTORY_SEPARATOR . 'package.xml') && !is_file($where . DIRECTORY_SEPARATOR . 'package.xml')) { return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: unable to save package.xml as' . ' "' . $where . DIRECTORY_SEPARATOR . 'package.xml"'); } if (!$this->_packagefile->validate(PEAR_VALIDATE_PACKAGING)) { return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: invalid package file'); } $pkginfo = $this->_packagefile->getArray(); $ext = $compress ? '.tgz' : '.tar'; $pkgver = $pkginfo['package'] . '-' . $pkginfo['version']; $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext; if (file_exists(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext) && !is_file(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext)) { return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: cannot create tgz file "' . getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext . '"'); } if ($pkgfile = $this->_packagefile->getPackageFile()) { $pkgdir = dirname(realpath($pkgfile)); $pkgfile = basename($pkgfile); } else { return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: package file object must ' . 'be created from a real file'); } // {{{ Create the package file list $filelist = array(); $i = 0; foreach ($this->_packagefile->getFilelist() as $fname => $atts) { $file = $pkgdir . DIRECTORY_SEPARATOR . $fname; if (!file_exists($file)) { return PEAR::raiseError("File does not exist: $fname"); } else { $filelist[$i++] = $file; if (!isset($atts['md5sum'])) { $this->_packagefile->setFileAttribute($fname, 'md5sum', md5_file($file)); } $packager->log(2, "Adding file $fname"); } } // }}} $packagexml = $this->toPackageFile($where, PEAR_VALIDATE_PACKAGING, 'package.xml', true); if ($packagexml) { $tar = new Archive_Tar($dest_package, $compress); $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors // ----- Creates with the package.xml file $ok = $tar->createModify(array($packagexml), '', $where); if (PEAR::isError($ok)) { return $ok; } elseif (!$ok) { return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: tarball creation failed'); } // ----- Add the content of the package if (!$tar->addModify($filelist, $pkgver, $pkgdir)) { return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: tarball creation failed'); } return $dest_package; } } /** * @param string|null directory to place the package.xml in, or null for a temporary dir * @param int one of the PEAR_VALIDATE_* constants * @param string name of the generated file * @param bool if true, then no analysis will be performed on role="php" files * @return string|PEAR_Error path to the created file on success */ function toPackageFile($where = null, $state = PEAR_VALIDATE_NORMAL, $name = 'package.xml', $nofilechecking = false) { if (!$this->_packagefile->validate($state, $nofilechecking)) { return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: invalid package.xml', null, null, null, $this->_packagefile->getValidationWarnings()); } if ($where === null) { if (!($where = System::mktemp(array('-d')))) { return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: mktemp failed'); } } elseif (!@System::mkDir(array('-p', $where))) { return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: "' . $where . '" could' . ' not be created'); } $newpkgfile = $where . DIRECTORY_SEPARATOR . $name; $np = @fopen($newpkgfile, 'wb'); if (!$np) { return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: unable to save ' . "$name as $newpkgfile"); } fwrite($np, $this->toXml($state, true)); fclose($np); return $newpkgfile; } /** * fix both XML encoding to be UTF8, and replace standard XML entities < > " & ' * * @param string $string * @return string * @access private */ function _fixXmlEncoding($string) { return strtr($string, array( '&' => '&', '>' => '>', '<' => '<', '"' => '"', '\'' => ''' )); } /** * Return an XML document based on the package info (as returned * by the PEAR_Common::infoFrom* methods). * * @return string XML data */ function toXml($state = PEAR_VALIDATE_NORMAL, $nofilevalidation = false) { $this->_packagefile->setDate(date('Y-m-d')); if (!$this->_packagefile->validate($state, $nofilevalidation)) { return false; } $pkginfo = $this->_packagefile->getArray(); static $maint_map = array( "handle" => "user", "name" => "name", "email" => "email", "role" => "role", ); $ret = "\n"; $ret .= "\n"; $ret .= "\n" . " $pkginfo[package]"; if (isset($pkginfo['extends'])) { $ret .= "\n$pkginfo[extends]"; } $ret .= "\n ".$this->_fixXmlEncoding($pkginfo['summary'])."\n" . " ".trim($this->_fixXmlEncoding($pkginfo['description']))."\n \n" . " \n"; foreach ($pkginfo['maintainers'] as $maint) { $ret .= " \n"; foreach ($maint_map as $idx => $elm) { $ret .= " <$elm>"; $ret .= $this->_fixXmlEncoding($maint[$idx]); $ret .= "\n"; } $ret .= " \n"; } $ret .= " \n"; $ret .= $this->_makeReleaseXml($pkginfo, false, $state); if (isset($pkginfo['changelog']) && count($pkginfo['changelog']) > 0) { $ret .= " \n"; foreach ($pkginfo['changelog'] as $oldrelease) { $ret .= $this->_makeReleaseXml($oldrelease, true); } $ret .= " \n"; } $ret .= "\n"; return $ret; } // }}} // {{{ _makeReleaseXml() /** * Generate part of an XML description with release information. * * @param array $pkginfo array with release information * @param bool $changelog whether the result will be in a changelog element * * @return string XML data * * @access private */ function _makeReleaseXml($pkginfo, $changelog = false, $state = PEAR_VALIDATE_NORMAL) { // XXX QUOTE ENTITIES IN PCDATA, OR EMBED IN CDATA BLOCKS!! $indent = $changelog ? " " : ""; $ret = "$indent \n"; if (!empty($pkginfo['version'])) { $ret .= "$indent $pkginfo[version]\n"; } if (!empty($pkginfo['release_date'])) { $ret .= "$indent $pkginfo[release_date]\n"; } if (!empty($pkginfo['release_license'])) { $ret .= "$indent $pkginfo[release_license]\n"; } if (!empty($pkginfo['release_state'])) { $ret .= "$indent $pkginfo[release_state]\n"; } if (!empty($pkginfo['release_notes'])) { $ret .= "$indent ".trim($this->_fixXmlEncoding($pkginfo['release_notes'])) ."\n$indent \n"; } if (!empty($pkginfo['release_warnings'])) { $ret .= "$indent ".$this->_fixXmlEncoding($pkginfo['release_warnings'])."\n"; } if (isset($pkginfo['release_deps']) && sizeof($pkginfo['release_deps']) > 0) { $ret .= "$indent \n"; foreach ($pkginfo['release_deps'] as $dep) { $ret .= "$indent _fixXmlEncoding($c['name']) . "\""; if (isset($c['default'])) { $ret .= " default=\"" . $this->_fixXmlEncoding($c['default']) . "\""; } $ret .= " prompt=\"" . $this->_fixXmlEncoding($c['prompt']) . "\""; $ret .= "/>\n"; } $ret .= "$indent \n"; } if (isset($pkginfo['provides'])) { foreach ($pkginfo['provides'] as $key => $what) { $ret .= "$indent recursiveXmlFilelist($pkginfo['filelist']); } else { foreach ($pkginfo['filelist'] as $file => $fa) { if (!isset($fa['role'])) { $fa['role'] = ''; } $ret .= "$indent _fixXmlEncoding($fa['baseinstalldir']) . '"'; } if (isset($fa['md5sum'])) { $ret .= " md5sum=\"$fa[md5sum]\""; } if (isset($fa['platform'])) { $ret .= " platform=\"$fa[platform]\""; } if (!empty($fa['install-as'])) { $ret .= ' install-as="' . $this->_fixXmlEncoding($fa['install-as']) . '"'; } $ret .= ' name="' . $this->_fixXmlEncoding($file) . '"'; if (empty($fa['replacements'])) { $ret .= "/>\n"; } else { $ret .= ">\n"; foreach ($fa['replacements'] as $r) { $ret .= "$indent $v) { $ret .= " $k=\"" . $this->_fixXmlEncoding($v) .'"'; } $ret .= "/>\n"; } $ret .= "$indent \n"; } } } $ret .= "$indent \n"; } $ret .= "$indent \n"; return $ret; } /** * @param array * @access protected */ function recursiveXmlFilelist($list) { $this->_dirs = array(); foreach ($list as $file => $attributes) { $this->_addDir($this->_dirs, explode('/', dirname($file)), $file, $attributes); } return $this->_formatDir($this->_dirs); } /** * @param array * @param array * @param string|null * @param array|null * @access private */ function _addDir(&$dirs, $dir, $file = null, $attributes = null) { if ($dir == array() || $dir == array('.')) { $dirs['files'][basename($file)] = $attributes; return; } $curdir = array_shift($dir); if (!isset($dirs['dirs'][$curdir])) { $dirs['dirs'][$curdir] = array(); } $this->_addDir($dirs['dirs'][$curdir], $dir, $file, $attributes); } /** * @param array * @param string * @param string * @access private */ function _formatDir($dirs, $indent = '', $curdir = '') { $ret = ''; if (!count($dirs)) { return ''; } if (isset($dirs['dirs'])) { uksort($dirs['dirs'], 'strnatcasecmp'); foreach ($dirs['dirs'] as $dir => $contents) { $usedir = "$curdir/$dir"; $ret .= "$indent \n"; $ret .= $this->_formatDir($contents, "$indent ", $usedir); $ret .= "$indent \n"; } } if (isset($dirs['files'])) { uksort($dirs['files'], 'strnatcasecmp'); foreach ($dirs['files'] as $file => $attribs) { $ret .= $this->_formatFile($file, $attribs, $indent); } } return $ret; } /** * @param string * @param array * @param string * @access private */ function _formatFile($file, $attributes, $indent) { $ret = "$indent _fixXmlEncoding($attributes['baseinstalldir']) . '"'; } if (isset($attributes['md5sum'])) { $ret .= " md5sum=\"$attributes[md5sum]\""; } if (isset($attributes['platform'])) { $ret .= " platform=\"$attributes[platform]\""; } if (!empty($attributes['install-as'])) { $ret .= ' install-as="' . $this->_fixXmlEncoding($attributes['install-as']) . '"'; } $ret .= ' name="' . $this->_fixXmlEncoding($file) . '"'; if (empty($attributes['replacements'])) { $ret .= "/>\n"; } else { $ret .= ">\n"; foreach ($attributes['replacements'] as $r) { $ret .= "$indent $v) { $ret .= " $k=\"" . $this->_fixXmlEncoding($v) .'"'; } $ret .= "/>\n"; } $ret .= "$indent \n"; } return $ret; } // {{{ _unIndent() /** * Unindent given string (?) * * @param string $str The string that has to be unindented. * @return string * @access private */ function _unIndent($str) { // remove leading newlines $str = preg_replace('/^[\r\n]+/', '', $str); // find whitespace at the beginning of the first line $indent_len = strspn($str, " \t"); $indent = substr($str, 0, $indent_len); $data = ''; // remove the same amount of whitespace from following lines foreach (explode("\n", $str) as $line) { if (substr($line, 0, $indent_len) == $indent) { $data .= substr($line, $indent_len) . "\n"; } } return $data; } /** * @return array */ function dependenciesToV2() { $arr = array(); $this->_convertDependencies2_0($arr); return $arr['dependencies']; } /** * Convert a package.xml version 1.0 into version 2.0 * * Note that this does a basic conversion, to allow more advanced * features like bundles and multiple releases * @param string the classname to instantiate and return. This must be * PEAR_PackageFile_v2 or a descendant * @param boolean if true, only valid, deterministic package.xml 1.0 as defined by the * strictest parameters will be converted * @return PEAR_PackageFile_v2|PEAR_Error */ function &toV2($class = 'PEAR_PackageFile_v2', $strict = false) { if ($strict) { if (!$this->_packagefile->validate()) { $a = PEAR::raiseError('invalid package.xml version 1.0 cannot be converted' . ' to version 2.0', null, null, null, $this->_packagefile->getValidationWarnings(true)); return $a; } } $arr = array( 'attribs' => array( 'version' => '2.0', 'xmlns' => 'http://pear.php.net/dtd/package-2.0', 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0', 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:schemaLocation' => "http://pear.php.net/dtd/tasks-1.0\n" . "http://pear.php.net/dtd/tasks-1.0.xsd\n" . "http://pear.php.net/dtd/package-2.0\n" . 'http://pear.php.net/dtd/package-2.0.xsd', ), 'name' => $this->_packagefile->getPackage(), 'channel' => 'pear.php.net', ); $arr['summary'] = $this->_packagefile->getSummary(); $arr['description'] = $this->_packagefile->getDescription(); $maintainers = $this->_packagefile->getMaintainers(); foreach ($maintainers as $maintainer) { if ($maintainer['role'] != 'lead') { continue; } $new = array( 'name' => $maintainer['name'], 'user' => $maintainer['handle'], 'email' => $maintainer['email'], 'active' => 'yes', ); $arr['lead'][] = $new; } if (!isset($arr['lead'])) { // some people... you know? $arr['lead'] = array( 'name' => 'unknown', 'user' => 'unknown', 'email' => 'noleadmaintainer@example.com', 'active' => 'no', ); } if (count($arr['lead']) == 1) { $arr['lead'] = $arr['lead'][0]; } foreach ($maintainers as $maintainer) { if ($maintainer['role'] == 'lead') { continue; } $new = array( 'name' => $maintainer['name'], 'user' => $maintainer['handle'], 'email' => $maintainer['email'], 'active' => 'yes', ); $arr[$maintainer['role']][] = $new; } if (isset($arr['developer']) && count($arr['developer']) == 1) { $arr['developer'] = $arr['developer'][0]; } if (isset($arr['contributor']) && count($arr['contributor']) == 1) { $arr['contributor'] = $arr['contributor'][0]; } if (isset($arr['helper']) && count($arr['helper']) == 1) { $arr['helper'] = $arr['helper'][0]; } $arr['date'] = $this->_packagefile->getDate(); $arr['version'] = array( 'release' => $this->_packagefile->getVersion(), 'api' => $this->_packagefile->getVersion(), ); $arr['stability'] = array( 'release' => $this->_packagefile->getState(), 'api' => $this->_packagefile->getState(), ); $licensemap = array( 'php' => 'http://www.php.net/license', 'php license' => 'http://www.php.net/license', 'lgpl' => 'http://www.gnu.org/copyleft/lesser.html', 'bsd' => 'http://www.opensource.org/licenses/bsd-license.php', 'bsd style' => 'http://www.opensource.org/licenses/bsd-license.php', 'bsd-style' => 'http://www.opensource.org/licenses/bsd-license.php', 'mit' => 'http://www.opensource.org/licenses/mit-license.php', 'gpl' => 'http://www.gnu.org/copyleft/gpl.html', 'apache' => 'http://www.opensource.org/licenses/apache2.0.php' ); if (isset($licensemap[strtolower($this->_packagefile->getLicense())])) { $arr['license'] = array( 'attribs' => array('uri' => $licensemap[strtolower($this->_packagefile->getLicense())]), '_content' => $this->_packagefile->getLicense() ); } else { // don't use bogus uri $arr['license'] = $this->_packagefile->getLicense(); } $arr['notes'] = $this->_packagefile->getNotes(); $temp = array(); $arr['contents'] = $this->_convertFilelist2_0($temp); $this->_convertDependencies2_0($arr); $release = ($this->_packagefile->getConfigureOptions() || $this->_isExtension) ? 'extsrcrelease' : 'phprelease'; if ($release == 'extsrcrelease') { $arr['channel'] = 'pecl.php.net'; $arr['providesextension'] = $arr['name']; // assumption } $arr[$release] = array(); if ($this->_packagefile->getConfigureOptions()) { $arr[$release]['configureoption'] = $this->_packagefile->getConfigureOptions(); foreach ($arr[$release]['configureoption'] as $i => $opt) { $arr[$release]['configureoption'][$i] = array('attribs' => $opt); } if (count($arr[$release]['configureoption']) == 1) { $arr[$release]['configureoption'] = $arr[$release]['configureoption'][0]; } } $this->_convertRelease2_0($arr[$release], $temp); if ($release == 'extsrcrelease' && count($arr[$release]) > 1) { // multiple extsrcrelease tags added in PEAR 1.4.1 $arr['dependencies']['required']['pearinstaller']['min'] = '1.4.1'; } if ($cl = $this->_packagefile->getChangelog()) { foreach ($cl as $release) { $rel = array(); $rel['version'] = array( 'release' => $release['version'], 'api' => $release['version'], ); if (!isset($release['release_state'])) { $release['release_state'] = 'stable'; } $rel['stability'] = array( 'release' => $release['release_state'], 'api' => $release['release_state'], ); if (isset($release['release_date'])) { $rel['date'] = $release['release_date']; } else { $rel['date'] = date('Y-m-d'); } if (isset($release['release_license'])) { if (isset($licensemap[strtolower($release['release_license'])])) { $uri = $licensemap[strtolower($release['release_license'])]; } else { $uri = 'http://www.example.com'; } $rel['license'] = array( 'attribs' => array('uri' => $uri), '_content' => $release['release_license'] ); } else { $rel['license'] = $arr['license']; } if (!isset($release['release_notes'])) { $release['release_notes'] = 'no release notes'; } $rel['notes'] = $release['release_notes']; $arr['changelog']['release'][] = $rel; } } $ret = new $class; $ret->setConfig($this->_packagefile->_config); if (isset($this->_packagefile->_logger) && is_object($this->_packagefile->_logger)) { $ret->setLogger($this->_packagefile->_logger); } $ret->fromArray($arr); return $ret; } /** * @param array * @param bool * @access private */ function _convertDependencies2_0(&$release, $internal = false) { $peardep = array('pearinstaller' => array('min' => '1.4.0b1')); // this is a lot safer $required = $optional = array(); $release['dependencies'] = array('required' => array()); if ($this->_packagefile->hasDeps()) { foreach ($this->_packagefile->getDeps() as $dep) { if (!isset($dep['optional']) || $dep['optional'] == 'no') { $required[] = $dep; } else { $optional[] = $dep; } } foreach (array('required', 'optional') as $arr) { $deps = array(); foreach ($$arr as $dep) { // organize deps by dependency type and name if (!isset($deps[$dep['type']])) { $deps[$dep['type']] = array(); } if (isset($dep['name'])) { $deps[$dep['type']][$dep['name']][] = $dep; } else { $deps[$dep['type']][] = $dep; } } do { if (isset($deps['php'])) { $php = array(); if (count($deps['php']) > 1) { $php = $this->_processPhpDeps($deps['php']); } else { if (!isset($deps['php'][0])) { // Buggy versions $key = key($deps['php']); $info = current($deps['php']); $deps['php'] = array($info[0]); } $php = $this->_processDep($deps['php'][0]); if (!$php) { break; // poor mans throw } } $release['dependencies'][$arr]['php'] = $php; } } while (false); do { if (isset($deps['pkg'])) { $pkg = array(); $pkg = $this->_processMultipleDepsName($deps['pkg']); if (!$pkg) { break; // poor mans throw } $release['dependencies'][$arr]['package'] = $pkg; } } while (false); do { if (isset($deps['ext'])) { $pkg = array(); $pkg = $this->_processMultipleDepsName($deps['ext']); $release['dependencies'][$arr]['extension'] = $pkg; } } while (false); // skip sapi - it's not supported so nobody will have used it // skip os - it's not supported in 1.0 } } if (isset($release['dependencies']['required'])) { $release['dependencies']['required'] = array_merge($peardep, $release['dependencies']['required']); } else { $release['dependencies']['required'] = $peardep; } if (!isset($release['dependencies']['required']['php'])) { $release['dependencies']['required']['php'] = array('min' => '4.0.0'); } $order = array(); $bewm = $release['dependencies']['required']; $order['php'] = $bewm['php']; $order['pearinstaller'] = $bewm['pearinstaller']; isset($bewm['package']) ? $order['package'] = $bewm['package'] :0; isset($bewm['extension']) ? $order['extension'] = $bewm['extension'] :0; $release['dependencies']['required'] = $order; } /** * @param array * @access private */ function _convertFilelist2_0(&$package) { $ret = array('dir' => array( 'attribs' => array('name' => '/'), 'file' => array() ) ); $package['platform'] = $package['install-as'] = array(); $this->_isExtension = false; foreach ($this->_packagefile->getFilelist() as $name => $file) { $file['name'] = $name; if (isset($file['role']) && $file['role'] == 'src') { $this->_isExtension = true; } if (isset($file['replacements'])) { $repl = $file['replacements']; unset($file['replacements']); } else { unset($repl); } if (isset($file['install-as'])) { $package['install-as'][$name] = $file['install-as']; unset($file['install-as']); } if (isset($file['platform'])) { $package['platform'][$name] = $file['platform']; unset($file['platform']); } $file = array('attribs' => $file); if (isset($repl)) { foreach ($repl as $replace ) { $file['tasks:replace'][] = array('attribs' => $replace); } if (count($repl) == 1) { $file['tasks:replace'] = $file['tasks:replace'][0]; } } $ret['dir']['file'][] = $file; } return $ret; } /** * Post-process special files with install-as/platform attributes and * make the release tag. * * This complex method follows this work-flow to create the release tags: * *
     * - if any install-as/platform exist, create a generic release and fill it with
     *   o  tags for 
     *   o  tags for 
     *   o  tags for 
     *   o  tags for 
     * - create a release for each platform encountered and fill with
     *   o  tags for 
     *   o  tags for 
     *   o  tags for 
     *   o  tags for 
     *   o  tags for 
     *   o  tags for 
     *   o  tags for 
     * 
* * It does this by accessing the $package parameter, which contains an array with * indices: * * - platform: mapping of file => OS the file should be installed on * - install-as: mapping of file => installed name * - osmap: mapping of OS => list of files that should be installed * on that OS * - notosmap: mapping of OS => list of files that should not be * installed on that OS * * @param array * @param array * @access private */ function _convertRelease2_0(&$release, $package) { //- if any install-as/platform exist, create a generic release and fill it with if (count($package['platform']) || count($package['install-as'])) { $generic = array(); $genericIgnore = array(); foreach ($package['install-as'] as $file => $as) { //o tags for if (!isset($package['platform'][$file])) { $generic[] = $file; continue; } //o tags for if (isset($package['platform'][$file]) && $package['platform'][$file][0] == '!') { $generic[] = $file; continue; } //o tags for if (isset($package['platform'][$file]) && $package['platform'][$file][0] != '!') { $genericIgnore[] = $file; continue; } } foreach ($package['platform'] as $file => $platform) { if (isset($package['install-as'][$file])) { continue; } if ($platform[0] != '!') { //o tags for $genericIgnore[] = $file; } } if (count($package['platform'])) { $oses = $notplatform = $platform = array(); foreach ($package['platform'] as $file => $os) { // get a list of oses if ($os[0] == '!') { if (isset($oses[substr($os, 1)])) { continue; } $oses[substr($os, 1)] = count($oses); } else { if (isset($oses[$os])) { continue; } $oses[$os] = count($oses); } } //- create a release for each platform encountered and fill with foreach ($oses as $os => $releaseNum) { $release[$releaseNum]['installconditions']['os']['name'] = $os; $release[$releaseNum]['filelist'] = array('install' => array(), 'ignore' => array()); foreach ($package['install-as'] as $file => $as) { //o tags for if (!isset($package['platform'][$file])) { $release[$releaseNum]['filelist']['install'][] = array( 'attribs' => array( 'name' => $file, 'as' => $as, ), ); continue; } //o tags for // if (isset($package['platform'][$file]) && $package['platform'][$file] == $os) { $release[$releaseNum]['filelist']['install'][] = array( 'attribs' => array( 'name' => $file, 'as' => $as, ), ); continue; } //o tags for // if (isset($package['platform'][$file]) && $package['platform'][$file] != "!$os" && $package['platform'][$file][0] == '!') { $release[$releaseNum]['filelist']['install'][] = array( 'attribs' => array( 'name' => $file, 'as' => $as, ), ); continue; } //o tags for // if (isset($package['platform'][$file]) && $package['platform'][$file] == "!$os") { $release[$releaseNum]['filelist']['ignore'][] = array( 'attribs' => array( 'name' => $file, ), ); continue; } //o tags for // if (isset($package['platform'][$file]) && $package['platform'][$file][0] != '!' && $package['platform'][$file] != $os) { $release[$releaseNum]['filelist']['ignore'][] = array( 'attribs' => array( 'name' => $file, ), ); continue; } } foreach ($package['platform'] as $file => $platform) { if (isset($package['install-as'][$file])) { continue; } //o tags for if ($platform == "!$os") { $release[$releaseNum]['filelist']['ignore'][] = array( 'attribs' => array( 'name' => $file, ), ); continue; } //o tags for if ($platform[0] != '!' && $platform != $os) { $release[$releaseNum]['filelist']['ignore'][] = array( 'attribs' => array( 'name' => $file, ), ); } } if (!count($release[$releaseNum]['filelist']['install'])) { unset($release[$releaseNum]['filelist']['install']); } if (!count($release[$releaseNum]['filelist']['ignore'])) { unset($release[$releaseNum]['filelist']['ignore']); } } if (count($generic) || count($genericIgnore)) { $release[count($oses)] = array(); if (count($generic)) { foreach ($generic as $file) { if (isset($package['install-as'][$file])) { $installas = $package['install-as'][$file]; } else { $installas = $file; } $release[count($oses)]['filelist']['install'][] = array( 'attribs' => array( 'name' => $file, 'as' => $installas, ) ); } } if (count($genericIgnore)) { foreach ($genericIgnore as $file) { $release[count($oses)]['filelist']['ignore'][] = array( 'attribs' => array( 'name' => $file, ) ); } } } // cleanup foreach ($release as $i => $rel) { if (isset($rel['filelist']['install']) && count($rel['filelist']['install']) == 1) { $release[$i]['filelist']['install'] = $release[$i]['filelist']['install'][0]; } if (isset($rel['filelist']['ignore']) && count($rel['filelist']['ignore']) == 1) { $release[$i]['filelist']['ignore'] = $release[$i]['filelist']['ignore'][0]; } } if (count($release) == 1) { $release = $release[0]; } } else { // no platform atts, but some install-as atts foreach ($package['install-as'] as $file => $value) { $release['filelist']['install'][] = array( 'attribs' => array( 'name' => $file, 'as' => $value ) ); } if (count($release['filelist']['install']) == 1) { $release['filelist']['install'] = $release['filelist']['install'][0]; } } } } /** * @param array * @return array * @access private */ function _processDep($dep) { if ($dep['type'] == 'php') { if ($dep['rel'] == 'has') { // come on - everyone has php! return false; } } $php = array(); if ($dep['type'] != 'php') { $php['name'] = $dep['name']; if ($dep['type'] == 'pkg') { $php['channel'] = 'pear.php.net'; } } switch ($dep['rel']) { case 'gt' : $php['min'] = $dep['version']; $php['exclude'] = $dep['version']; break; case 'ge' : if (!isset($dep['version'])) { if ($dep['type'] == 'php') { if (isset($dep['name'])) { $dep['version'] = $dep['name']; } } } $php['min'] = $dep['version']; break; case 'lt' : $php['max'] = $dep['version']; $php['exclude'] = $dep['version']; break; case 'le' : $php['max'] = $dep['version']; break; case 'eq' : $php['min'] = $dep['version']; $php['max'] = $dep['version']; break; case 'ne' : $php['exclude'] = $dep['version']; break; case 'not' : $php['conflicts'] = 'yes'; break; } return $php; } /** * @param array * @return array */ function _processPhpDeps($deps) { $test = array(); foreach ($deps as $dep) { $test[] = $this->_processDep($dep); } $min = array(); $max = array(); foreach ($test as $dep) { if (!$dep) { continue; } if (isset($dep['min'])) { $min[$dep['min']] = count($min); } if (isset($dep['max'])) { $max[$dep['max']] = count($max); } } if (count($min) > 0) { uksort($min, 'version_compare'); } if (count($max) > 0) { uksort($max, 'version_compare'); } if (count($min)) { // get the highest minimum $a = array_flip($min); $min = array_pop($a); } else { $min = false; } if (count($max)) { // get the lowest maximum $a = array_flip($max); $max = array_shift($a); } else { $max = false; } if ($min) { $php['min'] = $min; } if ($max) { $php['max'] = $max; } $exclude = array(); foreach ($test as $dep) { if (!isset($dep['exclude'])) { continue; } $exclude[] = $dep['exclude']; } if (count($exclude)) { $php['exclude'] = $exclude; } return $php; } /** * process multiple dependencies that have a name, like package deps * @param array * @return array * @access private */ function _processMultipleDepsName($deps) { $ret = $tests = array(); foreach ($deps as $name => $dep) { foreach ($dep as $d) { $tests[$name][] = $this->_processDep($d); } } foreach ($tests as $name => $test) { $max = $min = $php = array(); $php['name'] = $name; foreach ($test as $dep) { if (!$dep) { continue; } if (isset($dep['channel'])) { $php['channel'] = 'pear.php.net'; } if (isset($dep['conflicts']) && $dep['conflicts'] == 'yes') { $php['conflicts'] = 'yes'; } if (isset($dep['min'])) { $min[$dep['min']] = count($min); } if (isset($dep['max'])) { $max[$dep['max']] = count($max); } } if (count($min) > 0) { uksort($min, 'version_compare'); } if (count($max) > 0) { uksort($max, 'version_compare'); } if (count($min)) { // get the highest minimum $a = array_flip($min); $min = array_pop($a); } else { $min = false; } if (count($max)) { // get the lowest maximum $a = array_flip($max); $max = array_shift($a); } else { $max = false; } if ($min) { $php['min'] = $min; } if ($max) { $php['max'] = $max; } $exclude = array(); foreach ($test as $dep) { if (!isset($dep['exclude'])) { continue; } $exclude[] = $dep['exclude']; } if (count($exclude)) { $php['exclude'] = $exclude; } $ret[] = $php; } return $ret; } } ?> PK=1]>ɁɁ&pear/PEAR/PackageFile/Generator/v2.phpnu[ * @author Stephan Schmidt (original XML_Serializer code) * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * file/dir manipulation routines */ require_once 'System.php'; require_once 'XML/Util.php'; /** * This class converts a PEAR_PackageFile_v2 object into any output format. * * Supported output formats include array, XML string (using S. Schmidt's * XML_Serializer, slightly customized) * @category pear * @package PEAR * @author Greg Beaver * @author Stephan Schmidt (original XML_Serializer code) * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_PackageFile_Generator_v2 { /** * default options for the serialization * @access private * @var array $_defaultOptions */ var $_defaultOptions = array( 'indent' => ' ', // string used for indentation 'linebreak' => "\n", // string used for newlines 'typeHints' => false, // automatically add type hin attributes 'addDecl' => true, // add an XML declaration 'defaultTagName' => 'XML_Serializer_Tag', // tag used for indexed arrays or invalid names 'classAsTagName' => false, // use classname for objects in indexed arrays 'keyAttribute' => '_originalKey', // attribute where original key is stored 'typeAttribute' => '_type', // attribute for type (only if typeHints => true) 'classAttribute' => '_class', // attribute for class of objects (only if typeHints => true) 'scalarAsAttributes' => false, // scalar values (strings, ints,..) will be serialized as attribute 'prependAttributes' => '', // prepend string for attributes 'indentAttributes' => false, // indent the attributes, if set to '_auto', it will indent attributes so they all start at the same column 'mode' => 'simplexml', // use 'simplexml' to use parent name as tagname if transforming an indexed array 'addDoctype' => false, // add a doctype declaration 'doctype' => null, // supply a string or an array with id and uri ({@see XML_Util::getDoctypeDeclaration()} 'rootName' => 'package', // name of the root tag 'rootAttributes' => array( 'version' => '2.0', 'xmlns' => 'http://pear.php.net/dtd/package-2.0', 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0', 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd', ), // attributes of the root tag 'attributesArray' => 'attribs', // all values in this key will be treated as attributes 'contentName' => '_content', // this value will be used directly as content, instead of creating a new tag, may only be used in conjunction with attributesArray 'beautifyFilelist' => false, 'encoding' => 'UTF-8', ); /** * options for the serialization * @access private * @var array $options */ var $options = array(); /** * current tag depth * @var integer $_tagDepth */ var $_tagDepth = 0; /** * serilialized representation of the data * @var string $_serializedData */ var $_serializedData = null; /** * @var PEAR_PackageFile_v2 */ var $_packagefile; /** * @param PEAR_PackageFile_v2 */ function __construct(&$packagefile) { $this->_packagefile = &$packagefile; if (isset($this->_packagefile->encoding)) { $this->_defaultOptions['encoding'] = $this->_packagefile->encoding; } } /** * @return string */ function getPackagerVersion() { return '1.10.18'; } /** * @param PEAR_Packager * @param bool generate a .tgz or a .tar * @param string|null temporary directory to package in */ function toTgz(&$packager, $compress = true, $where = null) { $a = null; return $this->toTgz2($packager, $a, $compress, $where); } /** * Package up both a package.xml and package2.xml for the same release * @param PEAR_Packager * @param PEAR_PackageFile_v1 * @param bool generate a .tgz or a .tar * @param string|null temporary directory to package in */ function toTgz2(&$packager, &$pf1, $compress = true, $where = null) { require_once 'Archive/Tar.php'; if (!$this->_packagefile->isEquivalent($pf1)) { return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: "' . basename($pf1->getPackageFile()) . '" is not equivalent to "' . basename($this->_packagefile->getPackageFile()) . '"'); } if ($where === null) { if (!($where = System::mktemp(array('-d')))) { return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: mktemp failed'); } } elseif (!@System::mkDir(array('-p', $where))) { return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: "' . $where . '" could' . ' not be created'); } $file = $where . DIRECTORY_SEPARATOR . 'package.xml'; if (file_exists($file) && !is_file($file)) { return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: unable to save package.xml as' . ' "' . $file .'"'); } if (!$this->_packagefile->validate(PEAR_VALIDATE_PACKAGING)) { return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: invalid package.xml'); } $ext = $compress ? '.tgz' : '.tar'; $pkgver = $this->_packagefile->getPackage() . '-' . $this->_packagefile->getVersion(); $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext; if (file_exists($dest_package) && !is_file($dest_package)) { return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: cannot create tgz file "' . $dest_package . '"'); } $pkgfile = $this->_packagefile->getPackageFile(); if (!$pkgfile) { return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: package file object must ' . 'be created from a real file'); } $pkgdir = dirname(realpath($pkgfile)); $pkgfile = basename($pkgfile); // {{{ Create the package file list $filelist = array(); $i = 0; $this->_packagefile->flattenFilelist(); $contents = $this->_packagefile->getContents(); if (isset($contents['bundledpackage'])) { // bundles of packages $contents = $contents['bundledpackage']; if (!isset($contents[0])) { $contents = array($contents); } $packageDir = $where; foreach ($contents as $i => $package) { $fname = $package; $file = $pkgdir . DIRECTORY_SEPARATOR . $fname; if (!file_exists($file)) { return $packager->raiseError("File does not exist: $fname"); } $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname; System::mkdir(array('-p', dirname($tfile))); copy($file, $tfile); $filelist[$i++] = $tfile; $packager->log(2, "Adding package $fname"); } } else { // normal packages $contents = $contents['dir']['file']; if (!isset($contents[0])) { $contents = array($contents); } $packageDir = $where; foreach ($contents as $i => $file) { $fname = $file['attribs']['name']; $atts = $file['attribs']; $orig = $file; $file = $pkgdir . DIRECTORY_SEPARATOR . $fname; if (!file_exists($file)) { return $packager->raiseError("File does not exist: $fname"); } $origperms = fileperms($file); $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname; unset($orig['attribs']); if (count($orig)) { // file with tasks // run any package-time tasks $contents = file_get_contents($file); foreach ($orig as $tag => $raw) { $tag = str_replace( array($this->_packagefile->getTasksNs() . ':', '-'), array('', '_'), $tag); $task = "PEAR_Task_$tag"; $task = new $task($this->_packagefile->_config, $this->_packagefile->_logger, PEAR_TASK_PACKAGE); $task->init($raw, $atts, null); $res = $task->startSession($this->_packagefile, $contents, $tfile); if (!$res) { continue; // skip this task } if (PEAR::isError($res)) { return $res; } $contents = $res; // save changes System::mkdir(array('-p', dirname($tfile))); $wp = fopen($tfile, "wb"); fwrite($wp, $contents); fclose($wp); } } if (!file_exists($tfile)) { System::mkdir(array('-p', dirname($tfile))); copy($file, $tfile); } chmod($tfile, $origperms); $filelist[$i++] = $tfile; $this->_packagefile->setFileAttribute($fname, 'md5sum', md5_file($tfile), $i - 1); $packager->log(2, "Adding file $fname"); } } // }}} $name = $pf1 !== null ? 'package2.xml' : 'package.xml'; $packagexml = $this->toPackageFile($where, PEAR_VALIDATE_PACKAGING, $name); if ($packagexml) { $tar = new Archive_Tar($dest_package, $compress); $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors // ----- Creates with the package.xml file $ok = $tar->createModify(array($packagexml), '', $where); if (PEAR::isError($ok)) { return $packager->raiseError($ok); } elseif (!$ok) { return $packager->raiseError('PEAR_Packagefile_v2::toTgz(): adding ' . $name . ' failed'); } // ----- Add the content of the package if (!$tar->addModify($filelist, $pkgver, $where)) { return $packager->raiseError( 'PEAR_Packagefile_v2::toTgz(): tarball creation failed'); } // add the package.xml version 1.0 if ($pf1 !== null) { $pfgen = &$pf1->getDefaultGenerator(); $packagexml1 = $pfgen->toPackageFile($where, PEAR_VALIDATE_PACKAGING, 'package.xml', true); if (!$tar->addModify(array($packagexml1), '', $where)) { return $packager->raiseError( 'PEAR_Packagefile_v2::toTgz(): adding package.xml failed'); } } return $dest_package; } } function toPackageFile($where = null, $state = PEAR_VALIDATE_NORMAL, $name = 'package.xml') { if (!$this->_packagefile->validate($state)) { return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: invalid package.xml', null, null, null, $this->_packagefile->getValidationWarnings()); } if ($where === null) { if (!($where = System::mktemp(array('-d')))) { return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: mktemp failed'); } } elseif (!@System::mkDir(array('-p', $where))) { return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: "' . $where . '" could' . ' not be created'); } $newpkgfile = $where . DIRECTORY_SEPARATOR . $name; $np = @fopen($newpkgfile, 'wb'); if (!$np) { return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: unable to save ' . "$name as $newpkgfile"); } fwrite($np, $this->toXml($state)); fclose($np); return $newpkgfile; } function &toV2() { return $this->_packagefile; } /** * Return an XML document based on the package info (as returned * by the PEAR_Common::infoFrom* methods). * * @return string XML data */ function toXml($state = PEAR_VALIDATE_NORMAL, $options = array()) { $this->_packagefile->setDate(date('Y-m-d')); $this->_packagefile->setTime(date('H:i:s')); if (!$this->_packagefile->validate($state)) { return false; } if (is_array($options)) { $this->options = array_merge($this->_defaultOptions, $options); } else { $this->options = $this->_defaultOptions; } $arr = $this->_packagefile->getArray(); if (isset($arr['filelist'])) { unset($arr['filelist']); } if (isset($arr['_lastversion'])) { unset($arr['_lastversion']); } // Fix the notes a little bit if (isset($arr['notes'])) { // This trims out the indenting, needs fixing $arr['notes'] = "\n" . trim($arr['notes']) . "\n"; } if (isset($arr['changelog']) && !empty($arr['changelog'])) { // Fix for inconsistency how the array is filled depending on the changelog release amount if (!isset($arr['changelog']['release'][0])) { $release = $arr['changelog']['release']; unset($arr['changelog']['release']); $arr['changelog']['release'] = array(); $arr['changelog']['release'][0] = $release; } foreach (array_keys($arr['changelog']['release']) as $key) { $c =& $arr['changelog']['release'][$key]; if (isset($c['notes'])) { // This trims out the indenting, needs fixing $c['notes'] = "\n" . trim($c['notes']) . "\n"; } } } if ($state ^ PEAR_VALIDATE_PACKAGING && !isset($arr['bundle'])) { $use = $this->_recursiveXmlFilelist($arr['contents']['dir']['file']); unset($arr['contents']['dir']['file']); if (isset($use['dir'])) { $arr['contents']['dir']['dir'] = $use['dir']; } if (isset($use['file'])) { $arr['contents']['dir']['file'] = $use['file']; } $this->options['beautifyFilelist'] = true; } $arr['attribs']['packagerversion'] = '1.10.18'; if ($this->serialize($arr, $options)) { return $this->_serializedData . "\n"; } return false; } function _recursiveXmlFilelist($list) { $dirs = array(); if (isset($list['attribs'])) { $file = $list['attribs']['name']; unset($list['attribs']['name']); $attributes = $list['attribs']; $this->_addDir($dirs, explode('/', dirname($file)), $file, $attributes); } else { foreach ($list as $a) { $file = $a['attribs']['name']; $attributes = $a['attribs']; unset($a['attribs']); $this->_addDir($dirs, explode('/', dirname($file)), $file, $attributes, $a); } } $this->_formatDir($dirs); $this->_deFormat($dirs); return $dirs; } function _addDir(&$dirs, $dir, $file = null, $attributes = null, $tasks = null) { if (!$tasks) { $tasks = array(); } if ($dir == array() || $dir == array('.')) { $dirs['file'][basename($file)] = $tasks; $attributes['name'] = basename($file); $dirs['file'][basename($file)]['attribs'] = $attributes; return; } $curdir = array_shift($dir); if (!isset($dirs['dir'][$curdir])) { $dirs['dir'][$curdir] = array(); } $this->_addDir($dirs['dir'][$curdir], $dir, $file, $attributes, $tasks); } function _formatDir(&$dirs) { if (!count($dirs)) { return array(); } $newdirs = array(); if (isset($dirs['dir'])) { $newdirs['dir'] = $dirs['dir']; } if (isset($dirs['file'])) { $newdirs['file'] = $dirs['file']; } $dirs = $newdirs; if (isset($dirs['dir'])) { uksort($dirs['dir'], 'strnatcasecmp'); foreach ($dirs['dir'] as $dir => $contents) { $this->_formatDir($dirs['dir'][$dir]); } } if (isset($dirs['file'])) { uksort($dirs['file'], 'strnatcasecmp'); }; } function _deFormat(&$dirs) { if (!count($dirs)) { return array(); } $newdirs = array(); if (isset($dirs['dir'])) { foreach ($dirs['dir'] as $dir => $contents) { $newdir = array(); $newdir['attribs']['name'] = $dir; $this->_deFormat($contents); foreach ($contents as $tag => $val) { $newdir[$tag] = $val; } $newdirs['dir'][] = $newdir; } if (count($newdirs['dir']) == 1) { $newdirs['dir'] = $newdirs['dir'][0]; } } if (isset($dirs['file'])) { foreach ($dirs['file'] as $name => $file) { $newdirs['file'][] = $file; } if (count($newdirs['file']) == 1) { $newdirs['file'] = $newdirs['file'][0]; } } $dirs = $newdirs; } /** * reset all options to default options * * @access public * @see setOption(), XML_Unserializer() */ function resetOptions() { $this->options = $this->_defaultOptions; } /** * set an option * * You can use this method if you do not want to set all options in the constructor * * @access public * @see resetOption(), XML_Serializer() */ function setOption($name, $value) { $this->options[$name] = $value; } /** * sets several options at once * * You can use this method if you do not want to set all options in the constructor * * @access public * @see resetOption(), XML_Unserializer(), setOption() */ function setOptions($options) { $this->options = array_merge($this->options, $options); } /** * serialize data * * @access public * @param mixed $data data to serialize * @return boolean true on success, pear error on failure */ function serialize($data, $options = null) { // if options have been specified, use them instead // of the previously defined ones if (is_array($options)) { $optionsBak = $this->options; if (isset($options['overrideOptions']) && $options['overrideOptions'] == true) { $this->options = array_merge($this->_defaultOptions, $options); } else { $this->options = array_merge($this->options, $options); } } else { $optionsBak = null; } // start depth is zero $this->_tagDepth = 0; $this->_serializedData = ''; // serialize an array if (is_array($data)) { $tagName = isset($this->options['rootName']) ? $this->options['rootName'] : 'array'; $this->_serializedData .= $this->_serializeArray($data, $tagName, $this->options['rootAttributes']); } // add doctype declaration if ($this->options['addDoctype'] === true) { $this->_serializedData = XML_Util::getDoctypeDeclaration($tagName, $this->options['doctype']) . $this->options['linebreak'] . $this->_serializedData; } // build xml declaration if ($this->options['addDecl']) { $atts = array(); $encoding = isset($this->options['encoding']) ? $this->options['encoding'] : null; $this->_serializedData = XML_Util::getXMLDeclaration('1.0', $encoding) . $this->options['linebreak'] . $this->_serializedData; } if ($optionsBak !== null) { $this->options = $optionsBak; } return true; } /** * get the result of the serialization * * @access public * @return string serialized XML */ function getSerializedData() { if ($this->_serializedData === null) { return $this->raiseError('No serialized data available. Use XML_Serializer::serialize() first.', XML_SERIALIZER_ERROR_NO_SERIALIZATION); } return $this->_serializedData; } /** * serialize any value * * This method checks for the type of the value and calls the appropriate method * * @access private * @param mixed $value * @param string $tagName * @param array $attributes * @return string */ function _serializeValue($value, $tagName = null, $attributes = array()) { if (is_array($value)) { $xml = $this->_serializeArray($value, $tagName, $attributes); } elseif (is_object($value)) { $xml = $this->_serializeObject($value, $tagName); } else { $tag = array( 'qname' => $tagName, 'attributes' => $attributes, 'content' => $value ); $xml = $this->_createXMLTag($tag); } return $xml; } /** * serialize an array * * @access private * @param array $array array to serialize * @param string $tagName name of the root tag * @param array $attributes attributes for the root tag * @return string $string serialized data * @uses XML_Util::isValidName() to check, whether key has to be substituted */ function _serializeArray(&$array, $tagName = null, $attributes = array()) { $_content = null; /** * check for special attributes */ if ($this->options['attributesArray'] !== null) { if (isset($array[$this->options['attributesArray']])) { $attributes = $array[$this->options['attributesArray']]; unset($array[$this->options['attributesArray']]); } /** * check for special content */ if ($this->options['contentName'] !== null) { if (isset($array[$this->options['contentName']])) { $_content = $array[$this->options['contentName']]; unset($array[$this->options['contentName']]); } } } /* * if mode is set to simpleXML, check whether * the array is associative or indexed */ if (is_array($array) && $this->options['mode'] == 'simplexml') { $indexed = true; if (!count($array)) { $indexed = false; } foreach ($array as $key => $val) { if (!is_int($key)) { $indexed = false; break; } } if ($indexed && $this->options['mode'] == 'simplexml') { $string = ''; foreach ($array as $key => $val) { if ($this->options['beautifyFilelist'] && $tagName == 'dir') { if (!isset($this->_curdir)) { $this->_curdir = ''; } $savedir = $this->_curdir; if (isset($val['attribs'])) { if ($val['attribs']['name'] == '/') { $this->_curdir = '/'; } else { if ($this->_curdir == '/') { $this->_curdir = ''; } $this->_curdir .= '/' . $val['attribs']['name']; } } } $string .= $this->_serializeValue( $val, $tagName, $attributes); if ($this->options['beautifyFilelist'] && $tagName == 'dir') { $string .= ' '; if (empty($savedir)) { unset($this->_curdir); } else { $this->_curdir = $savedir; } } $string .= $this->options['linebreak']; // do indentation if ($this->options['indent'] !== null && $this->_tagDepth > 0) { $string .= str_repeat($this->options['indent'], $this->_tagDepth); } } return rtrim($string); } } if ($this->options['scalarAsAttributes'] === true) { foreach ($array as $key => $value) { if (is_scalar($value) && (XML_Util::isValidName($key) === true)) { unset($array[$key]); $attributes[$this->options['prependAttributes'].$key] = $value; } } } // check for empty array => create empty tag if (empty($array)) { $tag = array( 'qname' => $tagName, 'content' => $_content, 'attributes' => $attributes ); } else { $this->_tagDepth++; $tmp = $this->options['linebreak']; foreach ($array as $key => $value) { // do indentation if ($this->options['indent'] !== null && $this->_tagDepth > 0) { $tmp .= str_repeat($this->options['indent'], $this->_tagDepth); } // copy key $origKey = $key; // key cannot be used as tagname => use default tag $valid = XML_Util::isValidName($key); if (PEAR::isError($valid)) { if ($this->options['classAsTagName'] && is_object($value)) { $key = get_class($value); } else { $key = $this->options['defaultTagName']; } } $atts = array(); if ($this->options['typeHints'] === true) { $atts[$this->options['typeAttribute']] = gettype($value); if ($key !== $origKey) { $atts[$this->options['keyAttribute']] = (string)$origKey; } } if ($this->options['beautifyFilelist'] && $key == 'dir') { if (!isset($this->_curdir)) { $this->_curdir = ''; } $savedir = $this->_curdir; if (isset($value['attribs'])) { if ($value['attribs']['name'] == '/') { $this->_curdir = '/'; } else { $this->_curdir .= '/' . $value['attribs']['name']; } } } if (is_string($value) && $value && ($value[strlen($value) - 1] == "\n")) { $value .= str_repeat($this->options['indent'], $this->_tagDepth); } $tmp .= $this->_createXMLTag(array( 'qname' => $key, 'attributes' => $atts, 'content' => $value ) ); if ($this->options['beautifyFilelist'] && $key == 'dir') { if (isset($value['attribs'])) { $tmp .= ' '; if (empty($savedir)) { unset($this->_curdir); } else { $this->_curdir = $savedir; } } } $tmp .= $this->options['linebreak']; } $this->_tagDepth--; if ($this->options['indent']!==null && $this->_tagDepth>0) { $tmp .= str_repeat($this->options['indent'], $this->_tagDepth); } if (trim($tmp) === '') { $tmp = null; } $tag = array( 'qname' => $tagName, 'content' => $tmp, 'attributes' => $attributes ); } if ($this->options['typeHints'] === true) { if (!isset($tag['attributes'][$this->options['typeAttribute']])) { $tag['attributes'][$this->options['typeAttribute']] = 'array'; } } $string = $this->_createXMLTag($tag, false); return $string; } /** * create a tag from an array * this method awaits an array in the following format * array( * 'qname' => $tagName, * 'attributes' => array(), * 'content' => $content, // optional * 'namespace' => $namespace // optional * 'namespaceUri' => $namespaceUri // optional * ) * * @access private * @param array $tag tag definition * @param boolean $replaceEntities whether to replace XML entities in content or not * @return string $string XML tag */ function _createXMLTag($tag, $replaceEntities = true) { if ($this->options['indentAttributes'] !== false) { $multiline = true; $indent = str_repeat($this->options['indent'], $this->_tagDepth); if ($this->options['indentAttributes'] == '_auto') { $indent .= str_repeat(' ', (strlen($tag['qname'])+2)); } else { $indent .= $this->options['indentAttributes']; } } else { $indent = $multiline = false; } if (is_array($tag['content'])) { if (empty($tag['content'])) { $tag['content'] = ''; } } elseif(is_scalar($tag['content']) && (string)$tag['content'] == '') { $tag['content'] = ''; } if (is_scalar($tag['content']) || is_null($tag['content'])) { if ($replaceEntities === true) { $replaceEntities = XML_UTIL_ENTITIES_XML; } $tag = XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $this->options['linebreak']); } elseif (is_array($tag['content'])) { $tag = $this->_serializeArray($tag['content'], $tag['qname'], $tag['attributes']); } elseif (is_object($tag['content'])) { $tag = $this->_serializeObject($tag['content'], $tag['qname'], $tag['attributes']); } elseif (is_resource($tag['content'])) { settype($tag['content'], 'string'); $tag = XML_Util::createTagFromArray($tag, $replaceEntities); } return $tag; } } PK=1]HD__pear/PEAR/PackageFile/v2/rw.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a8 */ /** * For base class */ require_once 'PEAR/PackageFile/v2.php'; /** * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a8 */ class PEAR_PackageFile_v2_rw extends PEAR_PackageFile_v2 { /** * @param string Extension name * @return bool success of operation */ function setProvidesExtension($extension) { if (in_array($this->getPackageType(), array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) { if (!isset($this->_packageInfo['providesextension'])) { // ensure that the channel tag is set up in the right location $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), $extension, 'providesextension'); } $this->_packageInfo['providesextension'] = $extension; return true; } return false; } function setPackage($package) { $this->_isValid = 0; if (!isset($this->_packageInfo['attribs'])) { $this->_packageInfo = array_merge(array('attribs' => array( 'version' => '2.0', 'xmlns' => 'http://pear.php.net/dtd/package-2.0', 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0', 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd', )), $this->_packageInfo); } if (!isset($this->_packageInfo['name'])) { return $this->_packageInfo = array_merge(array('name' => $package), $this->_packageInfo); } $this->_packageInfo['name'] = $package; } /** * set this as a package.xml version 2.1 * @access private */ function _setPackageVersion2_1() { $info = array( 'version' => '2.1', 'xmlns' => 'http://pear.php.net/dtd/package-2.1', 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0', 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.1 http://pear.php.net/dtd/package-2.1.xsd', ); if (!isset($this->_packageInfo['attribs'])) { $this->_packageInfo = array_merge(array('attribs' => $info), $this->_packageInfo); } else { $this->_packageInfo['attribs'] = $info; } } function setUri($uri) { unset($this->_packageInfo['channel']); $this->_isValid = 0; if (!isset($this->_packageInfo['uri'])) { // ensure that the uri tag is set up in the right location $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('extends', 'summary', 'description', 'lead', 'developer', 'contributor', 'helper', 'date', 'time', 'version', 'stability', 'license', 'notes', 'contents', 'compatible', 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', 'extbinrelease', 'bundle', 'changelog'), $uri, 'uri'); } $this->_packageInfo['uri'] = $uri; } function setChannel($channel) { unset($this->_packageInfo['uri']); $this->_isValid = 0; if (!isset($this->_packageInfo['channel'])) { // ensure that the channel tag is set up in the right location $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('extends', 'summary', 'description', 'lead', 'developer', 'contributor', 'helper', 'date', 'time', 'version', 'stability', 'license', 'notes', 'contents', 'compatible', 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', 'extbinrelease', 'bundle', 'changelog'), $channel, 'channel'); } $this->_packageInfo['channel'] = $channel; } function setExtends($extends) { $this->_isValid = 0; if (!isset($this->_packageInfo['extends'])) { // ensure that the extends tag is set up in the right location $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('summary', 'description', 'lead', 'developer', 'contributor', 'helper', 'date', 'time', 'version', 'stability', 'license', 'notes', 'contents', 'compatible', 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', 'extbinrelease', 'bundle', 'changelog'), $extends, 'extends'); } $this->_packageInfo['extends'] = $extends; } function setSummary($summary) { $this->_isValid = 0; if (!isset($this->_packageInfo['summary'])) { // ensure that the summary tag is set up in the right location $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('description', 'lead', 'developer', 'contributor', 'helper', 'date', 'time', 'version', 'stability', 'license', 'notes', 'contents', 'compatible', 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', 'extbinrelease', 'bundle', 'changelog'), $summary, 'summary'); } $this->_packageInfo['summary'] = $summary; } function setDescription($desc) { $this->_isValid = 0; if (!isset($this->_packageInfo['description'])) { // ensure that the description tag is set up in the right location $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('lead', 'developer', 'contributor', 'helper', 'date', 'time', 'version', 'stability', 'license', 'notes', 'contents', 'compatible', 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', 'extbinrelease', 'bundle', 'changelog'), $desc, 'description'); } $this->_packageInfo['description'] = $desc; } /** * Adds a new maintainer - no checking of duplicates is performed, use * updatemaintainer for that purpose. */ function addMaintainer($role, $handle, $name, $email, $active = 'yes') { if (!in_array($role, array('lead', 'developer', 'contributor', 'helper'))) { return false; } if (isset($this->_packageInfo[$role])) { if (!isset($this->_packageInfo[$role][0])) { $this->_packageInfo[$role] = array($this->_packageInfo[$role]); } $this->_packageInfo[$role][] = array( 'name' => $name, 'user' => $handle, 'email' => $email, 'active' => $active, ); } else { $testarr = array('lead', 'developer', 'contributor', 'helper', 'date', 'time', 'version', 'stability', 'license', 'notes', 'contents', 'compatible', 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'); foreach (array('lead', 'developer', 'contributor', 'helper') as $testrole) { array_shift($testarr); if ($role == $testrole) { break; } } if (!isset($this->_packageInfo[$role])) { // ensure that the extends tag is set up in the right location $this->_packageInfo = $this->_insertBefore($this->_packageInfo, $testarr, array(), $role); } $this->_packageInfo[$role] = array( 'name' => $name, 'user' => $handle, 'email' => $email, 'active' => $active, ); } $this->_isValid = 0; } function updateMaintainer($newrole, $handle, $name, $email, $active = 'yes') { $found = false; foreach (array('lead', 'developer', 'contributor', 'helper') as $role) { if (!isset($this->_packageInfo[$role])) { continue; } $info = $this->_packageInfo[$role]; if (!isset($info[0])) { if ($info['user'] == $handle) { $found = true; break; } } foreach ($info as $i => $maintainer) { if (is_array($maintainer) && $maintainer['user'] == $handle) { $found = $i; break 2; } } } if ($found === false) { return $this->addMaintainer($newrole, $handle, $name, $email, $active); } if ($found !== false) { if ($found === true) { unset($this->_packageInfo[$role]); } else { unset($this->_packageInfo[$role][$found]); $this->_packageInfo[$role] = array_values($this->_packageInfo[$role]); } } $this->addMaintainer($newrole, $handle, $name, $email, $active); $this->_isValid = 0; } function deleteMaintainer($handle) { $found = false; foreach (array('lead', 'developer', 'contributor', 'helper') as $role) { if (!isset($this->_packageInfo[$role])) { continue; } if (!isset($this->_packageInfo[$role][0])) { $this->_packageInfo[$role] = array($this->_packageInfo[$role]); } foreach ($this->_packageInfo[$role] as $i => $maintainer) { if ($maintainer['user'] == $handle) { $found = $i; break; } } if ($found !== false) { unset($this->_packageInfo[$role][$found]); if (!count($this->_packageInfo[$role]) && $role == 'lead') { $this->_isValid = 0; } if (!count($this->_packageInfo[$role])) { unset($this->_packageInfo[$role]); return true; } $this->_packageInfo[$role] = array_values($this->_packageInfo[$role]); if (count($this->_packageInfo[$role]) == 1) { $this->_packageInfo[$role] = $this->_packageInfo[$role][0]; } return true; } if (count($this->_packageInfo[$role]) == 1) { $this->_packageInfo[$role] = $this->_packageInfo[$role][0]; } } return false; } function setReleaseVersion($version) { if (isset($this->_packageInfo['version']) && isset($this->_packageInfo['version']['release'])) { unset($this->_packageInfo['version']['release']); } $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $version, array( 'version' => array('stability', 'license', 'notes', 'contents', 'compatible', 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', 'extbinrelease', 'bundle', 'changelog'), 'release' => array('api'))); $this->_isValid = 0; } function setAPIVersion($version) { if (isset($this->_packageInfo['version']) && isset($this->_packageInfo['version']['api'])) { unset($this->_packageInfo['version']['api']); } $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $version, array( 'version' => array('stability', 'license', 'notes', 'contents', 'compatible', 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', 'extbinrelease', 'bundle', 'changelog'), 'api' => array())); $this->_isValid = 0; } /** * snapshot|devel|alpha|beta|stable */ function setReleaseStability($state) { if (isset($this->_packageInfo['stability']) && isset($this->_packageInfo['stability']['release'])) { unset($this->_packageInfo['stability']['release']); } $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $state, array( 'stability' => array('license', 'notes', 'contents', 'compatible', 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', 'extbinrelease', 'bundle', 'changelog'), 'release' => array('api'))); $this->_isValid = 0; } /** * @param devel|alpha|beta|stable */ function setAPIStability($state) { if (isset($this->_packageInfo['stability']) && isset($this->_packageInfo['stability']['api'])) { unset($this->_packageInfo['stability']['api']); } $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $state, array( 'stability' => array('license', 'notes', 'contents', 'compatible', 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', 'extbinrelease', 'bundle', 'changelog'), 'api' => array())); $this->_isValid = 0; } function setLicense($license, $uri = false, $filesource = false) { if (!isset($this->_packageInfo['license'])) { // ensure that the license tag is set up in the right location $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('notes', 'contents', 'compatible', 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', 'extbinrelease', 'bundle', 'changelog'), 0, 'license'); } if ($uri || $filesource) { $attribs = array(); if ($uri) { $attribs['uri'] = $uri; } $uri = true; // for test below if ($filesource) { $attribs['filesource'] = $filesource; } } $license = $uri ? array('attribs' => $attribs, '_content' => $license) : $license; $this->_packageInfo['license'] = $license; $this->_isValid = 0; } function setNotes($notes) { $this->_isValid = 0; if (!isset($this->_packageInfo['notes'])) { // ensure that the notes tag is set up in the right location $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('contents', 'compatible', 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', 'extbinrelease', 'bundle', 'changelog'), $notes, 'notes'); } $this->_packageInfo['notes'] = $notes; } /** * This is only used at install-time, after all serialization * is over. * @param string file name * @param string installed path */ function setInstalledAs($file, $path) { if ($path) { return $this->_packageInfo['filelist'][$file]['installed_as'] = $path; } unset($this->_packageInfo['filelist'][$file]['installed_as']); } /** * This is only used at install-time, after all serialization * is over. */ function installedFile($file, $atts) { if (isset($this->_packageInfo['filelist'][$file])) { $this->_packageInfo['filelist'][$file] = array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']); } else { $this->_packageInfo['filelist'][$file] = $atts['attribs']; } } /** * Reset the listing of package contents * @param string base installation dir for the whole package, if any */ function clearContents($baseinstall = false) { $this->_filesValid = false; $this->_isValid = 0; if (!isset($this->_packageInfo['contents'])) { $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('compatible', 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), array(), 'contents'); } if ($this->getPackageType() != 'bundle') { $this->_packageInfo['contents'] = array('dir' => array('attribs' => array('name' => '/'))); if ($baseinstall) { $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'] = $baseinstall; } } else { $this->_packageInfo['contents'] = array('bundledpackage' => array()); } } /** * @param string relative path of the bundled package. */ function addBundledPackage($path) { if ($this->getPackageType() != 'bundle') { return false; } $this->_filesValid = false; $this->_isValid = 0; $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $path, array( 'contents' => array('compatible', 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), 'bundledpackage' => array())); } /** * @param string file name * @param PEAR_Task_Common a read/write task */ function addTaskToFile($filename, $task) { if (!method_exists($task, 'getXml')) { return false; } if (!method_exists($task, 'getName')) { return false; } if (!method_exists($task, 'validate')) { return false; } if (!$task->validate()) { return false; } if (!isset($this->_packageInfo['contents']['dir']['file'])) { return false; } $this->getTasksNs(); // discover the tasks namespace if not done already $files = $this->_packageInfo['contents']['dir']['file']; if (!isset($files[0])) { $files = array($files); $ind = false; } else { $ind = true; } foreach ($files as $i => $file) { if (isset($file['attribs'])) { if ($file['attribs']['name'] == $filename) { if ($ind) { $t = isset($this->_packageInfo['contents']['dir']['file'][$i] ['attribs'][$this->_tasksNs . ':' . $task->getName()]) ? $this->_packageInfo['contents']['dir']['file'][$i] ['attribs'][$this->_tasksNs . ':' . $task->getName()] : false; if ($t && !isset($t[0])) { $this->_packageInfo['contents']['dir']['file'][$i] [$this->_tasksNs . ':' . $task->getName()] = array($t); } $this->_packageInfo['contents']['dir']['file'][$i][$this->_tasksNs . ':' . $task->getName()][] = $task->getXml(); } else { $t = isset($this->_packageInfo['contents']['dir']['file'] ['attribs'][$this->_tasksNs . ':' . $task->getName()]) ? $this->_packageInfo['contents']['dir']['file'] ['attribs'][$this->_tasksNs . ':' . $task->getName()] : false; if ($t && !isset($t[0])) { $this->_packageInfo['contents']['dir']['file'] [$this->_tasksNs . ':' . $task->getName()] = array($t); } $this->_packageInfo['contents']['dir']['file'][$this->_tasksNs . ':' . $task->getName()][] = $task->getXml(); } return true; } } } return false; } /** * @param string path to the file * @param string filename * @param array extra attributes */ function addFile($dir, $file, $attrs) { if ($this->getPackageType() == 'bundle') { return false; } $this->_filesValid = false; $this->_isValid = 0; $dir = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $dir); if ($dir == '/' || $dir == '') { $dir = ''; } else { $dir .= '/'; } $attrs['name'] = $dir . $file; if (!isset($this->_packageInfo['contents'])) { // ensure that the contents tag is set up $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('compatible', 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), array(), 'contents'); } if (isset($this->_packageInfo['contents']['dir']['file'])) { if (!isset($this->_packageInfo['contents']['dir']['file'][0])) { $this->_packageInfo['contents']['dir']['file'] = array($this->_packageInfo['contents']['dir']['file']); } $this->_packageInfo['contents']['dir']['file'][]['attribs'] = $attrs; } else { $this->_packageInfo['contents']['dir']['file']['attribs'] = $attrs; } } /** * @param string Dependent package name * @param string Dependent package's channel name * @param string minimum version of specified package that this release is guaranteed to be * compatible with * @param string maximum version of specified package that this release is guaranteed to be * compatible with * @param string versions of specified package that this release is not compatible with */ function addCompatiblePackage($name, $channel, $min, $max, $exclude = false) { $this->_isValid = 0; $set = array( 'name' => $name, 'channel' => $channel, 'min' => $min, 'max' => $max, ); if ($exclude) { $set['exclude'] = $exclude; } $this->_isValid = 0; $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array( 'compatible' => array('dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog') )); } /** * Removes the tag entirely */ function resetUsesrole() { if (isset($this->_packageInfo['usesrole'])) { unset($this->_packageInfo['usesrole']); } } /** * @param string * @param string package name or uri * @param string channel name if non-uri */ function addUsesrole($role, $packageOrUri, $channel = false) { $set = array('role' => $role); if ($channel) { $set['package'] = $packageOrUri; $set['channel'] = $channel; } else { $set['uri'] = $packageOrUri; } $this->_isValid = 0; $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array( 'usesrole' => array('usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog') )); } /** * Removes the tag entirely */ function resetUsestask() { if (isset($this->_packageInfo['usestask'])) { unset($this->_packageInfo['usestask']); } } /** * @param string * @param string package name or uri * @param string channel name if non-uri */ function addUsestask($task, $packageOrUri, $channel = false) { $set = array('task' => $task); if ($channel) { $set['package'] = $packageOrUri; $set['channel'] = $channel; } else { $set['uri'] = $packageOrUri; } $this->_isValid = 0; $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array( 'usestask' => array('srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog') )); } /** * Remove all compatible tags */ function clearCompatible() { unset($this->_packageInfo['compatible']); } /** * Reset dependencies prior to adding new ones */ function clearDeps() { if (!isset($this->_packageInfo['dependencies'])) { $this->_packageInfo = $this->_mergeTag($this->_packageInfo, array(), array( 'dependencies' => array('providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'))); } $this->_packageInfo['dependencies'] = array(); } /** * @param string minimum PHP version allowed * @param string maximum PHP version allowed * @param array $exclude incompatible PHP versions */ function setPhpDep($min, $max = false, $exclude = false) { $this->_isValid = 0; $dep = array( 'min' => $min, ); if ($max) { $dep['max'] = $max; } if ($exclude) { if (count($exclude) == 1) { $exclude = $exclude[0]; } $dep['exclude'] = $exclude; } if (isset($this->_packageInfo['dependencies']['required']['php'])) { $this->_stack->push(__FUNCTION__, 'warning', array('dep' => $this->_packageInfo['dependencies']['required']['php']), 'warning: PHP dependency already exists, overwriting'); unset($this->_packageInfo['dependencies']['required']['php']); } $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, array( 'dependencies' => array('providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), 'required' => array('optional', 'group'), 'php' => array('pearinstaller', 'package', 'subpackage', 'extension', 'os', 'arch') )); return true; } /** * @param string minimum allowed PEAR installer version * @param string maximum allowed PEAR installer version * @param string recommended PEAR installer version * @param array incompatible version of the PEAR installer */ function setPearinstallerDep($min, $max = false, $recommended = false, $exclude = false) { $this->_isValid = 0; $dep = array( 'min' => $min, ); if ($max) { $dep['max'] = $max; } if ($recommended) { $dep['recommended'] = $recommended; } if ($exclude) { if (count($exclude) == 1) { $exclude = $exclude[0]; } $dep['exclude'] = $exclude; } if (isset($this->_packageInfo['dependencies']['required']['pearinstaller'])) { $this->_stack->push(__FUNCTION__, 'warning', array('dep' => $this->_packageInfo['dependencies']['required']['pearinstaller']), 'warning: PEAR Installer dependency already exists, overwriting'); unset($this->_packageInfo['dependencies']['required']['pearinstaller']); } $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, array( 'dependencies' => array('providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), 'required' => array('optional', 'group'), 'pearinstaller' => array('package', 'subpackage', 'extension', 'os', 'arch') )); } /** * Mark a package as conflicting with this package * @param string package name * @param string package channel * @param string extension this package provides, if any * @param string|false minimum version required * @param string|false maximum version allowed * @param array|false versions to exclude from installation */ function addConflictingPackageDepWithChannel($name, $channel, $providesextension = false, $min = false, $max = false, $exclude = false) { $this->_isValid = 0; $dep = $this->_constructDep($name, $channel, false, $min, $max, false, $exclude, $providesextension, false, true); $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, array( 'dependencies' => array('providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), 'required' => array('optional', 'group'), 'package' => array('subpackage', 'extension', 'os', 'arch') )); } /** * Mark a package as conflicting with this package * @param string package name * @param string package channel * @param string extension this package provides, if any */ function addConflictingPackageDepWithUri($name, $uri, $providesextension = false) { $this->_isValid = 0; $dep = array( 'name' => $name, 'uri' => $uri, 'conflicts' => '', ); if ($providesextension) { $dep['providesextension'] = $providesextension; } $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, array( 'dependencies' => array('providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), 'required' => array('optional', 'group'), 'package' => array('subpackage', 'extension', 'os', 'arch') )); } function addDependencyGroup($name, $hint) { $this->_isValid = 0; $this->_packageInfo = $this->_mergeTag($this->_packageInfo, array('attribs' => array('name' => $name, 'hint' => $hint)), array( 'dependencies' => array('providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), 'group' => array(), )); } /** * @param string package name * @param string|false channel name, false if this is a uri * @param string|false uri name, false if this is a channel * @param string|false minimum version required * @param string|false maximum version allowed * @param string|false recommended installation version * @param array|false versions to exclude from installation * @param string extension this package provides, if any * @param bool if true, tells the installer to ignore the default optional dependency group * when installing this package * @param bool if true, tells the installer to negate this dependency (conflicts) * @return array * @access private */ function _constructDep($name, $channel, $uri, $min, $max, $recommended, $exclude, $providesextension = false, $nodefault = false, $conflicts = false) { $dep = array( 'name' => $name, ); if ($channel) { $dep['channel'] = $channel; } elseif ($uri) { $dep['uri'] = $uri; } if ($min) { $dep['min'] = $min; } if ($max) { $dep['max'] = $max; } if ($recommended) { $dep['recommended'] = $recommended; } if ($exclude) { if (is_array($exclude) && count($exclude) == 1) { $exclude = $exclude[0]; } $dep['exclude'] = $exclude; } if ($conflicts) { $dep['conflicts'] = ''; } if ($nodefault) { $dep['nodefault'] = ''; } if ($providesextension) { $dep['providesextension'] = $providesextension; } return $dep; } /** * @param package|subpackage * @param string group name * @param string package name * @param string package channel * @param string minimum version * @param string maximum version * @param string recommended version * @param array|false optional excluded versions * @param string extension this package provides, if any * @param bool if true, tells the installer to ignore the default optional dependency group * when installing this package * @return bool false if the dependency group has not been initialized with * {@link addDependencyGroup()}, or a subpackage is added with * a providesextension */ function addGroupPackageDepWithChannel($type, $groupname, $name, $channel, $min = false, $max = false, $recommended = false, $exclude = false, $providesextension = false, $nodefault = false) { if ($type == 'subpackage' && $providesextension) { return false; // subpackages must be php packages } $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude, $providesextension, $nodefault); return $this->_addGroupDependency($type, $dep, $groupname); } /** * @param package|subpackage * @param string group name * @param string package name * @param string package uri * @param string extension this package provides, if any * @param bool if true, tells the installer to ignore the default optional dependency group * when installing this package * @return bool false if the dependency group has not been initialized with * {@link addDependencyGroup()} */ function addGroupPackageDepWithURI($type, $groupname, $name, $uri, $providesextension = false, $nodefault = false) { if ($type == 'subpackage' && $providesextension) { return false; // subpackages must be php packages } $dep = $this->_constructDep($name, false, $uri, false, false, false, false, $providesextension, $nodefault); return $this->_addGroupDependency($type, $dep, $groupname); } /** * @param string group name (must be pre-existing) * @param string extension name * @param string minimum version allowed * @param string maximum version allowed * @param string recommended version * @param array incompatible versions */ function addGroupExtensionDep($groupname, $name, $min = false, $max = false, $recommended = false, $exclude = false) { $this->_isValid = 0; $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude); return $this->_addGroupDependency('extension', $dep, $groupname); } /** * @param package|subpackage|extension * @param array dependency contents * @param string name of the dependency group to add this to * @return boolean * @access private */ function _addGroupDependency($type, $dep, $groupname) { $arr = array('subpackage', 'extension'); if ($type != 'package') { array_shift($arr); } if ($type == 'extension') { array_shift($arr); } if (!isset($this->_packageInfo['dependencies']['group'])) { return false; } else { if (!isset($this->_packageInfo['dependencies']['group'][0])) { if ($this->_packageInfo['dependencies']['group']['attribs']['name'] == $groupname) { $this->_packageInfo['dependencies']['group'] = $this->_mergeTag( $this->_packageInfo['dependencies']['group'], $dep, array( $type => $arr )); $this->_isValid = 0; return true; } else { return false; } } else { foreach ($this->_packageInfo['dependencies']['group'] as $i => $group) { if ($group['attribs']['name'] == $groupname) { $this->_packageInfo['dependencies']['group'][$i] = $this->_mergeTag( $this->_packageInfo['dependencies']['group'][$i], $dep, array( $type => $arr )); $this->_isValid = 0; return true; } } return false; } } } /** * @param optional|required * @param string package name * @param string package channel * @param string minimum version * @param string maximum version * @param string recommended version * @param string extension this package provides, if any * @param bool if true, tells the installer to ignore the default optional dependency group * when installing this package * @param array|false optional excluded versions */ function addPackageDepWithChannel($type, $name, $channel, $min = false, $max = false, $recommended = false, $exclude = false, $providesextension = false, $nodefault = false) { if (!in_array($type, array('optional', 'required'), true)) { $type = 'required'; } $this->_isValid = 0; $arr = array('optional', 'group'); if ($type != 'required') { array_shift($arr); } $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude, $providesextension, $nodefault); $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, array( 'dependencies' => array('providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), $type => $arr, 'package' => array('subpackage', 'extension', 'os', 'arch') )); } /** * @param optional|required * @param string name of the package * @param string uri of the package * @param string extension this package provides, if any * @param bool if true, tells the installer to ignore the default optional dependency group * when installing this package */ function addPackageDepWithUri($type, $name, $uri, $providesextension = false, $nodefault = false) { $this->_isValid = 0; $arr = array('optional', 'group'); if ($type != 'required') { array_shift($arr); } $dep = $this->_constructDep($name, false, $uri, false, false, false, false, $providesextension, $nodefault); $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, array( 'dependencies' => array('providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), $type => $arr, 'package' => array('subpackage', 'extension', 'os', 'arch') )); } /** * @param optional|required optional, required * @param string package name * @param string package channel * @param string minimum version * @param string maximum version * @param string recommended version * @param array incompatible versions * @param bool if true, tells the installer to ignore the default optional dependency group * when installing this package */ function addSubpackageDepWithChannel($type, $name, $channel, $min = false, $max = false, $recommended = false, $exclude = false, $nodefault = false) { $this->_isValid = 0; $arr = array('optional', 'group'); if ($type != 'required') { array_shift($arr); } $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude, $nodefault); $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, array( 'dependencies' => array('providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), $type => $arr, 'subpackage' => array('extension', 'os', 'arch') )); } /** * @param optional|required optional, required * @param string package name * @param string package uri for download * @param bool if true, tells the installer to ignore the default optional dependency group * when installing this package */ function addSubpackageDepWithUri($type, $name, $uri, $nodefault = false) { $this->_isValid = 0; $arr = array('optional', 'group'); if ($type != 'required') { array_shift($arr); } $dep = $this->_constructDep($name, false, $uri, false, false, false, false, $nodefault); $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, array( 'dependencies' => array('providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), $type => $arr, 'subpackage' => array('extension', 'os', 'arch') )); } /** * @param optional|required optional, required * @param string extension name * @param string minimum version * @param string maximum version * @param string recommended version * @param array incompatible versions */ function addExtensionDep($type, $name, $min = false, $max = false, $recommended = false, $exclude = false) { $this->_isValid = 0; $arr = array('optional', 'group'); if ($type != 'required') { array_shift($arr); } $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude); $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, array( 'dependencies' => array('providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), $type => $arr, 'extension' => array('os', 'arch') )); } /** * @param string Operating system name * @param boolean true if this package cannot be installed on this OS */ function addOsDep($name, $conflicts = false) { $this->_isValid = 0; $dep = array('name' => $name); if ($conflicts) { $dep['conflicts'] = ''; } $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, array( 'dependencies' => array('providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), 'required' => array('optional', 'group'), 'os' => array('arch') )); } /** * @param string Architecture matching pattern * @param boolean true if this package cannot be installed on this architecture */ function addArchDep($pattern, $conflicts = false) { $this->_isValid = 0; $dep = array('pattern' => $pattern); if ($conflicts) { $dep['conflicts'] = ''; } $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, array( 'dependencies' => array('providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), 'required' => array('optional', 'group'), 'arch' => array() )); } /** * Set the kind of package, and erase all release tags * * - a php package is a PEAR-style package * - an extbin package is a PECL-style extension binary * - an extsrc package is a PECL-style source for a binary * - an zendextbin package is a PECL-style zend extension binary * - an zendextsrc package is a PECL-style source for a zend extension binary * - a bundle package is a collection of other pre-packaged packages * @param php|extbin|extsrc|zendextsrc|zendextbin|bundle * @return bool success */ function setPackageType($type) { $this->_isValid = 0; if (!in_array($type, array('php', 'extbin', 'extsrc', 'zendextsrc', 'zendextbin', 'bundle'))) { return false; } if (in_array($type, array('zendextsrc', 'zendextbin'))) { $this->_setPackageVersion2_1(); } if ($type != 'bundle') { $type .= 'release'; } foreach (array('phprelease', 'extbinrelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle') as $test) { unset($this->_packageInfo[$test]); } if (!isset($this->_packageInfo[$type])) { // ensure that the release tag is set up $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('changelog'), array(), $type); } $this->_packageInfo[$type] = array(); return true; } /** * @return bool true if package type is set up */ function addRelease() { if ($type = $this->getPackageType()) { if ($type != 'bundle') { $type .= 'release'; } $this->_packageInfo = $this->_mergeTag($this->_packageInfo, array(), array($type => array('changelog'))); return true; } return false; } /** * Get the current release tag in order to add to it * @param bool returns only releases that have installcondition if true * @return array|null */ function &_getCurrentRelease($strict = true) { if ($p = $this->getPackageType()) { if ($strict) { if ($p == 'extsrc' || $p == 'zendextsrc') { $a = null; return $a; } } if ($p != 'bundle') { $p .= 'release'; } if (isset($this->_packageInfo[$p][0])) { return $this->_packageInfo[$p][count($this->_packageInfo[$p]) - 1]; } else { return $this->_packageInfo[$p]; } } else { $a = null; return $a; } } /** * Add a file to the current release that should be installed under a different name * @param string path to file * @param string name the file should be installed as */ function addInstallAs($path, $as) { $r = &$this->_getCurrentRelease(); if ($r === null) { return false; } $this->_isValid = 0; $r = $this->_mergeTag($r, array('attribs' => array('name' => $path, 'as' => $as)), array( 'filelist' => array(), 'install' => array('ignore') )); } /** * Add a file to the current release that should be ignored * @param string path to file * @return bool success of operation */ function addIgnore($path) { $r = &$this->_getCurrentRelease(); if ($r === null) { return false; } $this->_isValid = 0; $r = $this->_mergeTag($r, array('attribs' => array('name' => $path)), array( 'filelist' => array(), 'ignore' => array() )); } /** * Add an extension binary package for this extension source code release * * Note that the package must be from the same channel as the extension source package * @param string */ function addBinarypackage($package) { if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') { return false; } $r = &$this->_getCurrentRelease(false); if ($r === null) { return false; } $this->_isValid = 0; $r = $this->_mergeTag($r, $package, array( 'binarypackage' => array('filelist'), )); } /** * Add a configureoption to an extension source package * @param string * @param string * @param string */ function addConfigureOption($name, $prompt, $default = null) { if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') { return false; } $r = &$this->_getCurrentRelease(false); if ($r === null) { return false; } $opt = array('attribs' => array('name' => $name, 'prompt' => $prompt)); if ($default !== null) { $opt['attribs']['default'] = $default; } $this->_isValid = 0; $r = $this->_mergeTag($r, $opt, array( 'configureoption' => array('binarypackage', 'filelist'), )); } /** * Set an installation condition based on php version for the current release set * @param string minimum version * @param string maximum version * @param false|array incompatible versions of PHP */ function setPhpInstallCondition($min, $max, $exclude = false) { $r = &$this->_getCurrentRelease(); if ($r === null) { return false; } $this->_isValid = 0; if (isset($r['installconditions']['php'])) { unset($r['installconditions']['php']); } $dep = array('min' => $min, 'max' => $max); if ($exclude) { if (is_array($exclude) && count($exclude) == 1) { $exclude = $exclude[0]; } $dep['exclude'] = $exclude; } if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') { $r = $this->_mergeTag($r, $dep, array( 'installconditions' => array('configureoption', 'binarypackage', 'filelist'), 'php' => array('extension', 'os', 'arch') )); } else { $r = $this->_mergeTag($r, $dep, array( 'installconditions' => array('filelist'), 'php' => array('extension', 'os', 'arch') )); } } /** * @param optional|required optional, required * @param string extension name * @param string minimum version * @param string maximum version * @param string recommended version * @param array incompatible versions */ function addExtensionInstallCondition($name, $min = false, $max = false, $recommended = false, $exclude = false) { $r = &$this->_getCurrentRelease(); if ($r === null) { return false; } $this->_isValid = 0; $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude); if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') { $r = $this->_mergeTag($r, $dep, array( 'installconditions' => array('configureoption', 'binarypackage', 'filelist'), 'extension' => array('os', 'arch') )); } else { $r = $this->_mergeTag($r, $dep, array( 'installconditions' => array('filelist'), 'extension' => array('os', 'arch') )); } } /** * Set an installation condition based on operating system for the current release set * @param string OS name * @param bool whether this OS is incompatible with the current release */ function setOsInstallCondition($name, $conflicts = false) { $r = &$this->_getCurrentRelease(); if ($r === null) { return false; } $this->_isValid = 0; if (isset($r['installconditions']['os'])) { unset($r['installconditions']['os']); } $dep = array('name' => $name); if ($conflicts) { $dep['conflicts'] = ''; } if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') { $r = $this->_mergeTag($r, $dep, array( 'installconditions' => array('configureoption', 'binarypackage', 'filelist'), 'os' => array('arch') )); } else { $r = $this->_mergeTag($r, $dep, array( 'installconditions' => array('filelist'), 'os' => array('arch') )); } } /** * Set an installation condition based on architecture for the current release set * @param string architecture pattern * @param bool whether this arch is incompatible with the current release */ function setArchInstallCondition($pattern, $conflicts = false) { $r = &$this->_getCurrentRelease(); if ($r === null) { return false; } $this->_isValid = 0; if (isset($r['installconditions']['arch'])) { unset($r['installconditions']['arch']); } $dep = array('pattern' => $pattern); if ($conflicts) { $dep['conflicts'] = ''; } if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') { $r = $this->_mergeTag($r, $dep, array( 'installconditions' => array('configureoption', 'binarypackage', 'filelist'), 'arch' => array() )); } else { $r = $this->_mergeTag($r, $dep, array( 'installconditions' => array('filelist'), 'arch' => array() )); } } /** * For extension binary releases, this is used to specify either the * static URI to a source package, or the package name and channel of the extsrc/zendextsrc * package it is based on. * @param string Package name, or full URI to source package (extsrc/zendextsrc type) */ function setSourcePackage($packageOrUri) { $this->_isValid = 0; if (isset($this->_packageInfo['channel'])) { $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), $packageOrUri, 'srcpackage'); } else { $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), $packageOrUri, 'srcuri'); } } /** * Generate a valid change log entry from the current package.xml * @param string|false */ function generateChangeLogEntry($notes = false) { return array( 'version' => array( 'release' => $this->getVersion('release'), 'api' => $this->getVersion('api'), ), 'stability' => $this->getStability(), 'date' => $this->getDate(), 'license' => $this->getLicense(true), 'notes' => $notes ? $notes : $this->getNotes() ); } /** * @param string release version to set change log notes for * @param array output of {@link generateChangeLogEntry()} */ function setChangelogEntry($releaseversion, $contents) { if (!isset($this->_packageInfo['changelog'])) { $this->_packageInfo['changelog']['release'] = $contents; return; } if (!isset($this->_packageInfo['changelog']['release'][0])) { if ($this->_packageInfo['changelog']['release']['version']['release'] == $releaseversion) { $this->_packageInfo['changelog']['release'] = array( $this->_packageInfo['changelog']['release']); } else { $this->_packageInfo['changelog']['release'] = array( $this->_packageInfo['changelog']['release']); return $this->_packageInfo['changelog']['release'][] = $contents; } } foreach($this->_packageInfo['changelog']['release'] as $index => $changelog) { if (isset($changelog['version']) && strnatcasecmp($changelog['version']['release'], $releaseversion) == 0) { $curlog = $index; } } if (isset($curlog)) { $this->_packageInfo['changelog']['release'][$curlog] = $contents; } else { $this->_packageInfo['changelog']['release'][] = $contents; } } /** * Remove the changelog entirely */ function clearChangeLog() { unset($this->_packageInfo['changelog']); } }PK=1]gLL&pear/PEAR/PackageFile/v2/Validator.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a8 */ /** * Private validation class used by PEAR_PackageFile_v2 - do not use directly, its * sole purpose is to split up the PEAR/PackageFile/v2.php file to make it smaller * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a8 * @access private */ class PEAR_PackageFile_v2_Validator { /** * @var array */ var $_packageInfo; /** * @var PEAR_PackageFile_v2 */ var $_pf; /** * @var PEAR_ErrorStack */ var $_stack; /** * @var int */ var $_isValid = 0; /** * @var int */ var $_filesValid = 0; /** * @var int */ var $_curState = 0; /** * @param PEAR_PackageFile_v2 * @param int */ function validate(&$pf, $state = PEAR_VALIDATE_NORMAL) { $this->_pf = &$pf; $this->_curState = $state; $this->_packageInfo = $this->_pf->getArray(); $this->_isValid = $this->_pf->_isValid; $this->_filesValid = $this->_pf->_filesValid; $this->_stack = &$pf->_stack; $this->_stack->getErrors(true); if (($this->_isValid & $state) == $state) { return true; } if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) { return false; } if (!isset($this->_packageInfo['attribs']['version']) || ($this->_packageInfo['attribs']['version'] != '2.0' && $this->_packageInfo['attribs']['version'] != '2.1') ) { $this->_noPackageVersion(); } $structure = array( 'name', 'channel|uri', '*extends', // can't be multiple, but this works fine 'summary', 'description', '+lead', // these all need content checks '*developer', '*contributor', '*helper', 'date', '*time', 'version', 'stability', 'license->?uri->?filesource', 'notes', 'contents', //special validation needed '*compatible', 'dependencies', //special validation needed '*usesrole', '*usestask', // reserve these for 1.4.0a1 to implement // this will allow a package.xml to gracefully say it // needs a certain package installed in order to implement a role or task '*providesextension', '*srcpackage|*srcuri', '+phprelease|+extsrcrelease|+extbinrelease|' . '+zendextsrcrelease|+zendextbinrelease|bundle', //special validation needed '*changelog', ); $test = $this->_packageInfo; if (isset($test['dependencies']) && isset($test['dependencies']['required']) && isset($test['dependencies']['required']['pearinstaller']) && isset($test['dependencies']['required']['pearinstaller']['min']) && '1.10.18' != '@package' . '_version@' && version_compare('1.10.18', $test['dependencies']['required']['pearinstaller']['min'], '<') ) { $this->_pearVersionTooLow($test['dependencies']['required']['pearinstaller']['min']); return false; } // ignore post-installation array fields if (array_key_exists('filelist', $test)) { unset($test['filelist']); } if (array_key_exists('_lastmodified', $test)) { unset($test['_lastmodified']); } if (array_key_exists('#binarypackage', $test)) { unset($test['#binarypackage']); } if (array_key_exists('old', $test)) { unset($test['old']); } if (array_key_exists('_lastversion', $test)) { unset($test['_lastversion']); } if (!$this->_stupidSchemaValidate($structure, $test, '')) { return false; } if (empty($this->_packageInfo['name'])) { $this->_tagCannotBeEmpty('name'); } $test = isset($this->_packageInfo['uri']) ? 'uri' :'channel'; if (empty($this->_packageInfo[$test])) { $this->_tagCannotBeEmpty($test); } if (is_array($this->_packageInfo['license']) && (!isset($this->_packageInfo['license']['_content']) || empty($this->_packageInfo['license']['_content']))) { $this->_tagCannotBeEmpty('license'); } elseif (empty($this->_packageInfo['license'])) { $this->_tagCannotBeEmpty('license'); } if (empty($this->_packageInfo['summary'])) { $this->_tagCannotBeEmpty('summary'); } if (empty($this->_packageInfo['description'])) { $this->_tagCannotBeEmpty('description'); } if (empty($this->_packageInfo['date'])) { $this->_tagCannotBeEmpty('date'); } if (empty($this->_packageInfo['notes'])) { $this->_tagCannotBeEmpty('notes'); } if (isset($this->_packageInfo['time']) && empty($this->_packageInfo['time'])) { $this->_tagCannotBeEmpty('time'); } if (isset($this->_packageInfo['dependencies'])) { $this->_validateDependencies(); } if (isset($this->_packageInfo['compatible'])) { $this->_validateCompatible(); } if (!isset($this->_packageInfo['bundle'])) { if (empty($this->_packageInfo['contents'])) { $this->_tagCannotBeEmpty('contents'); } if (!isset($this->_packageInfo['contents']['dir'])) { $this->_filelistMustContainDir('contents'); return false; } if (isset($this->_packageInfo['contents']['file'])) { $this->_filelistCannotContainFile('contents'); return false; } } $this->_validateMaintainers(); $this->_validateStabilityVersion(); $fail = false; if (array_key_exists('usesrole', $this->_packageInfo)) { $roles = $this->_packageInfo['usesrole']; if (!is_array($roles) || !isset($roles[0])) { $roles = array($roles); } foreach ($roles as $role) { if (!isset($role['role'])) { $this->_usesroletaskMustHaveRoleTask('usesrole', 'role'); $fail = true; } else { if (!isset($role['channel'])) { if (!isset($role['uri'])) { $this->_usesroletaskMustHaveChannelOrUri($role['role'], 'usesrole'); $fail = true; } } elseif (!isset($role['package'])) { $this->_usesroletaskMustHavePackage($role['role'], 'usesrole'); $fail = true; } } } } if (array_key_exists('usestask', $this->_packageInfo)) { $roles = $this->_packageInfo['usestask']; if (!is_array($roles) || !isset($roles[0])) { $roles = array($roles); } foreach ($roles as $role) { if (!isset($role['task'])) { $this->_usesroletaskMustHaveRoleTask('usestask', 'task'); $fail = true; } else { if (!isset($role['channel'])) { if (!isset($role['uri'])) { $this->_usesroletaskMustHaveChannelOrUri($role['task'], 'usestask'); $fail = true; } } elseif (!isset($role['package'])) { $this->_usesroletaskMustHavePackage($role['task'], 'usestask'); $fail = true; } } } } if ($fail) { return false; } $list = $this->_packageInfo['contents']; if (isset($list['dir']) && is_array($list['dir']) && isset($list['dir'][0])) { $this->_multipleToplevelDirNotAllowed(); return $this->_isValid = 0; } $this->_validateFilelist(); $this->_validateRelease(); if (!$this->_stack->hasErrors()) { $chan = $this->_pf->_registry->getChannel($this->_pf->getChannel(), true); if (PEAR::isError($chan)) { $this->_unknownChannel($this->_pf->getChannel()); } else { $valpack = $chan->getValidationPackage(); // for channel validator packages, always use the default PEAR validator. // otherwise, they can't be installed or packaged $validator = $chan->getValidationObject($this->_pf->getPackage()); if (!$validator) { $this->_stack->push(__FUNCTION__, 'error', array('channel' => $chan->getName(), 'package' => $this->_pf->getPackage(), 'name' => $valpack['_content'], 'version' => $valpack['attribs']['version']), 'package "%channel%/%package%" cannot be properly validated without ' . 'validation package "%channel%/%name%-%version%"'); return $this->_isValid = 0; } $validator->setPackageFile($this->_pf); $validator->validate($state); $failures = $validator->getFailures(); foreach ($failures['errors'] as $error) { $this->_stack->push(__FUNCTION__, 'error', $error, 'Channel validator error: field "%field%" - %reason%'); } foreach ($failures['warnings'] as $warning) { $this->_stack->push(__FUNCTION__, 'warning', $warning, 'Channel validator warning: field "%field%" - %reason%'); } } } $this->_pf->_isValid = $this->_isValid = !$this->_stack->hasErrors('error'); if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$this->_filesValid) { if ($this->_pf->getPackageType() == 'bundle') { if ($this->_analyzeBundledPackages()) { $this->_filesValid = $this->_pf->_filesValid = true; } else { $this->_pf->_isValid = $this->_isValid = 0; } } else { if (!$this->_analyzePhpFiles()) { $this->_pf->_isValid = $this->_isValid = 0; } else { $this->_filesValid = $this->_pf->_filesValid = true; } } } if ($this->_isValid) { return $this->_pf->_isValid = $this->_isValid = $state; } return $this->_pf->_isValid = $this->_isValid = 0; } function _stupidSchemaValidate($structure, $xml, $root) { if (!is_array($xml)) { $xml = array(); } $keys = array_keys($xml); reset($keys); $key = current($keys); while ($key == 'attribs' || $key == '_contents') { $key = next($keys); } $unfoundtags = $optionaltags = array(); $ret = true; $mismatch = false; foreach ($structure as $struc) { if ($key) { $tag = $xml[$key]; } $test = $this->_processStructure($struc); if (isset($test['choices'])) { $loose = true; foreach ($test['choices'] as $choice) { if ($key == $choice['tag']) { $key = next($keys); while ($key == 'attribs' || $key == '_contents') { $key = next($keys); } $unfoundtags = $optionaltags = array(); $mismatch = false; if ($key && $key != $choice['tag'] && isset($choice['multiple'])) { $unfoundtags[] = $choice['tag']; $optionaltags[] = $choice['tag']; if ($key) { $mismatch = true; } } $ret &= $this->_processAttribs($choice, $tag, $root); continue 2; } else { $unfoundtags[] = $choice['tag']; $mismatch = true; } if (!isset($choice['multiple']) || $choice['multiple'] != '*') { $loose = false; } else { $optionaltags[] = $choice['tag']; } } if (!$loose) { $this->_invalidTagOrder($unfoundtags, $key, $root); return false; } } else { if ($key != $test['tag']) { if (isset($test['multiple']) && $test['multiple'] != '*') { $unfoundtags[] = $test['tag']; $this->_invalidTagOrder($unfoundtags, $key, $root); return false; } else { if ($key) { $mismatch = true; } $unfoundtags[] = $test['tag']; $optionaltags[] = $test['tag']; } if (!isset($test['multiple'])) { $this->_invalidTagOrder($unfoundtags, $key, $root); return false; } continue; } else { $unfoundtags = $optionaltags = array(); $mismatch = false; } $key = next($keys); while ($key == 'attribs' || $key == '_contents') { $key = next($keys); } if ($key && $key != $test['tag'] && isset($test['multiple'])) { $unfoundtags[] = $test['tag']; $optionaltags[] = $test['tag']; $mismatch = true; } $ret &= $this->_processAttribs($test, $tag, $root); continue; } } if (!$mismatch && count($optionaltags)) { // don't error out on any optional tags $unfoundtags = array_diff($unfoundtags, $optionaltags); } if (count($unfoundtags)) { $this->_invalidTagOrder($unfoundtags, $key, $root); } elseif ($key) { // unknown tags $this->_invalidTagOrder('*no tags allowed here*', $key, $root); while ($key = next($keys)) { $this->_invalidTagOrder('*no tags allowed here*', $key, $root); } } return $ret; } function _processAttribs($choice, $tag, $context) { if (isset($choice['attribs'])) { if (!is_array($tag)) { $tag = array($tag); } $tags = $tag; if (!isset($tags[0])) { $tags = array($tags); } $ret = true; foreach ($tags as $i => $tag) { if (!is_array($tag) || !isset($tag['attribs'])) { foreach ($choice['attribs'] as $attrib) { if ($attrib[0] != '?') { $ret &= $this->_tagHasNoAttribs($choice['tag'], $context); continue 2; } } } foreach ($choice['attribs'] as $attrib) { if ($attrib[0] != '?') { if (!isset($tag['attribs'][$attrib])) { $ret &= $this->_tagMissingAttribute($choice['tag'], $attrib, $context); } } } } return $ret; } return true; } function _processStructure($key) { $ret = array(); if (count($pieces = explode('|', $key)) > 1) { $ret['choices'] = array(); foreach ($pieces as $piece) { $ret['choices'][] = $this->_processStructure($piece); } return $ret; } $multi = $key[0]; if ($multi == '+' || $multi == '*') { $ret['multiple'] = $key[0]; $key = substr($key, 1); } if (count($attrs = explode('->', $key)) > 1) { $ret['tag'] = array_shift($attrs); $ret['attribs'] = $attrs; } else { $ret['tag'] = $key; } return $ret; } function _validateStabilityVersion() { $structure = array('release', 'api'); $a = $this->_stupidSchemaValidate($structure, $this->_packageInfo['version'], ''); $a &= $this->_stupidSchemaValidate($structure, $this->_packageInfo['stability'], ''); if ($a) { if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', $this->_packageInfo['version']['release'])) { $this->_invalidVersion('release', $this->_packageInfo['version']['release']); } if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', $this->_packageInfo['version']['api'])) { $this->_invalidVersion('api', $this->_packageInfo['version']['api']); } if (!in_array($this->_packageInfo['stability']['release'], array('snapshot', 'devel', 'alpha', 'beta', 'stable'))) { $this->_invalidState('release', $this->_packageInfo['stability']['release']); } if (!in_array($this->_packageInfo['stability']['api'], array('devel', 'alpha', 'beta', 'stable'))) { $this->_invalidState('api', $this->_packageInfo['stability']['api']); } } } function _validateMaintainers() { $structure = array( 'name', 'user', 'email', 'active', ); foreach (array('lead', 'developer', 'contributor', 'helper') as $type) { if (!isset($this->_packageInfo[$type])) { continue; } if (isset($this->_packageInfo[$type][0])) { foreach ($this->_packageInfo[$type] as $lead) { $this->_stupidSchemaValidate($structure, $lead, '<' . $type . '>'); } } else { $this->_stupidSchemaValidate($structure, $this->_packageInfo[$type], '<' . $type . '>'); } } } function _validatePhpDep($dep, $installcondition = false) { $structure = array( 'min', '*max', '*exclude', ); $type = $installcondition ? '' : ''; $this->_stupidSchemaValidate($structure, $dep, $type); if (isset($dep['min'])) { if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/', $dep['min'])) { $this->_invalidVersion($type . '', $dep['min']); } } if (isset($dep['max'])) { if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/', $dep['max'])) { $this->_invalidVersion($type . '', $dep['max']); } } if (isset($dep['exclude'])) { if (!is_array($dep['exclude'])) { $dep['exclude'] = array($dep['exclude']); } foreach ($dep['exclude'] as $exclude) { if (!preg_match( '/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/', $exclude)) { $this->_invalidVersion($type . '', $exclude); } } } } function _validatePearinstallerDep($dep) { $structure = array( 'min', '*max', '*recommended', '*exclude', ); $this->_stupidSchemaValidate($structure, $dep, ''); if (isset($dep['min'])) { if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', $dep['min'])) { $this->_invalidVersion('', $dep['min']); } } if (isset($dep['max'])) { if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', $dep['max'])) { $this->_invalidVersion('', $dep['max']); } } if (isset($dep['recommended'])) { if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', $dep['recommended'])) { $this->_invalidVersion('', $dep['recommended']); } } if (isset($dep['exclude'])) { if (!is_array($dep['exclude'])) { $dep['exclude'] = array($dep['exclude']); } foreach ($dep['exclude'] as $exclude) { if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', $exclude)) { $this->_invalidVersion('', $exclude); } } } } function _validatePackageDep($dep, $group, $type = '') { if (isset($dep['uri'])) { if (isset($dep['conflicts'])) { $structure = array( 'name', 'uri', 'conflicts', '*providesextension', ); } else { $structure = array( 'name', 'uri', '*providesextension', ); } } else { if (isset($dep['conflicts'])) { $structure = array( 'name', 'channel', '*min', '*max', '*exclude', 'conflicts', '*providesextension', ); } else { $structure = array( 'name', 'channel', '*min', '*max', '*recommended', '*exclude', '*nodefault', '*providesextension', ); } } if (isset($dep['name'])) { $type .= '' . $dep['name'] . ''; } $this->_stupidSchemaValidate($structure, $dep, '' . $group . $type); if (isset($dep['uri']) && (isset($dep['min']) || isset($dep['max']) || isset($dep['recommended']) || isset($dep['exclude']))) { $this->_uriDepsCannotHaveVersioning('' . $group . $type); } if (isset($dep['channel']) && strtolower($dep['channel']) == '__uri') { $this->_DepchannelCannotBeUri('' . $group . $type); } if (isset($dep['min'])) { if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', $dep['min'])) { $this->_invalidVersion('' . $group . $type . '', $dep['min']); } } if (isset($dep['max'])) { if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', $dep['max'])) { $this->_invalidVersion('' . $group . $type . '', $dep['max']); } } if (isset($dep['recommended'])) { if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', $dep['recommended'])) { $this->_invalidVersion('' . $group . $type . '', $dep['recommended']); } } if (isset($dep['exclude'])) { if (!is_array($dep['exclude'])) { $dep['exclude'] = array($dep['exclude']); } foreach ($dep['exclude'] as $exclude) { if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', $exclude)) { $this->_invalidVersion('' . $group . $type . '', $exclude); } } } } function _validateSubpackageDep($dep, $group) { $this->_validatePackageDep($dep, $group, ''); if (isset($dep['providesextension'])) { $this->_subpackageCannotProvideExtension(isset($dep['name']) ? $dep['name'] : ''); } if (isset($dep['conflicts'])) { $this->_subpackagesCannotConflict(isset($dep['name']) ? $dep['name'] : ''); } } function _validateExtensionDep($dep, $group = false, $installcondition = false) { if (isset($dep['conflicts'])) { $structure = array( 'name', '*min', '*max', '*exclude', 'conflicts', ); } else { $structure = array( 'name', '*min', '*max', '*recommended', '*exclude', ); } if ($installcondition) { $type = ''; } else { $type = '' . $group . ''; } if (isset($dep['name'])) { $type .= '' . $dep['name'] . ''; } $this->_stupidSchemaValidate($structure, $dep, $type); if (isset($dep['min'])) { if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', $dep['min'])) { $this->_invalidVersion(substr($type, 1) . '_invalidVersion(substr($type, 1) . '_invalidVersion(substr($type, 1) . '_invalidVersion(substr($type, 1) . '' : ''; if ($this->_stupidSchemaValidate($structure, $dep, $type)) { if ($dep['name'] == '*') { if (array_key_exists('conflicts', $dep)) { $this->_cannotConflictWithAllOs($type); } } } } function _validateArchDep($dep, $installcondition = false) { $structure = array( 'pattern', '*conflicts', ); $type = $installcondition ? '' : ''; $this->_stupidSchemaValidate($structure, $dep, $type); } function _validateInstallConditions($cond, $release) { $structure = array( '*php', '*extension', '*os', '*arch', ); if (!$this->_stupidSchemaValidate($structure, $cond, $release)) { return false; } foreach (array('php', 'extension', 'os', 'arch') as $type) { if (isset($cond[$type])) { $iter = $cond[$type]; if (!is_array($iter) || !isset($iter[0])) { $iter = array($iter); } foreach ($iter as $package) { if ($type == 'extension') { $this->{"_validate{$type}Dep"}($package, false, true); } else { $this->{"_validate{$type}Dep"}($package, true); } } } } } function _validateDependencies() { $structure = array( 'required', '*optional', '*group->name->hint' ); if (!$this->_stupidSchemaValidate($structure, $this->_packageInfo['dependencies'], '')) { return false; } foreach (array('required', 'optional') as $simpledep) { if (isset($this->_packageInfo['dependencies'][$simpledep])) { if ($simpledep == 'optional') { $structure = array( '*package', '*subpackage', '*extension', ); } else { $structure = array( 'php', 'pearinstaller', '*package', '*subpackage', '*extension', '*os', '*arch', ); } if ($this->_stupidSchemaValidate($structure, $this->_packageInfo['dependencies'][$simpledep], "<$simpledep>")) { foreach (array('package', 'subpackage', 'extension') as $type) { if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) { $iter = $this->_packageInfo['dependencies'][$simpledep][$type]; if (!isset($iter[0])) { $iter = array($iter); } foreach ($iter as $package) { if ($type != 'extension') { if (isset($package['uri'])) { if (isset($package['channel'])) { $this->_UrlOrChannel($type, $package['name']); } } else { if (!isset($package['channel'])) { $this->_NoChannel($type, $package['name']); } } } $this->{"_validate{$type}Dep"}($package, "<$simpledep>"); } } } if ($simpledep == 'optional') { continue; } foreach (array('php', 'pearinstaller', 'os', 'arch') as $type) { if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) { $iter = $this->_packageInfo['dependencies'][$simpledep][$type]; if (!isset($iter[0])) { $iter = array($iter); } foreach ($iter as $package) { $this->{"_validate{$type}Dep"}($package); } } } } } } if (isset($this->_packageInfo['dependencies']['group'])) { $groups = $this->_packageInfo['dependencies']['group']; if (!isset($groups[0])) { $groups = array($groups); } $structure = array( '*package', '*subpackage', '*extension', ); foreach ($groups as $group) { if ($this->_stupidSchemaValidate($structure, $group, '')) { if (!PEAR_Validate::validGroupName($group['attribs']['name'])) { $this->_invalidDepGroupName($group['attribs']['name']); } foreach (array('package', 'subpackage', 'extension') as $type) { if (isset($group[$type])) { $iter = $group[$type]; if (!isset($iter[0])) { $iter = array($iter); } foreach ($iter as $package) { if ($type != 'extension') { if (isset($package['uri'])) { if (isset($package['channel'])) { $this->_UrlOrChannelGroup($type, $package['name'], $group['name']); } } else { if (!isset($package['channel'])) { $this->_NoChannelGroup($type, $package['name'], $group['name']); } } } $this->{"_validate{$type}Dep"}($package, ''); } } } } } } } function _validateCompatible() { $compat = $this->_packageInfo['compatible']; if (!isset($compat[0])) { $compat = array($compat); } $required = array('name', 'channel', 'min', 'max', '*exclude'); foreach ($compat as $package) { $type = ''; if (is_array($package) && array_key_exists('name', $package)) { $type .= '' . $package['name'] . ''; } $this->_stupidSchemaValidate($required, $package, $type); if (is_array($package) && array_key_exists('min', $package)) { if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', $package['min'])) { $this->_invalidVersion(substr($type, 1) . '_invalidVersion(substr($type, 1) . '_invalidVersion(substr($type, 1) . '_NoBundledPackages(); } if (!is_array($list['bundledpackage']) || !isset($list['bundledpackage'][0])) { return $this->_AtLeast2BundledPackages(); } foreach ($list['bundledpackage'] as $package) { if (!is_string($package)) { $this->_bundledPackagesMustBeFilename(); } } } function _validateFilelist($list = false, $allowignore = false, $dirs = '') { $iscontents = false; if (!$list) { $iscontents = true; $list = $this->_packageInfo['contents']; if (isset($this->_packageInfo['bundle'])) { return $this->_validateBundle($list); } } if ($allowignore) { $struc = array( '*install->name->as', '*ignore->name' ); } else { $struc = array( '*dir->name->?baseinstalldir', '*file->name->role->?baseinstalldir->?md5sum' ); if (isset($list['dir']) && isset($list['file'])) { // stave off validation errors without requiring a set order. $_old = $list; if (isset($list['attribs'])) { $list = array('attribs' => $_old['attribs']); } $list['dir'] = $_old['dir']; $list['file'] = $_old['file']; } } if (!isset($list['attribs']) || !isset($list['attribs']['name'])) { $unknown = $allowignore ? '' : ''; $dirname = $iscontents ? '' : $unknown; } else { $dirname = ''; if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~', str_replace('\\', '/', $list['attribs']['name']))) { // file contains .. parent directory or . cur directory $this->_invalidDirName($list['attribs']['name']); } } $res = $this->_stupidSchemaValidate($struc, $list, $dirname); if ($allowignore && $res) { $ignored_or_installed = array(); $this->_pf->getFilelist(); $fcontents = $this->_pf->getContents(); $filelist = array(); if (!isset($fcontents['dir']['file'][0])) { $fcontents['dir']['file'] = array($fcontents['dir']['file']); } foreach ($fcontents['dir']['file'] as $file) { $filelist[$file['attribs']['name']] = true; } if (isset($list['install'])) { if (!isset($list['install'][0])) { $list['install'] = array($list['install']); } foreach ($list['install'] as $file) { if (!isset($filelist[$file['attribs']['name']])) { $this->_notInContents($file['attribs']['name'], 'install'); continue; } if (array_key_exists($file['attribs']['name'], $ignored_or_installed)) { $this->_multipleInstallAs($file['attribs']['name']); } if (!isset($ignored_or_installed[$file['attribs']['name']])) { $ignored_or_installed[$file['attribs']['name']] = array(); } $ignored_or_installed[$file['attribs']['name']][] = 1; if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~', str_replace('\\', '/', $file['attribs']['as']))) { // file contains .. parent directory or . cur directory references $this->_invalidFileInstallAs($file['attribs']['name'], $file['attribs']['as']); } } } if (isset($list['ignore'])) { if (!isset($list['ignore'][0])) { $list['ignore'] = array($list['ignore']); } foreach ($list['ignore'] as $file) { if (!isset($filelist[$file['attribs']['name']])) { $this->_notInContents($file['attribs']['name'], 'ignore'); continue; } if (array_key_exists($file['attribs']['name'], $ignored_or_installed)) { $this->_ignoreAndInstallAs($file['attribs']['name']); } } } } if (!$allowignore && isset($list['file'])) { if (is_string($list['file'])) { $this->_oldStyleFileNotAllowed(); return false; } if (!isset($list['file'][0])) { // single file $list['file'] = array($list['file']); } foreach ($list['file'] as $i => $file) { if (isset($file['attribs']) && isset($file['attribs']['name'])) { if ($file['attribs']['name'][0] == '.' && $file['attribs']['name'][1] == '/') { // name is something like "./doc/whatever.txt" $this->_invalidFileName($file['attribs']['name'], $dirname); } if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~', str_replace('\\', '/', $file['attribs']['name']))) { // file contains .. parent directory or . cur directory $this->_invalidFileName($file['attribs']['name'], $dirname); } } if (isset($file['attribs']) && isset($file['attribs']['role'])) { if (!$this->_validateRole($file['attribs']['role'])) { if (isset($this->_packageInfo['usesrole'])) { $roles = $this->_packageInfo['usesrole']; if (!isset($roles[0])) { $roles = array($roles); } foreach ($roles as $role) { if ($role['role'] = $file['attribs']['role']) { $msg = 'This package contains role "%role%" and requires ' . 'package "%package%" to be used'; if (isset($role['uri'])) { $params = array('role' => $role['role'], 'package' => $role['uri']); } else { $params = array('role' => $role['role'], 'package' => $this->_pf->_registry-> parsedPackageNameToString(array('package' => $role['package'], 'channel' => $role['channel']), true)); } $this->_stack->push('_mustInstallRole', 'error', $params, $msg); } } } $this->_invalidFileRole($file['attribs']['name'], $dirname, $file['attribs']['role']); } } if (!isset($file['attribs'])) { continue; } $save = $file['attribs']; if ($dirs) { $save['name'] = $dirs . '/' . $save['name']; } unset($file['attribs']); if (count($file) && $this->_curState != PEAR_VALIDATE_DOWNLOADING) { // has tasks foreach ($file as $task => $value) { if ($tagClass = $this->_pf->getTask($task)) { if (!is_array($value) || !isset($value[0])) { $value = array($value); } foreach ($value as $v) { $ret = call_user_func(array($tagClass, 'validateXml'), $this->_pf, $v, $this->_pf->_config, $save); if (is_array($ret)) { $this->_invalidTask($task, $ret, isset($save['name']) ? $save['name'] : ''); } } } else { if (isset($this->_packageInfo['usestask'])) { $roles = $this->_packageInfo['usestask']; if (!isset($roles[0])) { $roles = array($roles); } foreach ($roles as $role) { if ($role['task'] = $task) { $msg = 'This package contains task "%task%" and requires ' . 'package "%package%" to be used'; if (isset($role['uri'])) { $params = array('task' => $role['task'], 'package' => $role['uri']); } else { $params = array('task' => $role['task'], 'package' => $this->_pf->_registry-> parsedPackageNameToString(array('package' => $role['package'], 'channel' => $role['channel']), true)); } $this->_stack->push('_mustInstallTask', 'error', $params, $msg); } } } $this->_unknownTask($task, $save['name']); } } } } } if (isset($list['ignore'])) { if (!$allowignore) { $this->_ignoreNotAllowed('ignore'); } } if (isset($list['install'])) { if (!$allowignore) { $this->_ignoreNotAllowed('install'); } } if (isset($list['file'])) { if ($allowignore) { $this->_fileNotAllowed('file'); } } if (isset($list['dir'])) { if ($allowignore) { $this->_fileNotAllowed('dir'); } else { if (!isset($list['dir'][0])) { $list['dir'] = array($list['dir']); } foreach ($list['dir'] as $dir) { if (isset($dir['attribs']) && isset($dir['attribs']['name'])) { if ($dir['attribs']['name'] == '/' || !isset($this->_packageInfo['contents']['dir']['dir'])) { // always use nothing if the filelist has already been flattened $newdirs = ''; } elseif ($dirs == '') { $newdirs = $dir['attribs']['name']; } else { $newdirs = $dirs . '/' . $dir['attribs']['name']; } } else { $newdirs = $dirs; } $this->_validateFilelist($dir, $allowignore, $newdirs); } } } } function _validateRelease() { if (isset($this->_packageInfo['phprelease'])) { $release = 'phprelease'; if (isset($this->_packageInfo['providesextension'])) { $this->_cannotProvideExtension($release); } if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) { $this->_cannotHaveSrcpackage($release); } $releases = $this->_packageInfo['phprelease']; if (!is_array($releases)) { return true; } if (!isset($releases[0])) { $releases = array($releases); } foreach ($releases as $rel) { $this->_stupidSchemaValidate(array( '*installconditions', '*filelist', ), $rel, ''); } } foreach (array('', 'zend') as $prefix) { $releasetype = $prefix . 'extsrcrelease'; if (isset($this->_packageInfo[$releasetype])) { $release = $releasetype; if (!isset($this->_packageInfo['providesextension'])) { $this->_mustProvideExtension($release); } if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) { $this->_cannotHaveSrcpackage($release); } $releases = $this->_packageInfo[$releasetype]; if (!is_array($releases)) { return true; } if (!isset($releases[0])) { $releases = array($releases); } foreach ($releases as $rel) { $this->_stupidSchemaValidate(array( '*installconditions', '*configureoption->name->prompt->?default', '*binarypackage', '*filelist', ), $rel, '<' . $releasetype . '>'); if (isset($rel['binarypackage'])) { if (!is_array($rel['binarypackage']) || !isset($rel['binarypackage'][0])) { $rel['binarypackage'] = array($rel['binarypackage']); } foreach ($rel['binarypackage'] as $bin) { if (!is_string($bin)) { $this->_binaryPackageMustBePackagename(); } } } } } $releasetype = 'extbinrelease'; if (isset($this->_packageInfo[$releasetype])) { $release = $releasetype; if (!isset($this->_packageInfo['providesextension'])) { $this->_mustProvideExtension($release); } if (isset($this->_packageInfo['channel']) && !isset($this->_packageInfo['srcpackage'])) { $this->_mustSrcPackage($release); } if (isset($this->_packageInfo['uri']) && !isset($this->_packageInfo['srcuri'])) { $this->_mustSrcuri($release); } $releases = $this->_packageInfo[$releasetype]; if (!is_array($releases)) { return true; } if (!isset($releases[0])) { $releases = array($releases); } foreach ($releases as $rel) { $this->_stupidSchemaValidate(array( '*installconditions', '*filelist', ), $rel, '<' . $releasetype . '>'); } } } if (isset($this->_packageInfo['bundle'])) { $release = 'bundle'; if (isset($this->_packageInfo['providesextension'])) { $this->_cannotProvideExtension($release); } if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) { $this->_cannotHaveSrcpackage($release); } $releases = $this->_packageInfo['bundle']; if (!is_array($releases) || !isset($releases[0])) { $releases = array($releases); } foreach ($releases as $rel) { $this->_stupidSchemaValidate(array( '*installconditions', '*filelist', ), $rel, ''); } } foreach ($releases as $rel) { if (is_array($rel) && array_key_exists('installconditions', $rel)) { $this->_validateInstallConditions($rel['installconditions'], "<$release>"); } if (is_array($rel) && array_key_exists('filelist', $rel)) { if ($rel['filelist']) { $this->_validateFilelist($rel['filelist'], true); } } } } /** * This is here to allow role extension through plugins * @param string */ function _validateRole($role) { return in_array($role, PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType())); } function _pearVersionTooLow($version) { $this->_stack->push(__FUNCTION__, 'error', array('version' => $version), 'This package.xml requires PEAR version %version% to parse properly, we are ' . 'version 1.10.18'); } function _invalidTagOrder($oktags, $actual, $root) { $this->_stack->push(__FUNCTION__, 'error', array('oktags' => $oktags, 'actual' => $actual, 'root' => $root), 'Invalid tag order in %root%, found <%actual%> expected one of "%oktags%"'); } function _ignoreNotAllowed($type) { $this->_stack->push(__FUNCTION__, 'error', array('type' => $type), '<%type%> is not allowed inside global , only inside ' . '//, use and only'); } function _fileNotAllowed($type) { $this->_stack->push(__FUNCTION__, 'error', array('type' => $type), '<%type%> is not allowed inside release , only inside ' . ', use and only'); } function _oldStyleFileNotAllowed() { $this->_stack->push(__FUNCTION__, 'error', array(), 'Old-style name is not allowed. Use' . ''); } function _tagMissingAttribute($tag, $attr, $context) { $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag, 'attribute' => $attr, 'context' => $context), 'tag <%tag%> in context "%context%" has no attribute "%attribute%"'); } function _tagHasNoAttribs($tag, $context) { $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag, 'context' => $context), 'tag <%tag%> has no attributes in context "%context%"'); } function _invalidInternalStructure() { $this->_stack->push(__FUNCTION__, 'exception', array(), 'internal array was not generated by compatible parser, or extreme parser error, cannot continue'); } function _invalidFileRole($file, $dir, $role) { $this->_stack->push(__FUNCTION__, 'error', array( 'file' => $file, 'dir' => $dir, 'role' => $role, 'roles' => PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType())), 'File "%file%" in directory "%dir%" has invalid role "%role%", should be one of %roles%'); } function _invalidFileName($file, $dir) { $this->_stack->push(__FUNCTION__, 'error', array( 'file' => $file), 'File "%file%" in directory "%dir%" cannot begin with "./" or contain ".."'); } function _invalidFileInstallAs($file, $as) { $this->_stack->push(__FUNCTION__, 'error', array( 'file' => $file, 'as' => $as), 'File "%file%" cannot contain "./" or contain ".."'); } function _invalidDirName($dir) { $this->_stack->push(__FUNCTION__, 'error', array( 'dir' => $file), 'Directory "%dir%" cannot begin with "./" or contain ".."'); } function _filelistCannotContainFile($filelist) { $this->_stack->push(__FUNCTION__, 'error', array('tag' => $filelist), '<%tag%> can only contain , contains . Use ' . ' as the first dir element'); } function _filelistMustContainDir($filelist) { $this->_stack->push(__FUNCTION__, 'error', array('tag' => $filelist), '<%tag%> must contain . Use as the ' . 'first dir element'); } function _tagCannotBeEmpty($tag) { $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag), '<%tag%> cannot be empty (<%tag%/>)'); } function _UrlOrChannel($type, $name) { $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'name' => $name), 'Required dependency <%type%> "%name%" can have either url OR ' . 'channel attributes, and not both'); } function _NoChannel($type, $name) { $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'name' => $name), 'Required dependency <%type%> "%name%" must have either url OR ' . 'channel attributes'); } function _UrlOrChannelGroup($type, $name, $group) { $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'name' => $name, 'group' => $group), 'Group "%group%" dependency <%type%> "%name%" can have either url OR ' . 'channel attributes, and not both'); } function _NoChannelGroup($type, $name, $group) { $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'name' => $name, 'group' => $group), 'Group "%group%" dependency <%type%> "%name%" must have either url OR ' . 'channel attributes'); } function _unknownChannel($channel) { $this->_stack->push(__FUNCTION__, 'error', array('channel' => $channel), 'Unknown channel "%channel%"'); } function _noPackageVersion() { $this->_stack->push(__FUNCTION__, 'error', array(), 'package.xml tag has no version attribute, or version is not 2.0'); } function _NoBundledPackages() { $this->_stack->push(__FUNCTION__, 'error', array(), 'No tag was found in , required for bundle packages'); } function _AtLeast2BundledPackages() { $this->_stack->push(__FUNCTION__, 'error', array(), 'At least 2 packages must be bundled in a bundle package'); } function _ChannelOrUri($name) { $this->_stack->push(__FUNCTION__, 'error', array('name' => $name), 'Bundled package "%name%" can have either a uri or a channel, not both'); } function _noChildTag($child, $tag) { $this->_stack->push(__FUNCTION__, 'error', array('child' => $child, 'tag' => $tag), 'Tag <%tag%> is missing child tag <%child%>'); } function _invalidVersion($type, $value) { $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'value' => $value), 'Version type <%type%> is not a valid version (%value%)'); } function _invalidState($type, $value) { $states = array('stable', 'beta', 'alpha', 'devel'); if ($type != 'api') { $states[] = 'snapshot'; } if (strtolower($value) == 'rc') { $this->_stack->push(__FUNCTION__, 'error', array('version' => $this->_packageInfo['version']['release']), 'RC is not a state, it is a version postfix, try %version%RC1, stability beta'); } $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'value' => $value, 'types' => $states), 'Stability type <%type%> is not a valid stability (%value%), must be one of ' . '%types%'); } function _invalidTask($task, $ret, $file) { switch ($ret[0]) { case PEAR_TASK_ERROR_MISSING_ATTRIB : $info = array('attrib' => $ret[1], 'task' => $task, 'file' => $file); $msg = 'task <%task%> is missing attribute "%attrib%" in file %file%'; break; case PEAR_TASK_ERROR_NOATTRIBS : $info = array('task' => $task, 'file' => $file); $msg = 'task <%task%> has no attributes in file %file%'; break; case PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE : $info = array('attrib' => $ret[1], 'values' => $ret[3], 'was' => $ret[2], 'task' => $task, 'file' => $file); $msg = 'task <%task%> attribute "%attrib%" has the wrong value "%was%" '. 'in file %file%, expecting one of "%values%"'; break; case PEAR_TASK_ERROR_INVALID : $info = array('reason' => $ret[1], 'task' => $task, 'file' => $file); $msg = 'task <%task%> in file %file% is invalid because of "%reason%"'; break; } $this->_stack->push(__FUNCTION__, 'error', $info, $msg); } function _unknownTask($task, $file) { $this->_stack->push(__FUNCTION__, 'error', array('task' => $task, 'file' => $file), 'Unknown task "%task%" passed in file '); } function _subpackageCannotProvideExtension($name) { $this->_stack->push(__FUNCTION__, 'error', array('name' => $name), 'Subpackage dependency "%name%" cannot use , ' . 'only package dependencies can use this tag'); } function _subpackagesCannotConflict($name) { $this->_stack->push(__FUNCTION__, 'error', array('name' => $name), 'Subpackage dependency "%name%" cannot use , ' . 'only package dependencies can use this tag'); } function _cannotProvideExtension($release) { $this->_stack->push(__FUNCTION__, 'error', array('release' => $release), '<%release%> packages cannot use , only extbinrelease, extsrcrelease, zendextsrcrelease, and zendextbinrelease can provide a PHP extension'); } function _mustProvideExtension($release) { $this->_stack->push(__FUNCTION__, 'error', array('release' => $release), '<%release%> packages must use to indicate which PHP extension is provided'); } function _cannotHaveSrcpackage($release) { $this->_stack->push(__FUNCTION__, 'error', array('release' => $release), '<%release%> packages cannot specify a source code package, only extension binaries may use the tag'); } function _mustSrcPackage($release) { $this->_stack->push(__FUNCTION__, 'error', array('release' => $release), '/ packages must specify a source code package with '); } function _mustSrcuri($release) { $this->_stack->push(__FUNCTION__, 'error', array('release' => $release), '/ packages must specify a source code package with '); } function _uriDepsCannotHaveVersioning($type) { $this->_stack->push(__FUNCTION__, 'error', array('type' => $type), '%type%: dependencies with a tag cannot have any versioning information'); } function _conflictingDepsCannotHaveVersioning($type) { $this->_stack->push(__FUNCTION__, 'error', array('type' => $type), '%type%: conflicting dependencies cannot have versioning info, use to ' . 'exclude specific versions of a dependency'); } function _DepchannelCannotBeUri($type) { $this->_stack->push(__FUNCTION__, 'error', array('type' => $type), '%type%: channel cannot be __uri, this is a pseudo-channel reserved for uri ' . 'dependencies only'); } function _bundledPackagesMustBeFilename() { $this->_stack->push(__FUNCTION__, 'error', array(), ' tags must contain only the filename of a package release ' . 'in the bundle'); } function _binaryPackageMustBePackagename() { $this->_stack->push(__FUNCTION__, 'error', array(), ' tags must contain the name of a package that is ' . 'a compiled version of this extsrc/zendextsrc package'); } function _fileNotFound($file) { $this->_stack->push(__FUNCTION__, 'error', array('file' => $file), 'File "%file%" in package.xml does not exist'); } function _notInContents($file, $tag) { $this->_stack->push(__FUNCTION__, 'error', array('file' => $file, 'tag' => $tag), '<%tag% name="%file%"> is invalid, file is not in '); } function _cannotValidateNoPathSet() { $this->_stack->push(__FUNCTION__, 'error', array(), 'Cannot validate files, no path to package file is set (use setPackageFile())'); } function _usesroletaskMustHaveChannelOrUri($role, $tag) { $this->_stack->push(__FUNCTION__, 'error', array('role' => $role, 'tag' => $tag), '<%tag%> for role "%role%" must contain either , or and '); } function _usesroletaskMustHavePackage($role, $tag) { $this->_stack->push(__FUNCTION__, 'error', array('role' => $role, 'tag' => $tag), '<%tag%> for role "%role%" must contain '); } function _usesroletaskMustHaveRoleTask($tag, $type) { $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag, 'type' => $type), '<%tag%> must contain <%type%> defining the %type% to be used'); } function _cannotConflictWithAllOs($type) { $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag), '%tag% cannot conflict with all OSes'); } function _invalidDepGroupName($name) { $this->_stack->push(__FUNCTION__, 'error', array('name' => $name), 'Invalid dependency group name "%name%"'); } function _multipleToplevelDirNotAllowed() { $this->_stack->push(__FUNCTION__, 'error', array(), 'Multiple top-level tags are not allowed. Enclose them ' . 'in a '); } function _multipleInstallAs($file) { $this->_stack->push(__FUNCTION__, 'error', array('file' => $file), 'Only one tag is allowed for file "%file%"'); } function _ignoreAndInstallAs($file) { $this->_stack->push(__FUNCTION__, 'error', array('file' => $file), 'Cannot have both and tags for file "%file%"'); } function _analyzeBundledPackages() { if (!$this->_isValid) { return false; } if (!$this->_pf->getPackageType() == 'bundle') { return false; } if (!isset($this->_pf->_packageFile)) { return false; } $dir_prefix = dirname($this->_pf->_packageFile); $common = new PEAR_Common; $log = isset($this->_pf->_logger) ? array(&$this->_pf->_logger, 'log') : array($common, 'log'); $info = $this->_pf->getContents(); $info = $info['bundledpackage']; if (!is_array($info)) { $info = array($info); } $pkg = new PEAR_PackageFile($this->_pf->_config); foreach ($info as $package) { if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $package)) { $this->_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR . $package); $this->_isValid = 0; continue; } call_user_func_array($log, array(1, "Analyzing bundled package $package")); PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $ret = $pkg->fromAnyFile($dir_prefix . DIRECTORY_SEPARATOR . $package, PEAR_VALIDATE_NORMAL); PEAR::popErrorHandling(); if (PEAR::isError($ret)) { call_user_func_array($log, array(0, "ERROR: package $package is not a valid " . 'package')); $inf = $ret->getUserInfo(); if (is_array($inf)) { foreach ($inf as $err) { call_user_func_array($log, array(1, $err['message'])); } } return false; } } return true; } function _analyzePhpFiles() { if (!$this->_isValid) { return false; } if (!isset($this->_pf->_packageFile)) { $this->_cannotValidateNoPathSet(); return false; } $dir_prefix = dirname($this->_pf->_packageFile); $common = new PEAR_Common; $log = isset($this->_pf->_logger) ? array(&$this->_pf->_logger, 'log') : array(&$common, 'log'); $info = $this->_pf->getContents(); if (!$info || !isset($info['dir']['file'])) { $this->_tagCannotBeEmpty('contents>_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR . $file); $this->_isValid = 0; continue; } if (in_array($fa['role'], PEAR_Installer_Role::getPhpRoles()) && $dir_prefix) { call_user_func_array($log, array(1, "Analyzing $file")); $srcinfo = $this->analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file); if ($srcinfo) { $provides = array_merge($provides, $this->_buildProvidesArray($srcinfo)); } } } $this->_packageName = $pn = $this->_pf->getPackage(); $pnl = strlen($pn); foreach ($provides as $key => $what) { if (isset($what['explicit']) || !$what) { // skip conformance checks if the provides entry is // specified in the package.xml file continue; } extract($what); if ($type == 'class') { if (!strncasecmp($name, $pn, $pnl)) { continue; } $this->_stack->push(__FUNCTION__, 'warning', array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn), 'in %file%: %type% "%name%" not prefixed with package name "%package%"'); } elseif ($type == 'function') { if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) { continue; } $this->_stack->push(__FUNCTION__, 'warning', array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn), 'in %file%: %type% "%name%" not prefixed with package name "%package%"'); } } return $this->_isValid; } /** * Analyze the source code of the given PHP file * * @param string Filename of the PHP file * @param boolean whether to analyze $file as the file contents * @return mixed */ function analyzeSourceCode($file, $string = false) { if (!function_exists("token_get_all")) { $this->_stack->push(__FUNCTION__, 'error', array('file' => $file), 'Parser error: token_get_all() function must exist to analyze source code, PHP may have been compiled with --disable-tokenizer'); return false; } if (!defined('T_DOC_COMMENT')) { define('T_DOC_COMMENT', T_COMMENT); } if (!defined('T_INTERFACE')) { define('T_INTERFACE', -1); } if (!defined('T_IMPLEMENTS')) { define('T_IMPLEMENTS', -1); } if ($string) { $contents = $file; } else { if (!$fp = @fopen($file, "r")) { return false; } fclose($fp); $contents = file_get_contents($file); } // Silence this function so we can catch PHP Warnings and show our own custom message $tokens = @token_get_all($contents); if (isset($php_errormsg)) { if (isset($this->_stack)) { $pn = $this->_pf->getPackage(); $this->_stack->push(__FUNCTION__, 'warning', array('file' => $file, 'package' => $pn), 'in %file%: Could not process file for unknown reasons,' . ' possibly a PHP parse error in %file% from %package%'); } } /* for ($i = 0; $i < sizeof($tokens); $i++) { @list($token, $data) = $tokens[$i]; if (is_string($token)) { var_dump($token); } else { print token_name($token) . ' '; var_dump(rtrim($data)); } } */ $look_for = 0; $paren_level = 0; $bracket_level = 0; $brace_level = 0; $lastphpdoc = ''; $current_class = ''; $current_interface = ''; $current_class_level = -1; $current_function = ''; $current_function_level = -1; $declared_classes = array(); $declared_interfaces = array(); $declared_functions = array(); $declared_methods = array(); $used_classes = array(); $used_functions = array(); $extends = array(); $implements = array(); $nodeps = array(); $inquote = false; $interface = false; for ($i = 0; $i < sizeof($tokens); $i++) { if (is_array($tokens[$i])) { list($token, $data) = $tokens[$i]; } else { $token = $tokens[$i]; $data = ''; } if ($inquote) { if ($token != '"' && $token != T_END_HEREDOC) { continue; } else { $inquote = false; continue; } } switch ($token) { case T_WHITESPACE : continue 2; case ';': if ($interface) { $current_function = ''; $current_function_level = -1; } break; case '"': case T_START_HEREDOC: $inquote = true; break; case T_CURLY_OPEN: case T_DOLLAR_OPEN_CURLY_BRACES: case '{': $brace_level++; continue 2; case '}': $brace_level--; if ($current_class_level == $brace_level) { $current_class = ''; $current_class_level = -1; } if ($current_function_level == $brace_level) { $current_function = ''; $current_function_level = -1; } continue 2; case '[': $bracket_level++; continue 2; case ']': $bracket_level--; continue 2; case '(': $paren_level++; continue 2; case ')': $paren_level--; continue 2; case T_INTERFACE: $interface = true; case T_CLASS: if (($current_class_level != -1) || ($current_function_level != -1)) { if (isset($this->_stack)) { $this->_stack->push(__FUNCTION__, 'error', array('file' => $file), 'Parser error: invalid PHP found in file "%file%"'); } else { PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"", PEAR_COMMON_ERROR_INVALIDPHP); } return false; } case T_FUNCTION: case T_NEW: case T_EXTENDS: case T_IMPLEMENTS: $look_for = $token; continue 2; case T_STRING: if ($look_for == T_CLASS) { $current_class = $data; $current_class_level = $brace_level; $declared_classes[] = $current_class; } elseif ($look_for == T_INTERFACE) { $current_interface = $data; $current_class_level = $brace_level; $declared_interfaces[] = $current_interface; } elseif ($look_for == T_IMPLEMENTS) { $implements[$current_class] = $data; } elseif ($look_for == T_EXTENDS) { $extends[$current_class] = $data; } elseif ($look_for == T_FUNCTION) { if ($current_class) { $current_function = "$current_class::$data"; $declared_methods[$current_class][] = $data; } elseif ($current_interface) { $current_function = "$current_interface::$data"; $declared_methods[$current_interface][] = $data; } else { $current_function = $data; $declared_functions[] = $current_function; } $current_function_level = $brace_level; $m = array(); } elseif ($look_for == T_NEW) { $used_classes[$data] = true; } $look_for = 0; continue 2; case T_VARIABLE: $look_for = 0; continue 2; case T_DOC_COMMENT: case T_COMMENT: if (preg_match('!^/\*\*\s!', $data)) { $lastphpdoc = $data; if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) { $nodeps = array_merge($nodeps, $m[1]); } } continue 2; case T_DOUBLE_COLON: $token = $tokens[$i - 1][0]; if (!($token == T_WHITESPACE || $token == T_STRING || $token == T_STATIC || $token == T_VARIABLE)) { if (isset($this->_stack)) { $this->_stack->push(__FUNCTION__, 'warning', array('file' => $file), 'Parser error: invalid PHP found in file "%file%"'); } else { PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"", PEAR_COMMON_ERROR_INVALIDPHP); } return false; } $class = $tokens[$i - 1][1]; if (strtolower($class) != 'parent') { $used_classes[$class] = true; } continue 2; } } return array( "source_file" => $file, "declared_classes" => $declared_classes, "declared_interfaces" => $declared_interfaces, "declared_methods" => $declared_methods, "declared_functions" => $declared_functions, "used_classes" => array_diff(array_keys($used_classes), $nodeps), "inheritance" => $extends, "implements" => $implements, ); } /** * Build a "provides" array from data returned by * analyzeSourceCode(). The format of the built array is like * this: * * array( * 'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'), * ... * ) * * * @param array $srcinfo array with information about a source file * as returned by the analyzeSourceCode() method. * * @return void * * @access private * */ function _buildProvidesArray($srcinfo) { if (!$this->_isValid) { return array(); } $providesret = array(); $file = basename($srcinfo['source_file']); $pn = isset($this->_pf) ? $this->_pf->getPackage() : ''; $pnl = strlen($pn); foreach ($srcinfo['declared_classes'] as $class) { $key = "class;$class"; if (isset($providesret[$key])) { continue; } $providesret[$key] = array('file'=> $file, 'type' => 'class', 'name' => $class); if (isset($srcinfo['inheritance'][$class])) { $providesret[$key]['extends'] = $srcinfo['inheritance'][$class]; } } foreach ($srcinfo['declared_methods'] as $class => $methods) { foreach ($methods as $method) { $function = "$class::$method"; $key = "function;$function"; if ($method[0] == '_' || !strcasecmp($method, $class) || isset($providesret[$key])) { continue; } $providesret[$key] = array('file'=> $file, 'type' => 'function', 'name' => $function); } } foreach ($srcinfo['declared_functions'] as $function) { $key = "function;$function"; if ($function[0] == '_' || isset($providesret[$key])) { continue; } if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) { $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\""; } $providesret[$key] = array('file'=> $file, 'type' => 'function', 'name' => $function); } return $providesret; } } PK=1]?@@pear/PEAR/PackageFile/v2.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * For error handling */ require_once 'PEAR/ErrorStack.php'; /** * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_PackageFile_v2 { /** * Parsed package information * @var array * @access private */ var $_packageInfo = array(); /** * path to package .tgz or false if this is a local/extracted package.xml * @var string|false * @access private */ var $_archiveFile; /** * path to package .xml or false if this is an abstract parsed-from-string xml * @var string|false * @access private */ var $_packageFile; /** * This is used by file analysis routines to log progress information * @var PEAR_Common * @access protected */ var $_logger; /** * This is set to the highest validation level that has been validated * * If the package.xml is invalid or unknown, this is set to 0. If * normal validation has occurred, this is set to PEAR_VALIDATE_NORMAL. If * downloading/installation validation has occurred it is set to PEAR_VALIDATE_DOWNLOADING * or INSTALLING, and so on up to PEAR_VALIDATE_PACKAGING. This allows validation * "caching" to occur, which is particularly important for package validation, so * that PHP files are not validated twice * @var int * @access private */ var $_isValid = 0; /** * True if the filelist has been validated * @param bool */ var $_filesValid = false; /** * @var PEAR_Registry * @access protected */ var $_registry; /** * @var PEAR_Config * @access protected */ var $_config; /** * Optional Dependency group requested for installation * @var string * @access private */ var $_requestedGroup = false; /** * @var PEAR_ErrorStack * @access protected */ var $_stack; /** * Namespace prefix used for tasks in this package.xml - use tasks: whenever possible */ var $_tasksNs; /** * Determines whether this packagefile was initialized only with partial package info * * If this package file was constructed via parsing REST, it will only contain * * - package name * - channel name * - dependencies * @var boolean * @access private */ var $_incomplete = true; /** * @var PEAR_PackageFile_v2_Validator */ var $_v2Validator; /** * The constructor merely sets up the private error stack */ function __construct() { $this->_stack = new PEAR_ErrorStack('PEAR_PackageFile_v2', false, null); $this->_isValid = false; } /** * PHP 4 style constructor for backwards compatibility. * Used by PEAR_PackageFileManager2 */ public function PEAR_PackageFile_v2() { $this->__construct(); } /** * To make unit-testing easier * @param PEAR_Frontend_* * @param array options * @param PEAR_Config * @return PEAR_Downloader * @access protected */ function &getPEARDownloader(&$i, $o, &$c) { $z = new PEAR_Downloader($i, $o, $c); return $z; } /** * To make unit-testing easier * @param PEAR_Config * @param array options * @param array package name as returned from {@link PEAR_Registry::parsePackageName()} * @param int PEAR_VALIDATE_* constant * @return PEAR_Dependency2 * @access protected */ function &getPEARDependency2(&$c, $o, $p, $s = PEAR_VALIDATE_INSTALLING) { if (!class_exists('PEAR_Dependency2')) { require_once 'PEAR/Dependency2.php'; } $z = new PEAR_Dependency2($c, $o, $p, $s); return $z; } function getInstalledBinary() { return isset($this->_packageInfo['#binarypackage']) ? $this->_packageInfo['#binarypackage'] : false; } /** * Installation of source package has failed, attempt to download and install the * binary version of this package. * @param PEAR_Installer * @return array|false */ function installBinary(&$installer) { if (!OS_WINDOWS) { $a = false; return $a; } if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') { $releasetype = $this->getPackageType() . 'release'; if (!is_array($installer->getInstallPackages())) { $a = false; return $a; } foreach ($installer->getInstallPackages() as $p) { if ($p->isExtension($this->_packageInfo['providesextension'])) { if ($p->getPackageType() != 'extsrc' && $p->getPackageType() != 'zendextsrc') { $a = false; return $a; // the user probably downloaded it separately } } } if (isset($this->_packageInfo[$releasetype]['binarypackage'])) { $installer->log(0, 'Attempting to download binary version of extension "' . $this->_packageInfo['providesextension'] . '"'); $params = $this->_packageInfo[$releasetype]['binarypackage']; if (!is_array($params) || !isset($params[0])) { $params = array($params); } if (isset($this->_packageInfo['channel'])) { foreach ($params as $i => $param) { $params[$i] = array('channel' => $this->_packageInfo['channel'], 'package' => $param, 'version' => $this->getVersion()); } } $dl = &$this->getPEARDownloader($installer->ui, $installer->getOptions(), $installer->config); $verbose = $dl->config->get('verbose'); $dl->config->set('verbose', -1); foreach ($params as $param) { PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $ret = $dl->download(array($param)); PEAR::popErrorHandling(); if (is_array($ret) && count($ret)) { break; } } $dl->config->set('verbose', $verbose); if (is_array($ret)) { if (count($ret) == 1) { $pf = $ret[0]->getPackageFile(); PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $err = $installer->install($ret[0]); PEAR::popErrorHandling(); if (is_array($err)) { $this->_packageInfo['#binarypackage'] = $ret[0]->getPackage(); // "install" self, so all dependencies will work transparently $this->_registry->addPackage2($this); $installer->log(0, 'Download and install of binary extension "' . $this->_registry->parsedPackageNameToString( array('channel' => $pf->getChannel(), 'package' => $pf->getPackage()), true) . '" successful'); $a = array($ret[0], $err); return $a; } $installer->log(0, 'Download and install of binary extension "' . $this->_registry->parsedPackageNameToString( array('channel' => $pf->getChannel(), 'package' => $pf->getPackage()), true) . '" failed'); } } } } $a = false; return $a; } /** * @return string|false Extension name */ function getProvidesExtension() { if (in_array($this->getPackageType(), array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) { if (isset($this->_packageInfo['providesextension'])) { return $this->_packageInfo['providesextension']; } } return false; } /** * @param string Extension name * @return bool */ function isExtension($extension) { if (in_array($this->getPackageType(), array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) { return $this->_packageInfo['providesextension'] == $extension; } return false; } /** * Tests whether every part of the package.xml 1.0 is represented in * this package.xml 2.0 * @param PEAR_PackageFile_v1 * @return bool */ function isEquivalent($pf1) { if (!$pf1) { return true; } if ($this->getPackageType() == 'bundle') { return false; } $this->_stack->getErrors(true); if (!$pf1->validate(PEAR_VALIDATE_NORMAL)) { return false; } $pass = true; if ($pf1->getPackage() != $this->getPackage()) { $this->_differentPackage($pf1->getPackage()); $pass = false; } if ($pf1->getVersion() != $this->getVersion()) { $this->_differentVersion($pf1->getVersion()); $pass = false; } if (trim($pf1->getSummary()) != $this->getSummary()) { $this->_differentSummary($pf1->getSummary()); $pass = false; } if (preg_replace('/\s+/', '', $pf1->getDescription()) != preg_replace('/\s+/', '', $this->getDescription())) { $this->_differentDescription($pf1->getDescription()); $pass = false; } if ($pf1->getState() != $this->getState()) { $this->_differentState($pf1->getState()); $pass = false; } if (!strstr(preg_replace('/\s+/', '', $this->getNotes()), preg_replace('/\s+/', '', $pf1->getNotes()))) { $this->_differentNotes($pf1->getNotes()); $pass = false; } $mymaintainers = $this->getMaintainers(); $yourmaintainers = $pf1->getMaintainers(); for ($i1 = 0; $i1 < count($yourmaintainers); $i1++) { $reset = false; for ($i2 = 0; $i2 < count($mymaintainers); $i2++) { if ($mymaintainers[$i2]['handle'] == $yourmaintainers[$i1]['handle']) { if ($mymaintainers[$i2]['role'] != $yourmaintainers[$i1]['role']) { $this->_differentRole($mymaintainers[$i2]['handle'], $yourmaintainers[$i1]['role'], $mymaintainers[$i2]['role']); $pass = false; } if ($mymaintainers[$i2]['email'] != $yourmaintainers[$i1]['email']) { $this->_differentEmail($mymaintainers[$i2]['handle'], $yourmaintainers[$i1]['email'], $mymaintainers[$i2]['email']); $pass = false; } if ($mymaintainers[$i2]['name'] != $yourmaintainers[$i1]['name']) { $this->_differentName($mymaintainers[$i2]['handle'], $yourmaintainers[$i1]['name'], $mymaintainers[$i2]['name']); $pass = false; } unset($mymaintainers[$i2]); $mymaintainers = array_values($mymaintainers); unset($yourmaintainers[$i1]); $yourmaintainers = array_values($yourmaintainers); $reset = true; break; } } if ($reset) { $i1 = -1; } } $this->_unmatchedMaintainers($mymaintainers, $yourmaintainers); $filelist = $this->getFilelist(); foreach ($pf1->getFilelist() as $file => $atts) { if (!isset($filelist[$file])) { $this->_missingFile($file); $pass = false; } } return $pass; } function _differentPackage($package) { $this->_stack->push(__FUNCTION__, 'error', array('package' => $package, 'self' => $this->getPackage()), 'package.xml 1.0 package "%package%" does not match "%self%"'); } function _differentVersion($version) { $this->_stack->push(__FUNCTION__, 'error', array('version' => $version, 'self' => $this->getVersion()), 'package.xml 1.0 version "%version%" does not match "%self%"'); } function _differentState($state) { $this->_stack->push(__FUNCTION__, 'error', array('state' => $state, 'self' => $this->getState()), 'package.xml 1.0 state "%state%" does not match "%self%"'); } function _differentRole($handle, $role, $selfrole) { $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle, 'role' => $role, 'self' => $selfrole), 'package.xml 1.0 maintainer "%handle%" role "%role%" does not match "%self%"'); } function _differentEmail($handle, $email, $selfemail) { $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle, 'email' => $email, 'self' => $selfemail), 'package.xml 1.0 maintainer "%handle%" email "%email%" does not match "%self%"'); } function _differentName($handle, $name, $selfname) { $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle, 'name' => $name, 'self' => $selfname), 'package.xml 1.0 maintainer "%handle%" name "%name%" does not match "%self%"'); } function _unmatchedMaintainers($my, $yours) { if ($my) { array_walk($my, function(&$i, $k) { $i = $i["handle"]; }); $this->_stack->push(__FUNCTION__, 'error', array('handles' => $my), 'package.xml 2.0 has unmatched extra maintainers "%handles%"'); } if ($yours) { array_walk($yours, function(&$i, $k) { $i = $i["handle"]; }); $this->_stack->push(__FUNCTION__, 'error', array('handles' => $yours), 'package.xml 1.0 has unmatched extra maintainers "%handles%"'); } } function _differentNotes($notes) { $truncnotes = strlen($notes) < 25 ? $notes : substr($notes, 0, 24) . '...'; $truncmynotes = strlen($this->getNotes()) < 25 ? $this->getNotes() : substr($this->getNotes(), 0, 24) . '...'; $this->_stack->push(__FUNCTION__, 'error', array('notes' => $truncnotes, 'self' => $truncmynotes), 'package.xml 1.0 release notes "%notes%" do not match "%self%"'); } function _differentSummary($summary) { $truncsummary = strlen($summary) < 25 ? $summary : substr($summary, 0, 24) . '...'; $truncmysummary = strlen($this->getsummary()) < 25 ? $this->getSummary() : substr($this->getsummary(), 0, 24) . '...'; $this->_stack->push(__FUNCTION__, 'error', array('summary' => $truncsummary, 'self' => $truncmysummary), 'package.xml 1.0 summary "%summary%" does not match "%self%"'); } function _differentDescription($description) { $truncdescription = trim(strlen($description) < 25 ? $description : substr($description, 0, 24) . '...'); $truncmydescription = trim(strlen($this->getDescription()) < 25 ? $this->getDescription() : substr($this->getdescription(), 0, 24) . '...'); $this->_stack->push(__FUNCTION__, 'error', array('description' => $truncdescription, 'self' => $truncmydescription), 'package.xml 1.0 description "%description%" does not match "%self%"'); } function _missingFile($file) { $this->_stack->push(__FUNCTION__, 'error', array('file' => $file), 'package.xml 1.0 file "%file%" is not present in '); } /** * WARNING - do not use this function unless you know what you're doing */ function setRawState($state) { if (!isset($this->_packageInfo['stability'])) { $this->_packageInfo['stability'] = array(); } $this->_packageInfo['stability']['release'] = $state; } /** * WARNING - do not use this function unless you know what you're doing */ function setRawCompatible($compatible) { $this->_packageInfo['compatible'] = $compatible; } /** * WARNING - do not use this function unless you know what you're doing */ function setRawPackage($package) { $this->_packageInfo['name'] = $package; } /** * WARNING - do not use this function unless you know what you're doing */ function setRawChannel($channel) { $this->_packageInfo['channel'] = $channel; } function setRequestedGroup($group) { $this->_requestedGroup = $group; } function getRequestedGroup() { if (isset($this->_requestedGroup)) { return $this->_requestedGroup; } return false; } /** * For saving in the registry. * * Set the last version that was installed * @param string */ function setLastInstalledVersion($version) { $this->_packageInfo['_lastversion'] = $version; } /** * @return string|false */ function getLastInstalledVersion() { if (isset($this->_packageInfo['_lastversion'])) { return $this->_packageInfo['_lastversion']; } return false; } /** * Determines whether this package.xml has post-install scripts or not * @return array|false */ function listPostinstallScripts() { $filelist = $this->getFilelist(); $contents = $this->getContents(); $contents = $contents['dir']['file']; if (!is_array($contents) || !isset($contents[0])) { $contents = array($contents); } $taskfiles = array(); foreach ($contents as $file) { $atts = $file['attribs']; unset($file['attribs']); if (count($file)) { $taskfiles[$atts['name']] = $file; } } $common = new PEAR_Common; $common->debug = $this->_config->get('verbose'); $this->_scripts = array(); $ret = array(); foreach ($taskfiles as $name => $tasks) { if (!isset($filelist[$name])) { // ignored files will not be in the filelist continue; } $atts = $filelist[$name]; foreach ($tasks as $tag => $raw) { $task = $this->getTask($tag); $task = new $task($this->_config, $common, PEAR_TASK_INSTALL); if ($task->isScript()) { $ret[] = $filelist[$name]['installed_as']; } } } if (count($ret)) { return $ret; } return false; } /** * Initialize post-install scripts for running * * This method can be used to detect post-install scripts, as the return value * indicates whether any exist * @return bool */ function initPostinstallScripts() { $filelist = $this->getFilelist(); $contents = $this->getContents(); $contents = $contents['dir']['file']; if (!is_array($contents) || !isset($contents[0])) { $contents = array($contents); } $taskfiles = array(); foreach ($contents as $file) { $atts = $file['attribs']; unset($file['attribs']); if (count($file)) { $taskfiles[$atts['name']] = $file; } } $common = new PEAR_Common; $common->debug = $this->_config->get('verbose'); $this->_scripts = array(); foreach ($taskfiles as $name => $tasks) { if (!isset($filelist[$name])) { // file was not installed due to installconditions continue; } $atts = $filelist[$name]; foreach ($tasks as $tag => $raw) { $taskname = $this->getTask($tag); $task = new $taskname($this->_config, $common, PEAR_TASK_INSTALL); if (!$task->isScript()) { continue; // scripts are only handled after installation } $lastversion = isset($this->_packageInfo['_lastversion']) ? $this->_packageInfo['_lastversion'] : null; $task->init($raw, $atts, $lastversion); $res = $task->startSession($this, $atts['installed_as'], null); if (!$res) { continue; // skip this file } if (PEAR::isError($res)) { return $res; } $this->_scripts[] = $task; } } if (count($this->_scripts)) { return true; } return false; } function runPostinstallScripts() { if ($this->initPostinstallScripts()) { $ui = &PEAR_Frontend::singleton(); if ($ui) { $ui->runPostinstallScripts($this->_scripts, $this); } } } /** * Convert a recursive set of and tags into a single tag with * tags. */ function flattenFilelist() { if (isset($this->_packageInfo['bundle'])) { return; } $filelist = array(); if (isset($this->_packageInfo['contents']['dir']['dir'])) { $this->_getFlattenedFilelist($filelist, $this->_packageInfo['contents']['dir']); if (!isset($filelist[1])) { $filelist = $filelist[0]; } $this->_packageInfo['contents']['dir']['file'] = $filelist; unset($this->_packageInfo['contents']['dir']['dir']); } else { // else already flattened but check for baseinstalldir propagation if (isset($this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'])) { if (isset($this->_packageInfo['contents']['dir']['file'][0])) { foreach ($this->_packageInfo['contents']['dir']['file'] as $i => $file) { if (isset($file['attribs']['baseinstalldir'])) { continue; } $this->_packageInfo['contents']['dir']['file'][$i]['attribs']['baseinstalldir'] = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir']; } } else { if (!isset($this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir'])) { $this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir'] = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir']; } } } } } /** * @param array the final flattened file list * @param array the current directory being processed * @param string|false any recursively inherited baeinstalldir attribute * @param string private recursion variable * @return array * @access protected */ function _getFlattenedFilelist(&$files, $dir, $baseinstall = false, $path = '') { if (isset($dir['attribs']) && isset($dir['attribs']['baseinstalldir'])) { $baseinstall = $dir['attribs']['baseinstalldir']; } if (isset($dir['dir'])) { if (!isset($dir['dir'][0])) { $dir['dir'] = array($dir['dir']); } foreach ($dir['dir'] as $subdir) { if (!isset($subdir['attribs']) || !isset($subdir['attribs']['name'])) { $name = '*unknown*'; } else { $name = $subdir['attribs']['name']; } $newpath = empty($path) ? $name : $path . '/' . $name; $this->_getFlattenedFilelist($files, $subdir, $baseinstall, $newpath); } } if (isset($dir['file'])) { if (!isset($dir['file'][0])) { $dir['file'] = array($dir['file']); } foreach ($dir['file'] as $file) { $attrs = $file['attribs']; $name = $attrs['name']; if ($baseinstall && !isset($attrs['baseinstalldir'])) { $attrs['baseinstalldir'] = $baseinstall; } $attrs['name'] = empty($path) ? $name : $path . '/' . $name; $attrs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $attrs['name']); $file['attribs'] = $attrs; $files[] = $file; } } } function setConfig(&$config) { $this->_config = &$config; $this->_registry = &$config->getRegistry(); } function setLogger(&$logger) { if (!is_object($logger) || !method_exists($logger, 'log')) { return PEAR::raiseError('Logger must be compatible with PEAR_Common::log'); } $this->_logger = &$logger; } /** * WARNING - do not use this function directly unless you know what you're doing */ function setDeps($deps) { $this->_packageInfo['dependencies'] = $deps; } /** * WARNING - do not use this function directly unless you know what you're doing */ function setCompatible($compat) { $this->_packageInfo['compatible'] = $compat; } function setPackagefile($file, $archive = false) { $this->_packageFile = $file; $this->_archiveFile = $archive ? $archive : $file; } /** * Wrapper to {@link PEAR_ErrorStack::getErrors()} * @param boolean determines whether to purge the error stack after retrieving * @return array */ function getValidationWarnings($purge = true) { return $this->_stack->getErrors($purge); } function getPackageFile() { return $this->_packageFile; } function getArchiveFile() { return $this->_archiveFile; } /** * Directly set the array that defines this packagefile * * WARNING: no validation. This should only be performed by internal methods * inside PEAR or by inputting an array saved from an existing PEAR_PackageFile_v2 * @param array */ function fromArray($pinfo) { unset($pinfo['old']); unset($pinfo['xsdversion']); // If the changelog isn't an array then it was passed in as an empty tag if (isset($pinfo['changelog']) && !is_array($pinfo['changelog'])) { unset($pinfo['changelog']); } $this->_incomplete = false; $this->_packageInfo = $pinfo; } function isIncomplete() { return $this->_incomplete; } /** * @return array */ function toArray($forreg = false) { if (!$this->validate(PEAR_VALIDATE_NORMAL)) { return false; } return $this->getArray($forreg); } function getArray($forReg = false) { if ($forReg) { $arr = $this->_packageInfo; $arr['old'] = array(); $arr['old']['version'] = $this->getVersion(); $arr['old']['release_date'] = $this->getDate(); $arr['old']['release_state'] = $this->getState(); $arr['old']['release_license'] = $this->getLicense(); $arr['old']['release_notes'] = $this->getNotes(); $arr['old']['release_deps'] = $this->getDeps(); $arr['old']['maintainers'] = $this->getMaintainers(); $arr['xsdversion'] = '2.0'; return $arr; } else { $info = $this->_packageInfo; unset($info['dirtree']); if (isset($info['_lastversion'])) { unset($info['_lastversion']); } if (isset($info['#binarypackage'])) { unset($info['#binarypackage']); } return $info; } } function packageInfo($field) { $arr = $this->getArray(true); if ($field == 'state') { return $arr['stability']['release']; } if ($field == 'api-version') { return $arr['version']['api']; } if ($field == 'api-state') { return $arr['stability']['api']; } if (isset($arr['old'][$field])) { if (!is_string($arr['old'][$field])) { return null; } return $arr['old'][$field]; } if (isset($arr[$field])) { if (!is_string($arr[$field])) { return null; } return $arr[$field]; } return null; } function getName() { return $this->getPackage(); } function getPackage() { if (isset($this->_packageInfo['name'])) { return $this->_packageInfo['name']; } return false; } function getChannel() { if (isset($this->_packageInfo['uri'])) { return '__uri'; } if (isset($this->_packageInfo['channel'])) { return strtolower($this->_packageInfo['channel']); } return false; } function getUri() { if (isset($this->_packageInfo['uri'])) { return $this->_packageInfo['uri']; } return false; } function getExtends() { if (isset($this->_packageInfo['extends'])) { return $this->_packageInfo['extends']; } return false; } function getSummary() { if (isset($this->_packageInfo['summary'])) { return $this->_packageInfo['summary']; } return false; } function getDescription() { if (isset($this->_packageInfo['description'])) { return $this->_packageInfo['description']; } return false; } function getMaintainers($raw = false) { if (!isset($this->_packageInfo['lead'])) { return false; } if ($raw) { $ret = array('lead' => $this->_packageInfo['lead']); (isset($this->_packageInfo['developer'])) ? $ret['developer'] = $this->_packageInfo['developer'] :null; (isset($this->_packageInfo['contributor'])) ? $ret['contributor'] = $this->_packageInfo['contributor'] :null; (isset($this->_packageInfo['helper'])) ? $ret['helper'] = $this->_packageInfo['helper'] :null; return $ret; } else { $ret = array(); $leads = isset($this->_packageInfo['lead'][0]) ? $this->_packageInfo['lead'] : array($this->_packageInfo['lead']); foreach ($leads as $lead) { $s = $lead; $s['handle'] = $s['user']; unset($s['user']); $s['role'] = 'lead'; $ret[] = $s; } if (isset($this->_packageInfo['developer'])) { $leads = isset($this->_packageInfo['developer'][0]) ? $this->_packageInfo['developer'] : array($this->_packageInfo['developer']); foreach ($leads as $maintainer) { $s = $maintainer; $s['handle'] = $s['user']; unset($s['user']); $s['role'] = 'developer'; $ret[] = $s; } } if (isset($this->_packageInfo['contributor'])) { $leads = isset($this->_packageInfo['contributor'][0]) ? $this->_packageInfo['contributor'] : array($this->_packageInfo['contributor']); foreach ($leads as $maintainer) { $s = $maintainer; $s['handle'] = $s['user']; unset($s['user']); $s['role'] = 'contributor'; $ret[] = $s; } } if (isset($this->_packageInfo['helper'])) { $leads = isset($this->_packageInfo['helper'][0]) ? $this->_packageInfo['helper'] : array($this->_packageInfo['helper']); foreach ($leads as $maintainer) { $s = $maintainer; $s['handle'] = $s['user']; unset($s['user']); $s['role'] = 'helper'; $ret[] = $s; } } return $ret; } return false; } function getLeads() { if (isset($this->_packageInfo['lead'])) { return $this->_packageInfo['lead']; } return false; } function getDevelopers() { if (isset($this->_packageInfo['developer'])) { return $this->_packageInfo['developer']; } return false; } function getContributors() { if (isset($this->_packageInfo['contributor'])) { return $this->_packageInfo['contributor']; } return false; } function getHelpers() { if (isset($this->_packageInfo['helper'])) { return $this->_packageInfo['helper']; } return false; } function setDate($date) { if (!isset($this->_packageInfo['date'])) { // ensure that the extends tag is set up in the right location $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('time', 'version', 'stability', 'license', 'notes', 'contents', 'compatible', 'dependencies', 'providesextension', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), array(), 'date'); } $this->_packageInfo['date'] = $date; $this->_isValid = 0; } function setTime($time) { $this->_isValid = 0; if (!isset($this->_packageInfo['time'])) { // ensure that the time tag is set up in the right location $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('version', 'stability', 'license', 'notes', 'contents', 'compatible', 'dependencies', 'providesextension', 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), $time, 'time'); } $this->_packageInfo['time'] = $time; } function getDate() { if (isset($this->_packageInfo['date'])) { return $this->_packageInfo['date']; } return false; } function getTime() { if (isset($this->_packageInfo['time'])) { return $this->_packageInfo['time']; } return false; } /** * @param package|api version category to return */ function getVersion($key = 'release') { if (isset($this->_packageInfo['version'][$key])) { return $this->_packageInfo['version'][$key]; } return false; } function getStability() { if (isset($this->_packageInfo['stability'])) { return $this->_packageInfo['stability']; } return false; } function getState($key = 'release') { if (isset($this->_packageInfo['stability'][$key])) { return $this->_packageInfo['stability'][$key]; } return false; } function getLicense($raw = false) { if (isset($this->_packageInfo['license'])) { if ($raw) { return $this->_packageInfo['license']; } if (is_array($this->_packageInfo['license'])) { return $this->_packageInfo['license']['_content']; } else { return $this->_packageInfo['license']; } } return false; } function getLicenseLocation() { if (!isset($this->_packageInfo['license']) || !is_array($this->_packageInfo['license'])) { return false; } return $this->_packageInfo['license']['attribs']; } function getNotes() { if (isset($this->_packageInfo['notes'])) { return $this->_packageInfo['notes']; } return false; } /** * Return the tag contents, if any * @return array|false */ function getUsesrole() { if (isset($this->_packageInfo['usesrole'])) { return $this->_packageInfo['usesrole']; } return false; } /** * Return the tag contents, if any * @return array|false */ function getUsestask() { if (isset($this->_packageInfo['usestask'])) { return $this->_packageInfo['usestask']; } return false; } /** * This should only be used to retrieve filenames and install attributes */ function getFilelist($preserve = false) { if (isset($this->_packageInfo['filelist']) && !$preserve) { return $this->_packageInfo['filelist']; } $this->flattenFilelist(); if ($contents = $this->getContents()) { $ret = array(); if (!isset($contents['dir'])) { return false; } if (!isset($contents['dir']['file'][0])) { $contents['dir']['file'] = array($contents['dir']['file']); } foreach ($contents['dir']['file'] as $file) { if (!isset($file['attribs']['name'])) { continue; } $name = $file['attribs']['name']; if (!$preserve) { $file = $file['attribs']; } $ret[$name] = $file; } if (!$preserve) { $this->_packageInfo['filelist'] = $ret; } return $ret; } return false; } /** * Return configure options array, if any * * @return array|false */ function getConfigureOptions() { if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') { return false; } $releases = $this->getReleases(); if (isset($releases[0])) { $releases = $releases[0]; } if (isset($releases['configureoption'])) { if (!isset($releases['configureoption'][0])) { $releases['configureoption'] = array($releases['configureoption']); } for ($i = 0; $i < count($releases['configureoption']); $i++) { $releases['configureoption'][$i] = $releases['configureoption'][$i]['attribs']; } return $releases['configureoption']; } return false; } /** * This is only used at install-time, after all serialization * is over. */ function resetFilelist() { $this->_packageInfo['filelist'] = array(); } /** * Retrieve a list of files that should be installed on this computer * @return array */ function getInstallationFilelist($forfilecheck = false) { $contents = $this->getFilelist(true); if (isset($contents['dir']['attribs']['baseinstalldir'])) { $base = $contents['dir']['attribs']['baseinstalldir']; } if (isset($this->_packageInfo['bundle'])) { return PEAR::raiseError( 'Exception: bundles should be handled in download code only'); } $release = $this->getReleases(); if ($release) { if (!isset($release[0])) { if (!isset($release['installconditions']) && !isset($release['filelist'])) { if ($forfilecheck) { return $this->getFilelist(); } return $contents; } $release = array($release); } $depchecker = &$this->getPEARDependency2($this->_config, array(), array('channel' => $this->getChannel(), 'package' => $this->getPackage()), PEAR_VALIDATE_INSTALLING); foreach ($release as $instance) { if (isset($instance['installconditions'])) { $installconditions = $instance['installconditions']; if (is_array($installconditions)) { PEAR::pushErrorHandling(PEAR_ERROR_RETURN); foreach ($installconditions as $type => $conditions) { if (!isset($conditions[0])) { $conditions = array($conditions); } foreach ($conditions as $condition) { $ret = $depchecker->{"validate{$type}Dependency"}($condition); if (PEAR::isError($ret)) { PEAR::popErrorHandling(); continue 3; // skip this release } } } PEAR::popErrorHandling(); } } // this is the release to use if (isset($instance['filelist'])) { // ignore files if (isset($instance['filelist']['ignore'])) { $ignore = isset($instance['filelist']['ignore'][0]) ? $instance['filelist']['ignore'] : array($instance['filelist']['ignore']); foreach ($ignore as $ig) { unset ($contents[$ig['attribs']['name']]); } } // install files as this name if (isset($instance['filelist']['install'])) { $installas = isset($instance['filelist']['install'][0]) ? $instance['filelist']['install'] : array($instance['filelist']['install']); foreach ($installas as $as) { $contents[$as['attribs']['name']]['attribs']['install-as'] = $as['attribs']['as']; } } } if ($forfilecheck) { foreach ($contents as $file => $attrs) { $contents[$file] = $attrs['attribs']; } } return $contents; } } else { // simple release - no installconditions or install-as if ($forfilecheck) { return $this->getFilelist(); } return $contents; } // no releases matched return PEAR::raiseError('No releases in package.xml matched the existing operating ' . 'system, extensions installed, or architecture, cannot install'); } /** * This is only used at install-time, after all serialization * is over. * @param string file name * @param string installed path */ function setInstalledAs($file, $path) { if ($path) { return $this->_packageInfo['filelist'][$file]['installed_as'] = $path; } unset($this->_packageInfo['filelist'][$file]['installed_as']); } function getInstalledLocation($file) { if (isset($this->_packageInfo['filelist'][$file]['installed_as'])) { return $this->_packageInfo['filelist'][$file]['installed_as']; } return false; } /** * This is only used at install-time, after all serialization * is over. */ function installedFile($file, $atts) { if (isset($this->_packageInfo['filelist'][$file])) { $this->_packageInfo['filelist'][$file] = array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']); } else { $this->_packageInfo['filelist'][$file] = $atts['attribs']; } } /** * Retrieve the contents tag */ function getContents() { if (isset($this->_packageInfo['contents'])) { return $this->_packageInfo['contents']; } return false; } /** * @param string full path to file * @param string attribute name * @param string attribute value * @param int risky but fast - use this to choose a file based on its position in the list * of files. Index is zero-based like PHP arrays. * @return bool success of operation */ function setFileAttribute($filename, $attr, $value, $index = false) { $this->_isValid = 0; if (in_array($attr, array('role', 'name', 'baseinstalldir'))) { $this->_filesValid = false; } if ($index !== false && isset($this->_packageInfo['contents']['dir']['file'][$index]['attribs'])) { $this->_packageInfo['contents']['dir']['file'][$index]['attribs'][$attr] = $value; return true; } if (!isset($this->_packageInfo['contents']['dir']['file'])) { return false; } $files = $this->_packageInfo['contents']['dir']['file']; if (!isset($files[0])) { $files = array($files); $ind = false; } else { $ind = true; } foreach ($files as $i => $file) { if (isset($file['attribs'])) { if ($file['attribs']['name'] == $filename) { if ($ind) { $this->_packageInfo['contents']['dir']['file'][$i]['attribs'][$attr] = $value; } else { $this->_packageInfo['contents']['dir']['file']['attribs'][$attr] = $value; } return true; } } } return false; } function setDirtree($path) { if (!isset($this->_packageInfo['dirtree'])) { $this->_packageInfo['dirtree'] = array(); } $this->_packageInfo['dirtree'][$path] = true; } function getDirtree() { if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) { return $this->_packageInfo['dirtree']; } return false; } function resetDirtree() { unset($this->_packageInfo['dirtree']); } /** * Determines whether this package claims it is compatible with the version of * the package that has a recommended version dependency * @param PEAR_PackageFile_v2|PEAR_PackageFile_v1|PEAR_Downloader_Package * @return boolean */ function isCompatible($pf) { if (!isset($this->_packageInfo['compatible'])) { return false; } if (!isset($this->_packageInfo['channel'])) { return false; } $me = $pf->getVersion(); $compatible = $this->_packageInfo['compatible']; if (!isset($compatible[0])) { $compatible = array($compatible); } $found = false; foreach ($compatible as $info) { if (strtolower($info['name']) == strtolower($pf->getPackage())) { if (strtolower($info['channel']) == strtolower($pf->getChannel())) { $found = true; break; } } } if (!$found) { return false; } if (isset($info['exclude'])) { if (!isset($info['exclude'][0])) { $info['exclude'] = array($info['exclude']); } foreach ($info['exclude'] as $exclude) { if (version_compare($me, $exclude, '==')) { return false; } } } if (version_compare($me, $info['min'], '>=') && version_compare($me, $info['max'], '<=')) { return true; } return false; } /** * @return array|false */ function getCompatible() { if (isset($this->_packageInfo['compatible'])) { return $this->_packageInfo['compatible']; } return false; } function getDependencies() { if (isset($this->_packageInfo['dependencies'])) { return $this->_packageInfo['dependencies']; } return false; } function isSubpackageOf($p) { return $p->isSubpackage($this); } /** * Determines whether the passed in package is a subpackage of this package. * * No version checking is done, only name verification. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 * @return bool */ function isSubpackage($p) { $sub = array(); if (isset($this->_packageInfo['dependencies']['required']['subpackage'])) { $sub = $this->_packageInfo['dependencies']['required']['subpackage']; if (!isset($sub[0])) { $sub = array($sub); } } if (isset($this->_packageInfo['dependencies']['optional']['subpackage'])) { $sub1 = $this->_packageInfo['dependencies']['optional']['subpackage']; if (!isset($sub1[0])) { $sub1 = array($sub1); } $sub = array_merge($sub, $sub1); } if (isset($this->_packageInfo['dependencies']['group'])) { $group = $this->_packageInfo['dependencies']['group']; if (!isset($group[0])) { $group = array($group); } foreach ($group as $deps) { if (isset($deps['subpackage'])) { $sub2 = $deps['subpackage']; if (!isset($sub2[0])) { $sub2 = array($sub2); } $sub = array_merge($sub, $sub2); } } } foreach ($sub as $dep) { if (strtolower($dep['name']) == strtolower($p->getPackage())) { if (isset($dep['channel'])) { if (strtolower($dep['channel']) == strtolower($p->getChannel())) { return true; } } else { if ($dep['uri'] == $p->getURI()) { return true; } } } } return false; } function dependsOn($package, $channel) { if (!($deps = $this->getDependencies())) { return false; } foreach (array('package', 'subpackage') as $type) { foreach (array('required', 'optional') as $needed) { if (isset($deps[$needed][$type])) { if (!isset($deps[$needed][$type][0])) { $deps[$needed][$type] = array($deps[$needed][$type]); } foreach ($deps[$needed][$type] as $dep) { $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri'; if (strtolower($dep['name']) == strtolower($package) && $depchannel == $channel) { return true; } } } } if (isset($deps['group'])) { if (!isset($deps['group'][0])) { $dep['group'] = array($deps['group']); } foreach ($deps['group'] as $group) { if (isset($group[$type])) { if (!is_array($group[$type])) { $group[$type] = array($group[$type]); } foreach ($group[$type] as $dep) { $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri'; if (strtolower($dep['name']) == strtolower($package) && $depchannel == $channel) { return true; } } } } } } return false; } /** * Get the contents of a dependency group * @param string * @return array|false */ function getDependencyGroup($name) { $name = strtolower($name); if (!isset($this->_packageInfo['dependencies']['group'])) { return false; } $groups = $this->_packageInfo['dependencies']['group']; if (!isset($groups[0])) { $groups = array($groups); } foreach ($groups as $group) { if (strtolower($group['attribs']['name']) == $name) { return $group; } } return false; } /** * Retrieve a partial package.xml 1.0 representation of dependencies * * a very limited representation of dependencies is returned by this method. * The tag for excluding certain versions of a dependency is * completely ignored. In addition, dependency groups are ignored, with the * assumption that all dependencies in dependency groups are also listed in * the optional group that work with all dependency groups * @param boolean return package.xml 2.0 tag * @return array|false */ function getDeps($raw = false, $nopearinstaller = false) { if (isset($this->_packageInfo['dependencies'])) { if ($raw) { return $this->_packageInfo['dependencies']; } $ret = array(); $map = array( 'php' => 'php', 'package' => 'pkg', 'subpackage' => 'pkg', 'extension' => 'ext', 'os' => 'os', 'pearinstaller' => 'pkg', ); foreach (array('required', 'optional') as $type) { $optional = ($type == 'optional') ? 'yes' : 'no'; if (!isset($this->_packageInfo['dependencies'][$type]) || empty($this->_packageInfo['dependencies'][$type])) { continue; } foreach ($this->_packageInfo['dependencies'][$type] as $dtype => $deps) { if ($dtype == 'pearinstaller' && $nopearinstaller) { continue; } if ((is_array($deps) && !isset($deps[0])) || !is_array($deps)) { $deps = array($deps); } foreach ($deps as $dep) { if (!isset($map[$dtype])) { // no support for arch type continue; } if ($dtype == 'pearinstaller') { $dep['name'] = 'PEAR'; $dep['channel'] = 'pear.php.net'; } $s = array('type' => $map[$dtype]); if (isset($dep['channel'])) { $s['channel'] = $dep['channel']; } if (isset($dep['uri'])) { $s['uri'] = $dep['uri']; } if (isset($dep['name'])) { $s['name'] = $dep['name']; } if (isset($dep['conflicts'])) { $s['rel'] = 'not'; } else { if (!isset($dep['min']) && !isset($dep['max'])) { $s['rel'] = 'has'; $s['optional'] = $optional; } elseif (isset($dep['min']) && isset($dep['max'])) { $s['rel'] = 'ge'; $s1 = $s; $s1['rel'] = 'le'; $s['version'] = $dep['min']; $s1['version'] = $dep['max']; if (isset($dep['channel'])) { $s1['channel'] = $dep['channel']; } if ($dtype != 'php') { $s['name'] = $dep['name']; $s1['name'] = $dep['name']; } $s['optional'] = $optional; $s1['optional'] = $optional; $ret[] = $s1; } elseif (isset($dep['min'])) { if (isset($dep['exclude']) && $dep['exclude'] == $dep['min']) { $s['rel'] = 'gt'; } else { $s['rel'] = 'ge'; } $s['version'] = $dep['min']; $s['optional'] = $optional; if ($dtype != 'php') { $s['name'] = $dep['name']; } } elseif (isset($dep['max'])) { if (isset($dep['exclude']) && $dep['exclude'] == $dep['max']) { $s['rel'] = 'lt'; } else { $s['rel'] = 'le'; } $s['version'] = $dep['max']; $s['optional'] = $optional; if ($dtype != 'php') { $s['name'] = $dep['name']; } } } $ret[] = $s; } } } if (count($ret)) { return $ret; } } return false; } /** * @return php|extsrc|extbin|zendextsrc|zendextbin|bundle|false */ function getPackageType() { if (isset($this->_packageInfo['phprelease'])) { return 'php'; } if (isset($this->_packageInfo['extsrcrelease'])) { return 'extsrc'; } if (isset($this->_packageInfo['extbinrelease'])) { return 'extbin'; } if (isset($this->_packageInfo['zendextsrcrelease'])) { return 'zendextsrc'; } if (isset($this->_packageInfo['zendextbinrelease'])) { return 'zendextbin'; } if (isset($this->_packageInfo['bundle'])) { return 'bundle'; } return false; } /** * @return array|false */ function getReleases() { $type = $this->getPackageType(); if ($type != 'bundle') { $type .= 'release'; } if ($this->getPackageType() && isset($this->_packageInfo[$type])) { return $this->_packageInfo[$type]; } return false; } /** * @return array */ function getChangelog() { if (isset($this->_packageInfo['changelog'])) { return $this->_packageInfo['changelog']; } return false; } function hasDeps() { return isset($this->_packageInfo['dependencies']); } function getPackagexmlVersion() { if (isset($this->_packageInfo['zendextsrcrelease'])) { return '2.1'; } if (isset($this->_packageInfo['zendextbinrelease'])) { return '2.1'; } return '2.0'; } /** * @return array|false */ function getSourcePackage() { if (isset($this->_packageInfo['extbinrelease']) || isset($this->_packageInfo['zendextbinrelease'])) { return array('channel' => $this->_packageInfo['srcchannel'], 'package' => $this->_packageInfo['srcpackage']); } return false; } function getBundledPackages() { if (isset($this->_packageInfo['bundle'])) { return $this->_packageInfo['contents']['bundledpackage']; } return false; } function getLastModified() { if (isset($this->_packageInfo['_lastmodified'])) { return $this->_packageInfo['_lastmodified']; } return false; } /** * Get the contents of a file listed within the package.xml * @param string * @return string */ function getFileContents($file) { if ($this->_archiveFile == $this->_packageFile) { // unpacked $dir = dirname($this->_packageFile); $file = $dir . DIRECTORY_SEPARATOR . $file; $file = str_replace(array('/', '\\'), array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file); if (file_exists($file) && is_readable($file)) { return implode('', file($file)); } } else { // tgz $tar = new Archive_Tar($this->_archiveFile); $tar->pushErrorHandling(PEAR_ERROR_RETURN); if ($file != 'package.xml' && $file != 'package2.xml') { $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file; } $file = $tar->extractInString($file); $tar->popErrorHandling(); if (PEAR::isError($file)) { return PEAR::raiseError("Cannot locate file '$file' in archive"); } return $file; } } function &getRW() { if (!class_exists('PEAR_PackageFile_v2_rw')) { require_once 'PEAR/PackageFile/v2/rw.php'; } $a = new PEAR_PackageFile_v2_rw; foreach (get_object_vars($this) as $name => $unused) { if (!isset($this->$name)) { continue; } if ($name == '_config' || $name == '_logger'|| $name == '_registry' || $name == '_stack') { $a->$name = &$this->$name; } else { $a->$name = $this->$name; } } return $a; } function &getDefaultGenerator() { if (!class_exists('PEAR_PackageFile_Generator_v2')) { require_once 'PEAR/PackageFile/Generator/v2.php'; } $a = new PEAR_PackageFile_Generator_v2($this); return $a; } function analyzeSourceCode($file, $string = false) { if (!isset($this->_v2Validator) || !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) { if (!class_exists('PEAR_PackageFile_v2_Validator')) { require_once 'PEAR/PackageFile/v2/Validator.php'; } $this->_v2Validator = new PEAR_PackageFile_v2_Validator; } return $this->_v2Validator->analyzeSourceCode($file, $string); } function validate($state = PEAR_VALIDATE_NORMAL) { if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) { return false; } if (!isset($this->_v2Validator) || !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) { if (!class_exists('PEAR_PackageFile_v2_Validator')) { require_once 'PEAR/PackageFile/v2/Validator.php'; } $this->_v2Validator = new PEAR_PackageFile_v2_Validator; } if (isset($this->_packageInfo['xsdversion'])) { unset($this->_packageInfo['xsdversion']); } return $this->_v2Validator->validate($this, $state); } function getTasksNs() { if (!isset($this->_tasksNs)) { if (isset($this->_packageInfo['attribs'])) { foreach ($this->_packageInfo['attribs'] as $name => $value) { if ($value == 'http://pear.php.net/dtd/tasks-1.0') { $this->_tasksNs = str_replace('xmlns:', '', $name); break; } } } } return $this->_tasksNs; } /** * Determine whether a task name is a valid task. Custom tasks may be defined * using subdirectories by putting a "-" in the name, as in * * Note that this method will auto-load the task class file and test for the existence * of the name with "-" replaced by "_" as in PEAR/Task/mycustom/task.php makes class * PEAR_Task_mycustom_task * @param string * @return boolean */ function getTask($task) { $this->getTasksNs(); // transform all '-' to '/' and 'tasks:' to '' so tasks:replace becomes replace $task = str_replace(array($this->_tasksNs . ':', '-'), array('', ' '), $task); $taskfile = str_replace(' ', '/', ucwords($task)); $task = str_replace(array(' ', '/'), '_', ucwords($task)); if (class_exists("PEAR_Task_$task")) { return "PEAR_Task_$task"; } $fp = @fopen("PEAR/Task/$taskfile.php", 'r', true); if ($fp) { fclose($fp); require_once "PEAR/Task/$taskfile.php"; return "PEAR_Task_$task"; } return false; } /** * Key-friendly array_splice * @param tagname to splice a value in before * @param mixed the value to splice in * @param string the new tag name */ function _ksplice($array, $key, $value, $newkey) { $offset = array_search($key, array_keys($array)); $after = array_slice($array, $offset); $before = array_slice($array, 0, $offset); $before[$newkey] = $value; return array_merge($before, $after); } /** * @param array a list of possible keys, in the order they may occur * @param mixed contents of the new package.xml tag * @param string tag name * @access private */ function _insertBefore($array, $keys, $contents, $newkey) { foreach ($keys as $key) { if (isset($array[$key])) { return $array = $this->_ksplice($array, $key, $contents, $newkey); } } $array[$newkey] = $contents; return $array; } /** * @param subsection of {@link $_packageInfo} * @param array|string tag contents * @param array format: *
     * array(
     *   tagname => array(list of tag names that follow this one),
     *   childtagname => array(list of child tag names that follow this one),
     * )
     * 
* * This allows construction of nested tags * @access private */ function _mergeTag($manip, $contents, $order) { if (count($order)) { foreach ($order as $tag => $curorder) { if (!isset($manip[$tag])) { // ensure that the tag is set up $manip = $this->_insertBefore($manip, $curorder, array(), $tag); } if (count($order) > 1) { $manip[$tag] = $this->_mergeTag($manip[$tag], $contents, array_slice($order, 1)); return $manip; } } } else { return $manip; } if (is_array($manip[$tag]) && !empty($manip[$tag]) && isset($manip[$tag][0])) { $manip[$tag][] = $contents; } else { if (is_array($manip[$tag]) && !count($manip[$tag])) { $manip[$tag] = $contents; } else { $manip[$tag] = array($manip[$tag]); $manip[$tag][] = $contents; } } return $manip; } } ?> PK=1]:(pear/PEAR/RunTest.phpnu[ * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.3.3 */ /** * for error handling */ require_once 'PEAR.php'; require_once 'PEAR/Config.php'; define('DETAILED', 1); putenv("PHP_PEAR_RUNTESTS=1"); /** * Simplified version of PHP's test suite * * Try it with: * * $ php -r 'include "../PEAR/RunTest.php"; $t=new PEAR_RunTest; $o=$t->run("./pear_system.phpt");print_r($o);' * * * @category pear * @package PEAR * @author Tomas V.V.Cox * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.3.3 */ class PEAR_RunTest { var $_headers = array(); var $_logger; var $_options; var $_php; var $tests_count; var $xdebug_loaded; /** * Saved value of php executable, used to reset $_php when we * have a test that uses cgi * * @var unknown_type */ var $_savephp; var $ini_overwrites = array( 'output_handler=', 'open_basedir=', 'disable_functions=', 'output_buffering=Off', 'display_errors=1', 'log_errors=0', 'html_errors=0', 'report_zend_debug=0', 'docref_root=', 'docref_ext=.html', 'error_prepend_string=', 'error_append_string=', 'auto_prepend_file=', 'auto_append_file=', 'xdebug.default_enable=0', 'allow_url_fopen=1', ); /** * An object that supports the PEAR_Common->log() signature, or null * @param PEAR_Common|null */ function __construct($logger = null, $options = array()) { if (!defined('E_DEPRECATED')) { define('E_DEPRECATED', 0); } if (!defined('E_STRICT')) { define('E_STRICT', 0); } $excluded_error_reporting = E_DEPRECATED; if (!defined('PHP_VERSION_ID') || PHP_VERSION_ID < 80400) { $excluded_error_reporting |= E_STRICT; } $this->ini_overwrites[] = 'error_reporting=' . (E_ALL & ~$excluded_error_reporting); if (!defined('PHP_VERSION_ID') || PHP_VERSION_ID < 80500) { $this->ini_overwrites[] = 'report_memleaks=0'; } if (is_null($logger)) { require_once 'PEAR/Common.php'; $logger = new PEAR_Common; } $this->_logger = $logger; $this->_options = $options; $conf = &PEAR_Config::singleton(); $this->_php = $conf->get('php_bin'); } /** * Taken from php-src/run-tests.php * * @param string $commandline command name * @param array $env * @param string $stdin standard input to pass to the command * @return unknown */ function system_with_timeout($commandline, $env = null, $stdin = null) { $data = ''; $proc = proc_open($commandline, array( 0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w') ), $pipes, null, $env, array('suppress_errors' => true)); if (!$proc) { return false; } if (is_string($stdin)) { fwrite($pipes[0], $stdin); } fclose($pipes[0]); while (true) { /* hide errors from interrupted syscalls */ $r = $pipes; unset($r[0]); $e = $w = []; $n = @stream_select($r, $w, $e, 60); if ($n === 0) { /* timed out */ $data .= "\n ** ERROR: process timed out **\n"; proc_terminate($proc); return array(1234567890, $data); } else if ($n > 0) { $line = fread($pipes[1], 8192); if (strlen($line) == 0) { /* EOF */ break; } $data .= $line; } } if (function_exists('proc_get_status')) { $stat = proc_get_status($proc); if ($stat['signaled']) { $data .= "\nTermsig=".$stat['stopsig']; } } $code = proc_close($proc); if (function_exists('proc_get_status')) { $code = $stat['exitcode']; } return array($code, $data); } /** * Turns a PHP INI string into an array * * Turns -d "include_path=/foo/bar" into this: * array( * 'include_path' => array( * 'operator' => '-d', * 'value' => '/foo/bar', * ) * ) * Works both with quotes and without * * @param string an PHP INI string, -d "include_path=/foo/bar" * @return array */ function iniString2array($ini_string) { if (!$ini_string) { return array(); } $split = preg_split('/[\s]|=/', $ini_string, -1, PREG_SPLIT_NO_EMPTY); $key = $split[1][0] == '"' ? substr($split[1], 1) : $split[1]; $value = $split[2][strlen($split[2]) - 1] == '"' ? substr($split[2], 0, -1) : $split[2]; // FIXME review if this is really the struct to go with $array = array($key => array('operator' => $split[0], 'value' => $value)); return $array; } function settings2array($settings, $ini_settings) { foreach ($settings as $setting) { if (strpos($setting, '=') !== false) { $setting = explode('=', $setting, 2); $name = trim(strtolower($setting[0])); $value = trim($setting[1]); $ini_settings[$name] = $value; } } return $ini_settings; } function settings2params($ini_settings) { $settings = ''; foreach ($ini_settings as $name => $value) { if (is_array($value)) { $operator = $value['operator']; $value = $value['value']; } else { $operator = '-d'; } $value = addslashes($value); $settings .= " $operator \"$name=$value\""; } return $settings; } function _preparePhpBin($php, $file, $ini_settings) { $file = escapeshellarg($file); $cmd = $php . $ini_settings . ' -f ' . $file; return $cmd; } function runPHPUnit($file, $ini_settings = '') { if (!file_exists($file) && file_exists(getcwd() . DIRECTORY_SEPARATOR . $file)) { $file = realpath(getcwd() . DIRECTORY_SEPARATOR . $file); } elseif (file_exists($file)) { $file = realpath($file); } $cmd = $this->_preparePhpBin($this->_php, $file, $ini_settings); if (isset($this->_logger)) { $this->_logger->log(2, 'Running command "' . $cmd . '"'); } $savedir = getcwd(); // in case the test moves us around chdir(dirname($file)); echo `$cmd`; chdir($savedir); return 'PASSED'; // we have no way of knowing this information so assume passing } /** * Runs an individual test case. * * @param string The filename of the test * @param array|string INI settings to be applied to the test run * @param integer Number what the current running test is of the * whole test suite being runned. * * @return string|object Returns PASSED, WARNED, FAILED depending on how the * test came out. * PEAR Error when the tester it self fails */ function run($file, $ini_settings = array(), $test_number = 1) { $this->_restorePHPBinary(); if (empty($this->_options['cgi'])) { // try to see if php-cgi is in the path $res = $this->system_with_timeout('php-cgi -v'); if (false !== $res && !(is_array($res) && in_array($res[0], array(-1, 127)))) { $this->_options['cgi'] = 'php-cgi'; } } if (1 < $len = strlen($this->tests_count)) { $test_number = str_pad($test_number, $len, ' ', STR_PAD_LEFT); $test_nr = "[$test_number/$this->tests_count] "; } else { $test_nr = ''; } $file = realpath($file); $section_text = $this->_readFile($file); if (PEAR::isError($section_text)) { return $section_text; } if (isset($section_text['POST_RAW']) && isset($section_text['UPLOAD'])) { return PEAR::raiseError("Cannot contain both POST_RAW and UPLOAD in test file: $file"); } $cwd = getcwd(); $pass_options = ''; if (!empty($this->_options['ini'])) { $pass_options = $this->_options['ini']; } if (is_string($ini_settings)) { $ini_settings = $this->iniString2array($ini_settings); } $ini_settings = $this->settings2array($this->ini_overwrites, $ini_settings); if ($section_text['INI']) { if (strpos($section_text['INI'], '{PWD}') !== false) { $section_text['INI'] = str_replace('{PWD}', dirname($file), $section_text['INI']); } $ini = preg_split( "/[\n\r]+/", $section_text['INI']); $ini_settings = $this->settings2array($ini, $ini_settings); } $ini_settings = $this->settings2params($ini_settings); $shortname = str_replace($cwd . DIRECTORY_SEPARATOR, '', $file); $tested = trim($section_text['TEST']); $tested.= !isset($this->_options['simple']) ? "[$shortname]" : ' '; if (!empty($section_text['POST']) || !empty($section_text['POST_RAW']) || !empty($section_text['UPLOAD']) || !empty($section_text['GET']) || !empty($section_text['COOKIE']) || !empty($section_text['EXPECTHEADERS'])) { if (empty($this->_options['cgi'])) { if (!isset($this->_options['quiet'])) { $this->_logger->log(0, "SKIP $test_nr$tested (reason: --cgi option needed for this test, type 'pear help run-tests')"); } if (isset($this->_options['tapoutput'])) { return array('ok', ' # skip --cgi option needed for this test, "pear help run-tests" for info'); } return 'SKIPPED'; } $this->_savePHPBinary(); $this->_php = $this->_options['cgi']; } $temp_dir = realpath(dirname($file)); $main_file_name = basename($file, 'phpt'); $diff_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'diff'; $log_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'log'; $exp_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'exp'; $output_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'out'; $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'mem'; $temp_file = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'php'; $temp_skipif = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'skip.php'; $temp_clean = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'clean.php'; $tmp_post = $temp_dir . DIRECTORY_SEPARATOR . uniqid('phpt.'); // unlink old test results $this->_cleanupOldFiles($file); // Check if test should be skipped. $res = $this->_runSkipIf($section_text, $temp_skipif, $tested, $ini_settings); if ($res == 'SKIPPED' || count($res) != 2) { return $res; } $info = $res['info']; $warn = $res['warn']; // We've satisfied the preconditions - run the test! if (isset($this->_options['coverage']) && $this->xdebug_loaded) { $xdebug_file = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'xdebug'; $text = "\n" . 'function coverage_shutdown() {' . "\n" . ' $xdebug = var_export(xdebug_get_code_coverage(), true);'; if (!function_exists('file_put_contents')) { $text .= "\n" . ' $fh = fopen(\'' . $xdebug_file . '\', "wb");' . "\n" . ' if ($fh !== false) {' . "\n" . ' fwrite($fh, $xdebug);' . "\n" . ' fclose($fh);' . "\n" . ' }'; } else { $text .= "\n" . ' file_put_contents(\'' . $xdebug_file . '\', $xdebug);'; } // Workaround for http://pear.php.net/bugs/bug.php?id=17292 $lines = explode("\n", $section_text['FILE']); $numLines = count($lines); $namespace = ''; $coverage_shutdown = 'coverage_shutdown'; if ( substr($lines[0], 0, 2) == 'save_text($temp_file, "save_text($temp_file, $section_text['FILE']); } $args = $section_text['ARGS'] ? ' -- '.$section_text['ARGS'] : ''; $cmd = $this->_preparePhpBin($this->_php, $temp_file, $ini_settings); $cmd.= "$args 2>&1"; if (isset($this->_logger)) { $this->_logger->log(2, 'Running command "' . $cmd . '"'); } // Reset environment from any previous test. $env = $this->_resetEnv($section_text, $temp_file); $section_text = $this->_processUpload($section_text, $file); if (PEAR::isError($section_text)) { return $section_text; } if (array_key_exists('POST_RAW', $section_text) && !empty($section_text['POST_RAW'])) { $post = trim($section_text['POST_RAW']); $raw_lines = explode("\n", $post); $request = ''; $started = false; foreach ($raw_lines as $i => $line) { if (empty($env['CONTENT_TYPE']) && preg_match('/^Content-Type:(.*)/i', $line, $res)) { $env['CONTENT_TYPE'] = trim(str_replace("\r", '', $res[1])); continue; } if ($started) { $request .= "\n"; } $started = true; $request .= $line; } $env['CONTENT_LENGTH'] = strlen($request); $env['REQUEST_METHOD'] = 'POST'; $this->save_text($tmp_post, $request); $cmd = "$this->_php$pass_options$ini_settings \"$temp_file\" 2>&1 < $tmp_post"; } elseif (array_key_exists('POST', $section_text) && !empty($section_text['POST'])) { $post = trim($section_text['POST']); $this->save_text($tmp_post, $post); $content_length = strlen($post); $env['REQUEST_METHOD'] = 'POST'; $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; $env['CONTENT_LENGTH'] = $content_length; $cmd = "$this->_php$pass_options$ini_settings \"$temp_file\" 2>&1 < $tmp_post"; } else { $env['REQUEST_METHOD'] = 'GET'; $env['CONTENT_TYPE'] = ''; $env['CONTENT_LENGTH'] = ''; } if (OS_WINDOWS && isset($section_text['RETURNS'])) { ob_start(); system($cmd, $return_value); $out = ob_get_contents(); ob_end_clean(); $section_text['RETURNS'] = (int) trim($section_text['RETURNS']); $returnfail = ($return_value != $section_text['RETURNS']); } else { $returnfail = false; $stdin = isset($section_text['STDIN']) ? $section_text['STDIN'] : null; $out = $this->system_with_timeout($cmd, $env, $stdin); $return_value = $out[0]; $out = $out[1]; } $output = preg_replace('/\r\n/', "\n", trim($out)); if (isset($tmp_post) && realpath($tmp_post) && file_exists($tmp_post)) { @unlink(realpath($tmp_post)); } chdir($cwd); // in case the test moves us around /* when using CGI, strip the headers from the output */ $output = $this->_stripHeadersCGI($output); if (isset($section_text['EXPECTHEADERS'])) { $testheaders = $this->_processHeaders($section_text['EXPECTHEADERS']); $missing = array_diff_assoc($testheaders, $this->_headers); $changed = ''; foreach ($missing as $header => $value) { if (isset($this->_headers[$header])) { $changed .= "-$header: $value\n+$header: "; $changed .= $this->_headers[$header]; } else { $changed .= "-$header: $value\n"; } } if ($missing) { // tack on failed headers to output: $output .= "\n====EXPECTHEADERS FAILURE====:\n$changed"; } } $this->_testCleanup($section_text, $temp_clean); // Does the output match what is expected? do { if (isset($section_text['EXPECTF']) || isset($section_text['EXPECTREGEX'])) { if (isset($section_text['EXPECTF'])) { $wanted = trim($section_text['EXPECTF']); } else { $wanted = trim($section_text['EXPECTREGEX']); } $wanted_re = preg_replace('/\r\n/', "\n", $wanted); if (isset($section_text['EXPECTF'])) { $wanted_re = preg_quote($wanted_re, '/'); // Stick to basics $wanted_re = str_replace("%s", ".+?", $wanted_re); //not greedy $wanted_re = str_replace("%S", ".*?", $wanted_re); //not greedy $wanted_re = str_replace("%i", "[+\-]?[0-9]+", $wanted_re); $wanted_re = str_replace("%d", "[0-9]+", $wanted_re); $wanted_re = str_replace("%x", "[0-9a-fA-F]+", $wanted_re); $wanted_re = str_replace("%f", "[+\-]?\.?[0-9]+\.?[0-9]*(E-?[0-9]+)?", $wanted_re); $wanted_re = str_replace("%c", ".", $wanted_re); // %f allows two points "-.0.0" but that is the best *simple* expression } /* DEBUG YOUR REGEX HERE var_dump($wanted_re); print(str_repeat('=', 80) . "\n"); var_dump($output); */ if (!$returnfail && preg_match("/^$wanted_re\$/s", $output)) { if (file_exists($temp_file)) { unlink($temp_file); } if (array_key_exists('FAIL', $section_text)) { break; } if (!isset($this->_options['quiet'])) { $this->_logger->log(0, "PASS $test_nr$tested$info"); } if (isset($this->_options['tapoutput'])) { return array('ok', ' - ' . $tested); } return 'PASSED'; } } else { if (isset($section_text['EXPECTFILE'])) { $f = $temp_dir . '/' . trim($section_text['EXPECTFILE']); if (!($fp = @fopen($f, 'rb'))) { return PEAR::raiseError('--EXPECTFILE-- section file ' . $f . ' not found'); } fclose($fp); $section_text['EXPECT'] = file_get_contents($f); } if (isset($section_text['EXPECT'])) { $wanted = preg_replace('/\r\n/', "\n", trim($section_text['EXPECT'])); } else { $wanted = ''; } // compare and leave on success if (!$returnfail && 0 == strcmp($output, $wanted)) { if (file_exists($temp_file)) { unlink($temp_file); } if (array_key_exists('FAIL', $section_text)) { break; } if (!isset($this->_options['quiet'])) { $this->_logger->log(0, "PASS $test_nr$tested$info"); } if (isset($this->_options['tapoutput'])) { return array('ok', ' - ' . $tested); } return 'PASSED'; } } } while (false); if (array_key_exists('FAIL', $section_text)) { // we expect a particular failure // this is only used for testing PEAR_RunTest $expectf = isset($section_text['EXPECTF']) ? $wanted_re : null; $faildiff = $this->generate_diff($wanted, $output, null, $expectf); $faildiff = preg_replace('/\r/', '', $faildiff); $wanted = preg_replace('/\r/', '', trim($section_text['FAIL'])); if ($faildiff == $wanted) { if (!isset($this->_options['quiet'])) { $this->_logger->log(0, "PASS $test_nr$tested$info"); } if (isset($this->_options['tapoutput'])) { return array('ok', ' - ' . $tested); } return 'PASSED'; } unset($section_text['EXPECTF']); $output = $faildiff; if (isset($section_text['RETURNS'])) { return PEAR::raiseError('Cannot have both RETURNS and FAIL in the same test: ' . $file); } } // Test failed so we need to report details. $txt = $warn ? 'WARN ' : 'FAIL '; $this->_logger->log(0, $txt . $test_nr . $tested . $info); // write .exp $res = $this->_writeLog($exp_filename, $wanted); if (PEAR::isError($res)) { return $res; } // write .out $res = $this->_writeLog($output_filename, $output); if (PEAR::isError($res)) { return $res; } // write .diff $returns = isset($section_text['RETURNS']) ? array(trim($section_text['RETURNS']), $return_value) : null; $expectf = isset($section_text['EXPECTF']) ? $wanted_re : null; $data = $this->generate_diff($wanted, $output, $returns, $expectf); $res = $this->_writeLog($diff_filename, $data); if (isset($this->_options['showdiff'])) { $this->_logger->log(0, "========DIFF========"); $this->_logger->log(0, $data); $this->_logger->log(0, "========DONE========"); } if (PEAR::isError($res)) { return $res; } // write .log $data = " ---- EXPECTED OUTPUT $wanted ---- ACTUAL OUTPUT $output ---- FAILED "; if ($returnfail) { $data .= " ---- EXPECTED RETURN $section_text[RETURNS] ---- ACTUAL RETURN $return_value "; } $res = $this->_writeLog($log_filename, $data); if (PEAR::isError($res)) { return $res; } if (isset($this->_options['tapoutput'])) { $wanted = explode("\n", $wanted); $wanted = "# Expected output:\n#\n#" . implode("\n#", $wanted); $output = explode("\n", $output); $output = "#\n#\n# Actual output:\n#\n#" . implode("\n#", $output); return array($wanted . $output . 'not ok', ' - ' . $tested); } return $warn ? 'WARNED' : 'FAILED'; } function generate_diff($wanted, $output, $rvalue, $wanted_re) { $w = explode("\n", $wanted); $o = explode("\n", $output); $wr = explode("\n", $wanted_re); $w1 = array_diff_assoc($w, $o); $o1 = array_diff_assoc($o, $w); $o2 = $w2 = array(); foreach ($w1 as $idx => $val) { if (!$wanted_re || !isset($wr[$idx]) || !isset($o1[$idx]) || !preg_match('/^' . $wr[$idx] . '\\z/', $o1[$idx])) { $w2[sprintf("%03d<", $idx)] = sprintf("%03d- ", $idx + 1) . $val; } } foreach ($o1 as $idx => $val) { if (!$wanted_re || !isset($wr[$idx]) || !preg_match('/^' . $wr[$idx] . '\\z/', $val)) { $o2[sprintf("%03d>", $idx)] = sprintf("%03d+ ", $idx + 1) . $val; } } $diff = array_merge($w2, $o2); ksort($diff); $extra = $rvalue ? "##EXPECTED: $rvalue[0]\r\n##RETURNED: $rvalue[1]" : ''; return implode("\r\n", $diff) . $extra; } // Write the given text to a temporary file, and return the filename. function save_text($filename, $text) { if (!$fp = fopen($filename, 'w')) { return PEAR::raiseError("Cannot open file '" . $filename . "' (save_text)"); } fwrite($fp, $text); fclose($fp); if (1 < DETAILED) echo " FILE $filename {{{ $text }}} "; } function _cleanupOldFiles($file) { $temp_dir = realpath(dirname($file)); $mainFileName = basename($file, 'phpt'); $diff_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'diff'; $log_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'log'; $exp_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'exp'; $output_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'out'; $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'mem'; $temp_file = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'php'; $temp_skipif = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'skip.php'; $temp_clean = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'clean.php'; $tmp_post = $temp_dir . DIRECTORY_SEPARATOR . uniqid('phpt.'); // unlink old test results @unlink($diff_filename); @unlink($log_filename); @unlink($exp_filename); @unlink($output_filename); @unlink($memcheck_filename); @unlink($temp_file); @unlink($temp_skipif); @unlink($tmp_post); @unlink($temp_clean); } function _runSkipIf($section_text, $temp_skipif, $tested, $ini_settings) { $info = ''; $warn = false; if (array_key_exists('SKIPIF', $section_text) && trim($section_text['SKIPIF'])) { $this->save_text($temp_skipif, $section_text['SKIPIF']); $output = $this->system_with_timeout("$this->_php$ini_settings -f \"$temp_skipif\""); $output = $output[1]; $loutput = ltrim($output); unlink($temp_skipif); if (!strncasecmp('skip', $loutput, 4)) { $skipreason = "SKIP $tested"; if (preg_match('/^\s*skip\s*(.+)\s*/i', $output, $m)) { $skipreason .= '(reason: ' . $m[1] . ')'; } if (!isset($this->_options['quiet'])) { $this->_logger->log(0, $skipreason); } if (isset($this->_options['tapoutput'])) { return array('ok', ' # skip ' . $reason); } return 'SKIPPED'; } if (!strncasecmp('info', $loutput, 4) && preg_match('/^\s*info\s*(.+)\s*/i', $output, $m)) { $info = " (info: $m[1])"; } if (!strncasecmp('warn', $loutput, 4) && preg_match('/^\s*warn\s*(.+)\s*/i', $output, $m)) { $warn = true; /* only if there is a reason */ $info = " (warn: $m[1])"; } } return array('warn' => $warn, 'info' => $info); } function _stripHeadersCGI($output) { $this->headers = array(); if (!empty($this->_options['cgi']) && $this->_php == $this->_options['cgi'] && preg_match("/^(.*?)(?:\n\n(.*)|\\z)/s", $output, $match)) { $output = isset($match[2]) ? trim($match[2]) : ''; $this->_headers = $this->_processHeaders($match[1]); } return $output; } /** * Return an array that can be used with array_diff() to compare headers * * @param string $text */ function _processHeaders($text) { $headers = array(); $rh = preg_split("/[\n\r]+/", $text); foreach ($rh as $line) { if (strpos($line, ':')!== false) { $line = explode(':', $line, 2); $headers[trim($line[0])] = trim($line[1]); } } return $headers; } function _readFile($file) { // Load the sections of the test file. $section_text = array( 'TEST' => '(unnamed test)', 'SKIPIF' => '', 'GET' => '', 'COOKIE' => '', 'POST' => '', 'ARGS' => '', 'INI' => '', 'CLEAN' => '', ); if (!is_file($file) || !$fp = fopen($file, "r")) { return PEAR::raiseError("Cannot open test file: $file"); } $section = ''; while (!feof($fp)) { $line = fgets($fp); // Match the beginning of a section. if (preg_match('/^--([_A-Z]+)--/', $line, $r)) { $section = $r[1]; $section_text[$section] = ''; continue; } elseif (empty($section)) { fclose($fp); return PEAR::raiseError("Invalid sections formats in test file: $file"); } // Add to the section text. $section_text[$section] .= $line; } fclose($fp); return $section_text; } function _writeLog($logname, $data) { if (!$log = fopen($logname, 'w')) { return PEAR::raiseError("Cannot create test log - $logname"); } fwrite($log, $data); fclose($log); } function _resetEnv($section_text, $temp_file) { $env = $_ENV; $env['REDIRECT_STATUS'] = ''; $env['QUERY_STRING'] = ''; $env['PATH_TRANSLATED'] = ''; $env['SCRIPT_FILENAME'] = ''; $env['REQUEST_METHOD'] = ''; $env['CONTENT_TYPE'] = ''; $env['CONTENT_LENGTH'] = ''; if (!empty($section_text['ENV'])) { if (strpos($section_text['ENV'], '{PWD}') !== false) { $section_text['ENV'] = str_replace('{PWD}', dirname($temp_file), $section_text['ENV']); } foreach (explode("\n", trim($section_text['ENV'])) as $e) { $e = explode('=', trim($e), 2); if (!empty($e[0]) && isset($e[1])) { $env[$e[0]] = $e[1]; } } } if (array_key_exists('GET', $section_text)) { $env['QUERY_STRING'] = trim($section_text['GET']); } else { $env['QUERY_STRING'] = ''; } if (array_key_exists('COOKIE', $section_text)) { $env['HTTP_COOKIE'] = trim($section_text['COOKIE']); } else { $env['HTTP_COOKIE'] = ''; } $env['REDIRECT_STATUS'] = '1'; $env['PATH_TRANSLATED'] = $temp_file; $env['SCRIPT_FILENAME'] = $temp_file; return $env; } function _processUpload($section_text, $file) { if (array_key_exists('UPLOAD', $section_text) && !empty($section_text['UPLOAD'])) { $upload_files = trim($section_text['UPLOAD']); $upload_files = explode("\n", $upload_files); $request = "Content-Type: multipart/form-data; boundary=---------------------------20896060251896012921717172737\n" . "-----------------------------20896060251896012921717172737\n"; foreach ($upload_files as $fileinfo) { $fileinfo = explode('=', $fileinfo); if (count($fileinfo) != 2) { return PEAR::raiseError("Invalid UPLOAD section in test file: $file"); } if (!realpath(dirname($file) . '/' . $fileinfo[1])) { return PEAR::raiseError("File for upload does not exist: $fileinfo[1] " . "in test file: $file"); } $file_contents = file_get_contents(dirname($file) . '/' . $fileinfo[1]); $fileinfo[1] = basename($fileinfo[1]); $request .= "Content-Disposition: form-data; name=\"$fileinfo[0]\"; filename=\"$fileinfo[1]\"\n"; $request .= "Content-Type: text/plain\n\n"; $request .= $file_contents . "\n" . "-----------------------------20896060251896012921717172737\n"; } if (array_key_exists('POST', $section_text) && !empty($section_text['POST'])) { // encode POST raw $post = trim($section_text['POST']); $post = explode('&', $post); foreach ($post as $i => $post_info) { $post_info = explode('=', $post_info); if (count($post_info) != 2) { return PEAR::raiseError("Invalid POST data in test file: $file"); } $post_info[0] = rawurldecode($post_info[0]); $post_info[1] = rawurldecode($post_info[1]); $post[$i] = $post_info; } foreach ($post as $post_info) { $request .= "Content-Disposition: form-data; name=\"$post_info[0]\"\n\n"; $request .= $post_info[1] . "\n" . "-----------------------------20896060251896012921717172737\n"; } unset($section_text['POST']); } $section_text['POST_RAW'] = $request; } return $section_text; } function _testCleanup($section_text, $temp_clean) { if ($section_text['CLEAN']) { $this->_restorePHPBinary(); // perform test cleanup $this->save_text($temp_clean, $section_text['CLEAN']); $output = $this->system_with_timeout("$this->_php $temp_clean 2>&1"); if (strlen($output[1])) { echo "BORKED --CLEAN-- section! output:\n", $output[1]; } if (file_exists($temp_clean)) { unlink($temp_clean); } } } function _savePHPBinary() { $this->_savephp = $this->_php; } function _restorePHPBinary() { if (isset($this->_savephp)) { $this->_php = $this->_savephp; unset($this->_savephp); } } } PK=1]ݻƬ pear/PEAR/ChannelFile/Parser.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * base xml parser class */ require_once 'PEAR/XMLParser.php'; require_once 'PEAR/ChannelFile.php'; /** * Parser for channel.xml * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_ChannelFile_Parser extends PEAR_XMLParser { var $_config; var $_logger; var $_registry; function setConfig(&$c) { $this->_config = &$c; $this->_registry = &$c->getRegistry(); } function setLogger(&$l) { $this->_logger = &$l; } function parse($data, $file) { if (PEAR::isError($err = parent::parse($data, $file))) { return $err; } $ret = new PEAR_ChannelFile; $ret->setConfig($this->_config); if (isset($this->_logger)) { $ret->setLogger($this->_logger); } $ret->fromArray($this->_unserializedData); // make sure the filelist is in the easy to read format needed $ret->flattenFilelist(); $ret->setPackagefile($file, $archive); return $ret; } }PK=1]d֣Spear/PEAR/Command/Pickle.xmlnu[ Build PECL Package doPackage pi Z Do not gzip the package file n Print the name of the packaged file. [descfile] Creates a PECL package from its package2.xml file. An automatic conversion will be made to a package.xml 1.0 and written out to disk in the current directory as "package.xml". Note that only simple package.xml 2.0 will be converted. package.xml 2.0 with: - dependency types other than required/optional PECL package/ext/php/pearinstaller - more than one extsrcrelease or zendextsrcrelease - zendextbinrelease, extbinrelease, phprelease, or bundle release type - dependency groups - ignore tags in release filelist - tasks other than replace - custom roles will cause pickle to fail, and output an error message. If your package2.xml uses any of these features, you are best off using PEAR_PackageFileManager to generate both package.xml. PK=1]RՂՂpear/PEAR/Command/Channels.phpnu[ * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * base class */ require_once 'PEAR/Command/Common.php'; define('PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS', -500); /** * PEAR commands for managing channels. * * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_Command_Channels extends PEAR_Command_Common { var $commands = array( 'list-channels' => array( 'summary' => 'List Available Channels', 'function' => 'doList', 'shortcut' => 'lc', 'options' => array(), 'doc' => ' List all available channels for installation. ', ), 'update-channels' => array( 'summary' => 'Update the Channel List', 'function' => 'doUpdateAll', 'shortcut' => 'uc', 'options' => array(), 'doc' => ' List all installed packages in all channels. ' ), 'channel-delete' => array( 'summary' => 'Remove a Channel From the List', 'function' => 'doDelete', 'shortcut' => 'cde', 'options' => array(), 'doc' => ' Delete a channel from the registry. You may not remove any channel that has installed packages. ' ), 'channel-add' => array( 'summary' => 'Add a Channel', 'function' => 'doAdd', 'shortcut' => 'ca', 'options' => array(), 'doc' => ' Add a private channel to the channel list. Note that all public channels should be synced using "update-channels". Parameter may be either a local file or remote URL to a channel.xml. ' ), 'channel-update' => array( 'summary' => 'Update an Existing Channel', 'function' => 'doUpdate', 'shortcut' => 'cu', 'options' => array( 'force' => array( 'shortopt' => 'f', 'doc' => 'will force download of new channel.xml if an existing channel name is used', ), 'channel' => array( 'shortopt' => 'c', 'arg' => 'CHANNEL', 'doc' => 'will force download of new channel.xml if an existing channel name is used', ), ), 'doc' => '[|] Update a channel in the channel list directly. Note that all public channels can be synced using "update-channels". Parameter may be a local or remote channel.xml, or the name of an existing channel. ' ), 'channel-info' => array( 'summary' => 'Retrieve Information on a Channel', 'function' => 'doInfo', 'shortcut' => 'ci', 'options' => array(), 'doc' => ' List the files in an installed package. ' ), 'channel-alias' => array( 'summary' => 'Specify an alias to a channel name', 'function' => 'doAlias', 'shortcut' => 'cha', 'options' => array(), 'doc' => ' Specify a specific alias to use for a channel name. The alias may not be an existing channel name or alias. ' ), 'channel-discover' => array( 'summary' => 'Initialize a Channel from its server', 'function' => 'doDiscover', 'shortcut' => 'di', 'options' => array(), 'doc' => '[|] Initialize a channel from its server and create a local channel.xml. If is in the format ":@" then and will be set as the login username/password for . Use caution when passing the username/password in this way, as it may allow other users on your computer to briefly view your username/ password via the system\'s process list. ' ), 'channel-login' => array( 'summary' => 'Connects and authenticates to remote channel server', 'shortcut' => 'cli', 'function' => 'doLogin', 'options' => array(), 'doc' => ' Log in to a remote channel server. If is not supplied, the default channel is used. To use remote functions in the installer that require any kind of privileges, you need to log in first. The username and password you enter here will be stored in your per-user PEAR configuration (~/.pearrc on Unix-like systems). After logging in, your username and password will be sent along in subsequent operations on the remote server.', ), 'channel-logout' => array( 'summary' => 'Logs out from the remote channel server', 'shortcut' => 'clo', 'function' => 'doLogout', 'options' => array(), 'doc' => ' Logs out from a remote channel server. If is not supplied, the default channel is used. This command does not actually connect to the remote server, it only deletes the stored username and password from your user configuration.', ), ); /** * PEAR_Command_Registry constructor. * * @access public */ function __construct(&$ui, &$config) { parent::__construct($ui, $config); } function _sortChannels($a, $b) { return strnatcasecmp($a->getName(), $b->getName()); } function doList($command, $options, $params) { $reg = &$this->config->getRegistry(); $registered = $reg->getChannels(); usort($registered, array(&$this, '_sortchannels')); $i = $j = 0; $data = array( 'caption' => 'Registered Channels:', 'border' => true, 'headline' => array('Channel', 'Alias', 'Summary') ); foreach ($registered as $channel) { $data['data'][] = array($channel->getName(), $channel->getAlias(), $channel->getSummary()); } if (count($registered) === 0) { $data = '(no registered channels)'; } $this->ui->outputData($data, $command); return true; } function doUpdateAll($command, $options, $params) { $reg = &$this->config->getRegistry(); $channels = $reg->getChannels(); $success = true; foreach ($channels as $channel) { if ($channel->getName() != '__uri') { PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $err = $this->doUpdate('channel-update', $options, array($channel->getName())); if (PEAR::isError($err)) { $this->ui->outputData($err->getMessage(), $command); $success = false; } else { $success &= $err; } } } return $success; } function doInfo($command, $options, $params) { if (count($params) !== 1) { return $this->raiseError("No channel specified"); } $reg = &$this->config->getRegistry(); $channel = strtolower($params[0]); if ($reg->channelExists($channel)) { $chan = $reg->getChannel($channel); if (PEAR::isError($chan)) { return $this->raiseError($chan); } } else { if (strpos($channel, '://')) { $downloader = &$this->getDownloader(); $tmpdir = $this->config->get('temp_dir'); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $loc = $downloader->downloadHttp($channel, $this->ui, $tmpdir); PEAR::staticPopErrorHandling(); if (PEAR::isError($loc)) { return $this->raiseError('Cannot open "' . $channel . '" (' . $loc->getMessage() . ')'); } else { $contents = implode('', file($loc)); } } else { if (!file_exists($params[0])) { return $this->raiseError('Unknown channel "' . $channel . '"'); } $fp = fopen($params[0], 'r'); if (!$fp) { return $this->raiseError('Cannot open "' . $params[0] . '"'); } $contents = ''; while (!feof($fp)) { $contents .= fread($fp, 1024); } fclose($fp); } if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } $chan = new PEAR_ChannelFile; $chan->fromXmlString($contents); $chan->validate(); if ($errs = $chan->getErrors(true)) { foreach ($errs as $err) { $this->ui->outputData($err['level'] . ': ' . $err['message']); } return $this->raiseError('Channel file "' . $params[0] . '" is not valid'); } } if (!$chan) { return $this->raiseError('Serious error: Channel "' . $params[0] . '" has a corrupted registry entry'); } $channel = $chan->getName(); $caption = 'Channel ' . $channel . ' Information:'; $data1 = array( 'caption' => $caption, 'border' => true); $data1['data']['server'] = array('Name and Server', $chan->getName()); if ($chan->getAlias() != $chan->getName()) { $data1['data']['alias'] = array('Alias', $chan->getAlias()); } $data1['data']['summary'] = array('Summary', $chan->getSummary()); $validate = $chan->getValidationPackage(); $data1['data']['vpackage'] = array('Validation Package Name', $validate['_content']); $data1['data']['vpackageversion'] = array('Validation Package Version', $validate['attribs']['version']); $d = array(); $d['main'] = $data1; $data['data'] = array(); $data['caption'] = 'Server Capabilities'; $data['headline'] = array('Type', 'Version/REST type', 'Function Name/REST base'); if ($chan->supportsREST()) { if ($chan->supportsREST()) { $funcs = $chan->getFunctions('rest'); if (!isset($funcs[0])) { $funcs = array($funcs); } foreach ($funcs as $protocol) { $data['data'][] = array('rest', $protocol['attribs']['type'], $protocol['_content']); } } } else { $data['data'][] = array('No supported protocols'); } $d['protocols'] = $data; $data['data'] = array(); $mirrors = $chan->getMirrors(); if ($mirrors) { $data['caption'] = 'Channel ' . $channel . ' Mirrors:'; unset($data['headline']); foreach ($mirrors as $mirror) { $data['data'][] = array($mirror['attribs']['host']); $d['mirrors'] = $data; } foreach ($mirrors as $i => $mirror) { $data['data'] = array(); $data['caption'] = 'Mirror ' . $mirror['attribs']['host'] . ' Capabilities'; $data['headline'] = array('Type', 'Version/REST type', 'Function Name/REST base'); if ($chan->supportsREST($mirror['attribs']['host'])) { if ($chan->supportsREST($mirror['attribs']['host'])) { $funcs = $chan->getFunctions('rest', $mirror['attribs']['host']); if (!isset($funcs[0])) { $funcs = array($funcs); } foreach ($funcs as $protocol) { $data['data'][] = array('rest', $protocol['attribs']['type'], $protocol['_content']); } } } else { $data['data'][] = array('No supported protocols'); } $d['mirrorprotocols' . $i] = $data; } } $this->ui->outputData($d, 'channel-info'); } // }}} function doDelete($command, $options, $params) { if (count($params) !== 1) { return $this->raiseError('channel-delete: no channel specified'); } $reg = &$this->config->getRegistry(); if (!$reg->channelExists($params[0])) { return $this->raiseError('channel-delete: channel "' . $params[0] . '" does not exist'); } $channel = $reg->channelName($params[0]); if ($channel == 'pear.php.net') { return $this->raiseError('Cannot delete the pear.php.net channel'); } if ($channel == 'pecl.php.net') { return $this->raiseError('Cannot delete the pecl.php.net channel'); } if ($channel == 'doc.php.net') { return $this->raiseError('Cannot delete the doc.php.net channel'); } if ($channel == '__uri') { return $this->raiseError('Cannot delete the __uri pseudo-channel'); } if (PEAR::isError($err = $reg->listPackages($channel))) { return $err; } if (count($err)) { return $this->raiseError('Channel "' . $channel . '" has installed packages, cannot delete'); } if (!$reg->deleteChannel($channel)) { return $this->raiseError('Channel "' . $channel . '" deletion failed'); } else { $this->config->deleteChannel($channel); $this->ui->outputData('Channel "' . $channel . '" deleted', $command); } } function doAdd($command, $options, $params) { if (count($params) !== 1) { return $this->raiseError('channel-add: no channel file specified'); } if (strpos($params[0], '://')) { $downloader = &$this->getDownloader(); $tmpdir = $this->config->get('temp_dir'); if (!file_exists($tmpdir)) { require_once 'System.php'; PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $err = System::mkdir(array('-p', $tmpdir)); PEAR::staticPopErrorHandling(); if (PEAR::isError($err)) { return $this->raiseError('channel-add: temp_dir does not exist: "' . $tmpdir . '" - You can change this location with "pear config-set temp_dir"'); } } if (!is_writable($tmpdir)) { return $this->raiseError('channel-add: temp_dir is not writable: "' . $tmpdir . '" - You can change this location with "pear config-set temp_dir"'); } PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $loc = $downloader->downloadHttp($params[0], $this->ui, $tmpdir, null, false); PEAR::staticPopErrorHandling(); if (PEAR::isError($loc)) { return $this->raiseError('channel-add: Cannot open "' . $params[0] . '" (' . $loc->getMessage() . ')'); } list($loc, $lastmodified) = $loc; $contents = implode('', file($loc)); } else { $lastmodified = $fp = false; if (file_exists($params[0])) { $fp = fopen($params[0], 'r'); } if (!$fp) { return $this->raiseError('channel-add: cannot open "' . $params[0] . '"'); } $contents = ''; while (!feof($fp)) { $contents .= fread($fp, 1024); } fclose($fp); } if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } $channel = new PEAR_ChannelFile; PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $result = $channel->fromXmlString($contents); PEAR::staticPopErrorHandling(); if (!$result) { $exit = false; if (count($errors = $channel->getErrors(true))) { foreach ($errors as $error) { $this->ui->outputData(ucfirst($error['level'] . ': ' . $error['message'])); if (!$exit) { $exit = $error['level'] == 'error' ? true : false; } } if ($exit) { return $this->raiseError('channel-add: invalid channel.xml file'); } } } $reg = &$this->config->getRegistry(); if ($reg->channelExists($channel->getName())) { return $this->raiseError('channel-add: Channel "' . $channel->getName() . '" exists, use channel-update to update entry', PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS); } $ret = $reg->addChannel($channel, $lastmodified); if (PEAR::isError($ret)) { return $ret; } if (!$ret) { return $this->raiseError('channel-add: adding Channel "' . $channel->getName() . '" to registry failed'); } $this->config->setChannels($reg->listChannels()); $this->config->writeConfigFile(); $this->ui->outputData('Adding Channel "' . $channel->getName() . '" succeeded', $command); } function doUpdate($command, $options, $params) { if (count($params) !== 1) { return $this->raiseError("No channel file specified"); } $tmpdir = $this->config->get('temp_dir'); if (!file_exists($tmpdir)) { require_once 'System.php'; PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $err = System::mkdir(array('-p', $tmpdir)); PEAR::staticPopErrorHandling(); if (PEAR::isError($err)) { return $this->raiseError('channel-add: temp_dir does not exist: "' . $tmpdir . '" - You can change this location with "pear config-set temp_dir"'); } } if (!is_writable($tmpdir)) { return $this->raiseError('channel-add: temp_dir is not writable: "' . $tmpdir . '" - You can change this location with "pear config-set temp_dir"'); } $reg = &$this->config->getRegistry(); $lastmodified = false; if ((!file_exists($params[0]) || is_dir($params[0])) && $reg->channelExists(strtolower($params[0]))) { $c = $reg->getChannel(strtolower($params[0])); if (PEAR::isError($c)) { return $this->raiseError($c); } $this->ui->outputData("Updating channel \"$params[0]\"", $command); $dl = &$this->getDownloader(array()); // if force is specified, use a timestamp of "1" to force retrieval $lastmodified = isset($options['force']) ? false : $c->lastModified(); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $contents = $dl->downloadHttp('https://' . $c->getName() . '/channel.xml', $this->ui, $tmpdir, null, $lastmodified); PEAR::staticPopErrorHandling(); if (PEAR::isError($contents)) { // Attempt to fall back to http $this->ui->outputData("Channel \"$params[0]\" is not responding over https://, failed with message: " . $contents->getMessage()); $this->ui->outputData("Trying channel \"$params[0]\" over http:// instead"); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $contents = $dl->downloadHttp('http://' . $c->getName() . '/channel.xml', $this->ui, $tmpdir, null, $lastmodified); PEAR::staticPopErrorHandling(); if (PEAR::isError($contents)) { return $this->raiseError('Cannot retrieve channel.xml for channel "' . $c->getName() . '" (' . $contents->getMessage() . ')'); } } if (is_array($contents)) { list($contents, $lastmodified) = $contents; } if (!$contents) { $this->ui->outputData("Channel \"$params[0]\" is up to date"); return; } $contents = implode('', file($contents)); if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } $channel = new PEAR_ChannelFile; $channel->fromXmlString($contents); if (!$channel->getErrors()) { // security check: is the downloaded file for the channel we got it from? if (strtolower($channel->getName()) != strtolower($c->getName())) { if (!isset($options['force'])) { return $this->raiseError('ERROR: downloaded channel definition file' . ' for channel "' . $channel->getName() . '" from channel "' . strtolower($c->getName()) . '"'); } $this->ui->log(0, 'WARNING: downloaded channel definition file' . ' for channel "' . $channel->getName() . '" from channel "' . strtolower($c->getName()) . '"'); } } } else { if (strpos($params[0], '://')) { $dl = &$this->getDownloader(); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $loc = $dl->downloadHttp($params[0], $this->ui, $tmpdir, null, $lastmodified); PEAR::staticPopErrorHandling(); if (PEAR::isError($loc)) { return $this->raiseError("Cannot open " . $params[0] . ' (' . $loc->getMessage() . ')'); } list($loc, $lastmodified) = $loc; $contents = implode('', file($loc)); } else { $fp = false; if (file_exists($params[0])) { $fp = fopen($params[0], 'r'); } if (!$fp) { return $this->raiseError("Cannot open " . $params[0]); } $contents = ''; while (!feof($fp)) { $contents .= fread($fp, 1024); } fclose($fp); } if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } $channel = new PEAR_ChannelFile; $channel->fromXmlString($contents); } $exit = false; if (count($errors = $channel->getErrors(true))) { foreach ($errors as $error) { $this->ui->outputData(ucfirst($error['level'] . ': ' . $error['message'])); if (!$exit) { $exit = $error['level'] == 'error' ? true : false; } } if ($exit) { return $this->raiseError('Invalid channel.xml file'); } } if (!$reg->channelExists($channel->getName())) { return $this->raiseError('Error: Channel "' . $channel->getName() . '" does not exist, use channel-add to add an entry'); } $ret = $reg->updateChannel($channel, $lastmodified); if (PEAR::isError($ret)) { return $ret; } if (!$ret) { return $this->raiseError('Updating Channel "' . $channel->getName() . '" in registry failed'); } $this->config->setChannels($reg->listChannels()); $this->config->writeConfigFile(); $this->ui->outputData('Update of Channel "' . $channel->getName() . '" succeeded'); } function &getDownloader() { if (!class_exists('PEAR_Downloader')) { require_once 'PEAR/Downloader.php'; } $a = new PEAR_Downloader($this->ui, array(), $this->config); return $a; } function doAlias($command, $options, $params) { if (count($params) === 1) { return $this->raiseError('No channel alias specified'); } if (count($params) !== 2 || (!empty($params[1]) && $params[1][0] == '-')) { return $this->raiseError( 'Invalid format, correct is: channel-alias channel alias'); } $reg = &$this->config->getRegistry(); if (!$reg->channelExists($params[0], true)) { $extra = ''; if ($reg->isAlias($params[0])) { $extra = ' (use "channel-alias ' . $reg->channelName($params[0]) . ' ' . strtolower($params[1]) . '")'; } return $this->raiseError('"' . $params[0] . '" is not a valid channel' . $extra); } if ($reg->isAlias($params[1])) { return $this->raiseError('Channel "' . $reg->channelName($params[1]) . '" is ' . 'already aliased to "' . strtolower($params[1]) . '", cannot re-alias'); } $chan = $reg->getChannel($params[0]); if (PEAR::isError($chan)) { return $this->raiseError('Corrupt registry? Error retrieving channel "' . $params[0] . '" information (' . $chan->getMessage() . ')'); } // make it a local alias if (!$chan->setAlias(strtolower($params[1]), true)) { return $this->raiseError('Alias "' . strtolower($params[1]) . '" is not a valid channel alias'); } $reg->updateChannel($chan); $this->ui->outputData('Channel "' . $chan->getName() . '" aliased successfully to "' . strtolower($params[1]) . '"'); } /** * The channel-discover command * * @param string $command command name * @param array $options option_name => value * @param array $params list of additional parameters. * $params[0] should contain a string with either: * - or * - :@ * @return null|PEAR_Error */ function doDiscover($command, $options, $params) { if (count($params) !== 1) { return $this->raiseError("No channel server specified"); } // Look for the possible input format ":@" if (preg_match('/^(.+):(.+)@(.+)\\z/', $params[0], $matches)) { $username = $matches[1]; $password = $matches[2]; $channel = $matches[3]; } else { $channel = $params[0]; } $reg = &$this->config->getRegistry(); if ($reg->channelExists($channel)) { if (!$reg->isAlias($channel)) { return $this->raiseError("Channel \"$channel\" is already initialized", PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS); } return $this->raiseError("A channel alias named \"$channel\" " . 'already exists, aliasing channel "' . $reg->channelName($channel) . '"'); } $this->pushErrorHandling(PEAR_ERROR_RETURN); $err = $this->doAdd($command, $options, array('http://' . $channel . '/channel.xml')); $this->popErrorHandling(); if (PEAR::isError($err)) { if ($err->getCode() === PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS) { return $this->raiseError("Discovery of channel \"$channel\" failed (" . $err->getMessage() . ')'); } // Attempt fetch via https $this->ui->outputData("Discovering channel $channel over http:// failed with message: " . $err->getMessage()); $this->ui->outputData("Trying to discover channel $channel over https:// instead"); $this->pushErrorHandling(PEAR_ERROR_RETURN); $err = $this->doAdd($command, $options, array('https://' . $channel . '/channel.xml')); $this->popErrorHandling(); if (PEAR::isError($err)) { return $this->raiseError("Discovery of channel \"$channel\" failed (" . $err->getMessage() . ')'); } } // Store username/password if they were given // Arguably we should do a logintest on the channel here, but since // that's awkward on a REST-based channel (even "pear login" doesn't // do it for those), and XML-RPC is deprecated, it's fairly pointless. if (isset($username)) { $this->config->set('username', $username, 'user', $channel); $this->config->set('password', $password, 'user', $channel); $this->config->store(); $this->ui->outputData("Stored login for channel \"$channel\" using username \"$username\"", $command); } $this->ui->outputData("Discovery of channel \"$channel\" succeeded", $command); } /** * Execute the 'login' command. * * @param string $command command name * @param array $options option_name => value * @param array $params list of additional parameters * * @return bool TRUE on success or * a PEAR error on failure * * @access public */ function doLogin($command, $options, $params) { $reg = &$this->config->getRegistry(); // If a parameter is supplied, use that as the channel to log in to $channel = isset($params[0]) ? $params[0] : $this->config->get('default_channel'); $chan = $reg->getChannel($channel); if (PEAR::isError($chan)) { return $this->raiseError($chan); } $server = $this->config->get('preferred_mirror', null, $channel); $username = $this->config->get('username', null, $channel); if (empty($username)) { $username = isset($_ENV['USER']) ? $_ENV['USER'] : null; } $this->ui->outputData("Logging in to $server.", $command); list($username, $password) = $this->ui->userDialog( $command, array('Username', 'Password'), array('text', 'password'), array($username, '') ); $username = trim($username); $password = trim($password); $ourfile = $this->config->getConfFile('user'); if (!$ourfile) { $ourfile = $this->config->getConfFile('system'); } $this->config->set('username', $username, 'user', $channel); $this->config->set('password', $password, 'user', $channel); if ($chan->supportsREST()) { $ok = true; } if ($ok !== true) { return $this->raiseError('Login failed!'); } $this->ui->outputData("Logged in.", $command); // avoid changing any temporary settings changed with -d $ourconfig = new PEAR_Config($ourfile, $ourfile); $ourconfig->set('username', $username, 'user', $channel); $ourconfig->set('password', $password, 'user', $channel); $ourconfig->store(); return true; } /** * Execute the 'logout' command. * * @param string $command command name * @param array $options option_name => value * @param array $params list of additional parameters * * @return bool TRUE on success or * a PEAR error on failure * * @access public */ function doLogout($command, $options, $params) { $reg = &$this->config->getRegistry(); // If a parameter is supplied, use that as the channel to log in to $channel = isset($params[0]) ? $params[0] : $this->config->get('default_channel'); $chan = $reg->getChannel($channel); if (PEAR::isError($chan)) { return $this->raiseError($chan); } $server = $this->config->get('preferred_mirror', null, $channel); $this->ui->outputData("Logging out from $server.", $command); $this->config->remove('username', 'user', $channel); $this->config->remove('password', 'user', $channel); $this->config->store(); return true; } } PK=1] \ pear/PEAR/Command/Remote.xmlnu[ Information About Remote Packages doRemoteInfo ri <package> Get details on a package from the server. List Available Upgrades doListUpgrades lu i output fully channel-aware data, even on failure [preferred_state] List releases on the server of packages you have installed where a newer version is available with the same release state (stable etc.) or the state passed as the second parameter. List Remote Packages doRemoteList rl c specify a channel other than the default channel CHAN Lists the packages available on the configured server along with the latest stable release of each package. Search remote package database doSearch sp c specify a channel other than the default channel CHAN a search packages from all known channels i output fully channel-aware data, even on failure [packagename] [packageinfo] Lists all packages which match the search parameters. The first parameter is a fragment of a packagename. The default channel will be used unless explicitly overridden. The second parameter will be used to match any portion of the summary/description List All Packages doListAll la c specify a channel other than the default channel CHAN i output fully channel-aware data, even on failure Lists the packages available on the configured server along with the latest stable release of each package. Download Package doDownload d Z download an uncompressed (.tar) file <package>... Download package tarballs. The files will be named as suggested by the server, for example if you download the DB package and the latest stable version of DB is 1.6.5, the downloaded file will be DB-1.6.5.tgz. Clear Web Services Cache doClearCache cc Clear the XML-RPC/REST cache. See also the cache_ttl configuration parameter. PK=1]>>pear/PEAR/Command/Pickle.phpnu[ * @copyright 2005-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.1 */ /** * base class */ require_once 'PEAR/Command/Common.php'; /** * PEAR commands for login/logout * * @category pear * @package PEAR * @author Greg Beaver * @copyright 2005-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.1 */ class PEAR_Command_Pickle extends PEAR_Command_Common { var $commands = array( 'pickle' => array( 'summary' => 'Build PECL Package', 'function' => 'doPackage', 'shortcut' => 'pi', 'options' => array( 'nocompress' => array( 'shortopt' => 'Z', 'doc' => 'Do not gzip the package file' ), 'showname' => array( 'shortopt' => 'n', 'doc' => 'Print the name of the packaged file.', ), ), 'doc' => '[descfile] Creates a PECL package from its package2.xml file. An automatic conversion will be made to a package.xml 1.0 and written out to disk in the current directory as "package.xml". Note that only simple package.xml 2.0 will be converted. package.xml 2.0 with: - dependency types other than required/optional PECL package/ext/php/pearinstaller - more than one extsrcrelease or zendextsrcrelease - zendextbinrelease, extbinrelease, phprelease, or bundle release type - dependency groups - ignore tags in release filelist - tasks other than replace - custom roles will cause pickle to fail, and output an error message. If your package2.xml uses any of these features, you are best off using PEAR_PackageFileManager to generate both package.xml. ' ), ); /** * PEAR_Command_Package constructor. * * @access public */ function __construct(&$ui, &$config) { parent::__construct($ui, $config); } /** * For unit-testing ease * * @return PEAR_Packager */ function &getPackager() { if (!class_exists('PEAR_Packager')) { require_once 'PEAR/Packager.php'; } $a = new PEAR_Packager; return $a; } /** * For unit-testing ease * * @param PEAR_Config $config * @param bool $debug * @param string|null $tmpdir * @return PEAR_PackageFile */ function &getPackageFile($config, $debug = false) { if (!class_exists('PEAR_Common')) { require_once 'PEAR/Common.php'; } if (!class_exists('PEAR_PackageFile')) { require_once 'PEAR/PackageFile.php'; } $a = new PEAR_PackageFile($config, $debug); $common = new PEAR_Common; $common->ui = $this->ui; $a->setLogger($common); return $a; } function doPackage($command, $options, $params) { $this->output = ''; $pkginfofile = isset($params[0]) ? $params[0] : 'package2.xml'; $packager = &$this->getPackager(); if (PEAR::isError($err = $this->_convertPackage($pkginfofile))) { return $err; } $compress = empty($options['nocompress']) ? true : false; $result = $packager->package($pkginfofile, $compress, 'package.xml'); if (PEAR::isError($result)) { return $this->raiseError($result); } // Don't want output, only the package file name just created if (isset($options['showname'])) { $this->ui->outputData($result, $command); } return true; } function _convertPackage($packagexml) { $pkg = &$this->getPackageFile($this->config); $pf2 = &$pkg->fromPackageFile($packagexml, PEAR_VALIDATE_NORMAL); if (!is_a($pf2, 'PEAR_PackageFile_v2')) { return $this->raiseError('Cannot process "' . $packagexml . '", is not a package.xml 2.0'); } require_once 'PEAR/PackageFile/v1.php'; $pf = new PEAR_PackageFile_v1; $pf->setConfig($this->config); if ($pf2->getPackageType() != 'extsrc' && $pf2->getPackageType() != 'zendextsrc') { return $this->raiseError('Cannot safely convert "' . $packagexml . '", is not an extension source package. Using a PEAR_PackageFileManager-based ' . 'script is an option'); } if (is_array($pf2->getUsesRole())) { return $this->raiseError('Cannot safely convert "' . $packagexml . '", contains custom roles. Using a PEAR_PackageFileManager-based script or ' . 'the convert command is an option'); } if (is_array($pf2->getUsesTask())) { return $this->raiseError('Cannot safely convert "' . $packagexml . '", contains custom tasks. Using a PEAR_PackageFileManager-based script or ' . 'the convert command is an option'); } $deps = $pf2->getDependencies(); if (isset($deps['group'])) { return $this->raiseError('Cannot safely convert "' . $packagexml . '", contains dependency groups. Using a PEAR_PackageFileManager-based script ' . 'or the convert command is an option'); } if (isset($deps['required']['subpackage']) || isset($deps['optional']['subpackage'])) { return $this->raiseError('Cannot safely convert "' . $packagexml . '", contains subpackage dependencies. Using a PEAR_PackageFileManager-based '. 'script is an option'); } if (isset($deps['required']['os'])) { return $this->raiseError('Cannot safely convert "' . $packagexml . '", contains os dependencies. Using a PEAR_PackageFileManager-based '. 'script is an option'); } if (isset($deps['required']['arch'])) { return $this->raiseError('Cannot safely convert "' . $packagexml . '", contains arch dependencies. Using a PEAR_PackageFileManager-based '. 'script is an option'); } $pf->setPackage($pf2->getPackage()); $pf->setSummary($pf2->getSummary()); $pf->setDescription($pf2->getDescription()); foreach ($pf2->getMaintainers() as $maintainer) { $pf->addMaintainer($maintainer['role'], $maintainer['handle'], $maintainer['name'], $maintainer['email']); } $pf->setVersion($pf2->getVersion()); $pf->setDate($pf2->getDate()); $pf->setLicense($pf2->getLicense()); $pf->setState($pf2->getState()); $pf->setNotes($pf2->getNotes()); $pf->addPhpDep($deps['required']['php']['min'], 'ge'); if (isset($deps['required']['php']['max'])) { $pf->addPhpDep($deps['required']['php']['max'], 'le'); } if (isset($deps['required']['package'])) { if (!isset($deps['required']['package'][0])) { $deps['required']['package'] = array($deps['required']['package']); } foreach ($deps['required']['package'] as $dep) { if (!isset($dep['channel'])) { return $this->raiseError('Cannot safely convert "' . $packagexml . '"' . ' contains uri-based dependency on a package. Using a ' . 'PEAR_PackageFileManager-based script is an option'); } if ($dep['channel'] != 'pear.php.net' && $dep['channel'] != 'pecl.php.net' && $dep['channel'] != 'doc.php.net') { return $this->raiseError('Cannot safely convert "' . $packagexml . '"' . ' contains dependency on a non-standard channel package. Using a ' . 'PEAR_PackageFileManager-based script is an option'); } if (isset($dep['conflicts'])) { return $this->raiseError('Cannot safely convert "' . $packagexml . '"' . ' contains conflicts dependency. Using a ' . 'PEAR_PackageFileManager-based script is an option'); } if (isset($dep['exclude'])) { $this->ui->outputData('WARNING: exclude tags are ignored in conversion'); } if (isset($dep['min'])) { $pf->addPackageDep($dep['name'], $dep['min'], 'ge'); } if (isset($dep['max'])) { $pf->addPackageDep($dep['name'], $dep['max'], 'le'); } } } if (isset($deps['required']['extension'])) { if (!isset($deps['required']['extension'][0])) { $deps['required']['extension'] = array($deps['required']['extension']); } foreach ($deps['required']['extension'] as $dep) { if (isset($dep['conflicts'])) { return $this->raiseError('Cannot safely convert "' . $packagexml . '"' . ' contains conflicts dependency. Using a ' . 'PEAR_PackageFileManager-based script is an option'); } if (isset($dep['exclude'])) { $this->ui->outputData('WARNING: exclude tags are ignored in conversion'); } if (isset($dep['min'])) { $pf->addExtensionDep($dep['name'], $dep['min'], 'ge'); } if (isset($dep['max'])) { $pf->addExtensionDep($dep['name'], $dep['max'], 'le'); } } } if (isset($deps['optional']['package'])) { if (!isset($deps['optional']['package'][0])) { $deps['optional']['package'] = array($deps['optional']['package']); } foreach ($deps['optional']['package'] as $dep) { if (!isset($dep['channel'])) { return $this->raiseError('Cannot safely convert "' . $packagexml . '"' . ' contains uri-based dependency on a package. Using a ' . 'PEAR_PackageFileManager-based script is an option'); } if ($dep['channel'] != 'pear.php.net' && $dep['channel'] != 'pecl.php.net' && $dep['channel'] != 'doc.php.net') { return $this->raiseError('Cannot safely convert "' . $packagexml . '"' . ' contains dependency on a non-standard channel package. Using a ' . 'PEAR_PackageFileManager-based script is an option'); } if (isset($dep['exclude'])) { $this->ui->outputData('WARNING: exclude tags are ignored in conversion'); } if (isset($dep['min'])) { $pf->addPackageDep($dep['name'], $dep['min'], 'ge', 'yes'); } if (isset($dep['max'])) { $pf->addPackageDep($dep['name'], $dep['max'], 'le', 'yes'); } } } if (isset($deps['optional']['extension'])) { if (!isset($deps['optional']['extension'][0])) { $deps['optional']['extension'] = array($deps['optional']['extension']); } foreach ($deps['optional']['extension'] as $dep) { if (isset($dep['exclude'])) { $this->ui->outputData('WARNING: exclude tags are ignored in conversion'); } if (isset($dep['min'])) { $pf->addExtensionDep($dep['name'], $dep['min'], 'ge', 'yes'); } if (isset($dep['max'])) { $pf->addExtensionDep($dep['name'], $dep['max'], 'le', 'yes'); } } } $contents = $pf2->getContents(); $release = $pf2->getReleases(); if (isset($releases[0])) { return $this->raiseError('Cannot safely process "' . $packagexml . '" contains ' . 'multiple extsrcrelease/zendextsrcrelease tags. Using a PEAR_PackageFileManager-based script ' . 'or the convert command is an option'); } if ($configoptions = $pf2->getConfigureOptions()) { foreach ($configoptions as $option) { $default = isset($option['default']) ? $option['default'] : false; $pf->addConfigureOption($option['name'], $option['prompt'], $default); } } if (isset($release['filelist']['ignore'])) { return $this->raiseError('Cannot safely process "' . $packagexml . '" contains ' . 'ignore tags. Using a PEAR_PackageFileManager-based script or the convert' . ' command is an option'); } if (isset($release['filelist']['install']) && !isset($release['filelist']['install'][0])) { $release['filelist']['install'] = array($release['filelist']['install']); } if (isset($contents['dir']['attribs']['baseinstalldir'])) { $baseinstalldir = $contents['dir']['attribs']['baseinstalldir']; } else { $baseinstalldir = false; } if (!isset($contents['dir']['file'][0])) { $contents['dir']['file'] = array($contents['dir']['file']); } foreach ($contents['dir']['file'] as $file) { if ($baseinstalldir && !isset($file['attribs']['baseinstalldir'])) { $file['attribs']['baseinstalldir'] = $baseinstalldir; } $processFile = $file; unset($processFile['attribs']); if (count($processFile)) { foreach ($processFile as $name => $task) { if ($name != $pf2->getTasksNs() . ':replace') { return $this->raiseError('Cannot safely process "' . $packagexml . '" contains tasks other than replace. Using a ' . 'PEAR_PackageFileManager-based script is an option.'); } $file['attribs']['replace'][] = $task; } } if (!in_array($file['attribs']['role'], PEAR_Common::getFileRoles())) { return $this->raiseError('Cannot safely convert "' . $packagexml . '", contains custom roles. Using a PEAR_PackageFileManager-based script ' . 'or the convert command is an option'); } if (isset($release['filelist']['install'])) { foreach ($release['filelist']['install'] as $installas) { if ($installas['attribs']['name'] == $file['attribs']['name']) { $file['attribs']['install-as'] = $installas['attribs']['as']; } } } $pf->addFile('/', $file['attribs']['name'], $file['attribs']); } if ($pf2->getChangeLog()) { $this->ui->outputData('WARNING: changelog is not translated to package.xml ' . '1.0, use PEAR_PackageFileManager-based script if you need changelog-' . 'translation for package.xml 1.0'); } $gen = &$pf->getDefaultGenerator(); $gen->toPackageFile('.'); } } PK=1]j!!pear/PEAR/Command/Install.xmlnu[ Install Package doInstall i f will overwrite newer installed packages l do not check for recommended dependency version n ignore dependencies, install anyway r do not install files, only register the package as installed s soft install, fail silently, or upgrade if already installed B don't build C extensions D OPTION1=VALUE[ OPTION2=VALUE] Z request uncompressed files when downloading R root directory used when installing files (ala PHP's INSTALL_ROOT), use packagingroot for RPM DIR P root directory used when packaging files, like RPM packaging DIR force install even if there were errors a install all required and optional dependencies o install all required dependencies O do not attempt to download any urls or contact channels p Only list the packages that would be downloaded [channel/]<package> ... Installs one or more PEAR packages. You can specify a package to install in four ways: "Package-1.0.tgz" : installs from a local file "http://example.com/Package-1.0.tgz" : installs from anywhere on the net. "package.xml" : installs the package described in package.xml. Useful for testing, or for wrapping a PEAR package in another package manager such as RPM. "Package[-version/state][.tar]" : queries your default channel's server ({config master_server}) and downloads the newest package with the preferred quality/state ({config preferred_state}). To retrieve Package version 1.1, use "Package-1.1," to retrieve Package state beta, use "Package-beta." To retrieve an uncompressed file, append .tar (make sure there is no file by the same name first) To download a package from another channel, prefix with the channel name like "channel/Package" More than one package may be specified at once. It is ok to mix these four ways of specifying packages. Upgrade Package doInstall up c upgrade packages from a specific channel CHAN f overwrite newer installed packages l do not check for recommended dependency version n ignore dependencies, upgrade anyway r do not install files, only register the package as upgraded B don't build C extensions Z request uncompressed files when downloading R root directory used when installing files (ala PHP's INSTALL_ROOT) DIR force install even if there were errors a install all required and optional dependencies o install all required dependencies O do not attempt to download any urls or contact channels p Only list the packages that would be downloaded <package> ... Upgrades one or more PEAR packages. See documentation for the "install" command for ways to specify a package. When upgrading, your package will be updated if the provided new package has a higher version number (use the -f option if you need to upgrade anyway). More than one package may be specified at once. Upgrade All Packages [Deprecated in favor of calling upgrade with no parameters] doUpgradeAll ua c upgrade packages from a specific channel CHAN n ignore dependencies, upgrade anyway r do not install files, only register the package as upgraded B don't build C extensions Z request uncompressed files when downloading R root directory used when installing files (ala PHP's INSTALL_ROOT), use packagingroot for RPM DIR force install even if there were errors do not check for recommended dependency version WARNING: This function is deprecated in favor of using the upgrade command with no params Upgrades all packages that have a newer release available. Upgrades are done only if there is a release available of the state specified in "preferred_state" (currently {config preferred_state}), or a state considered more stable. Un-install Package doUninstall un n ignore dependencies, uninstall anyway r do not remove files, only register the packages as not installed R root directory used when installing files (ala PHP's INSTALL_ROOT) DIR force install even if there were errors O do not attempt to uninstall remotely [channel/]<package> ... Uninstalls one or more PEAR packages. More than one package may be specified at once. Prefix with channel name to uninstall from a channel not in your default channel ({config default_channel}) Unpacks a Pecl Package doBundle bun d Optional destination directory for unpacking (defaults to current path or "ext" if exists) DIR f Force the unpacking even if there were errors in the package <package> Unpacks a Pecl Package into the selected location. It will download the package if needed. Run Post-Install Scripts bundled with a package doRunScripts rs <package> Run post-installation scripts in package <package>, if any exist. PK=1]Oʹʹpear/PEAR/Command/Registry.phpnu[ * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ /** * base class */ require_once 'PEAR/Command/Common.php'; /** * PEAR commands for registry manipulation * * @category pear * @package PEAR * @author Stig Bakken * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ class PEAR_Command_Registry extends PEAR_Command_Common { var $commands = array( 'list' => array( 'summary' => 'List Installed Packages In The Default Channel', 'function' => 'doList', 'shortcut' => 'l', 'options' => array( 'channel' => array( 'shortopt' => 'c', 'doc' => 'list installed packages from this channel', 'arg' => 'CHAN', ), 'allchannels' => array( 'shortopt' => 'a', 'doc' => 'list installed packages from all channels', ), 'channelinfo' => array( 'shortopt' => 'i', 'doc' => 'output fully channel-aware data, even on failure', ), ), 'doc' => ' If invoked without parameters, this command lists the PEAR packages installed in your php_dir ({config php_dir}). With a parameter, it lists the files in a package. ', ), 'list-files' => array( 'summary' => 'List Files In Installed Package', 'function' => 'doFileList', 'shortcut' => 'fl', 'options' => array(), 'doc' => ' List the files in an installed package. ' ), 'shell-test' => array( 'summary' => 'Shell Script Test', 'function' => 'doShellTest', 'shortcut' => 'st', 'options' => array(), 'doc' => ' [[relation] version] Tests if a package is installed in the system. Will exit(1) if it is not. The version comparison operator. One of: <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne The version to compare with '), 'info' => array( 'summary' => 'Display information about a package', 'function' => 'doInfo', 'shortcut' => 'in', 'options' => array(), 'doc' => ' Displays information about a package. The package argument may be a local package file, an URL to a package file, or the name of an installed package.' ) ); /** * PEAR_Command_Registry constructor. * * @access public */ function __construct(&$ui, &$config) { parent::__construct($ui, $config); } function _sortinfo($a, $b) { $apackage = isset($a['package']) ? $a['package'] : $a['name']; $bpackage = isset($b['package']) ? $b['package'] : $b['name']; return strcmp($apackage, $bpackage); } function doList($command, $options, $params) { $reg = &$this->config->getRegistry(); $channelinfo = isset($options['channelinfo']); if (isset($options['allchannels']) && !$channelinfo) { return $this->doListAll($command, array(), $params); } if (isset($options['allchannels']) && $channelinfo) { // allchannels with $channelinfo unset($options['allchannels']); $channels = $reg->getChannels(); $errors = array(); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); foreach ($channels as $channel) { $options['channel'] = $channel->getName(); $ret = $this->doList($command, $options, $params); if (PEAR::isError($ret)) { $errors[] = $ret; } } PEAR::staticPopErrorHandling(); if (count($errors)) { // for now, only give first error return PEAR::raiseError($errors[0]); } return true; } if (count($params) === 1) { return $this->doFileList($command, $options, $params); } if (isset($options['channel'])) { if (!$reg->channelExists($options['channel'])) { return $this->raiseError('Channel "' . $options['channel'] .'" does not exist'); } $channel = $reg->channelName($options['channel']); } else { $channel = $this->config->get('default_channel'); } $installed = $reg->packageInfo(null, null, $channel); usort($installed, array(&$this, '_sortinfo')); $data = array( 'caption' => 'Installed packages, channel ' . $channel . ':', 'border' => true, 'headline' => array('Package', 'Version', 'State'), 'channel' => $channel, ); if ($channelinfo) { $data['headline'] = array('Channel', 'Package', 'Version', 'State'); } if (count($installed) && !isset($data['data'])) { $data['data'] = array(); } foreach ($installed as $package) { $pobj = $reg->getPackage(isset($package['package']) ? $package['package'] : $package['name'], $channel); if ($channelinfo) { $packageinfo = array($pobj->getChannel(), $pobj->getPackage(), $pobj->getVersion(), $pobj->getState() ? $pobj->getState() : null); } else { $packageinfo = array($pobj->getPackage(), $pobj->getVersion(), $pobj->getState() ? $pobj->getState() : null); } $data['data'][] = $packageinfo; } if (count($installed) === 0) { if (!$channelinfo) { $data = '(no packages installed from channel ' . $channel . ')'; } else { $data = array( 'caption' => 'Installed packages, channel ' . $channel . ':', 'border' => true, 'channel' => $channel, 'data' => array(array('(no packages installed)')), ); } } $this->ui->outputData($data, $command); return true; } function doListAll($command, $options, $params) { // This duplicate code is deprecated over // list --channelinfo, which gives identical // output for list and list --allchannels. $reg = &$this->config->getRegistry(); $installed = $reg->packageInfo(null, null, null); foreach ($installed as $channel => $packages) { usort($packages, array($this, '_sortinfo')); $data = array( 'caption' => 'Installed packages, channel ' . $channel . ':', 'border' => true, 'headline' => array('Package', 'Version', 'State'), 'channel' => $channel ); foreach ($packages as $package) { $p = isset($package['package']) ? $package['package'] : $package['name']; $pobj = $reg->getPackage($p, $channel); $data['data'][] = array($pobj->getPackage(), $pobj->getVersion(), $pobj->getState() ? $pobj->getState() : null); } // Adds a blank line after each section $data['data'][] = array(); if (count($packages) === 0) { $data = array( 'caption' => 'Installed packages, channel ' . $channel . ':', 'border' => true, 'data' => array(array('(no packages installed)'), array()), 'channel' => $channel ); } $this->ui->outputData($data, $command); } return true; } function doFileList($command, $options, $params) { if (count($params) !== 1) { return $this->raiseError('list-files expects 1 parameter'); } $reg = &$this->config->getRegistry(); $fp = false; if (!is_dir($params[0]) && (file_exists($params[0]) || $fp = @fopen($params[0], 'r'))) { if ($fp) { fclose($fp); } if (!class_exists('PEAR_PackageFile')) { require_once 'PEAR/PackageFile.php'; } $pkg = new PEAR_PackageFile($this->config, $this->_debug); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $info = &$pkg->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL); PEAR::staticPopErrorHandling(); $headings = array('Package File', 'Install Path'); $installed = false; } else { PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel')); PEAR::staticPopErrorHandling(); if (PEAR::isError($parsed)) { return $this->raiseError($parsed); } $info = &$reg->getPackage($parsed['package'], $parsed['channel']); $headings = array('Type', 'Install Path'); $installed = true; } if (PEAR::isError($info)) { return $this->raiseError($info); } if ($info === null) { return $this->raiseError("`$params[0]' not installed"); } $list = ($info->getPackagexmlVersion() == '1.0' || $installed) ? $info->getFilelist() : $info->getContents(); if ($installed) { $caption = 'Installed Files For ' . $params[0]; } else { $caption = 'Contents of ' . basename($params[0]); } $data = array( 'caption' => $caption, 'border' => true, 'headline' => $headings); if ($info->getPackagexmlVersion() == '1.0' || $installed) { foreach ($list as $file => $att) { if ($installed) { if (empty($att['installed_as'])) { continue; } $data['data'][] = array($att['role'], $att['installed_as']); } else { if (isset($att['baseinstalldir']) && !in_array($att['role'], array('test', 'data', 'doc'))) { $dest = $att['baseinstalldir'] . DIRECTORY_SEPARATOR . $file; } else { $dest = $file; } switch ($att['role']) { case 'test': case 'data': case 'doc': $role = $att['role']; if ($role == 'test') { $role .= 's'; } $dest = $this->config->get($role . '_dir') . DIRECTORY_SEPARATOR . $info->getPackage() . DIRECTORY_SEPARATOR . $dest; break; case 'php': default: $dest = $this->config->get('php_dir') . DIRECTORY_SEPARATOR . $dest; } $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR; $dest = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"), array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $dest); $file = preg_replace('!/+!', '/', $file); $data['data'][] = array($file, $dest); } } } else { // package.xml 2.0, not installed if (!isset($list['dir']['file'][0])) { $list['dir']['file'] = array($list['dir']['file']); } foreach ($list['dir']['file'] as $att) { $att = $att['attribs']; $file = $att['name']; $role = &PEAR_Installer_Role::factory($info, $att['role'], $this->config); $role->setup($this, $info, $att, $file); if (!$role->isInstallable()) { $dest = '(not installable)'; } else { $dest = $role->processInstallation($info, $att, $file, ''); if (PEAR::isError($dest)) { $dest = '(Unknown role "' . $att['role'] . ')'; } else { list(,, $dest) = $dest; } } $data['data'][] = array($file, $dest); } } $this->ui->outputData($data, $command); return true; } function doShellTest($command, $options, $params) { if (count($params) < 1) { return PEAR::raiseError('ERROR, usage: pear shell-test packagename [[relation] version]'); } PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $reg = &$this->config->getRegistry(); $info = $reg->parsePackageName($params[0], $this->config->get('default_channel')); if (PEAR::isError($info)) { exit(1); // invalid package name } $package = $info['package']; $channel = $info['channel']; // "pear shell-test Foo" if (!$reg->packageExists($package, $channel)) { if ($channel == 'pecl.php.net') { if ($reg->packageExists($package, 'pear.php.net')) { $channel = 'pear.php.net'; // magically change channels for extensions } } } if (count($params) === 1) { if (!$reg->packageExists($package, $channel)) { exit(1); } // "pear shell-test Foo 1.0" } elseif (count($params) === 2) { $v = $reg->packageInfo($package, 'version', $channel); if (!$v || !version_compare("$v", "{$params[1]}", "ge")) { exit(1); } // "pear shell-test Foo ge 1.0" } elseif (count($params) === 3) { $v = $reg->packageInfo($package, 'version', $channel); if (!$v || !version_compare("$v", "{$params[2]}", $params[1])) { exit(1); } } else { PEAR::staticPopErrorHandling(); $this->raiseError("$command: expects 1 to 3 parameters"); exit(1); } } function doInfo($command, $options, $params) { if (count($params) !== 1) { return $this->raiseError('pear info expects 1 parameter'); } $info = $fp = false; $reg = &$this->config->getRegistry(); if (is_file($params[0]) && !is_dir($params[0]) && (file_exists($params[0]) || $fp = @fopen($params[0], 'r')) ) { if ($fp) { fclose($fp); } if (!class_exists('PEAR_PackageFile')) { require_once 'PEAR/PackageFile.php'; } $pkg = new PEAR_PackageFile($this->config, $this->_debug); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $obj = &$pkg->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL); PEAR::staticPopErrorHandling(); if (PEAR::isError($obj)) { $uinfo = $obj->getUserInfo(); if (is_array($uinfo)) { foreach ($uinfo as $message) { if (is_array($message)) { $message = $message['message']; } $this->ui->outputData($message); } } return $this->raiseError($obj); } if ($obj->getPackagexmlVersion() != '1.0') { return $this->_doInfo2($command, $options, $params, $obj, false); } $info = $obj->toArray(); } else { $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel')); if (PEAR::isError($parsed)) { return $this->raiseError($parsed); } $package = $parsed['package']; $channel = $parsed['channel']; $info = $reg->packageInfo($package, null, $channel); if (isset($info['old'])) { $obj = $reg->getPackage($package, $channel); return $this->_doInfo2($command, $options, $params, $obj, true); } } if (PEAR::isError($info)) { return $info; } if (empty($info)) { $this->raiseError("No information found for `$params[0]'"); return; } unset($info['filelist']); unset($info['dirtree']); unset($info['changelog']); if (isset($info['xsdversion'])) { $info['package.xml version'] = $info['xsdversion']; unset($info['xsdversion']); } if (isset($info['packagerversion'])) { $info['packaged with PEAR version'] = $info['packagerversion']; unset($info['packagerversion']); } $keys = array_keys($info); $longtext = array('description', 'summary'); foreach ($keys as $key) { if (is_array($info[$key])) { switch ($key) { case 'maintainers': { $i = 0; $mstr = ''; foreach ($info[$key] as $m) { if ($i++ > 0) { $mstr .= "\n"; } $mstr .= $m['name'] . " <"; if (isset($m['email'])) { $mstr .= $m['email']; } else { $mstr .= $m['handle'] . '@php.net'; } $mstr .= "> ($m[role])"; } $info[$key] = $mstr; break; } case 'release_deps': { $i = 0; $dstr = ''; foreach ($info[$key] as $d) { if (isset($this->_deps_rel_trans[$d['rel']])) { $rel = $this->_deps_rel_trans[$d['rel']]; } else { $rel = $d['rel']; } if (isset($this->_deps_type_trans[$d['type']])) { $type = ucfirst($this->_deps_type_trans[$d['type']]); } else { $type = $d['type']; } if (isset($d['name'])) { $name = $d['name'] . ' '; } else { $name = ''; } if (isset($d['version'])) { $version = $d['version'] . ' '; } else { $version = ''; } if (isset($d['optional']) && $d['optional'] == 'yes') { $optional = ' (optional)'; } else { $optional = ''; } $dstr .= "$type $name$rel $version$optional\n"; } $info[$key] = $dstr; break; } case 'provides' : { $debug = $this->config->get('verbose'); if ($debug < 2) { $pstr = 'Classes: '; } else { $pstr = ''; } $i = 0; foreach ($info[$key] as $p) { if ($debug < 2 && $p['type'] != "class") { continue; } // Only print classes when verbosity mode is < 2 if ($debug < 2) { if ($i++ > 0) { $pstr .= ", "; } $pstr .= $p['name']; } else { if ($i++ > 0) { $pstr .= "\n"; } $pstr .= ucfirst($p['type']) . " " . $p['name']; if (isset($p['explicit']) && $p['explicit'] == 1) { $pstr .= " (explicit)"; } } } $info[$key] = $pstr; break; } case 'configure_options' : { foreach ($info[$key] as $i => $p) { $info[$key][$i] = array_map(null, array_keys($p), array_values($p)); $info[$key][$i] = array_map( function($a) { return join(" = ", $a); }, $info[$key][$i]); $info[$key][$i] = implode(', ', $info[$key][$i]); } $info[$key] = implode("\n", $info[$key]); break; } default: { $info[$key] = implode(", ", $info[$key]); break; } } } if ($key == '_lastmodified') { $hdate = date('Y-m-d', $info[$key]); unset($info[$key]); $info['Last Modified'] = $hdate; } elseif ($key == '_lastversion') { $info['Previous Installed Version'] = $info[$key] ? $info[$key] : '- None -'; unset($info[$key]); } else { $info[$key] = trim($info[$key]); if (in_array($key, $longtext)) { $info[$key] = preg_replace('/ +/', ' ', $info[$key]); } } } $caption = 'About ' . $info['package'] . '-' . $info['version']; $data = array( 'caption' => $caption, 'border' => true); foreach ($info as $key => $value) { $key = ucwords(trim(str_replace('_', ' ', $key))); $data['data'][] = array($key, $value); } $data['raw'] = $info; $this->ui->outputData($data, 'package-info'); } /** * @access private */ function _doInfo2($command, $options, $params, &$obj, $installed) { $reg = &$this->config->getRegistry(); $caption = 'About ' . $obj->getChannel() . '/' .$obj->getPackage() . '-' . $obj->getVersion(); $data = array( 'caption' => $caption, 'border' => true); switch ($obj->getPackageType()) { case 'php' : $release = 'PEAR-style PHP-based Package'; break; case 'extsrc' : $release = 'PECL-style PHP extension (source code)'; break; case 'zendextsrc' : $release = 'PECL-style Zend extension (source code)'; break; case 'extbin' : $release = 'PECL-style PHP extension (binary)'; break; case 'zendextbin' : $release = 'PECL-style Zend extension (binary)'; break; case 'bundle' : $release = 'Package bundle (collection of packages)'; break; } $extends = $obj->getExtends(); $extends = $extends ? $obj->getPackage() . ' (extends ' . $extends . ')' : $obj->getPackage(); if ($src = $obj->getSourcePackage()) { $extends .= ' (source package ' . $src['channel'] . '/' . $src['package'] . ')'; } $info = array( 'Release Type' => $release, 'Name' => $extends, 'Channel' => $obj->getChannel(), 'Summary' => preg_replace('/ +/', ' ', $obj->getSummary()), 'Description' => preg_replace('/ +/', ' ', $obj->getDescription()), ); $info['Maintainers'] = ''; foreach (array('lead', 'developer', 'contributor', 'helper') as $role) { $leads = $obj->{"get{$role}s"}(); if (!$leads) { continue; } if (isset($leads['active'])) { $leads = array($leads); } foreach ($leads as $lead) { if (!empty($info['Maintainers'])) { $info['Maintainers'] .= "\n"; } $active = $lead['active'] == 'no' ? ', inactive' : ''; $info['Maintainers'] .= $lead['name'] . ' <'; $info['Maintainers'] .= $lead['email'] . "> ($role$active)"; } } $info['Release Date'] = $obj->getDate(); if ($time = $obj->getTime()) { $info['Release Date'] .= ' ' . $time; } $info['Release Version'] = $obj->getVersion() . ' (' . $obj->getState() . ')'; $info['API Version'] = $obj->getVersion('api') . ' (' . $obj->getState('api') . ')'; $info['License'] = $obj->getLicense(); $uri = $obj->getLicenseLocation(); if ($uri) { if (isset($uri['uri'])) { $info['License'] .= ' (' . $uri['uri'] . ')'; } else { $extra = $obj->getInstalledLocation($info['filesource']); if ($extra) { $info['License'] .= ' (' . $uri['filesource'] . ')'; } } } $info['Release Notes'] = $obj->getNotes(); if ($compat = $obj->getCompatible()) { if (!isset($compat[0])) { $compat = array($compat); } $info['Compatible with'] = ''; foreach ($compat as $package) { $info['Compatible with'] .= $package['channel'] . '/' . $package['name'] . "\nVersions >= " . $package['min'] . ', <= ' . $package['max']; if (isset($package['exclude'])) { if (is_array($package['exclude'])) { $package['exclude'] = implode(', ', $package['exclude']); } if (!isset($info['Not Compatible with'])) { $info['Not Compatible with'] = ''; } else { $info['Not Compatible with'] .= "\n"; } $info['Not Compatible with'] .= $package['channel'] . '/' . $package['name'] . "\nVersions " . $package['exclude']; } } } $usesrole = $obj->getUsesrole(); if ($usesrole) { if (!isset($usesrole[0])) { $usesrole = array($usesrole); } foreach ($usesrole as $roledata) { if (isset($info['Uses Custom Roles'])) { $info['Uses Custom Roles'] .= "\n"; } else { $info['Uses Custom Roles'] = ''; } if (isset($roledata['package'])) { $rolepackage = $reg->parsedPackageNameToString($roledata, true); } else { $rolepackage = $roledata['uri']; } $info['Uses Custom Roles'] .= $roledata['role'] . ' (' . $rolepackage . ')'; } } $usestask = $obj->getUsestask(); if ($usestask) { if (!isset($usestask[0])) { $usestask = array($usestask); } foreach ($usestask as $taskdata) { if (isset($info['Uses Custom Tasks'])) { $info['Uses Custom Tasks'] .= "\n"; } else { $info['Uses Custom Tasks'] = ''; } if (isset($taskdata['package'])) { $taskpackage = $reg->parsedPackageNameToString($taskdata, true); } else { $taskpackage = $taskdata['uri']; } $info['Uses Custom Tasks'] .= $taskdata['task'] . ' (' . $taskpackage . ')'; } } $deps = $obj->getDependencies(); $info['Required Dependencies'] = 'PHP version ' . $deps['required']['php']['min']; if (isset($deps['required']['php']['max'])) { $info['Required Dependencies'] .= '-' . $deps['required']['php']['max'] . "\n"; } else { $info['Required Dependencies'] .= "\n"; } if (isset($deps['required']['php']['exclude'])) { if (!isset($info['Not Compatible with'])) { $info['Not Compatible with'] = ''; } else { $info['Not Compatible with'] .= "\n"; } if (is_array($deps['required']['php']['exclude'])) { $deps['required']['php']['exclude'] = implode(', ', $deps['required']['php']['exclude']); } $info['Not Compatible with'] .= "PHP versions\n " . $deps['required']['php']['exclude']; } $info['Required Dependencies'] .= 'PEAR installer version'; if (isset($deps['required']['pearinstaller']['max'])) { $info['Required Dependencies'] .= 's ' . $deps['required']['pearinstaller']['min'] . '-' . $deps['required']['pearinstaller']['max']; } else { $info['Required Dependencies'] .= ' ' . $deps['required']['pearinstaller']['min'] . ' or newer'; } if (isset($deps['required']['pearinstaller']['exclude'])) { if (!isset($info['Not Compatible with'])) { $info['Not Compatible with'] = ''; } else { $info['Not Compatible with'] .= "\n"; } if (is_array($deps['required']['pearinstaller']['exclude'])) { $deps['required']['pearinstaller']['exclude'] = implode(', ', $deps['required']['pearinstaller']['exclude']); } $info['Not Compatible with'] .= "PEAR installer\n Versions " . $deps['required']['pearinstaller']['exclude']; } foreach (array('Package', 'Extension') as $type) { $index = strtolower($type); if (isset($deps['required'][$index])) { if (isset($deps['required'][$index]['name'])) { $deps['required'][$index] = array($deps['required'][$index]); } foreach ($deps['required'][$index] as $package) { if (isset($package['conflicts'])) { $infoindex = 'Not Compatible with'; if (!isset($info['Not Compatible with'])) { $info['Not Compatible with'] = ''; } else { $info['Not Compatible with'] .= "\n"; } } else { $infoindex = 'Required Dependencies'; $info[$infoindex] .= "\n"; } if ($index == 'extension') { $name = $package['name']; } else { if (isset($package['channel'])) { $name = $package['channel'] . '/' . $package['name']; } else { $name = '__uri/' . $package['name'] . ' (static URI)'; } } $info[$infoindex] .= "$type $name"; if (isset($package['uri'])) { $info[$infoindex] .= "\n Download URI: $package[uri]"; continue; } if (isset($package['max']) && isset($package['min'])) { $info[$infoindex] .= " \n Versions " . $package['min'] . '-' . $package['max']; } elseif (isset($package['min'])) { $info[$infoindex] .= " \n Version " . $package['min'] . ' or newer'; } elseif (isset($package['max'])) { $info[$infoindex] .= " \n Version " . $package['max'] . ' or older'; } if (isset($package['recommended'])) { $info[$infoindex] .= "\n Recommended version: $package[recommended]"; } if (isset($package['exclude'])) { if (!isset($info['Not Compatible with'])) { $info['Not Compatible with'] = ''; } else { $info['Not Compatible with'] .= "\n"; } if (is_array($package['exclude'])) { $package['exclude'] = implode(', ', $package['exclude']); } $package['package'] = $package['name']; // for parsedPackageNameToString if (isset($package['conflicts'])) { $info['Not Compatible with'] .= '=> except '; } $info['Not Compatible with'] .= 'Package ' . $reg->parsedPackageNameToString($package, true); $info['Not Compatible with'] .= "\n Versions " . $package['exclude']; } } } } if (isset($deps['required']['os'])) { if (isset($deps['required']['os']['name'])) { $dep['required']['os']['name'] = array($dep['required']['os']['name']); } foreach ($dep['required']['os'] as $os) { if (isset($os['conflicts']) && $os['conflicts'] == 'yes') { if (!isset($info['Not Compatible with'])) { $info['Not Compatible with'] = ''; } else { $info['Not Compatible with'] .= "\n"; } $info['Not Compatible with'] .= "$os[name] Operating System"; } else { $info['Required Dependencies'] .= "\n"; $info['Required Dependencies'] .= "$os[name] Operating System"; } } } if (isset($deps['required']['arch'])) { if (isset($deps['required']['arch']['pattern'])) { $dep['required']['arch']['pattern'] = array($dep['required']['os']['pattern']); } foreach ($dep['required']['arch'] as $os) { if (isset($os['conflicts']) && $os['conflicts'] == 'yes') { if (!isset($info['Not Compatible with'])) { $info['Not Compatible with'] = ''; } else { $info['Not Compatible with'] .= "\n"; } $info['Not Compatible with'] .= "OS/Arch matching pattern '/$os[pattern]/'"; } else { $info['Required Dependencies'] .= "\n"; $info['Required Dependencies'] .= "OS/Arch matching pattern '/$os[pattern]/'"; } } } if (isset($deps['optional'])) { foreach (array('Package', 'Extension') as $type) { $index = strtolower($type); if (isset($deps['optional'][$index])) { if (isset($deps['optional'][$index]['name'])) { $deps['optional'][$index] = array($deps['optional'][$index]); } foreach ($deps['optional'][$index] as $package) { if (isset($package['conflicts']) && $package['conflicts'] == 'yes') { $infoindex = 'Not Compatible with'; if (!isset($info['Not Compatible with'])) { $info['Not Compatible with'] = ''; } else { $info['Not Compatible with'] .= "\n"; } } else { $infoindex = 'Optional Dependencies'; if (!isset($info['Optional Dependencies'])) { $info['Optional Dependencies'] = ''; } else { $info['Optional Dependencies'] .= "\n"; } } if ($index == 'extension') { $name = $package['name']; } else { if (isset($package['channel'])) { $name = $package['channel'] . '/' . $package['name']; } else { $name = '__uri/' . $package['name'] . ' (static URI)'; } } $info[$infoindex] .= "$type $name"; if (isset($package['uri'])) { $info[$infoindex] .= "\n Download URI: $package[uri]"; continue; } if ($infoindex == 'Not Compatible with') { // conflicts is only used to say that all versions conflict continue; } if (isset($package['max']) && isset($package['min'])) { $info[$infoindex] .= " \n Versions " . $package['min'] . '-' . $package['max']; } elseif (isset($package['min'])) { $info[$infoindex] .= " \n Version " . $package['min'] . ' or newer'; } elseif (isset($package['max'])) { $info[$infoindex] .= " \n Version " . $package['min'] . ' or older'; } if (isset($package['recommended'])) { $info[$infoindex] .= "\n Recommended version: $package[recommended]"; } if (isset($package['exclude'])) { if (!isset($info['Not Compatible with'])) { $info['Not Compatible with'] = ''; } else { $info['Not Compatible with'] .= "\n"; } if (is_array($package['exclude'])) { $package['exclude'] = implode(', ', $package['exclude']); } $info['Not Compatible with'] .= "Package $package\n Versions " . $package['exclude']; } } } } } if (isset($deps['group'])) { if (!isset($deps['group'][0])) { $deps['group'] = array($deps['group']); } foreach ($deps['group'] as $group) { $info['Dependency Group ' . $group['attribs']['name']] = $group['attribs']['hint']; $groupindex = $group['attribs']['name'] . ' Contents'; $info[$groupindex] = ''; foreach (array('Package', 'Extension') as $type) { $index = strtolower($type); if (isset($group[$index])) { if (isset($group[$index]['name'])) { $group[$index] = array($group[$index]); } foreach ($group[$index] as $package) { if (!empty($info[$groupindex])) { $info[$groupindex] .= "\n"; } if ($index == 'extension') { $name = $package['name']; } else { if (isset($package['channel'])) { $name = $package['channel'] . '/' . $package['name']; } else { $name = '__uri/' . $package['name'] . ' (static URI)'; } } if (isset($package['uri'])) { if (isset($package['conflicts']) && $package['conflicts'] == 'yes') { $info[$groupindex] .= "Not Compatible with $type $name"; } else { $info[$groupindex] .= "$type $name"; } $info[$groupindex] .= "\n Download URI: $package[uri]"; continue; } if (isset($package['conflicts']) && $package['conflicts'] == 'yes') { $info[$groupindex] .= "Not Compatible with $type $name"; continue; } $info[$groupindex] .= "$type $name"; if (isset($package['max']) && isset($package['min'])) { $info[$groupindex] .= " \n Versions " . $package['min'] . '-' . $package['max']; } elseif (isset($package['min'])) { $info[$groupindex] .= " \n Version " . $package['min'] . ' or newer'; } elseif (isset($package['max'])) { $info[$groupindex] .= " \n Version " . $package['min'] . ' or older'; } if (isset($package['recommended'])) { $info[$groupindex] .= "\n Recommended version: $package[recommended]"; } if (isset($package['exclude'])) { if (!isset($info['Not Compatible with'])) { $info['Not Compatible with'] = ''; } else { $info[$groupindex] .= "Not Compatible with\n"; } if (is_array($package['exclude'])) { $package['exclude'] = implode(', ', $package['exclude']); } $info[$groupindex] .= " Package $package\n Versions " . $package['exclude']; } } } } } } if ($obj->getPackageType() == 'bundle') { $info['Bundled Packages'] = ''; foreach ($obj->getBundledPackages() as $package) { if (!empty($info['Bundled Packages'])) { $info['Bundled Packages'] .= "\n"; } if (isset($package['uri'])) { $info['Bundled Packages'] .= '__uri/' . $package['name']; $info['Bundled Packages'] .= "\n (URI: $package[uri]"; } else { $info['Bundled Packages'] .= $package['channel'] . '/' . $package['name']; } } } $info['package.xml version'] = '2.0'; if ($installed) { if ($obj->getLastModified()) { $info['Last Modified'] = date('Y-m-d H:i', $obj->getLastModified()); } $v = $obj->getLastInstalledVersion(); $info['Previous Installed Version'] = $v ? $v : '- None -'; } foreach ($info as $key => $value) { $data['data'][] = array($key, $value); } $data['raw'] = $obj->getArray(); // no validation needed $this->ui->outputData($data, 'package-info'); } } PK=1]%Fiipear/PEAR/Command/Test.xmlnu[ Run Regression Tests doRunTests rt r Run tests in child directories, recursively. 4 dirs deep maximum i actual string of settings to pass to php in format " -d setting=blah" SETTINGS l Log test runs/results as they are run q Only display detail for failed tests s Display simple output for all tests p Treat parameters as installed packages from which to run tests u Search parameters for AllTests.php, and use that to run phpunit-based tests If none is found, all .phpt tests will be tried instead. t Output run-tests.log in TAP-compliant format c CGI php executable (needed for tests with POST/GET section) PHPCGI x Generate a code coverage report (requires Xdebug 2.0.0+) [testfile|dir ...] Run regression tests with PHP's regression testing script (run-tests.php). PK=1]-X"u"upear/PEAR/Command/Remote.phpnu[ * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ /** * base class */ require_once 'PEAR/Command/Common.php'; require_once 'PEAR/REST.php'; /** * PEAR commands for remote server querying * * @category pear * @package PEAR * @author Stig Bakken * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ class PEAR_Command_Remote extends PEAR_Command_Common { var $commands = array( 'remote-info' => array( 'summary' => 'Information About Remote Packages', 'function' => 'doRemoteInfo', 'shortcut' => 'ri', 'options' => array(), 'doc' => ' Get details on a package from the server.', ), 'list-upgrades' => array( 'summary' => 'List Available Upgrades', 'function' => 'doListUpgrades', 'shortcut' => 'lu', 'options' => array( 'channelinfo' => array( 'shortopt' => 'i', 'doc' => 'output fully channel-aware data, even on failure', ), ), 'doc' => '[preferred_state] List releases on the server of packages you have installed where a newer version is available with the same release state (stable etc.) or the state passed as the second parameter.' ), 'remote-list' => array( 'summary' => 'List Remote Packages', 'function' => 'doRemoteList', 'shortcut' => 'rl', 'options' => array( 'channel' => array( 'shortopt' => 'c', 'doc' => 'specify a channel other than the default channel', 'arg' => 'CHAN', ) ), 'doc' => ' Lists the packages available on the configured server along with the latest stable release of each package.', ), 'search' => array( 'summary' => 'Search remote package database', 'function' => 'doSearch', 'shortcut' => 'sp', 'options' => array( 'channel' => array( 'shortopt' => 'c', 'doc' => 'specify a channel other than the default channel', 'arg' => 'CHAN', ), 'allchannels' => array( 'shortopt' => 'a', 'doc' => 'search packages from all known channels', ), 'channelinfo' => array( 'shortopt' => 'i', 'doc' => 'output fully channel-aware data, even on failure', ), ), 'doc' => '[packagename] [packageinfo] Lists all packages which match the search parameters. The first parameter is a fragment of a packagename. The default channel will be used unless explicitly overridden. The second parameter will be used to match any portion of the summary/description', ), 'list-all' => array( 'summary' => 'List All Packages', 'function' => 'doListAll', 'shortcut' => 'la', 'options' => array( 'channel' => array( 'shortopt' => 'c', 'doc' => 'specify a channel other than the default channel', 'arg' => 'CHAN', ), 'channelinfo' => array( 'shortopt' => 'i', 'doc' => 'output fully channel-aware data, even on failure', ), ), 'doc' => ' Lists the packages available on the configured server along with the latest stable release of each package.', ), 'download' => array( 'summary' => 'Download Package', 'function' => 'doDownload', 'shortcut' => 'd', 'options' => array( 'nocompress' => array( 'shortopt' => 'Z', 'doc' => 'download an uncompressed (.tar) file', ), ), 'doc' => '... Download package tarballs. The files will be named as suggested by the server, for example if you download the DB package and the latest stable version of DB is 1.6.5, the downloaded file will be DB-1.6.5.tgz.', ), 'clear-cache' => array( 'summary' => 'Clear Web Services Cache', 'function' => 'doClearCache', 'shortcut' => 'cc', 'options' => array(), 'doc' => ' Clear the REST cache. See also the cache_ttl configuration parameter. ', ), ); /** * PEAR_Command_Remote constructor. * * @access public */ function __construct(&$ui, &$config) { parent::__construct($ui, $config); } function _checkChannelForStatus($channel, $chan) { if (PEAR::isError($chan)) { $this->raiseError($chan); } if (!is_a($chan, 'PEAR_ChannelFile')) { return $this->raiseError('Internal corruption error: invalid channel "' . $channel . '"'); } $rest = new PEAR_REST($this->config); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $mirror = $this->config->get('preferred_mirror', null, $channel); $a = $rest->downloadHttp('http://' . $channel . '/channel.xml', $chan->lastModified()); PEAR::staticPopErrorHandling(); if (!PEAR::isError($a) && $a) { $this->ui->outputData('WARNING: channel "' . $channel . '" has ' . 'updated its protocols, use "' . PEAR_RUNTYPE . ' channel-update ' . $channel . '" to update'); } } function doRemoteInfo($command, $options, $params) { if (sizeof($params) != 1) { return $this->raiseError("$command expects one param: the remote package name"); } $savechannel = $channel = $this->config->get('default_channel'); $reg = &$this->config->getRegistry(); $package = $params[0]; $parsed = $reg->parsePackageName($package, $channel); if (PEAR::isError($parsed)) { return $this->raiseError('Invalid package name "' . $package . '"'); } $channel = $parsed['channel']; $this->config->set('default_channel', $channel); $chan = $reg->getChannel($channel); if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) { return $e; } $mirror = $this->config->get('preferred_mirror'); if ($chan->supportsREST($mirror) && $base = $chan->getBaseURL('REST1.0', $mirror)) { $rest = &$this->config->getREST('1.0', array()); $info = $rest->packageInfo($base, $parsed['package'], $channel); } if (!isset($info)) { return $this->raiseError('No supported protocol was found'); } if (PEAR::isError($info)) { $this->config->set('default_channel', $savechannel); return $this->raiseError($info); } if (!isset($info['name'])) { return $this->raiseError('No remote package "' . $package . '" was found'); } $installed = $reg->packageInfo($info['name'], null, $channel); $info['installed'] = $installed ? $installed['version'] : '- no -'; if (is_array($info['installed'])) { $info['installed'] = $info['installed']['release']; } $this->ui->outputData($info, $command); $this->config->set('default_channel', $savechannel); return true; } function doRemoteList($command, $options, $params) { $savechannel = $channel = $this->config->get('default_channel'); $reg = &$this->config->getRegistry(); if (isset($options['channel'])) { $channel = $options['channel']; if (!$reg->channelExists($channel)) { return $this->raiseError('Channel "' . $channel . '" does not exist'); } $this->config->set('default_channel', $channel); } $chan = $reg->getChannel($channel); if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) { return $e; } $list_options = false; if ($this->config->get('preferred_state') == 'stable') { $list_options = true; } $available = array(); if ($chan->supportsREST($this->config->get('preferred_mirror')) && $base = $chan->getBaseURL('REST1.1', $this->config->get('preferred_mirror')) ) { // use faster list-all if available $rest = &$this->config->getREST('1.1', array()); $available = $rest->listAll($base, $list_options, true, false, false, $chan->getName()); } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) && $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) { $rest = &$this->config->getREST('1.0', array()); $available = $rest->listAll($base, $list_options, true, false, false, $chan->getName()); } if (PEAR::isError($available)) { $this->config->set('default_channel', $savechannel); return $this->raiseError($available); } $i = $j = 0; $data = array( 'caption' => 'Channel ' . $channel . ' Available packages:', 'border' => true, 'headline' => array('Package', 'Version'), 'channel' => $channel ); if (count($available) == 0) { $data = '(no packages available yet)'; } else { foreach ($available as $name => $info) { $version = (isset($info['stable']) && $info['stable']) ? $info['stable'] : '-n/a-'; $data['data'][] = array($name, $version); } } $this->ui->outputData($data, $command); $this->config->set('default_channel', $savechannel); return true; } function doListAll($command, $options, $params) { $savechannel = $channel = $this->config->get('default_channel'); $reg = &$this->config->getRegistry(); if (isset($options['channel'])) { $channel = $options['channel']; if (!$reg->channelExists($channel)) { return $this->raiseError("Channel \"$channel\" does not exist"); } $this->config->set('default_channel', $channel); } $list_options = false; if ($this->config->get('preferred_state') == 'stable') { $list_options = true; } $chan = $reg->getChannel($channel); if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) { return $e; } if ($chan->supportsREST($this->config->get('preferred_mirror')) && $base = $chan->getBaseURL('REST1.1', $this->config->get('preferred_mirror'))) { // use faster list-all if available $rest = &$this->config->getREST('1.1', array()); $available = $rest->listAll($base, $list_options, false, false, false, $chan->getName()); } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) && $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) { $rest = &$this->config->getREST('1.0', array()); $available = $rest->listAll($base, $list_options, false, false, false, $chan->getName()); } if (PEAR::isError($available)) { $this->config->set('default_channel', $savechannel); return $this->raiseError('The package list could not be fetched from the remote server. Please try again. (Debug info: "' . $available->getMessage() . '")'); } $data = array( 'caption' => 'All packages [Channel ' . $channel . ']:', 'border' => true, 'headline' => array('Package', 'Latest', 'Local'), 'channel' => $channel, ); if (isset($options['channelinfo'])) { // add full channelinfo $data['caption'] = 'Channel ' . $channel . ' All packages:'; $data['headline'] = array('Channel', 'Package', 'Latest', 'Local', 'Description', 'Dependencies'); } $local_pkgs = $reg->listPackages($channel); foreach ($available as $name => $info) { $installed = $reg->packageInfo($name, null, $channel); if ($installed && is_array($installed['version'])) { $installed['version'] = $installed['version']['release']; } $desc = $info['summary']; if (isset($params[$name])) { $desc .= "\n\n".$info['description']; } if (isset($options['mode'])) { if ($options['mode'] == 'installed' && !isset($installed['version'])) { continue; } if ($options['mode'] == 'notinstalled' && isset($installed['version'])) { continue; } if ($options['mode'] == 'upgrades' && (!isset($installed['version']) || version_compare($installed['version'], $info['stable'], '>='))) { continue; } } $pos = array_search(strtolower($name), $local_pkgs); if ($pos !== false) { unset($local_pkgs[$pos]); } if (isset($info['stable']) && !$info['stable']) { $info['stable'] = null; } if (isset($options['channelinfo'])) { // add full channelinfo if ($info['stable'] === $info['unstable']) { $state = $info['state']; } else { $state = 'stable'; } $latest = $info['stable'].' ('.$state.')'; $local = ''; if (isset($installed['version'])) { $inst_state = $reg->packageInfo($name, 'release_state', $channel); $local = $installed['version'].' ('.$inst_state.')'; } $packageinfo = array( $channel, $name, $latest, $local, isset($desc) ? $desc : null, isset($info['deps']) ? $info['deps'] : null, ); } else { $packageinfo = array( $reg->channelAlias($channel) . '/' . $name, isset($info['stable']) ? $info['stable'] : null, isset($installed['version']) ? $installed['version'] : null, isset($desc) ? $desc : null, isset($info['deps']) ? $info['deps'] : null, ); } $data['data'][$info['category']][] = $packageinfo; } if (isset($options['mode']) && in_array($options['mode'], array('notinstalled', 'upgrades'))) { $this->config->set('default_channel', $savechannel); $this->ui->outputData($data, $command); return true; } foreach ($local_pkgs as $name) { $info = &$reg->getPackage($name, $channel); $data['data']['Local'][] = array( $reg->channelAlias($channel) . '/' . $info->getPackage(), '', $info->getVersion(), $info->getSummary(), $info->getDeps() ); } $this->config->set('default_channel', $savechannel); $this->ui->outputData($data, $command); return true; } function doSearch($command, $options, $params) { if ((!isset($params[0]) || empty($params[0])) && (!isset($params[1]) || empty($params[1]))) { return $this->raiseError('no valid search string supplied'); } $channelinfo = isset($options['channelinfo']); $reg = &$this->config->getRegistry(); if (isset($options['allchannels'])) { // search all channels unset($options['allchannels']); $channels = $reg->getChannels(); $errors = array(); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); foreach ($channels as $channel) { if ($channel->getName() != '__uri') { $options['channel'] = $channel->getName(); $ret = $this->doSearch($command, $options, $params); if (PEAR::isError($ret)) { $errors[] = $ret; } } } PEAR::staticPopErrorHandling(); if (count($errors) !== 0) { // for now, only give first error return PEAR::raiseError($errors[0]); } return true; } $savechannel = $channel = $this->config->get('default_channel'); $package = strtolower($params[0]); $summary = isset($params[1]) ? $params[1] : false; if (isset($options['channel'])) { $reg = &$this->config->getRegistry(); $channel = $options['channel']; if (!$reg->channelExists($channel)) { return $this->raiseError('Channel "' . $channel . '" does not exist'); } $this->config->set('default_channel', $channel); } $chan = $reg->getChannel($channel); if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) { return $e; } if ($chan->supportsREST($this->config->get('preferred_mirror')) && $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) { $rest = &$this->config->getREST('1.0', array()); $available = $rest->listAll($base, false, false, $package, $summary, $chan->getName()); } if (PEAR::isError($available)) { $this->config->set('default_channel', $savechannel); return $this->raiseError($available); } if (!$available && !$channelinfo) { // clean exit when not found, no error ! $data = 'no packages found that match pattern "' . $package . '", for channel '.$channel.'.'; $this->ui->outputData($data); $this->config->set('default_channel', $channel); return true; } if ($channelinfo) { $data = array( 'caption' => 'Matched packages, channel ' . $channel . ':', 'border' => true, 'headline' => array('Channel', 'Package', 'Stable/(Latest)', 'Local'), 'channel' => $channel ); } else { $data = array( 'caption' => 'Matched packages, channel ' . $channel . ':', 'border' => true, 'headline' => array('Package', 'Stable/(Latest)', 'Local'), 'channel' => $channel ); } if (!$available && $channelinfo) { unset($data['headline']); $data['data'] = 'No packages found that match pattern "' . $package . '".'; $available = array(); } foreach ($available as $name => $info) { $installed = $reg->packageInfo($name, null, $channel); $desc = $info['summary']; if (isset($params[$name])) $desc .= "\n\n".$info['description']; if (!isset($info['stable']) || !$info['stable']) { $version_remote = 'none'; } else { if ($info['unstable']) { $version_remote = $info['unstable']; } else { $version_remote = $info['stable']; } $version_remote .= ' ('.$info['state'].')'; } $version = is_array($installed['version']) ? $installed['version']['release'] : $installed['version']; if ($channelinfo) { $packageinfo = array( $channel, $name, $version_remote, $version, $desc, ); } else { $packageinfo = array( $name, $version_remote, $version, $desc, ); } $data['data'][$info['category']][] = $packageinfo; } $this->ui->outputData($data, $command); $this->config->set('default_channel', $channel); return true; } function &getDownloader($options) { if (!class_exists('PEAR_Downloader')) { require_once 'PEAR/Downloader.php'; } $a = new PEAR_Downloader($this->ui, $options, $this->config); return $a; } function doDownload($command, $options, $params) { // make certain that dependencies are ignored $options['downloadonly'] = 1; // eliminate error messages for preferred_state-related errors /* TODO: Should be an option, but until now download does respect preferred state */ /* $options['ignorepreferred_state'] = 1; */ // eliminate error messages for preferred_state-related errors $downloader = &$this->getDownloader($options); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $e = $downloader->setDownloadDir(getcwd()); PEAR::staticPopErrorHandling(); if (PEAR::isError($e)) { return $this->raiseError('Current directory is not writeable, cannot download'); } $errors = array(); $downloaded = array(); $err = $downloader->download($params); if (PEAR::isError($err)) { return $err; } $errors = $downloader->getErrorMsgs(); if (count($errors)) { foreach ($errors as $error) { if ($error !== null) { $this->ui->outputData($error); } } return $this->raiseError("$command failed"); } $downloaded = $downloader->getDownloadedPackages(); foreach ($downloaded as $pkg) { $this->ui->outputData("File $pkg[file] downloaded", $command); } return true; } function downloadCallback($msg, $params = null) { if ($msg == 'done') { $this->bytes_downloaded = $params; } } function doListUpgrades($command, $options, $params) { require_once 'PEAR/Common.php'; if (isset($params[0]) && !is_array(PEAR_Common::betterStates($params[0]))) { return $this->raiseError($params[0] . ' is not a valid state (stable/beta/alpha/devel/etc.) try "pear help list-upgrades"'); } $savechannel = $channel = $this->config->get('default_channel'); $reg = &$this->config->getRegistry(); foreach ($reg->listChannels() as $channel) { $inst = array_flip($reg->listPackages($channel)); if (!count($inst)) { continue; } if ($channel == '__uri') { continue; } $this->config->set('default_channel', $channel); $state = empty($params[0]) ? $this->config->get('preferred_state') : $params[0]; $caption = $channel . ' Available Upgrades'; $chan = $reg->getChannel($channel); if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) { return $e; } $latest = array(); $base2 = false; $preferred_mirror = $this->config->get('preferred_mirror'); if ($chan->supportsREST($preferred_mirror) && ( ($base2 = $chan->getBaseURL('REST1.3', $preferred_mirror)) || ($base = $chan->getBaseURL('REST1.0', $preferred_mirror)) ) ) { if ($base2) { $rest = &$this->config->getREST('1.3', array()); $base = $base2; } else { $rest = &$this->config->getREST('1.0', array()); } if (empty($state) || $state == 'any') { $state = false; } else { $caption .= ' (' . implode(', ', PEAR_Common::betterStates($state, true)) . ')'; } PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $latest = $rest->listLatestUpgrades($base, $state, $inst, $channel, $reg); PEAR::staticPopErrorHandling(); } if (PEAR::isError($latest)) { $this->ui->outputData($latest->getMessage()); continue; } $caption .= ':'; if (PEAR::isError($latest)) { $this->config->set('default_channel', $savechannel); return $latest; } $data = array( 'caption' => $caption, 'border' => 1, 'headline' => array('Channel', 'Package', 'Local', 'Remote', 'Size'), 'channel' => $channel ); foreach ((array)$latest as $pkg => $info) { $package = strtolower($pkg); if (!isset($inst[$package])) { // skip packages we don't have installed continue; } extract($info); $inst_version = $reg->packageInfo($package, 'version', $channel); $inst_state = $reg->packageInfo($package, 'release_state', $channel); if (version_compare("$version", "$inst_version", "le")) { // installed version is up-to-date continue; } if ($filesize >= 20480) { $filesize += 1024 - ($filesize % 1024); $fs = sprintf("%dkB", $filesize / 1024); } elseif ($filesize > 0) { $filesize += 103 - ($filesize % 103); $fs = sprintf("%.1fkB", $filesize / 1024.0); } else { $fs = " -"; // XXX center instead } $data['data'][] = array($channel, $pkg, "$inst_version ($inst_state)", "$version ($state)", $fs); } if (isset($options['channelinfo'])) { if (empty($data['data'])) { unset($data['headline']); if (count($inst) == 0) { $data['data'] = '(no packages installed)'; } else { $data['data'] = '(no upgrades available)'; } } $this->ui->outputData($data, $command); } else { if (empty($data['data'])) { $this->ui->outputData('Channel ' . $channel . ': No upgrades available'); } else { $this->ui->outputData($data, $command); } } } $this->config->set('default_channel', $savechannel); return true; } function doClearCache($command, $options, $params) { $cache_dir = $this->config->get('cache_dir'); $verbose = $this->config->get('verbose'); $output = ''; if (!file_exists($cache_dir) || !is_dir($cache_dir)) { return $this->raiseError("$cache_dir does not exist or is not a directory"); } if (!($dp = @opendir($cache_dir))) { return $this->raiseError("opendir($cache_dir) failed: $php_errormsg"); } if ($verbose >= 1) { $output .= "reading directory $cache_dir\n"; } $num = 0; while ($ent = readdir($dp)) { if (preg_match('/rest.cache(file|id)\\z/', $ent)) { $path = $cache_dir . DIRECTORY_SEPARATOR . $ent; if (file_exists($path)) { $ok = @unlink($path); } else { $ok = false; $php_errormsg = ''; } if ($ok) { if ($verbose >= 2) { $output .= "deleted $path\n"; } $num++; } elseif ($verbose >= 1) { $output .= "failed to delete $path $php_errormsg\n"; } } } closedir($dp); if ($verbose >= 1) { $output .= "$num cache entries cleared\n"; } $this->ui->outputData(rtrim($output), $command); return $num; } } PK=1]wzzpear/PEAR/Command/Channels.xmlnu[ List Available Channels doList lc List all available channels for installation. Update the Channel List doUpdateAll uc List all installed packages in all channels. Remove a Channel From the List doDelete cde <channel name> Delete a channel from the registry. You may not remove any channel that has installed packages. Add a Channel doAdd ca <channel.xml> Add a private channel to the channel list. Note that all public channels should be synced using "update-channels". Parameter may be either a local file or remote URL to a channel.xml. Update an Existing Channel doUpdate cu f will force download of new channel.xml if an existing channel name is used c will force download of new channel.xml if an existing channel name is used CHANNEL [<channel.xml>|<channel name>] Update a channel in the channel list directly. Note that all public channels can be synced using "update-channels". Parameter may be a local or remote channel.xml, or the name of an existing channel. Retrieve Information on a Channel doInfo ci <package> List the files in an installed package. Specify an alias to a channel name doAlias cha <channel> <alias> Specify a specific alias to use for a channel name. The alias may not be an existing channel name or alias. Initialize a Channel from its server doDiscover di [<channel.xml>|<channel name>] Initialize a channel from its server and create a local channel.xml. If <channel name> is in the format "<username>:<password>@<channel>" then <username> and <password> will be set as the login username/password for <channel>. Use caution when passing the username/password in this way, as it may allow other users on your computer to briefly view your username/ password via the system's process list. Connects and authenticates to remote channel server doLogin cli <channel name> Log in to a remote channel server. If <channel name> is not supplied, the default channel is used. To use remote functions in the installer that require any kind of privileges, you need to log in first. The username and password you enter here will be stored in your per-user PEAR configuration (~/.pearrc on Unix-like systems). After logging in, your username and password will be sent along in subsequent operations on the remote server. Logs out from the remote channel server doLogout clo <channel name> Logs out from a remote channel server. If <channel name> is not supplied, the default channel is used. This command does not actually connect to the remote server, it only deletes the stored username and password from your user configuration. PK=1]##h/h/pear/PEAR/Command/Test.phpnu[ * @author Martin Jansen * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ /** * base class */ require_once 'PEAR/Command/Common.php'; /** * PEAR commands for login/logout * * @category pear * @package PEAR * @author Stig Bakken * @author Martin Jansen * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ class PEAR_Command_Test extends PEAR_Command_Common { var $commands = array( 'run-tests' => array( 'summary' => 'Run Regression Tests', 'function' => 'doRunTests', 'shortcut' => 'rt', 'options' => array( 'recur' => array( 'shortopt' => 'r', 'doc' => 'Run tests in child directories, recursively. 4 dirs deep maximum', ), 'ini' => array( 'shortopt' => 'i', 'doc' => 'actual string of settings to pass to php in format " -d setting=blah"', 'arg' => 'SETTINGS' ), 'realtimelog' => array( 'shortopt' => 'l', 'doc' => 'Log test runs/results as they are run', ), 'quiet' => array( 'shortopt' => 'q', 'doc' => 'Only display detail for failed tests', ), 'simple' => array( 'shortopt' => 's', 'doc' => 'Display simple output for all tests', ), 'package' => array( 'shortopt' => 'p', 'doc' => 'Treat parameters as installed packages from which to run tests', ), 'phpunit' => array( 'shortopt' => 'u', 'doc' => 'Search parameters for AllTests.php, and use that to run phpunit-based tests If none is found, all .phpt tests will be tried instead.', ), 'tapoutput' => array( 'shortopt' => 't', 'doc' => 'Output run-tests.log in TAP-compliant format', ), 'cgi' => array( 'shortopt' => 'c', 'doc' => 'CGI php executable (needed for tests with POST/GET section)', 'arg' => 'PHPCGI', ), 'coverage' => array( 'shortopt' => 'x', 'doc' => 'Generate a code coverage report (requires Xdebug 2.0.0+)', ), 'showdiff' => array( 'shortopt' => 'd', 'doc' => 'Output diff on test failure', ), ), 'doc' => '[testfile|dir ...] Run regression tests with PHP\'s regression testing script (run-tests.php).', ), ); var $output; /** * PEAR_Command_Test constructor. * * @access public */ function __construct(&$ui, &$config) { parent::__construct($ui, $config); } function doRunTests($command, $options, $params) { if (isset($options['phpunit']) && isset($options['tapoutput'])) { return $this->raiseError('ERROR: cannot use both --phpunit and --tapoutput at the same time'); } require_once 'PEAR/Common.php'; require_once 'System.php'; $log = new PEAR_Common; $log->ui = &$this->ui; // slightly hacky, but it will work $tests = array(); $depth = isset($options['recur']) ? 14 : 1; if (!count($params)) { $params[] = '.'; } if (isset($options['package'])) { $oldparams = $params; $params = array(); $reg = &$this->config->getRegistry(); foreach ($oldparams as $param) { $pname = $reg->parsePackageName($param, $this->config->get('default_channel')); if (PEAR::isError($pname)) { return $this->raiseError($pname); } $package = &$reg->getPackage($pname['package'], $pname['channel']); if (!$package) { return PEAR::raiseError('Unknown package "' . $reg->parsedPackageNameToString($pname) . '"'); } $filelist = $package->getFilelist(); foreach ($filelist as $name => $atts) { if (isset($atts['role']) && $atts['role'] != 'test') { continue; } if (isset($options['phpunit']) && preg_match('/AllTests\.php\\z/i', $name)) { $params[] = $atts['installed_as']; continue; } elseif (!preg_match('/\.phpt\\z/', $name)) { continue; } $params[] = $atts['installed_as']; } } } foreach ($params as $p) { if (is_dir($p)) { if (isset($options['phpunit'])) { $dir = System::find(array($p, '-type', 'f', '-maxdepth', $depth, '-name', 'AllTests.php')); if (count($dir)) { foreach ($dir as $p) { $p = realpath($p); if (!count($tests) || (count($tests) && strlen($p) < strlen($tests[0]))) { // this is in a higher-level directory, use this one instead. $tests = array($p); } } } continue; } $args = array($p, '-type', 'f', '-name', '*.phpt'); } else { if (isset($options['phpunit'])) { if (preg_match('/AllTests\.php\\z/i', $p)) { $p = realpath($p); if (!count($tests) || (count($tests) && strlen($p) < strlen($tests[0]))) { // this is in a higher-level directory, use this one instead. $tests = array($p); } } continue; } if (file_exists($p) && preg_match('/\.phpt$/', $p)) { $tests[] = $p; continue; } if (!preg_match('/\.phpt\\z/', $p)) { $p .= '.phpt'; } $args = array(dirname($p), '-type', 'f', '-name', $p); } if (!isset($options['recur'])) { $args[] = '-maxdepth'; $args[] = 1; } $dir = System::find($args); $tests = array_merge($tests, $dir); } $ini_settings = ''; if (isset($options['ini'])) { $ini_settings .= $options['ini']; } if (isset($_ENV['TEST_PHP_INCLUDE_PATH'])) { $ini_settings .= " -d include_path={$_ENV['TEST_PHP_INCLUDE_PATH']}"; } if ($ini_settings) { $this->ui->outputData('Using INI settings: "' . $ini_settings . '"'); } $skipped = $passed = $failed = array(); $tests_count = count($tests); $this->ui->outputData('Running ' . $tests_count . ' tests', $command); $start = time(); if (isset($options['realtimelog']) && file_exists('run-tests.log')) { unlink('run-tests.log'); } if (isset($options['tapoutput'])) { $tap = '1..' . $tests_count . "\n"; } require_once 'PEAR/RunTest.php'; $run = new PEAR_RunTest($log, $options); $run->tests_count = $tests_count; if (isset($options['coverage']) && extension_loaded('xdebug')){ $run->xdebug_loaded = true; } else { $run->xdebug_loaded = false; } $j = $i = 1; foreach ($tests as $t) { if (isset($options['realtimelog'])) { $fp = @fopen('run-tests.log', 'a'); if ($fp) { fwrite($fp, "Running test [$i / $tests_count] $t..."); fclose($fp); } } PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); if (isset($options['phpunit'])) { $result = $run->runPHPUnit($t, $ini_settings); } else { $result = $run->run($t, $ini_settings, $j); } PEAR::staticPopErrorHandling(); if (PEAR::isError($result)) { $this->ui->log($result->getMessage()); continue; } if (isset($options['tapoutput'])) { $tap .= $result[0] . ' ' . $i . $result[1] . "\n"; continue; } if (isset($options['realtimelog'])) { $fp = @fopen('run-tests.log', 'a'); if ($fp) { fwrite($fp, "$result\n"); fclose($fp); } } if ($result == 'FAILED') { $failed[] = $t; } if ($result == 'PASSED') { $passed[] = $t; } if ($result == 'SKIPPED') { $skipped[] = $t; } $j++; } $total = date('i:s', time() - $start); if (isset($options['tapoutput'])) { $fp = @fopen('run-tests.log', 'w'); if ($fp) { fwrite($fp, $tap, strlen($tap)); fclose($fp); $this->ui->outputData('wrote TAP-format log to "' .realpath('run-tests.log') . '"', $command); } } else { if (count($failed)) { $output = "TOTAL TIME: $total\n"; $output .= count($passed) . " PASSED TESTS\n"; $output .= count($skipped) . " SKIPPED TESTS\n"; $output .= count($failed) . " FAILED TESTS:\n"; foreach ($failed as $failure) { $output .= $failure . "\n"; } $mode = isset($options['realtimelog']) ? 'a' : 'w'; $fp = @fopen('run-tests.log', $mode); if ($fp) { fwrite($fp, $output, strlen($output)); fclose($fp); $this->ui->outputData('wrote log to "' . realpath('run-tests.log') . '"', $command); } } elseif (file_exists('run-tests.log') && !is_dir('run-tests.log')) { @unlink('run-tests.log'); } } $this->ui->outputData('TOTAL TIME: ' . $total); $this->ui->outputData(count($passed) . ' PASSED TESTS', $command); $this->ui->outputData(count($skipped) . ' SKIPPED TESTS', $command); if (count($failed)) { $this->ui->outputData(count($failed) . ' FAILED TESTS:', $command); foreach ($failed as $failure) { $this->ui->outputData($failure, $command); } } if (count($failed) == 0) { return true; } return $this->raiseError('Some tests failed'); } } PK=1]Wz pear/PEAR/Command/Auth.phpnu[ * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 * @deprecated since 1.8.0alpha1 */ /** * base class */ require_once 'PEAR/Command/Channels.php'; /** * PEAR commands for login/logout * * @category pear * @package PEAR * @author Stig Bakken * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 * @deprecated since 1.8.0alpha1 */ class PEAR_Command_Auth extends PEAR_Command_Channels { var $commands = array( 'login' => array( 'summary' => 'Connects and authenticates to remote server [Deprecated in favor of channel-login]', 'shortcut' => 'li', 'function' => 'doLogin', 'options' => array(), 'doc' => ' WARNING: This function is deprecated in favor of using channel-login Log in to a remote channel server. If is not supplied, the default channel is used. To use remote functions in the installer that require any kind of privileges, you need to log in first. The username and password you enter here will be stored in your per-user PEAR configuration (~/.pearrc on Unix-like systems). After logging in, your username and password will be sent along in subsequent operations on the remote server.', ), 'logout' => array( 'summary' => 'Logs out from the remote server [Deprecated in favor of channel-logout]', 'shortcut' => 'lo', 'function' => 'doLogout', 'options' => array(), 'doc' => ' WARNING: This function is deprecated in favor of using channel-logout Logs out from the remote server. This command does not actually connect to the remote server, it only deletes the stored username and password from your user configuration.', ) ); /** * PEAR_Command_Auth constructor. * * @access public */ function __construct(&$ui, &$config) { parent::__construct($ui, $config); } } PK=1]݄ Downloads each available package from the default channel doDownloadAll da c specify a channel other than the default channel CHAN Requests a list of available packages from the default channel ({config default_channel}) and downloads them to current working directory. Note: only packages within preferred_state ({config preferred_state}) will be downloaded PK=1]Y}pear/PEAR/Command/Mirror.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.2.0 */ /** * base class */ require_once 'PEAR/Command/Common.php'; /** * PEAR commands for providing file mirrors * * @category pear * @package PEAR * @author Alexander Merz * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.2.0 */ class PEAR_Command_Mirror extends PEAR_Command_Common { var $commands = array( 'download-all' => array( 'summary' => 'Downloads each available package from the default channel', 'function' => 'doDownloadAll', 'shortcut' => 'da', 'options' => array( 'channel' => array( 'shortopt' => 'c', 'doc' => 'specify a channel other than the default channel', 'arg' => 'CHAN', ), ), 'doc' => ' Requests a list of available packages from the default channel ({config default_channel}) and downloads them to current working directory. Note: only packages within preferred_state ({config preferred_state}) will be downloaded' ), ); /** * PEAR_Command_Mirror constructor. * * @access public * @param object PEAR_Frontend a reference to an frontend * @param object PEAR_Config a reference to the configuration data */ function __construct(&$ui, &$config) { parent::__construct($ui, $config); } /** * For unit-testing */ function &factory($a) { $a = &PEAR_Command::factory($a, $this->config); return $a; } /** * retrieves a list of avaible Packages from master server * and downloads them * * @access public * @param string $command the command * @param array $options the command options before the command * @param array $params the stuff after the command name * @return bool true if successful * @throw PEAR_Error */ function doDownloadAll($command, $options, $params) { $savechannel = $this->config->get('default_channel'); $reg = &$this->config->getRegistry(); $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel'); if (!$reg->channelExists($channel)) { $this->config->set('default_channel', $savechannel); return $this->raiseError('Channel "' . $channel . '" does not exist'); } $this->config->set('default_channel', $channel); $this->ui->outputData('Using Channel ' . $this->config->get('default_channel')); $chan = $reg->getChannel($channel); if (PEAR::isError($chan)) { return $this->raiseError($chan); } if ($chan->supportsREST($this->config->get('preferred_mirror')) && $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) { $rest = &$this->config->getREST('1.0', array()); $remoteInfo = array_flip($rest->listPackages($base, $channel)); } if (PEAR::isError($remoteInfo)) { return $remoteInfo; } $cmd = &$this->factory("download"); if (PEAR::isError($cmd)) { return $cmd; } $this->ui->outputData('Using Preferred State of ' . $this->config->get('preferred_state')); $this->ui->outputData('Gathering release information, please wait...'); /** * Error handling not necessary, because already done by * the download command */ PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $err = $cmd->run('download', array('downloadonly' => true), array_keys($remoteInfo)); PEAR::staticPopErrorHandling(); $this->config->set('default_channel', $savechannel); if (PEAR::isError($err)) { $this->ui->outputData($err->getMessage()); } return true; } } PK=1]*؀;;pear/PEAR/Command/Install.phpnu[ * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ /** * base class */ require_once 'PEAR/Command/Common.php'; /** * PEAR commands for installation or deinstallation/upgrading of * packages. * * @category pear * @package PEAR * @author Stig Bakken * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ class PEAR_Command_Install extends PEAR_Command_Common { // {{{ properties var $commands = array( 'install' => array( 'summary' => 'Install Package', 'function' => 'doInstall', 'shortcut' => 'i', 'options' => array( 'force' => array( 'shortopt' => 'f', 'doc' => 'will overwrite newer installed packages', ), 'loose' => array( 'shortopt' => 'l', 'doc' => 'do not check for recommended dependency version', ), 'nodeps' => array( 'shortopt' => 'n', 'doc' => 'ignore dependencies, install anyway', ), 'register-only' => array( 'shortopt' => 'r', 'doc' => 'do not install files, only register the package as installed', ), 'soft' => array( 'shortopt' => 's', 'doc' => 'soft install, fail silently, or upgrade if already installed', ), 'nobuild' => array( 'shortopt' => 'B', 'doc' => 'don\'t build C extensions', ), 'configureoptions' => array( 'shortopt' => 'D', 'arg' => 'OPTION1=VALUE[ OPTION2=VALUE]', 'doc' => 'space-delimited list of configure options', ), 'nocompress' => array( 'shortopt' => 'Z', 'doc' => 'request uncompressed files when downloading', ), 'installroot' => array( 'shortopt' => 'R', 'arg' => 'DIR', 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT), use packagingroot for RPM', ), 'packagingroot' => array( 'shortopt' => 'P', 'arg' => 'DIR', 'doc' => 'root directory used when packaging files, like RPM packaging', ), 'ignore-errors' => array( 'doc' => 'force install even if there were errors', ), 'alldeps' => array( 'shortopt' => 'a', 'doc' => 'install all required and optional dependencies', ), 'onlyreqdeps' => array( 'shortopt' => 'o', 'doc' => 'install all required dependencies', ), 'offline' => array( 'shortopt' => 'O', 'doc' => 'do not attempt to download any urls or contact channels', ), 'pretend' => array( 'shortopt' => 'p', 'doc' => 'Only list the packages that would be downloaded', ), ), 'doc' => '[channel/] ... Installs one or more PEAR packages. You can specify a package to install in four ways: "Package-1.0.tgz" : installs from a local file "http://example.com/Package-1.0.tgz" : installs from anywhere on the net. "package.xml" : installs the package described in package.xml. Useful for testing, or for wrapping a PEAR package in another package manager such as RPM. "Package[-version/state][.tar]" : queries your default channel\'s server ({config master_server}) and downloads the newest package with the preferred quality/state ({config preferred_state}). To retrieve Package version 1.1, use "Package-1.1," to retrieve Package state beta, use "Package-beta." To retrieve an uncompressed file, append .tar (make sure there is no file by the same name first) To download a package from another channel, prefix with the channel name like "channel/Package" More than one package may be specified at once. It is ok to mix these four ways of specifying packages. '), 'upgrade' => array( 'summary' => 'Upgrade Package', 'function' => 'doInstall', 'shortcut' => 'up', 'options' => array( 'channel' => array( 'shortopt' => 'c', 'doc' => 'upgrade packages from a specific channel', 'arg' => 'CHAN', ), 'force' => array( 'shortopt' => 'f', 'doc' => 'overwrite newer installed packages', ), 'loose' => array( 'shortopt' => 'l', 'doc' => 'do not check for recommended dependency version', ), 'nodeps' => array( 'shortopt' => 'n', 'doc' => 'ignore dependencies, upgrade anyway', ), 'register-only' => array( 'shortopt' => 'r', 'doc' => 'do not install files, only register the package as upgraded', ), 'nobuild' => array( 'shortopt' => 'B', 'doc' => 'don\'t build C extensions', ), 'nocompress' => array( 'shortopt' => 'Z', 'doc' => 'request uncompressed files when downloading', ), 'installroot' => array( 'shortopt' => 'R', 'arg' => 'DIR', 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)', ), 'ignore-errors' => array( 'doc' => 'force install even if there were errors', ), 'alldeps' => array( 'shortopt' => 'a', 'doc' => 'install all required and optional dependencies', ), 'onlyreqdeps' => array( 'shortopt' => 'o', 'doc' => 'install all required dependencies', ), 'offline' => array( 'shortopt' => 'O', 'doc' => 'do not attempt to download any urls or contact channels', ), 'pretend' => array( 'shortopt' => 'p', 'doc' => 'Only list the packages that would be downloaded', ), ), 'doc' => ' ... Upgrades one or more PEAR packages. See documentation for the "install" command for ways to specify a package. When upgrading, your package will be updated if the provided new package has a higher version number (use the -f option if you need to upgrade anyway). More than one package may be specified at once. '), 'upgrade-all' => array( 'summary' => 'Upgrade All Packages [Deprecated in favor of calling upgrade with no parameters]', 'function' => 'doUpgradeAll', 'shortcut' => 'ua', 'options' => array( 'channel' => array( 'shortopt' => 'c', 'doc' => 'upgrade packages from a specific channel', 'arg' => 'CHAN', ), 'nodeps' => array( 'shortopt' => 'n', 'doc' => 'ignore dependencies, upgrade anyway', ), 'register-only' => array( 'shortopt' => 'r', 'doc' => 'do not install files, only register the package as upgraded', ), 'nobuild' => array( 'shortopt' => 'B', 'doc' => 'don\'t build C extensions', ), 'nocompress' => array( 'shortopt' => 'Z', 'doc' => 'request uncompressed files when downloading', ), 'installroot' => array( 'shortopt' => 'R', 'arg' => 'DIR', 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT), use packagingroot for RPM', ), 'ignore-errors' => array( 'doc' => 'force install even if there were errors', ), 'loose' => array( 'doc' => 'do not check for recommended dependency version', ), ), 'doc' => ' WARNING: This function is deprecated in favor of using the upgrade command with no params Upgrades all packages that have a newer release available. Upgrades are done only if there is a release available of the state specified in "preferred_state" (currently {config preferred_state}), or a state considered more stable. '), 'uninstall' => array( 'summary' => 'Un-install Package', 'function' => 'doUninstall', 'shortcut' => 'un', 'options' => array( 'nodeps' => array( 'shortopt' => 'n', 'doc' => 'ignore dependencies, uninstall anyway', ), 'register-only' => array( 'shortopt' => 'r', 'doc' => 'do not remove files, only register the packages as not installed', ), 'installroot' => array( 'shortopt' => 'R', 'arg' => 'DIR', 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)', ), 'ignore-errors' => array( 'doc' => 'force install even if there were errors', ), 'offline' => array( 'shortopt' => 'O', 'doc' => 'do not attempt to uninstall remotely', ), ), 'doc' => '[channel/] ... Uninstalls one or more PEAR packages. More than one package may be specified at once. Prefix with channel name to uninstall from a channel not in your default channel ({config default_channel}) '), 'bundle' => array( 'summary' => 'Unpacks a Pecl Package', 'function' => 'doBundle', 'shortcut' => 'bun', 'options' => array( 'destination' => array( 'shortopt' => 'd', 'arg' => 'DIR', 'doc' => 'Optional destination directory for unpacking (defaults to current path or "ext" if exists)', ), 'force' => array( 'shortopt' => 'f', 'doc' => 'Force the unpacking even if there were errors in the package', ), ), 'doc' => ' Unpacks a Pecl Package into the selected location. It will download the package if needed. '), 'run-scripts' => array( 'summary' => 'Run Post-Install Scripts bundled with a package', 'function' => 'doRunScripts', 'shortcut' => 'rs', 'options' => array( ), 'doc' => ' Run post-installation scripts in package , if any exist. '), ); // }}} // {{{ constructor /** * PEAR_Command_Install constructor. * * @access public */ function __construct(&$ui, &$config) { parent::__construct($ui, $config); } // }}} /** * For unit testing purposes */ function &getDownloader(&$ui, $options, &$config) { if (!class_exists('PEAR_Downloader')) { require_once 'PEAR/Downloader.php'; } $a = new PEAR_Downloader($ui, $options, $config); return $a; } /** * For unit testing purposes */ function &getInstaller(&$ui) { if (!class_exists('PEAR_Installer')) { require_once 'PEAR/Installer.php'; } $a = new PEAR_Installer($ui); return $a; } function enableExtension($binaries, $type) { if (!($phpini = $this->config->get('php_ini', null, 'pear.php.net'))) { return PEAR::raiseError('configuration option "php_ini" is not set to php.ini location'); } $ini = $this->_parseIni($phpini); if (PEAR::isError($ini)) { return $ini; } $line = 0; if ($type == 'extsrc' || $type == 'extbin') { $search = 'extensions'; $enable = 'extension'; } else { $search = 'zend_extensions'; ob_start(); phpinfo(INFO_GENERAL); $info = ob_get_contents(); ob_end_clean(); $debug = function_exists('leak') ? '_debug' : ''; $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : ''; $enable = 'zend_extension' . $debug . $ts; } foreach ($ini[$search] as $line => $extension) { if (in_array($extension, $binaries, true) || in_array( $ini['extension_dir'] . DIRECTORY_SEPARATOR . $extension, $binaries, true)) { // already enabled - assume if one is, all are return true; } } if ($line) { $newini = array_slice($ini['all'], 0, $line); } else { $newini = array(); } foreach ($binaries as $binary) { if ($ini['extension_dir']) { $binary = basename($binary); } $newini[] = $enable . '="' . $binary . '"' . (OS_UNIX ? "\n" : "\r\n"); } $newini = array_merge($newini, array_slice($ini['all'], $line)); $fp = @fopen($phpini, 'wb'); if (!$fp) { return PEAR::raiseError('cannot open php.ini "' . $phpini . '" for writing'); } foreach ($newini as $line) { fwrite($fp, $line); } fclose($fp); return true; } function disableExtension($binaries, $type) { if (!($phpini = $this->config->get('php_ini', null, 'pear.php.net'))) { return PEAR::raiseError('configuration option "php_ini" is not set to php.ini location'); } $ini = $this->_parseIni($phpini); if (PEAR::isError($ini)) { return $ini; } $line = 0; if ($type == 'extsrc' || $type == 'extbin') { $search = 'extensions'; $enable = 'extension'; } else { $search = 'zend_extensions'; ob_start(); phpinfo(INFO_GENERAL); $info = ob_get_contents(); ob_end_clean(); $debug = function_exists('leak') ? '_debug' : ''; $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : ''; $enable = 'zend_extension' . $debug . $ts; } $found = false; foreach ($ini[$search] as $line => $extension) { if (in_array($extension, $binaries, true) || in_array( $ini['extension_dir'] . DIRECTORY_SEPARATOR . $extension, $binaries, true)) { $found = true; break; } } if (!$found) { // not enabled return true; } $fp = @fopen($phpini, 'wb'); if (!$fp) { return PEAR::raiseError('cannot open php.ini "' . $phpini . '" for writing'); } if ($line) { $newini = array_slice($ini['all'], 0, $line); // delete the enable line $newini = array_merge($newini, array_slice($ini['all'], $line + 1)); } else { $newini = array_slice($ini['all'], 1); } foreach ($newini as $line) { fwrite($fp, $line); } fclose($fp); return true; } function _parseIni($filename) { if (!file_exists($filename)) { return PEAR::raiseError('php.ini "' . $filename . '" does not exist'); } if (filesize($filename) > 300000) { return PEAR::raiseError('php.ini "' . $filename . '" is too large, aborting'); } ob_start(); phpinfo(INFO_GENERAL); $info = ob_get_contents(); ob_end_clean(); $debug = function_exists('leak') ? '_debug' : ''; $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : ''; $zend_extension_line = 'zend_extension' . $debug . $ts; $all = @file($filename); if ($all === false) { return PEAR::raiseError('php.ini "' . $filename .'" could not be read'); } $zend_extensions = $extensions = array(); // assume this is right, but pull from the php.ini if it is found $extension_dir = ini_get('extension_dir'); foreach ($all as $linenum => $line) { $line = trim($line); if (!$line) { continue; } if ($line[0] == ';') { continue; } if (strtolower(substr($line, 0, 13)) == 'extension_dir') { $line = trim(substr($line, 13)); if ($line[0] == '=') { $x = trim(substr($line, 1)); $x = explode(';', $x); $extension_dir = str_replace('"', '', array_shift($x)); continue; } } if (strtolower(substr($line, 0, 9)) == 'extension') { $line = trim(substr($line, 9)); if ($line[0] == '=') { $x = trim(substr($line, 1)); $x = explode(';', $x); $extensions[$linenum] = str_replace('"', '', array_shift($x)); continue; } } if (strtolower(substr($line, 0, strlen($zend_extension_line))) == $zend_extension_line) { $line = trim(substr($line, strlen($zend_extension_line))); if ($line[0] == '=') { $x = trim(substr($line, 1)); $x = explode(';', $x); $zend_extensions[$linenum] = str_replace('"', '', array_shift($x)); continue; } } } return array( 'extensions' => $extensions, 'zend_extensions' => $zend_extensions, 'extension_dir' => $extension_dir, 'all' => $all, ); } // {{{ doInstall() function doInstall($command, $options, $params) { if (!class_exists('PEAR_PackageFile')) { require_once 'PEAR/PackageFile.php'; } if (isset($options['installroot']) && isset($options['packagingroot'])) { return $this->raiseError('ERROR: cannot use both --installroot and --packagingroot'); } $reg = &$this->config->getRegistry(); $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel'); if (!$reg->channelExists($channel)) { return $this->raiseError('Channel "' . $channel . '" does not exist'); } if (empty($this->installer)) { $this->installer = &$this->getInstaller($this->ui); } if ($command == 'upgrade' || $command == 'upgrade-all') { // If people run the upgrade command but pass nothing, emulate a upgrade-all if ($command == 'upgrade' && empty($params)) { return $this->doUpgradeAll($command, $options, $params); } $options['upgrade'] = true; } else { $packages = $params; } $instreg = &$reg; // instreg used to check if package is installed if (isset($options['packagingroot']) && !isset($options['upgrade'])) { $packrootphp_dir = $this->installer->_prependPath( $this->config->get('php_dir', null, 'pear.php.net'), $options['packagingroot']); $metadata_dir = $this->config->get('metadata_dir', null, 'pear.php.net'); if ($metadata_dir) { $metadata_dir = $this->installer->_prependPath( $metadata_dir, $options['packagingroot']); } $instreg = new PEAR_Registry($packrootphp_dir, false, false, $metadata_dir); // other instreg! if ($this->config->get('verbose') > 2) { $this->ui->outputData('using package root: ' . $options['packagingroot']); } } $abstractpackages = $otherpackages = array(); // parse params PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); foreach ($params as $param) { if (strpos($param, 'http://') === 0) { $otherpackages[] = $param; continue; } if (strpos($param, 'channel://') === false && @file_exists($param)) { if (isset($options['force'])) { $otherpackages[] = $param; continue; } $pkg = new PEAR_PackageFile($this->config); $pf = $pkg->fromAnyFile($param, PEAR_VALIDATE_DOWNLOADING); if (PEAR::isError($pf)) { $otherpackages[] = $param; continue; } $exists = $reg->packageExists($pf->getPackage(), $pf->getChannel()); $pversion = $reg->packageInfo($pf->getPackage(), 'version', $pf->getChannel()); $version_compare = version_compare($pf->getVersion(), $pversion, '<='); if ($exists && $version_compare) { if ($this->config->get('verbose')) { $this->ui->outputData('Ignoring installed package ' . $reg->parsedPackageNameToString( array('package' => $pf->getPackage(), 'channel' => $pf->getChannel()), true)); } continue; } $otherpackages[] = $param; continue; } $e = $reg->parsePackageName($param, $channel); if (PEAR::isError($e)) { $otherpackages[] = $param; } else { $abstractpackages[] = $e; } } PEAR::staticPopErrorHandling(); // if there are any local package .tgz or remote static url, we can't // filter. The filter only works for abstract packages if (count($abstractpackages) && !isset($options['force'])) { // when not being forced, only do necessary upgrades/installs if (isset($options['upgrade'])) { $abstractpackages = $this->_filterUptodatePackages($abstractpackages, $command); } else { $count = count($abstractpackages); foreach ($abstractpackages as $i => $package) { if (isset($package['group'])) { // do not filter out install groups continue; } if ($instreg->packageExists($package['package'], $package['channel'])) { if ($count > 1) { if ($this->config->get('verbose')) { $this->ui->outputData('Ignoring installed package ' . $reg->parsedPackageNameToString($package, true)); } unset($abstractpackages[$i]); } elseif ($count === 1) { // Lets try to upgrade it since it's already installed $options['upgrade'] = true; } } } } $abstractpackages = array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages); } elseif (count($abstractpackages)) { $abstractpackages = array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages); } $packages = array_merge($abstractpackages, $otherpackages); if (!count($packages)) { $c = ''; if (isset($options['channel'])){ $c .= ' in channel "' . $options['channel'] . '"'; } $this->ui->outputData('Nothing to ' . $command . $c); return true; } $this->downloader = &$this->getDownloader($this->ui, $options, $this->config); $errors = $downloaded = $binaries = array(); $downloaded = &$this->downloader->download($packages); if (PEAR::isError($downloaded)) { return $this->raiseError($downloaded); } $errors = $this->downloader->getErrorMsgs(); if (count($errors)) { $err = array(); $err['data'] = array(); foreach ($errors as $error) { if ($error !== null) { $err['data'][] = array($error); } } if (!empty($err['data'])) { $err['headline'] = 'Install Errors'; $this->ui->outputData($err); } if (!count($downloaded)) { return $this->raiseError("$command failed"); } } $data = array( 'headline' => 'Packages that would be Installed' ); if (isset($options['pretend'])) { foreach ($downloaded as $package) { $data['data'][] = array($reg->parsedPackageNameToString($package->getParsedPackage())); } $this->ui->outputData($data, 'pretend'); return true; } $this->installer->setOptions($options); $this->installer->sortPackagesForInstall($downloaded); if (PEAR::isError($err = $this->installer->setDownloadedPackages($downloaded))) { $this->raiseError($err->getMessage()); return true; } $binaries = $extrainfo = array(); foreach ($downloaded as $param) { PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $info = $this->installer->install($param, $options); PEAR::staticPopErrorHandling(); if (PEAR::isError($info)) { $oldinfo = $info; $pkg = &$param->getPackageFile(); if ($info->getCode() != PEAR_INSTALLER_NOBINARY) { if (!($info = $pkg->installBinary($this->installer))) { return $this->raiseError('ERROR: ' .$oldinfo->getMessage()); } // we just installed a different package than requested, // let's change the param and info so that the rest of this works $param = $info[0]; $info = $info[1]; } } if (!is_array($info)) { return $this->raiseError("$command failed"); } if ($param->getPackageType() == 'extsrc' || $param->getPackageType() == 'extbin' || $param->getPackageType() == 'zendextsrc' || $param->getPackageType() == 'zendextbin' ) { $pkg = &$param->getPackageFile(); if ($instbin = $pkg->getInstalledBinary()) { $instpkg = &$instreg->getPackage($instbin, $pkg->getChannel()); } else { $instpkg = &$instreg->getPackage($pkg->getPackage(), $pkg->getChannel()); } foreach ($instpkg->getFilelist() as $name => $atts) { $pinfo = pathinfo($atts['installed_as']); if (!isset($pinfo['extension']) || in_array($pinfo['extension'], array('c', 'h')) ) { continue; // make sure we don't match php_blah.h } if ((strpos($pinfo['basename'], 'php_') === 0 && $pinfo['extension'] == 'dll') || // most unices $pinfo['extension'] == 'so' || // hp-ux $pinfo['extension'] == 'sl') { $binaries[] = array($atts['installed_as'], $pinfo); break; } } if (count($binaries)) { foreach ($binaries as $pinfo) { PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $ret = $this->enableExtension(array($pinfo[0]), $param->getPackageType()); PEAR::staticPopErrorHandling(); if (PEAR::isError($ret)) { $extrainfo[] = $ret->getMessage(); if ($param->getPackageType() == 'extsrc' || $param->getPackageType() == 'extbin') { $exttype = 'extension'; $extpath = $pinfo[1]['basename']; } else { $exttype = 'zend_extension'; $extpath = $atts['installed_as']; } $extrainfo[] = 'You should add "' . $exttype . '=' . $extpath . '" to php.ini'; } else { $extrainfo[] = 'Extension ' . $instpkg->getProvidesExtension() . ' enabled in php.ini'; } } } } if ($this->config->get('verbose') > 0) { $chan = $param->getChannel(); $label = $reg->parsedPackageNameToString( array( 'channel' => $chan, 'package' => $param->getPackage(), 'version' => $param->getVersion(), )); $out = array('data' => "$command ok: $label"); if (isset($info['release_warnings'])) { $out['release_warnings'] = $info['release_warnings']; } $this->ui->outputData($out, $command); if (!isset($options['register-only']) && !isset($options['offline'])) { if ($this->config->isDefinedLayer('ftp')) { PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $info = $this->installer->ftpInstall($param); PEAR::staticPopErrorHandling(); if (PEAR::isError($info)) { $this->ui->outputData($info->getMessage()); $this->ui->outputData("remote install failed: $label"); } else { $this->ui->outputData("remote install ok: $label"); } } } } $deps = $param->getDeps(); if ($deps) { if (isset($deps['group'])) { $groups = $deps['group']; if (!isset($groups[0])) { $groups = array($groups); } foreach ($groups as $group) { if ($group['attribs']['name'] == 'default') { // default group is always installed, unless the user // explicitly chooses to install another group continue; } $extrainfo[] = $param->getPackage() . ': Optional feature ' . $group['attribs']['name'] . ' available (' . $group['attribs']['hint'] . ')'; } $extrainfo[] = $param->getPackage() . ': To install optional features use "pear install ' . $reg->parsedPackageNameToString( array('package' => $param->getPackage(), 'channel' => $param->getChannel()), true) . '#featurename"'; } } $pkg = &$instreg->getPackage($param->getPackage(), $param->getChannel()); // $pkg may be NULL if install is a 'fake' install via --packagingroot if (is_object($pkg)) { $pkg->setConfig($this->config); if ($list = $pkg->listPostinstallScripts()) { $pn = $reg->parsedPackageNameToString(array('channel' => $param->getChannel(), 'package' => $param->getPackage()), true); $extrainfo[] = $pn . ' has post-install scripts:'; foreach ($list as $file) { $extrainfo[] = $file; } $extrainfo[] = $param->getPackage() . ': Use "pear run-scripts ' . $pn . '" to finish setup.'; $extrainfo[] = 'DO NOT RUN SCRIPTS FROM UNTRUSTED SOURCES'; } } } if (count($extrainfo)) { foreach ($extrainfo as $info) { $this->ui->outputData($info); } } return true; } // }}} // {{{ doUpgradeAll() function doUpgradeAll($command, $options, $params) { $reg = &$this->config->getRegistry(); $upgrade = array(); if (isset($options['channel'])) { $channels = array($options['channel']); } else { $channels = $reg->listChannels(); } foreach ($channels as $channel) { if ($channel == '__uri') { continue; } // parse name with channel foreach ($reg->listPackages($channel) as $name) { $upgrade[] = $reg->parsedPackageNameToString(array( 'channel' => $channel, 'package' => $name )); } } $err = $this->doInstall($command, $options, $upgrade); if (PEAR::isError($err)) { $this->ui->outputData($err->getMessage(), $command); } } // }}} // {{{ doUninstall() function doUninstall($command, $options, $params) { if (count($params) < 1) { return $this->raiseError("Please supply the package(s) you want to uninstall"); } if (empty($this->installer)) { $this->installer = &$this->getInstaller($this->ui); } if (isset($options['remoteconfig'])) { $e = $this->config->readFTPConfigFile($options['remoteconfig']); if (!PEAR::isError($e)) { $this->installer->setConfig($this->config); } } $reg = &$this->config->getRegistry(); $newparams = array(); $binaries = array(); $badparams = array(); foreach ($params as $pkg) { $channel = $this->config->get('default_channel'); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $parsed = $reg->parsePackageName($pkg, $channel); PEAR::staticPopErrorHandling(); if (!$parsed || PEAR::isError($parsed)) { $badparams[] = $pkg; continue; } $package = $parsed['package']; $channel = $parsed['channel']; $info = &$reg->getPackage($package, $channel); if ($info === null && ($channel == 'pear.php.net' || $channel == 'pecl.php.net')) { // make sure this isn't a package that has flipped from pear to pecl but // used a package.xml 1.0 $testc = ($channel == 'pear.php.net') ? 'pecl.php.net' : 'pear.php.net'; $info = &$reg->getPackage($package, $testc); if ($info !== null) { $channel = $testc; } } if ($info === null) { $badparams[] = $pkg; } else { $newparams[] = &$info; // check for binary packages (this is an alias for those packages if so) if ($installedbinary = $info->getInstalledBinary()) { $this->ui->log('adding binary package ' . $reg->parsedPackageNameToString(array('channel' => $channel, 'package' => $installedbinary), true)); $newparams[] = &$reg->getPackage($installedbinary, $channel); } // add the contents of a dependency group to the list of installed packages if (isset($parsed['group'])) { $group = $info->getDependencyGroup($parsed['group']); if ($group) { $installed = $reg->getInstalledGroup($group); if ($installed) { foreach ($installed as $i => $p) { $newparams[] = &$installed[$i]; } } } } } } $err = $this->installer->sortPackagesForUninstall($newparams); if (PEAR::isError($err)) { $this->ui->outputData($err->getMessage(), $command); return true; } $params = $newparams; // twist this to use it to check on whether dependent packages are also being uninstalled // for circular dependencies like subpackages $this->installer->setUninstallPackages($newparams); $params = array_merge($params, $badparams); $binaries = array(); foreach ($params as $pkg) { $this->installer->pushErrorHandling(PEAR_ERROR_RETURN); if ($err = $this->installer->uninstall($pkg, $options)) { $this->installer->popErrorHandling(); if (PEAR::isError($err)) { $this->ui->outputData($err->getMessage(), $command); continue; } if ($pkg->getPackageType() == 'extsrc' || $pkg->getPackageType() == 'extbin' || $pkg->getPackageType() == 'zendextsrc' || $pkg->getPackageType() == 'zendextbin') { if ($instbin = $pkg->getInstalledBinary()) { continue; // this will be uninstalled later } foreach ($pkg->getFilelist() as $name => $atts) { $pinfo = pathinfo($atts['installed_as']); if (!isset($pinfo['extension']) || in_array($pinfo['extension'], array('c', 'h'))) { continue; // make sure we don't match php_blah.h } if ((strpos($pinfo['basename'], 'php_') === 0 && $pinfo['extension'] == 'dll') || // most unices $pinfo['extension'] == 'so' || // hp-ux $pinfo['extension'] == 'sl') { $binaries[] = array($atts['installed_as'], $pinfo); break; } } if (count($binaries)) { foreach ($binaries as $pinfo) { PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $ret = $this->disableExtension(array($pinfo[0]), $pkg->getPackageType()); PEAR::staticPopErrorHandling(); if (PEAR::isError($ret)) { $extrainfo[] = $ret->getMessage(); if ($pkg->getPackageType() == 'extsrc' || $pkg->getPackageType() == 'extbin') { $exttype = 'extension'; } else { ob_start(); phpinfo(INFO_GENERAL); $info = ob_get_contents(); ob_end_clean(); $debug = function_exists('leak') ? '_debug' : ''; $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : ''; $exttype = 'zend_extension' . $debug . $ts; } $this->ui->outputData('Unable to remove "' . $exttype . '=' . $pinfo[1]['basename'] . '" from php.ini', $command); } else { $this->ui->outputData('Extension ' . $pkg->getProvidesExtension() . ' disabled in php.ini', $command); } } } } $savepkg = $pkg; if ($this->config->get('verbose') > 0) { if (is_object($pkg)) { $pkg = $reg->parsedPackageNameToString($pkg); } $this->ui->outputData("uninstall ok: $pkg", $command); } if (!isset($options['offline']) && is_object($savepkg) && defined('PEAR_REMOTEINSTALL_OK')) { if ($this->config->isDefinedLayer('ftp')) { $this->installer->pushErrorHandling(PEAR_ERROR_RETURN); $info = $this->installer->ftpUninstall($savepkg); $this->installer->popErrorHandling(); if (PEAR::isError($info)) { $this->ui->outputData($info->getMessage()); $this->ui->outputData("remote uninstall failed: $pkg"); } else { $this->ui->outputData("remote uninstall ok: $pkg"); } } } } else { $this->installer->popErrorHandling(); if (!is_object($pkg)) { return $this->raiseError("uninstall failed: $pkg"); } $pkg = $reg->parsedPackageNameToString($pkg); } } return true; } // }}} // }}} // {{{ doBundle() /* (cox) It just downloads and untars the package, does not do any check that the PEAR_Installer::_installFile() does. */ function doBundle($command, $options, $params) { $opts = array( 'force' => true, 'nodeps' => true, 'soft' => true, 'downloadonly' => true ); $downloader = &$this->getDownloader($this->ui, $opts, $this->config); $reg = &$this->config->getRegistry(); if (count($params) < 1) { return $this->raiseError("Please supply the package you want to bundle"); } if (isset($options['destination'])) { if (!is_dir($options['destination'])) { System::mkdir('-p ' . $options['destination']); } $dest = realpath($options['destination']); } else { $pwd = getcwd(); $dir = $pwd . DIRECTORY_SEPARATOR . 'ext'; $dest = is_dir($dir) ? $dir : $pwd; } PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $err = $downloader->setDownloadDir($dest); PEAR::staticPopErrorHandling(); if (PEAR::isError($err)) { return PEAR::raiseError('download directory "' . $dest . '" is not writeable.'); } $result = &$downloader->download(array($params[0])); if (PEAR::isError($result)) { return $result; } if (!isset($result[0])) { return $this->raiseError('unable to unpack ' . $params[0]); } $pkgfile = &$result[0]->getPackageFile(); $pkgname = $pkgfile->getName(); $pkgversion = $pkgfile->getVersion(); // Unpacking ------------------------------------------------- $dest .= DIRECTORY_SEPARATOR . $pkgname; $orig = $pkgname . '-' . $pkgversion; $tar = new Archive_Tar($pkgfile->getArchiveFile()); if (!$tar->extractModify($dest, $orig)) { return $this->raiseError('unable to unpack ' . $pkgfile->getArchiveFile()); } $this->ui->outputData("Package ready at '$dest'"); // }}} } // }}} function doRunScripts($command, $options, $params) { if (!isset($params[0])) { return $this->raiseError('run-scripts expects 1 parameter: a package name'); } $reg = &$this->config->getRegistry(); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel')); PEAR::staticPopErrorHandling(); if (PEAR::isError($parsed)) { return $this->raiseError($parsed); } $package = &$reg->getPackage($parsed['package'], $parsed['channel']); if (!is_object($package)) { return $this->raiseError('Could not retrieve package "' . $params[0] . '" from registry'); } $package->setConfig($this->config); $package->runPostinstallScripts(); $this->ui->outputData('Install scripts complete', $command); return true; } /** * Given a list of packages, filter out those ones that are already up to date * * @param $packages: packages, in parsed array format ! * @return list of packages that can be upgraded */ function _filterUptodatePackages($packages, $command) { $reg = &$this->config->getRegistry(); $latestReleases = array(); $ret = array(); foreach ($packages as $package) { if (isset($package['group'])) { $ret[] = $package; continue; } $channel = $package['channel']; $name = $package['package']; if (!$reg->packageExists($name, $channel)) { $ret[] = $package; continue; } if (!isset($latestReleases[$channel])) { // fill in cache for this channel $chan = $reg->getChannel($channel); if (PEAR::isError($chan)) { return $this->raiseError($chan); } $base2 = false; $preferred_mirror = $this->config->get('preferred_mirror', null, $channel); if ($chan->supportsREST($preferred_mirror) && ( //($base2 = $chan->getBaseURL('REST1.4', $preferred_mirror)) || ($base = $chan->getBaseURL('REST1.0', $preferred_mirror)) ) ) { $dorest = true; } PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); if (!isset($package['state'])) { $state = $this->config->get('preferred_state', null, $channel); } else { $state = $package['state']; } if ($dorest) { if ($base2) { $rest = &$this->config->getREST('1.4', array()); $base = $base2; } else { $rest = &$this->config->getREST('1.0', array()); } $installed = array_flip($reg->listPackages($channel)); $latest = $rest->listLatestUpgrades($base, $state, $installed, $channel, $reg); } PEAR::staticPopErrorHandling(); if (PEAR::isError($latest)) { $this->ui->outputData('Error getting channel info from ' . $channel . ': ' . $latest->getMessage()); continue; } $latestReleases[$channel] = array_change_key_case($latest); } // check package for latest release $name_lower = strtolower($name); if (isset($latestReleases[$channel][$name_lower])) { // if not set, up to date $inst_version = $reg->packageInfo($name, 'version', $channel); $channel_version = $latestReleases[$channel][$name_lower]['version']; if (version_compare($channel_version, $inst_version, 'le')) { // installed version is up-to-date continue; } // maintain BC if ($command == 'upgrade-all') { $this->ui->outputData(array('data' => 'Will upgrade ' . $reg->parsedPackageNameToString($package)), $command); } $ret[] = $package; } } return $ret; } } PK=1]\S S pear/PEAR/Command/Build.phpnu[ * @author Tomas V.V.Cox * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ /** * base class */ require_once 'PEAR/Command/Common.php'; /** * PEAR commands for building extensions. * * @category pear * @package PEAR * @author Stig Bakken * @author Tomas V.V.Cox * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ class PEAR_Command_Build extends PEAR_Command_Common { var $commands = array( 'build' => array( 'summary' => 'Build an Extension From C Source', 'function' => 'doBuild', 'shortcut' => 'b', 'options' => array( 'configureoptions' => array( 'shortopt' => 'D', 'arg' => 'OPTION1=VALUE[ OPTION2=VALUE]', 'doc' => 'space-delimited list of configure options', ), ), 'doc' => '[package.xml] Builds one or more extensions contained in a package.' ), ); /** * PEAR_Command_Build constructor. * * @access public */ function __construct(&$ui, &$config) { parent::__construct($ui, $config); } function doBuild($command, $options, $params) { require_once 'PEAR/Builder.php'; if (sizeof($params) < 1) { $params[0] = 'package.xml'; } $configureoptions = empty($options['configureoptions']) ? '' : $options['configureoptions']; $builder = new PEAR_Builder($configureoptions, $this->ui); $this->debug = $this->config->get('verbose'); $err = $builder->build($params[0], array(&$this, 'buildCallback')); if (PEAR::isError($err)) { return $err; } return true; } function buildCallback($what, $data) { if (($what == 'cmdoutput' && $this->debug > 1) || ($what == 'output' && $this->debug > 0)) { $this->ui->outputData(rtrim($data), 'build'); } } } PK=1]s6 6 pear/PEAR/Command/Config.xmlnu[ Show All Settings doConfigShow csh c show configuration variables for another channel CHAN [layer] Displays all configuration values. An optional argument may be used to tell which configuration layer to display. Valid configuration layers are "user", "system" and "default". To display configurations for different channels, set the default_channel configuration variable and run config-show again. Show One Setting doConfigGet cg c show configuration variables for another channel CHAN <parameter> [layer] Displays the value of one configuration parameter. The first argument is the name of the parameter, an optional second argument may be used to tell which configuration layer to look in. Valid configuration layers are "user", "system" and "default". If no layer is specified, a value will be picked from the first layer that defines the parameter, in the order just specified. The configuration value will be retrieved for the channel specified by the default_channel configuration variable. Change Setting doConfigSet cs c show configuration variables for another channel CHAN <parameter> <value> [layer] Sets the value of one configuration parameter. The first argument is the name of the parameter, the second argument is the new value. Some parameters are subject to validation, and the command will fail with an error message if the new value does not make sense. An optional third argument may be used to specify in which layer to set the configuration parameter. The default layer is "user". The configuration value will be set for the current channel, which is controlled by the default_channel configuration variable. Show Information About Setting doConfigHelp ch [parameter] Displays help for a configuration parameter. Without arguments it displays help for all configuration parameters. Create a Default configuration file doConfigCreate coc w create a config file for a windows install <root path> <filename> Create a default configuration file with all directory configuration variables set to subdirectories of <root path>, and save it as <filename>. This is useful especially for creating a configuration file for a remote PEAR installation (using the --remoteconfig option of install, upgrade, and uninstall). PK=1]c  pear/PEAR/Command/Common.phpnu[ * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ /** * base class */ require_once 'PEAR.php'; /** * PEAR commands base class * * @category pear * @package PEAR * @author Stig Bakken * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ class PEAR_Command_Common extends PEAR { /** * PEAR_Config object used to pass user system and configuration * on when executing commands * * @var PEAR_Config */ var $config; /** * @var PEAR_Registry * @access protected */ var $_registry; /** * User Interface object, for all interaction with the user. * @var object */ var $ui; var $_deps_rel_trans = array( 'lt' => '<', 'le' => '<=', 'eq' => '=', 'ne' => '!=', 'gt' => '>', 'ge' => '>=', 'has' => '==' ); var $_deps_type_trans = array( 'pkg' => 'package', 'ext' => 'extension', 'php' => 'PHP', 'prog' => 'external program', 'ldlib' => 'external library for linking', 'rtlib' => 'external runtime library', 'os' => 'operating system', 'websrv' => 'web server', 'sapi' => 'SAPI backend' ); /** * PEAR_Command_Common constructor. * * @access public */ function __construct(&$ui, &$config) { parent::__construct(); $this->config = &$config; $this->ui = &$ui; } /** * Return a list of all the commands defined by this class. * @return array list of commands * @access public */ function getCommands() { $ret = array(); foreach (array_keys($this->commands) as $command) { $ret[$command] = $this->commands[$command]['summary']; } return $ret; } /** * Return a list of all the command shortcuts defined by this class. * @return array shortcut => command * @access public */ function getShortcuts() { $ret = array(); foreach (array_keys($this->commands) as $command) { if (isset($this->commands[$command]['shortcut'])) { $ret[$this->commands[$command]['shortcut']] = $command; } } return $ret; } function getOptions($command) { $shortcuts = $this->getShortcuts(); if (isset($shortcuts[$command])) { $command = $shortcuts[$command]; } if (isset($this->commands[$command]) && isset($this->commands[$command]['options'])) { return $this->commands[$command]['options']; } return null; } function getGetoptArgs($command, &$short_args, &$long_args) { $short_args = ''; $long_args = array(); if (empty($this->commands[$command]) || empty($this->commands[$command]['options'])) { return; } reset($this->commands[$command]['options']); foreach ($this->commands[$command]['options'] as $option => $info) { $larg = $sarg = ''; if (isset($info['arg'])) { if ($info['arg'][0] == '(') { $larg = '=='; $sarg = '::'; $arg = substr($info['arg'], 1, -1); } else { $larg = '='; $sarg = ':'; $arg = $info['arg']; } } if (isset($info['shortopt'])) { $short_args .= $info['shortopt'] . $sarg; } $long_args[] = $option . $larg; } } /** * Returns the help message for the given command * * @param string $command The command * @return mixed A fail string if the command does not have help or * a two elements array containing [0]=>help string, * [1]=> help string for the accepted cmd args */ function getHelp($command) { $config = &PEAR_Config::singleton(); if (!isset($this->commands[$command])) { return "No such command \"$command\""; } $help = null; if (isset($this->commands[$command]['doc'])) { $help = $this->commands[$command]['doc']; } if (empty($help)) { // XXX (cox) Fallback to summary if there is no doc (show both?) if (!isset($this->commands[$command]['summary'])) { return "No help for command \"$command\""; } $help = $this->commands[$command]['summary']; } if (preg_match_all('/{config\s+([^\}]+)}/', $help, $matches)) { foreach($matches[0] as $k => $v) { $help = preg_replace("/$v/", $config->get($matches[1][$k]), $help); } } return array($help, $this->getHelpArgs($command)); } /** * Returns the help for the accepted arguments of a command * * @param string $command * @return string The help string */ function getHelpArgs($command) { if (isset($this->commands[$command]['options']) && count($this->commands[$command]['options'])) { $help = "Options:\n"; foreach ($this->commands[$command]['options'] as $k => $v) { if (isset($v['arg'])) { if ($v['arg'][0] == '(') { $arg = substr($v['arg'], 1, -1); $sapp = " [$arg]"; $lapp = "[=$arg]"; } else { $sapp = " $v[arg]"; $lapp = "=$v[arg]"; } } else { $sapp = $lapp = ""; } if (isset($v['shortopt'])) { $s = $v['shortopt']; $help .= " -$s$sapp, --$k$lapp\n"; } else { $help .= " --$k$lapp\n"; } $p = " "; $doc = rtrim(str_replace("\n", "\n$p", $v['doc'])); $help .= " $doc\n"; } return $help; } return null; } function run($command, $options, $params) { if (empty($this->commands[$command]['function'])) { // look for shortcuts foreach (array_keys($this->commands) as $cmd) { if (isset($this->commands[$cmd]['shortcut']) && $this->commands[$cmd]['shortcut'] == $command) { if (empty($this->commands[$cmd]['function'])) { return $this->raiseError("unknown command `$command'"); } else { $func = $this->commands[$cmd]['function']; } $command = $cmd; //$command = $this->commands[$cmd]['function']; break; } } } else { $func = $this->commands[$command]['function']; } return $this->$func($command, $options, $params); } } PK=1] upear/PEAR/Command/Build.xmlnu[ Build an Extension From C Source doBuild b D OPTION1=VALUE[ OPTION2=VALUE] [package.xml] Builds one or more extensions contained in a package. PK=1]upear/PEAR/Command/Registry.xmlnu[ List Installed Packages In The Default Channel doList l c list installed packages from this channel CHAN a list installed packages from all channels i output fully channel-aware data, even on failure <package> If invoked without parameters, this command lists the PEAR packages installed in your php_dir ({config php_dir}). With a parameter, it lists the files in a package. List Files In Installed Package doFileList fl <package> List the files in an installed package. Shell Script Test doShellTest st <package> [[relation] version] Tests if a package is installed in the system. Will exit(1) if it is not. <relation> The version comparison operator. One of: <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne <version> The version to compare with Display information about a package doInfo in <package> Displays information about a package. The package argument may be a local package file, an URL to a package file, or the name of an installed package. PK=1]4{pear/PEAR/Command/Auth.xmlnu[ Connects and authenticates to remote server [Deprecated in favor of channel-login] doLogin li <channel name> WARNING: This function is deprecated in favor of using channel-login Log in to a remote channel server. If <channel name> is not supplied, the default channel is used. To use remote functions in the installer that require any kind of privileges, you need to log in first. The username and password you enter here will be stored in your per-user PEAR configuration (~/.pearrc on Unix-like systems). After logging in, your username and password will be sent along in subsequent operations on the remote server. Logs out from the remote server [Deprecated in favor of channel-logout] doLogout lo WARNING: This function is deprecated in favor of using channel-logout Logs out from the remote server. This command does not actually connect to the remote server, it only deletes the stored username and password from your user configuration. PK=1]Z h66pear/PEAR/Command/Package.xmlnu[ Build Package doPackage p Z Do not gzip the package file n Print the name of the packaged file. [descfile] [descfile2] Creates a PEAR package from its description file (usually called package.xml). If a second packagefile is passed in, then the packager will check to make sure that one is a package.xml version 1.0, and the other is a package.xml version 2.0. The package.xml version 1.0 will be saved as "package.xml" in the archive, and the other as "package2.xml" in the archive" Validate Package Consistency doPackageValidate pv Run a "cvs diff" for all files in a package doCvsDiff cd q Be quiet Q Be really quiet D Diff against revision of DATE DATE R Diff against tag for package release REL REL r Diff against revision REV REV c Generate context diff u Generate unified diff i Ignore case, consider upper- and lower-case letters equivalent b Ignore changes in amount of white space B Ignore changes that insert or delete blank lines Report only whether the files differ, no details n Don't do anything, just pretend <package.xml> Compares all the files in a package. Without any options, this command will compare the current code with the last checked-in code. Using the -r or -R option you may compare the current code with that of a specific release. Set SVN Release Tag doSvnTag sv q Be quiet F Move (slide) tag if it exists d Remove tag n Don't do anything, just pretend <package.xml> [files...] Sets a SVN tag on all files in a package. Use this command after you have packaged a distribution tarball with the "package" command to tag what revisions of what files were in that release. If need to fix something after running svntag once, but before the tarball is released to the public, use the "slide" option to move the release tag. to include files (such as a second package.xml, or tests not included in the release), pass them as additional parameters. Set CVS Release Tag doCvsTag ct q Be quiet Q Be really quiet F Move (slide) tag if it exists d Remove tag n Don't do anything, just pretend <package.xml> [files...] Sets a CVS tag on all files in a package. Use this command after you have packaged a distribution tarball with the "package" command to tag what revisions of what files were in that release. If need to fix something after running cvstag once, but before the tarball is released to the public, use the "slide" option to move the release tag. to include files (such as a second package.xml, or tests not included in the release), pass them as additional parameters. Show package dependencies doPackageDependencies pd <package-file> or <package.xml> or <install-package-name> List all dependencies the package has. Can take a tgz / tar file, package.xml or a package name of an installed package. Sign a package distribution file doSign si v Display GnuPG output <package-file> Signs a package distribution (.tar or .tgz) file with GnuPG. Builds an RPM spec file from a PEAR package doMakeRPM rpm t Use FILE as RPM spec file template FILE p Use FORMAT as format string for RPM package name, %s is replaced by the PEAR package name, defaults to "PEAR::%s". FORMAT <package-file> Creates an RPM .spec file for wrapping a PEAR package inside an RPM package. Intended to be used from the SPECS directory, with the PEAR package tarball in the SOURCES directory: $ pear makerpm ../SOURCES/Net_Socket-1.0.tgz Wrote RPM spec file PEAR::Net_Geo-1.0.spec $ rpm -bb PEAR::Net_Socket-1.0.spec ... Wrote: /usr/src/redhat/RPMS/i386/PEAR::Net_Socket-1.0-1.i386.rpm Convert a package.xml 1.0 to package.xml 2.0 format doConvert c2 f do not beautify the filelist. [descfile] [descfile2] Converts a package.xml in 1.0 format into a package.xml in 2.0 format. The new file will be named package2.xml by default, and package.xml will be used as the old file by default. This is not the most intelligent conversion, and should only be used for automated conversion or learning the format. PK=1]]ҜҜpear/PEAR/Command/Package.phpnu[ * @author Martin Jansen * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ /** * base class */ require_once 'PEAR/Command/Common.php'; /** * PEAR commands for login/logout * * @category pear * @package PEAR * @author Stig Bakken * @author Martin Jansen * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ class PEAR_Command_Package extends PEAR_Command_Common { var $commands = array( 'package' => array( 'summary' => 'Build Package', 'function' => 'doPackage', 'shortcut' => 'p', 'options' => array( 'nocompress' => array( 'shortopt' => 'Z', 'doc' => 'Do not gzip the package file' ), 'showname' => array( 'shortopt' => 'n', 'doc' => 'Print the name of the packaged file.', ), ), 'doc' => '[descfile] [descfile2] Creates a PEAR package from its description file (usually called package.xml). If a second packagefile is passed in, then the packager will check to make sure that one is a package.xml version 1.0, and the other is a package.xml version 2.0. The package.xml version 1.0 will be saved as "package.xml" in the archive, and the other as "package2.xml" in the archive" ' ), 'package-validate' => array( 'summary' => 'Validate Package Consistency', 'function' => 'doPackageValidate', 'shortcut' => 'pv', 'options' => array(), 'doc' => ' ', ), 'cvsdiff' => array( 'summary' => 'Run a "cvs diff" for all files in a package', 'function' => 'doCvsDiff', 'shortcut' => 'cd', 'options' => array( 'quiet' => array( 'shortopt' => 'q', 'doc' => 'Be quiet', ), 'reallyquiet' => array( 'shortopt' => 'Q', 'doc' => 'Be really quiet', ), 'date' => array( 'shortopt' => 'D', 'doc' => 'Diff against revision of DATE', 'arg' => 'DATE', ), 'release' => array( 'shortopt' => 'R', 'doc' => 'Diff against tag for package release REL', 'arg' => 'REL', ), 'revision' => array( 'shortopt' => 'r', 'doc' => 'Diff against revision REV', 'arg' => 'REV', ), 'context' => array( 'shortopt' => 'c', 'doc' => 'Generate context diff', ), 'unified' => array( 'shortopt' => 'u', 'doc' => 'Generate unified diff', ), 'ignore-case' => array( 'shortopt' => 'i', 'doc' => 'Ignore case, consider upper- and lower-case letters equivalent', ), 'ignore-whitespace' => array( 'shortopt' => 'b', 'doc' => 'Ignore changes in amount of white space', ), 'ignore-blank-lines' => array( 'shortopt' => 'B', 'doc' => 'Ignore changes that insert or delete blank lines', ), 'brief' => array( 'doc' => 'Report only whether the files differ, no details', ), 'dry-run' => array( 'shortopt' => 'n', 'doc' => 'Don\'t do anything, just pretend', ), ), 'doc' => ' Compares all the files in a package. Without any options, this command will compare the current code with the last checked-in code. Using the -r or -R option you may compare the current code with that of a specific release. ', ), 'svntag' => array( 'summary' => 'Set SVN Release Tag', 'function' => 'doSvnTag', 'shortcut' => 'sv', 'options' => array( 'quiet' => array( 'shortopt' => 'q', 'doc' => 'Be quiet', ), 'slide' => array( 'shortopt' => 'F', 'doc' => 'Move (slide) tag if it exists', ), 'delete' => array( 'shortopt' => 'd', 'doc' => 'Remove tag', ), 'dry-run' => array( 'shortopt' => 'n', 'doc' => 'Don\'t do anything, just pretend', ), ), 'doc' => ' [files...] Sets a SVN tag on all files in a package. Use this command after you have packaged a distribution tarball with the "package" command to tag what revisions of what files were in that release. If need to fix something after running svntag once, but before the tarball is released to the public, use the "slide" option to move the release tag. to include files (such as a second package.xml, or tests not included in the release), pass them as additional parameters. ', ), 'cvstag' => array( 'summary' => 'Set CVS Release Tag', 'function' => 'doCvsTag', 'shortcut' => 'ct', 'options' => array( 'quiet' => array( 'shortopt' => 'q', 'doc' => 'Be quiet', ), 'reallyquiet' => array( 'shortopt' => 'Q', 'doc' => 'Be really quiet', ), 'slide' => array( 'shortopt' => 'F', 'doc' => 'Move (slide) tag if it exists', ), 'delete' => array( 'shortopt' => 'd', 'doc' => 'Remove tag', ), 'dry-run' => array( 'shortopt' => 'n', 'doc' => 'Don\'t do anything, just pretend', ), ), 'doc' => ' [files...] Sets a CVS tag on all files in a package. Use this command after you have packaged a distribution tarball with the "package" command to tag what revisions of what files were in that release. If need to fix something after running cvstag once, but before the tarball is released to the public, use the "slide" option to move the release tag. to include files (such as a second package.xml, or tests not included in the release), pass them as additional parameters. ', ), 'package-dependencies' => array( 'summary' => 'Show package dependencies', 'function' => 'doPackageDependencies', 'shortcut' => 'pd', 'options' => array(), 'doc' => ' or or List all dependencies the package has. Can take a tgz / tar file, package.xml or a package name of an installed package.' ), 'sign' => array( 'summary' => 'Sign a package distribution file', 'function' => 'doSign', 'shortcut' => 'si', 'options' => array( 'verbose' => array( 'shortopt' => 'v', 'doc' => 'Display GnuPG output', ), ), 'doc' => ' Signs a package distribution (.tar or .tgz) file with GnuPG.', ), 'makerpm' => array( 'summary' => 'Builds an RPM spec file from a PEAR package', 'function' => 'doMakeRPM', 'shortcut' => 'rpm', 'options' => array( 'spec-template' => array( 'shortopt' => 't', 'arg' => 'FILE', 'doc' => 'Use FILE as RPM spec file template' ), 'rpm-pkgname' => array( 'shortopt' => 'p', 'arg' => 'FORMAT', 'doc' => 'Use FORMAT as format string for RPM package name, %s is replaced by the PEAR package name, defaults to "PEAR::%s".', ), ), 'doc' => ' Creates an RPM .spec file for wrapping a PEAR package inside an RPM package. Intended to be used from the SPECS directory, with the PEAR package tarball in the SOURCES directory: $ pear makerpm ../SOURCES/Net_Socket-1.0.tgz Wrote RPM spec file PEAR::Net_Geo-1.0.spec $ rpm -bb PEAR::Net_Socket-1.0.spec ... Wrote: /usr/src/redhat/RPMS/i386/PEAR::Net_Socket-1.0-1.i386.rpm ', ), 'convert' => array( 'summary' => 'Convert a package.xml 1.0 to package.xml 2.0 format', 'function' => 'doConvert', 'shortcut' => 'c2', 'options' => array( 'flat' => array( 'shortopt' => 'f', 'doc' => 'do not beautify the filelist.', ), ), 'doc' => '[descfile] [descfile2] Converts a package.xml in 1.0 format into a package.xml in 2.0 format. The new file will be named package2.xml by default, and package.xml will be used as the old file by default. This is not the most intelligent conversion, and should only be used for automated conversion or learning the format. ' ), ); var $output; /** * PEAR_Command_Package constructor. * * @access public */ function __construct(&$ui, &$config) { parent::__construct($ui, $config); } function _displayValidationResults($err, $warn, $strict = false) { foreach ($err as $e) { $this->output .= "Error: $e\n"; } foreach ($warn as $w) { $this->output .= "Warning: $w\n"; } $this->output .= sprintf('Validation: %d error(s), %d warning(s)'."\n", sizeof($err), sizeof($warn)); if ($strict && count($err) > 0) { $this->output .= "Fix these errors and try again."; return false; } return true; } function &getPackager() { if (!class_exists('PEAR_Packager')) { require_once 'PEAR/Packager.php'; } $a = new PEAR_Packager; return $a; } function &getPackageFile($config, $debug = false) { if (!class_exists('PEAR_Common')) { require_once 'PEAR/Common.php'; } if (!class_exists('PEAR_PackageFile')) { require_once 'PEAR/PackageFile.php'; } $a = new PEAR_PackageFile($config, $debug); $common = new PEAR_Common; $common->ui = $this->ui; $a->setLogger($common); return $a; } function doPackage($command, $options, $params) { $this->output = ''; $pkginfofile = isset($params[0]) ? $params[0] : 'package.xml'; $pkg2 = isset($params[1]) ? $params[1] : null; if (!$pkg2 && !isset($params[0]) && file_exists('package2.xml')) { $pkg2 = 'package2.xml'; } $packager = &$this->getPackager(); $compress = empty($options['nocompress']) ? true : false; $result = $packager->package($pkginfofile, $compress, $pkg2); if (PEAR::isError($result)) { return $this->raiseError($result); } // Don't want output, only the package file name just created if (isset($options['showname'])) { $this->output = $result; } if ($this->output) { $this->ui->outputData($this->output, $command); } return true; } function doPackageValidate($command, $options, $params) { $this->output = ''; if (count($params) < 1) { $params[0] = 'package.xml'; } $obj = &$this->getPackageFile($this->config, $this->_debug); $obj->rawReturn(); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $info = $obj->fromTgzFile($params[0], PEAR_VALIDATE_NORMAL); if (PEAR::isError($info)) { $info = $obj->fromPackageFile($params[0], PEAR_VALIDATE_NORMAL); } else { $archive = $info->getArchiveFile(); $tar = new Archive_Tar($archive); $tar->extract(dirname($info->getPackageFile())); $info->setPackageFile(dirname($info->getPackageFile()) . DIRECTORY_SEPARATOR . $info->getPackage() . '-' . $info->getVersion() . DIRECTORY_SEPARATOR . basename($info->getPackageFile())); } PEAR::staticPopErrorHandling(); if (PEAR::isError($info)) { return $this->raiseError($info); } $valid = false; if ($info->getPackagexmlVersion() == '2.0') { if ($valid = $info->validate(PEAR_VALIDATE_NORMAL)) { $info->flattenFileList(); $valid = $info->validate(PEAR_VALIDATE_PACKAGING); } } else { $valid = $info->validate(PEAR_VALIDATE_PACKAGING); } $err = $warn = array(); if ($errors = $info->getValidationWarnings()) { foreach ($errors as $error) { if ($error['level'] == 'warning') { $warn[] = $error['message']; } else { $err[] = $error['message']; } } } $this->_displayValidationResults($err, $warn); $this->ui->outputData($this->output, $command); return true; } function doSvnTag($command, $options, $params) { $this->output = ''; $_cmd = $command; if (count($params) < 1) { $help = $this->getHelp($command); return $this->raiseError("$command: missing parameter: $help[0]"); } $packageFile = realpath($params[0]); $dir = dirname($packageFile); $dir = substr($dir, strrpos($dir, DIRECTORY_SEPARATOR) + 1); $obj = &$this->getPackageFile($this->config, $this->_debug); $info = $obj->fromAnyFile($packageFile, PEAR_VALIDATE_NORMAL); if (PEAR::isError($info)) { return $this->raiseError($info); } $err = $warn = array(); if (!$info->validate()) { foreach ($info->getValidationWarnings() as $error) { if ($error['level'] == 'warning') { $warn[] = $error['message']; } else { $err[] = $error['message']; } } } if (!$this->_displayValidationResults($err, $warn, true)) { $this->ui->outputData($this->output, $command); return $this->raiseError('SVN tag failed'); } $version = $info->getVersion(); $package = $info->getName(); $svntag = "$package-$version"; if (isset($options['delete'])) { return $this->_svnRemoveTag($version, $package, $svntag, $packageFile, $options); } $path = $this->_svnFindPath($packageFile); // Check if there are any modified files $fp = popen('svn st --xml ' . dirname($packageFile), "r"); $out = ''; while ($line = fgets($fp, 1024)) { $out .= rtrim($line)."\n"; } pclose($fp); if (!isset($options['quiet']) && strpos($out, 'item="modified"')) { $params = array(array( 'name' => 'modified', 'type' => 'yesno', 'default' => 'no', 'prompt' => 'You have files in your SVN checkout (' . $path['from'] . ') that have been modified but not committed, do you still want to tag ' . $version . '?', )); $answers = $this->ui->confirmDialog($params); if (!in_array($answers['modified'], array('y', 'yes', 'on', '1'))) { return true; } } if (isset($options['slide'])) { $this->_svnRemoveTag($version, $package, $svntag, $packageFile, $options); } // Check if tag already exists $releaseTag = $path['local']['base'] . 'tags' . DIRECTORY_SEPARATOR . $svntag; $existsCommand = 'svn ls ' . $path['base'] . 'tags/'; $fp = popen($existsCommand, "r"); $out = ''; while ($line = fgets($fp, 1024)) { $out .= rtrim($line)."\n"; } pclose($fp); if (in_array($svntag . DIRECTORY_SEPARATOR, explode("\n", $out))) { $this->ui->outputData($this->output, $command); return $this->raiseError('SVN tag ' . $svntag . ' for ' . $package . ' already exists.'); } elseif (file_exists($path['local']['base'] . 'tags') === false) { return $this->raiseError('Can not locate the tags directory at ' . $path['local']['base'] . 'tags'); } elseif (is_writeable($path['local']['base'] . 'tags') === false) { return $this->raiseError('Can not write to the tag directory at ' . $path['local']['base'] . 'tags'); } else { $makeCommand = 'svn mkdir ' . $releaseTag; $this->output .= "+ $makeCommand\n"; if (empty($options['dry-run'])) { // We need to create the tag dir. $fp = popen($makeCommand, "r"); $out = ''; while ($line = fgets($fp, 1024)) { $out .= rtrim($line)."\n"; } pclose($fp); $this->output .= "$out\n"; } } $command = 'svn'; if (isset($options['quiet'])) { $command .= ' -q'; } $command .= ' copy --parents '; $dir = dirname($packageFile); $dir = substr($dir, strrpos($dir, DIRECTORY_SEPARATOR) + 1); $files = array_keys($info->getFilelist()); if (!in_array(basename($packageFile), $files)) { $files[] = basename($packageFile); } array_shift($params); if (count($params)) { // add in additional files to be tagged (package files and such) $files = array_merge($files, $params); } $commands = array(); foreach ($files as $file) { if (!file_exists($file)) { $file = $dir . DIRECTORY_SEPARATOR . $file; } $commands[] = $command . ' ' . escapeshellarg($file) . ' ' . escapeshellarg($releaseTag . DIRECTORY_SEPARATOR . $file); } $this->output .= implode("\n", $commands) . "\n"; if (empty($options['dry-run'])) { foreach ($commands as $command) { $fp = popen($command, "r"); while ($line = fgets($fp, 1024)) { $this->output .= rtrim($line)."\n"; } pclose($fp); } } $command = 'svn ci -m "Tagging the ' . $version . ' release" ' . $releaseTag . "\n"; $this->output .= "+ $command\n"; if (empty($options['dry-run'])) { $fp = popen($command, "r"); while ($line = fgets($fp, 1024)) { $this->output .= rtrim($line)."\n"; } pclose($fp); } $this->ui->outputData($this->output, $_cmd); return true; } function _svnFindPath($file) { $xml = ''; $command = "svn info --xml $file"; $fp = popen($command, "r"); while ($line = fgets($fp, 1024)) { $xml .= rtrim($line)."\n"; } pclose($fp); $url_tag = strpos($xml, ''); $url = substr($xml, $url_tag + 5, strpos($xml, '', $url_tag + 5) - ($url_tag + 5)); $path = array(); $path['from'] = substr($url, 0, strrpos($url, '/')); $path['base'] = substr($path['from'], 0, strrpos($path['from'], '/') + 1); // Figure out the local paths - see http://pear.php.net/bugs/17463 $pos = strpos($file, DIRECTORY_SEPARATOR . 'trunk' . DIRECTORY_SEPARATOR); if ($pos === false) { $pos = strpos($file, DIRECTORY_SEPARATOR . 'branches' . DIRECTORY_SEPARATOR); } $path['local']['base'] = substr($file, 0, $pos + 1); return $path; } function _svnRemoveTag($version, $package, $tag, $packageFile, $options) { $command = 'svn'; if (isset($options['quiet'])) { $command .= ' -q'; } $command .= ' remove'; $command .= ' -m "Removing tag for the ' . $version . ' release."'; $path = $this->_svnFindPath($packageFile); $command .= ' ' . $path['base'] . 'tags/' . $tag; if ($this->config->get('verbose') > 1) { $this->output .= "+ $command\n"; } $this->output .= "+ $command\n"; if (empty($options['dry-run'])) { $fp = popen($command, "r"); while ($line = fgets($fp, 1024)) { $this->output .= rtrim($line)."\n"; } pclose($fp); } $this->ui->outputData($this->output, $command); return true; } function doCvsTag($command, $options, $params) { $this->output = ''; $_cmd = $command; if (count($params) < 1) { $help = $this->getHelp($command); return $this->raiseError("$command: missing parameter: $help[0]"); } $packageFile = realpath($params[0]); $obj = &$this->getPackageFile($this->config, $this->_debug); $info = $obj->fromAnyFile($packageFile, PEAR_VALIDATE_NORMAL); if (PEAR::isError($info)) { return $this->raiseError($info); } $err = $warn = array(); if (!$info->validate()) { foreach ($info->getValidationWarnings() as $error) { if ($error['level'] == 'warning') { $warn[] = $error['message']; } else { $err[] = $error['message']; } } } if (!$this->_displayValidationResults($err, $warn, true)) { $this->ui->outputData($this->output, $command); return $this->raiseError('CVS tag failed'); } $version = $info->getVersion(); $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $version); $cvstag = "RELEASE_$cvsversion"; $files = array_keys($info->getFilelist()); $command = 'cvs'; if (isset($options['quiet'])) { $command .= ' -q'; } if (isset($options['reallyquiet'])) { $command .= ' -Q'; } $command .= ' tag'; if (isset($options['slide'])) { $command .= ' -F'; } if (isset($options['delete'])) { $command .= ' -d'; } $command .= ' ' . $cvstag . ' ' . escapeshellarg($params[0]); array_shift($params); if (count($params)) { // add in additional files to be tagged $files = array_merge($files, $params); } $dir = dirname($packageFile); $dir = substr($dir, strrpos($dir, '/') + 1); foreach ($files as $file) { if (!file_exists($file)) { $file = $dir . DIRECTORY_SEPARATOR . $file; } $command .= ' ' . escapeshellarg($file); } if ($this->config->get('verbose') > 1) { $this->output .= "+ $command\n"; } $this->output .= "+ $command\n"; if (empty($options['dry-run'])) { $fp = popen($command, "r"); while ($line = fgets($fp, 1024)) { $this->output .= rtrim($line)."\n"; } pclose($fp); } $this->ui->outputData($this->output, $_cmd); return true; } function doCvsDiff($command, $options, $params) { $this->output = ''; if (sizeof($params) < 1) { $help = $this->getHelp($command); return $this->raiseError("$command: missing parameter: $help[0]"); } $file = realpath($params[0]); $obj = &$this->getPackageFile($this->config, $this->_debug); $info = $obj->fromAnyFile($file, PEAR_VALIDATE_NORMAL); if (PEAR::isError($info)) { return $this->raiseError($info); } $err = $warn = array(); if (!$info->validate()) { foreach ($info->getValidationWarnings() as $error) { if ($error['level'] == 'warning') { $warn[] = $error['message']; } else { $err[] = $error['message']; } } } if (!$this->_displayValidationResults($err, $warn, true)) { $this->ui->outputData($this->output, $command); return $this->raiseError('CVS diff failed'); } $info1 = $info->getFilelist(); $files = $info1; $cmd = "cvs"; if (isset($options['quiet'])) { $cmd .= ' -q'; unset($options['quiet']); } if (isset($options['reallyquiet'])) { $cmd .= ' -Q'; unset($options['reallyquiet']); } if (isset($options['release'])) { $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $options['release']); $cvstag = "RELEASE_$cvsversion"; $options['revision'] = $cvstag; unset($options['release']); } $execute = true; if (isset($options['dry-run'])) { $execute = false; unset($options['dry-run']); } $cmd .= ' diff'; // the rest of the options are passed right on to "cvs diff" foreach ($options as $option => $optarg) { $arg = $short = false; if (isset($this->commands[$command]['options'][$option])) { $arg = $this->commands[$command]['options'][$option]['arg']; $short = $this->commands[$command]['options'][$option]['shortopt']; } $cmd .= $short ? " -$short" : " --$option"; if ($arg && $optarg) { $cmd .= ($short ? '' : '=') . escapeshellarg($optarg); } } foreach ($files as $file) { $cmd .= ' ' . escapeshellarg($file['name']); } if ($this->config->get('verbose') > 1) { $this->output .= "+ $cmd\n"; } if ($execute) { $fp = popen($cmd, "r"); while ($line = fgets($fp, 1024)) { $this->output .= rtrim($line)."\n"; } pclose($fp); } $this->ui->outputData($this->output, $command); return true; } function doPackageDependencies($command, $options, $params) { // $params[0] -> the PEAR package to list its information if (count($params) !== 1) { return $this->raiseError("bad parameter(s), try \"help $command\""); } $obj = &$this->getPackageFile($this->config, $this->_debug); if (is_file($params[0]) || strpos($params[0], '.xml') > 0) { $info = $obj->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL); } else { $reg = $this->config->getRegistry(); $info = $obj->fromArray($reg->packageInfo($params[0])); } if (PEAR::isError($info)) { return $this->raiseError($info); } $deps = $info->getDeps(); if (is_array($deps)) { if ($info->getPackagexmlVersion() == '1.0') { $data = array( 'caption' => 'Dependencies for pear/' . $info->getPackage(), 'border' => true, 'headline' => array("Required?", "Type", "Name", "Relation", "Version"), ); foreach ($deps as $d) { if (isset($d['optional'])) { if ($d['optional'] == 'yes') { $req = 'No'; } else { $req = 'Yes'; } } else { $req = 'Yes'; } if (isset($this->_deps_rel_trans[$d['rel']])) { $rel = $this->_deps_rel_trans[$d['rel']]; } else { $rel = $d['rel']; } if (isset($this->_deps_type_trans[$d['type']])) { $type = ucfirst($this->_deps_type_trans[$d['type']]); } else { $type = $d['type']; } if (isset($d['name'])) { $name = $d['name']; } else { $name = ''; } if (isset($d['version'])) { $version = $d['version']; } else { $version = ''; } $data['data'][] = array($req, $type, $name, $rel, $version); } } else { // package.xml 2.0 dependencies display require_once 'PEAR/Dependency2.php'; $deps = $info->getDependencies(); $reg = &$this->config->getRegistry(); if (is_array($deps)) { $data = array( 'caption' => 'Dependencies for ' . $info->getPackage(), 'border' => true, 'headline' => array("Required?", "Type", "Name", 'Versioning', 'Group'), ); foreach ($deps as $type => $subd) { $req = ($type == 'required') ? 'Yes' : 'No'; if ($type == 'group' && isset($subd['attribs']['name'])) { $group = $subd['attribs']['name']; } else { $group = ''; } if (!isset($subd[0])) { $subd = array($subd); } foreach ($subd as $groupa) { foreach ($groupa as $deptype => $depinfo) { if ($deptype == 'attribs') { continue; } if ($deptype == 'pearinstaller') { $deptype = 'pear Installer'; } if (!isset($depinfo[0])) { $depinfo = array($depinfo); } foreach ($depinfo as $inf) { $name = ''; if (isset($inf['channel'])) { $alias = $reg->channelAlias($inf['channel']); if (!$alias) { $alias = '(channel?) ' .$inf['channel']; } $name = $alias . '/'; } if (isset($inf['name'])) { $name .= $inf['name']; } elseif (isset($inf['pattern'])) { $name .= $inf['pattern']; } else { $name .= ''; } if (isset($inf['uri'])) { $name .= ' [' . $inf['uri'] . ']'; } if (isset($inf['conflicts'])) { $ver = 'conflicts'; } else { $ver = PEAR_Dependency2::_getExtraString($inf); } $data['data'][] = array($req, ucfirst($deptype), $name, $ver, $group); } } } } } } $this->ui->outputData($data, $command); return true; } // Fallback $this->ui->outputData("This package does not have any dependencies.", $command); } function doSign($command, $options, $params) { // should move most of this code into PEAR_Packager // so it'll be easy to implement "pear package --sign" if (count($params) !== 1) { return $this->raiseError("bad parameter(s), try \"help $command\""); } require_once 'System.php'; require_once 'Archive/Tar.php'; if (!file_exists($params[0])) { return $this->raiseError("file does not exist: $params[0]"); } $obj = $this->getPackageFile($this->config, $this->_debug); $info = $obj->fromTgzFile($params[0], PEAR_VALIDATE_NORMAL); if (PEAR::isError($info)) { return $this->raiseError($info); } $tar = new Archive_Tar($params[0]); $tmpdir = $this->config->get('temp_dir'); $tmpdir = System::mktemp(' -t "' . $tmpdir . '" -d pearsign'); if (!$tar->extractList('package2.xml package.xml package.sig', $tmpdir)) { return $this->raiseError("failed to extract tar file"); } if (file_exists("$tmpdir/package.sig")) { return $this->raiseError("package already signed"); } $packagexml = 'package.xml'; if (file_exists("$tmpdir/package2.xml")) { $packagexml = 'package2.xml'; } if (file_exists("$tmpdir/package.sig")) { unlink("$tmpdir/package.sig"); } if (!file_exists("$tmpdir/$packagexml")) { return $this->raiseError("Extracted file $tmpdir/$packagexml not found."); } $input = $this->ui->userDialog($command, array('GnuPG Passphrase'), array('password')); if (!isset($input[0])) { //use empty passphrase $input[0] = ''; } $devnull = (isset($options['verbose'])) ? '' : ' 2>/dev/null'; $gpg = popen("gpg --batch --passphrase-fd 0 --armor --detach-sign --output $tmpdir/package.sig $tmpdir/$packagexml" . $devnull, "w"); if (!$gpg) { return $this->raiseError("gpg command failed"); } fwrite($gpg, "$input[0]\n"); if (pclose($gpg) || !file_exists("$tmpdir/package.sig")) { return $this->raiseError("gpg sign failed"); } if (!$tar->addModify("$tmpdir/package.sig", '', $tmpdir)) { return $this->raiseError('failed adding signature to file'); } $this->ui->outputData("Package signed.", $command); return true; } /** * For unit testing purposes */ function &getInstaller(&$ui) { if (!class_exists('PEAR_Installer')) { require_once 'PEAR/Installer.php'; } $a = new PEAR_Installer($ui); return $a; } /** * For unit testing purposes */ function &getCommandPackaging(&$ui, &$config) { if (!class_exists('PEAR_Command_Packaging')) { if ($fp = @fopen('PEAR/Command/Packaging.php', 'r', true)) { fclose($fp); include_once 'PEAR/Command/Packaging.php'; } } if (class_exists('PEAR_Command_Packaging')) { $a = new PEAR_Command_Packaging($ui, $config); } else { $a = null; } return $a; } function doMakeRPM($command, $options, $params) { // Check to see if PEAR_Command_Packaging is installed, and // transparently switch to use the "make-rpm-spec" command from it // instead, if it does. Otherwise, continue to use the old version // of "makerpm" supplied with this package (PEAR). $packaging_cmd = $this->getCommandPackaging($this->ui, $this->config); if ($packaging_cmd !== null) { $this->ui->outputData('PEAR_Command_Packaging is installed; using '. 'newer "make-rpm-spec" command instead'); return $packaging_cmd->run('make-rpm-spec', $options, $params); } $this->ui->outputData('WARNING: "pear makerpm" is no longer available; an '. 'improved version is available via "pear make-rpm-spec", which '. 'is available by installing PEAR_Command_Packaging'); return true; } function doConvert($command, $options, $params) { $packagexml = isset($params[0]) ? $params[0] : 'package.xml'; $newpackagexml = isset($params[1]) ? $params[1] : dirname($packagexml) . DIRECTORY_SEPARATOR . 'package2.xml'; $pkg = &$this->getPackageFile($this->config, $this->_debug); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $pf = $pkg->fromPackageFile($packagexml, PEAR_VALIDATE_NORMAL); PEAR::staticPopErrorHandling(); if (PEAR::isError($pf)) { if (is_array($pf->getUserInfo())) { foreach ($pf->getUserInfo() as $warning) { $this->ui->outputData($warning['message']); } } return $this->raiseError($pf); } if (is_a($pf, 'PEAR_PackageFile_v2')) { $this->ui->outputData($packagexml . ' is already a package.xml version 2.0'); return true; } $gen = &$pf->getDefaultGenerator(); $newpf = &$gen->toV2(); $newpf->setPackagefile($newpackagexml); $gen = &$newpf->getDefaultGenerator(); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $state = (isset($options['flat']) ? PEAR_VALIDATE_PACKAGING : PEAR_VALIDATE_NORMAL); $saved = $gen->toPackageFile(dirname($newpackagexml), $state, basename($newpackagexml)); PEAR::staticPopErrorHandling(); if (PEAR::isError($saved)) { if (is_array($saved->getUserInfo())) { foreach ($saved->getUserInfo() as $warning) { $this->ui->outputData($warning['message']); } } $this->ui->outputData($saved->getMessage()); return true; } $this->ui->outputData('Wrote new version 2.0 package.xml to "' . $saved . '"'); return true; } } PK=1];ek<k<pear/PEAR/Command/Config.phpnu[ * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ /** * base class */ require_once 'PEAR/Command/Common.php'; /** * PEAR commands for managing configuration data. * * @category pear * @package PEAR * @author Stig Bakken * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ class PEAR_Command_Config extends PEAR_Command_Common { var $commands = array( 'config-show' => array( 'summary' => 'Show All Settings', 'function' => 'doConfigShow', 'shortcut' => 'csh', 'options' => array( 'channel' => array( 'shortopt' => 'c', 'doc' => 'show configuration variables for another channel', 'arg' => 'CHAN', ), ), 'doc' => '[layer] Displays all configuration values. An optional argument may be used to tell which configuration layer to display. Valid configuration layers are "user", "system" and "default". To display configurations for different channels, set the default_channel configuration variable and run config-show again. ', ), 'config-get' => array( 'summary' => 'Show One Setting', 'function' => 'doConfigGet', 'shortcut' => 'cg', 'options' => array( 'channel' => array( 'shortopt' => 'c', 'doc' => 'show configuration variables for another channel', 'arg' => 'CHAN', ), ), 'doc' => ' [layer] Displays the value of one configuration parameter. The first argument is the name of the parameter, an optional second argument may be used to tell which configuration layer to look in. Valid configuration layers are "user", "system" and "default". If no layer is specified, a value will be picked from the first layer that defines the parameter, in the order just specified. The configuration value will be retrieved for the channel specified by the default_channel configuration variable. ', ), 'config-set' => array( 'summary' => 'Change Setting', 'function' => 'doConfigSet', 'shortcut' => 'cs', 'options' => array( 'channel' => array( 'shortopt' => 'c', 'doc' => 'show configuration variables for another channel', 'arg' => 'CHAN', ), ), 'doc' => ' [layer] Sets the value of one configuration parameter. The first argument is the name of the parameter, the second argument is the new value. Some parameters are subject to validation, and the command will fail with an error message if the new value does not make sense. An optional third argument may be used to specify in which layer to set the configuration parameter. The default layer is "user". The configuration value will be set for the current channel, which is controlled by the default_channel configuration variable. ', ), 'config-help' => array( 'summary' => 'Show Information About Setting', 'function' => 'doConfigHelp', 'shortcut' => 'ch', 'options' => array(), 'doc' => '[parameter] Displays help for a configuration parameter. Without arguments it displays help for all configuration parameters. ', ), 'config-create' => array( 'summary' => 'Create a Default configuration file', 'function' => 'doConfigCreate', 'shortcut' => 'coc', 'options' => array( 'windows' => array( 'shortopt' => 'w', 'doc' => 'create a config file for a windows install', ), ), 'doc' => ' Create a default configuration file with all directory configuration variables set to subdirectories of , and save it as . This is useful especially for creating a configuration file for a remote PEAR installation (using the --remoteconfig option of install, upgrade, and uninstall). ', ), ); /** * PEAR_Command_Config constructor. * * @access public */ function __construct(&$ui, &$config) { parent::__construct($ui, $config); } function doConfigShow($command, $options, $params) { $layer = null; if (is_array($params)) { $layer = isset($params[0]) ? $params[0] : null; } // $params[0] -> the layer if ($error = $this->_checkLayer($layer)) { return $this->raiseError("config-show:$error"); } $keys = $this->config->getKeys(); sort($keys); $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel'); $reg = &$this->config->getRegistry(); if (!$reg->channelExists($channel)) { return $this->raiseError('Channel "' . $channel . '" does not exist'); } $channel = $reg->channelName($channel); $data = array('caption' => 'Configuration (channel ' . $channel . '):'); foreach ($keys as $key) { $type = $this->config->getType($key); $value = $this->config->get($key, $layer, $channel); if ($type == 'password' && $value) { $value = '********'; } if ($value === false) { $value = 'false'; } elseif ($value === true) { $value = 'true'; } $data['data'][$this->config->getGroup($key)][] = array($this->config->getPrompt($key) , $key, $value); } foreach ($this->config->getLayers() as $layer) { $data['data']['Config Files'][] = array(ucfirst($layer) . ' Configuration File', 'Filename' , $this->config->getConfFile($layer)); } $this->ui->outputData($data, $command); return true; } function doConfigGet($command, $options, $params) { $args_cnt = is_array($params) ? count($params) : 0; switch ($args_cnt) { case 1: $config_key = $params[0]; $layer = null; break; case 2: $config_key = $params[0]; $layer = $params[1]; if ($error = $this->_checkLayer($layer)) { return $this->raiseError("config-get:$error"); } break; case 0: default: return $this->raiseError("config-get expects 1 or 2 parameters"); } $reg = &$this->config->getRegistry(); $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel'); if (!$reg->channelExists($channel)) { return $this->raiseError('Channel "' . $channel . '" does not exist'); } $channel = $reg->channelName($channel); $this->ui->outputData($this->config->get($config_key, $layer, $channel), $command); return true; } function doConfigSet($command, $options, $params) { // $param[0] -> a parameter to set // $param[1] -> the value for the parameter // $param[2] -> the layer $failmsg = ''; if (count($params) < 2 || count($params) > 3) { $failmsg .= "config-set expects 2 or 3 parameters"; return PEAR::raiseError($failmsg); } if (isset($params[2]) && ($error = $this->_checkLayer($params[2]))) { $failmsg .= $error; return PEAR::raiseError("config-set:$failmsg"); } $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel'); $reg = &$this->config->getRegistry(); if (!$reg->channelExists($channel)) { return $this->raiseError('Channel "' . $channel . '" does not exist'); } $channel = $reg->channelName($channel); if ($params[0] == 'default_channel' && !$reg->channelExists($params[1])) { return $this->raiseError('Channel "' . $params[1] . '" does not exist'); } if ($params[0] == 'preferred_mirror' && ( !$reg->mirrorExists($channel, $params[1]) && (!$reg->channelExists($params[1]) || $channel != $params[1]) ) ) { $msg = 'Channel Mirror "' . $params[1] . '" does not exist'; $msg .= ' in your registry for channel "' . $channel . '".'; $msg .= "\n" . 'Attempt to run "pear channel-update ' . $channel .'"'; $msg .= ' if you believe this mirror should exist as you may'; $msg .= ' have outdated channel information.'; return $this->raiseError($msg); } if (count($params) == 2) { array_push($params, 'user'); $layer = 'user'; } else { $layer = $params[2]; } array_push($params, $channel); if (!call_user_func_array(array(&$this->config, 'set'), $params)) { array_pop($params); $failmsg = "config-set (" . implode(", ", $params) . ") failed, channel $channel"; } else { $this->config->store($layer); } if ($failmsg) { return $this->raiseError($failmsg); } $this->ui->outputData('config-set succeeded', $command); return true; } function doConfigHelp($command, $options, $params) { if (empty($params)) { $params = $this->config->getKeys(); } $data['caption'] = "Config help" . ((count($params) == 1) ? " for $params[0]" : ''); $data['headline'] = array('Name', 'Type', 'Description'); $data['border'] = true; foreach ($params as $name) { $type = $this->config->getType($name); $docs = $this->config->getDocs($name); if ($type == 'set') { $docs = rtrim($docs) . "\nValid set: " . implode(' ', $this->config->getSetValues($name)); } $data['data'][] = array($name, $type, $docs); } $this->ui->outputData($data, $command); } function doConfigCreate($command, $options, $params) { if (count($params) != 2) { return PEAR::raiseError('config-create: must have 2 parameters, root path and ' . 'filename to save as'); } $root = $params[0]; // Clean up the DIRECTORY_SEPARATOR mess $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR; $root = preg_replace(array('!\\\\+!', '!/+!', "!$ds2+!"), array('/', '/', '/'), $root); if ($root[0] != '/') { if (!isset($options['windows'])) { return PEAR::raiseError('Root directory must be an absolute path beginning ' . 'with "/", was: "' . $root . '"'); } if (!preg_match('/^[A-Za-z]:/', $root)) { return PEAR::raiseError('Root directory must be an absolute path beginning ' . 'with "\\" or "C:\\", was: "' . $root . '"'); } } $windows = isset($options['windows']); if ($windows) { $root = str_replace('/', '\\', $root); } if (!file_exists($params[1]) && !@touch($params[1])) { return PEAR::raiseError('Could not create "' . $params[1] . '"'); } $params[1] = realpath($params[1]); $config = new PEAR_Config($params[1], '#no#system#config#', false, false); if ($root[strlen($root) - 1] == '/') { $root = substr($root, 0, strlen($root) - 1); } $config->noRegistry(); $config->set('php_dir', $windows ? "$root\\pear\\php" : "$root/pear/php", 'user'); $config->set('data_dir', $windows ? "$root\\pear\\data" : "$root/pear/data"); $config->set('www_dir', $windows ? "$root\\pear\\www" : "$root/pear/www"); $config->set('cfg_dir', $windows ? "$root\\pear\\cfg" : "$root/pear/cfg"); $config->set('ext_dir', $windows ? "$root\\pear\\ext" : "$root/pear/ext"); $config->set('doc_dir', $windows ? "$root\\pear\\docs" : "$root/pear/docs"); $config->set('test_dir', $windows ? "$root\\pear\\tests" : "$root/pear/tests"); $config->set('cache_dir', $windows ? "$root\\pear\\cache" : "$root/pear/cache"); $config->set('download_dir', $windows ? "$root\\pear\\download" : "$root/pear/download"); $config->set('temp_dir', $windows ? "$root\\pear\\temp" : "$root/pear/temp"); $config->set('bin_dir', $windows ? "$root\\pear" : "$root/pear"); $config->set('man_dir', $windows ? "$root\\pear\\man" : "$root/pear/man"); $config->writeConfigFile(); $this->_showConfig($config); $this->ui->outputData('Successfully created default configuration file "' . $params[1] . '"', $command); } function _showConfig(&$config) { $params = array('user'); $keys = $config->getKeys(); sort($keys); $channel = 'pear.php.net'; $data = array('caption' => 'Configuration (channel ' . $channel . '):'); foreach ($keys as $key) { $type = $config->getType($key); $value = $config->get($key, 'user', $channel); if ($type == 'password' && $value) { $value = '********'; } if ($value === false) { $value = 'false'; } elseif ($value === true) { $value = 'true'; } $data['data'][$config->getGroup($key)][] = array($config->getPrompt($key) , $key, $value); } foreach ($config->getLayers() as $layer) { $data['data']['Config Files'][] = array(ucfirst($layer) . ' Configuration File', 'Filename' , $config->getConfFile($layer)); } $this->ui->outputData($data, 'config-show'); return true; } /** * Checks if a layer is defined or not * * @param string $layer The layer to search for * @return mixed False on no error or the error message */ function _checkLayer($layer = null) { if (!empty($layer) && $layer != 'default') { $layers = $this->config->getLayers(); if (!in_array($layer, $layers)) { return " only the layers: \"" . implode('" or "', $layers) . "\" are supported"; } } return false; } } PK=1]\t;pear/PEAR/XMLParser.phpnu[ * @author Stephan Schmidt (original XML_Unserializer code) * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * Parser for any xml file * @category pear * @package PEAR * @author Greg Beaver * @author Stephan Schmidt (original XML_Unserializer code) * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_XMLParser { /** * unserilialized data * @var string $_serializedData */ var $_unserializedData = null; /** * name of the root tag * @var string $_root */ var $_root = null; /** * stack for all data that is found * @var array $_dataStack */ var $_dataStack = array(); /** * stack for all values that are generated * @var array $_valStack */ var $_valStack = array(); /** * current tag depth * @var int $_depth */ var $_depth = 0; /** * The XML encoding to use * @var string $encoding */ var $encoding = 'ISO-8859-1'; /** * @return array */ function getData() { return $this->_unserializedData; } /** * @param string xml content * @return true|PEAR_Error */ function parse($data) { if (!extension_loaded('xml')) { include_once 'PEAR.php'; return PEAR::raiseError("XML Extension not found", 1); } $this->_dataStack = $this->_valStack = array(); $this->_depth = 0; if ( strpos($data, 'encoding="UTF-8"') || strpos($data, 'encoding="utf-8"') || strpos($data, "encoding='UTF-8'") || strpos($data, "encoding='utf-8'") ) { $this->encoding = 'UTF-8'; } $xp = xml_parser_create($this->encoding); xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, 0); xml_set_object($xp, $this); xml_set_element_handler($xp, 'startHandler', 'endHandler'); xml_set_character_data_handler($xp, 'cdataHandler'); if (!xml_parse($xp, $data)) { $msg = xml_error_string(xml_get_error_code($xp)); $line = xml_get_current_line_number($xp); xml_parser_free($xp); include_once 'PEAR.php'; return PEAR::raiseError("XML Error: '$msg' on line '$line'", 2); } xml_parser_free($xp); return true; } /** * Start element handler for XML parser * * @access private * @param object $parser XML parser object * @param string $element XML element * @param array $attribs attributes of XML tag * @return void */ function startHandler($parser, $element, $attribs) { $this->_depth++; $this->_dataStack[$this->_depth] = null; $val = array( 'name' => $element, 'value' => null, 'type' => 'string', 'childrenKeys' => array(), 'aggregKeys' => array() ); if (count($attribs) > 0) { $val['children'] = array(); $val['type'] = 'array'; $val['children']['attribs'] = $attribs; } array_push($this->_valStack, $val); } /** * post-process data * * @param string $data * @param string $element element name */ function postProcess($data, $element) { return trim($data); } /** * End element handler for XML parser * * @access private * @param object XML parser object * @param string * @return void */ function endHandler($parser, $element) { $value = array_pop($this->_valStack); $data = $this->postProcess($this->_dataStack[$this->_depth], $element); // adjust type of the value switch (strtolower($value['type'])) { // unserialize an array case 'array': if ($data !== '') { $value['children']['_content'] = $data; } $value['value'] = isset($value['children']) ? $value['children'] : array(); break; /* * unserialize a null value */ case 'null': $data = null; break; /* * unserialize any scalar value */ default: settype($data, $value['type']); $value['value'] = $data; break; } $parent = array_pop($this->_valStack); if ($parent === null) { $this->_unserializedData = &$value['value']; $this->_root = &$value['name']; return true; } // parent has to be an array if (!isset($parent['children']) || !is_array($parent['children'])) { $parent['children'] = array(); if ($parent['type'] != 'array') { $parent['type'] = 'array'; } } if (!empty($value['name'])) { // there already has been a tag with this name if (in_array($value['name'], $parent['childrenKeys'])) { // no aggregate has been created for this tag if (!in_array($value['name'], $parent['aggregKeys'])) { if (isset($parent['children'][$value['name']])) { $parent['children'][$value['name']] = array($parent['children'][$value['name']]); } else { $parent['children'][$value['name']] = array(); } array_push($parent['aggregKeys'], $value['name']); } array_push($parent['children'][$value['name']], $value['value']); } else { $parent['children'][$value['name']] = &$value['value']; array_push($parent['childrenKeys'], $value['name']); } } else { array_push($parent['children'],$value['value']); } array_push($this->_valStack, $parent); $this->_depth--; } /** * Handler for character data * * @access private * @param object XML parser object * @param string CDATA * @return void */ function cdataHandler($parser, $cdata) { $this->_dataStack[$this->_depth] .= $cdata; } }PK=1]x_((pear/PEAR/Registry.phpnu[ * @author Tomas V. V. Cox * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ /** * for PEAR_Error */ require_once 'PEAR.php'; require_once 'PEAR/DependencyDB.php'; define('PEAR_REGISTRY_ERROR_LOCK', -2); define('PEAR_REGISTRY_ERROR_FORMAT', -3); define('PEAR_REGISTRY_ERROR_FILE', -4); define('PEAR_REGISTRY_ERROR_CONFLICT', -5); define('PEAR_REGISTRY_ERROR_CHANNEL_FILE', -6); /** * Administration class used to maintain the installed package database. * @category pear * @package PEAR * @author Stig Bakken * @author Tomas V. V. Cox * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_Registry extends PEAR { /** * File containing all channel information. * @var string */ var $channels = ''; /** Directory where registry files are stored. * @var string */ var $statedir = ''; /** File where the file map is stored * @var string */ var $filemap = ''; /** Directory where registry files for channels are stored. * @var string */ var $channelsdir = ''; /** Name of file used for locking the registry * @var string */ var $lockfile = ''; /** File descriptor used during locking * @var resource */ var $lock_fp = null; /** Mode used during locking * @var int */ var $lock_mode = 0; // XXX UNUSED /** Cache of package information. Structure: * array( * 'package' => array('id' => ... ), * ... ) * @var array */ var $pkginfo_cache = array(); /** Cache of file map. Structure: * array( '/path/to/file' => 'package', ... ) * @var array */ var $filemap_cache = array(); /** * @var false|PEAR_ChannelFile */ var $_pearChannel; /** * @var false|PEAR_ChannelFile */ var $_peclChannel; /** * @var false|PEAR_ChannelFile */ var $_docChannel; /** * @var PEAR_DependencyDB */ var $_dependencyDB; /** * @var PEAR_Config */ var $_config; /** * PEAR_Registry constructor. * * @param string (optional) PEAR install directory (for .php files) * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PEAR channel, if * default values are not desired. Only used the very first time a PEAR * repository is initialized * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PECL channel, if * default values are not desired. Only used the very first time a PEAR * repository is initialized * * @access public */ function __construct($pear_install_dir = PEAR_INSTALL_DIR, $pear_channel = false, $pecl_channel = false, $pear_metadata_dir = '') { parent::__construct(); $this->setInstallDir($pear_install_dir, $pear_metadata_dir); $this->_pearChannel = $pear_channel; $this->_peclChannel = $pecl_channel; $this->_config = false; } function setInstallDir($pear_install_dir = PEAR_INSTALL_DIR, $pear_metadata_dir = '') { $ds = DIRECTORY_SEPARATOR; $this->install_dir = $pear_install_dir; if (!$pear_metadata_dir) { $pear_metadata_dir = $pear_install_dir; } $this->channelsdir = $pear_metadata_dir.$ds.'.channels'; $this->statedir = $pear_metadata_dir.$ds.'.registry'; $this->filemap = $pear_metadata_dir.$ds.'.filemap'; $this->lockfile = $pear_metadata_dir.$ds.'.lock'; } function hasWriteAccess() { if (!file_exists($this->install_dir)) { $dir = $this->install_dir; while ($dir && $dir != '.') { $olddir = $dir; $dir = dirname($dir); if ($dir != '.' && file_exists($dir)) { if (is_writeable($dir)) { return true; } return false; } if ($dir == $olddir) { // this can happen in safe mode return @is_writable($dir); } } return false; } return is_writeable($this->install_dir); } function setConfig(&$config, $resetInstallDir = true) { $this->_config = &$config; if ($resetInstallDir) { $this->setInstallDir($config->get('php_dir'), $config->get('metadata_dir')); } } function _initializeChannelDirs() { static $running = false; if (!$running) { $running = true; $ds = DIRECTORY_SEPARATOR; if (!is_dir($this->channelsdir) || !file_exists($this->channelsdir . $ds . 'pear.php.net.reg') || !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') || !file_exists($this->channelsdir . $ds . 'doc.php.net.reg') || !file_exists($this->channelsdir . $ds . '__uri.reg')) { if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) { $pear_channel = $this->_pearChannel; if (!is_a($pear_channel, 'PEAR_ChannelFile') || !$pear_channel->validate()) { if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } $pear_channel = new PEAR_ChannelFile; $pear_channel->setAlias('pear'); $pear_channel->setServer('pear.php.net'); $pear_channel->setSummary('PHP Extension and Application Repository'); $pear_channel->setDefaultPEARProtocols(); $pear_channel->setBaseURL('REST1.0', 'https://pear.php.net/rest/'); $pear_channel->setBaseURL('REST1.1', 'https://pear.php.net/rest/'); $pear_channel->setBaseURL('REST1.3', 'https://pear.php.net/rest/'); //$pear_channel->setBaseURL('REST1.4', 'https://pear.php.net/rest/'); } else { $pear_channel->setServer('pear.php.net'); $pear_channel->setAlias('pear'); } $pear_channel->validate(); $this->_addChannel($pear_channel); } if (!file_exists($this->channelsdir . $ds . 'pecl.php.net.reg')) { $pecl_channel = $this->_peclChannel; if (!is_a($pecl_channel, 'PEAR_ChannelFile') || !$pecl_channel->validate()) { if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } $pecl_channel = new PEAR_ChannelFile; $pecl_channel->setAlias('pecl'); $pecl_channel->setServer('pecl.php.net'); $pecl_channel->setSummary('PHP Extension Community Library'); $pecl_channel->setDefaultPEARProtocols(); $pecl_channel->setBaseURL('REST1.0', 'https://pecl.php.net/rest/'); $pecl_channel->setBaseURL('REST1.1', 'https://pecl.php.net/rest/'); $pecl_channel->setValidationPackage('PEAR_Validator_PECL', '1.0'); } else { $pecl_channel->setServer('pecl.php.net'); $pecl_channel->setAlias('pecl'); } $pecl_channel->validate(); $this->_addChannel($pecl_channel); } if (!file_exists($this->channelsdir . $ds . 'doc.php.net.reg')) { $doc_channel = $this->_docChannel; if (!is_a($doc_channel, 'PEAR_ChannelFile') || !$doc_channel->validate()) { if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } $doc_channel = new PEAR_ChannelFile; $doc_channel->setAlias('phpdocs'); $doc_channel->setServer('doc.php.net'); $doc_channel->setSummary('PHP Documentation Team'); $doc_channel->setDefaultPEARProtocols(); $doc_channel->setBaseURL('REST1.0', 'https://doc.php.net/rest/'); $doc_channel->setBaseURL('REST1.1', 'https://doc.php.net/rest/'); $doc_channel->setBaseURL('REST1.3', 'https://doc.php.net/rest/'); } else { $doc_channel->setServer('doc.php.net'); $doc_channel->setAlias('doc'); } $doc_channel->validate(); $this->_addChannel($doc_channel); } if (!file_exists($this->channelsdir . $ds . '__uri.reg')) { if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } $private = new PEAR_ChannelFile; $private->setName('__uri'); $private->setDefaultPEARProtocols(); $private->setBaseURL('REST1.0', '****'); $private->setSummary('Pseudo-channel for static packages'); $this->_addChannel($private); } $this->_rebuildFileMap(); } $running = false; } } function _initializeDirs() { $ds = DIRECTORY_SEPARATOR; // XXX Compatibility code should be removed in the future // rename all registry files if any to lowercase if (!OS_WINDOWS && file_exists($this->statedir) && is_dir($this->statedir) && $handle = opendir($this->statedir)) { $dest = $this->statedir . $ds; while (false !== ($file = readdir($handle))) { if (preg_match('/^.*[A-Z].*\.reg\\z/', $file)) { rename($dest . $file, $dest . strtolower($file)); } } closedir($handle); } $this->_initializeChannelDirs(); if (!file_exists($this->filemap)) { $this->_rebuildFileMap(); } $this->_initializeDepDB(); } function _initializeDepDB() { if (!isset($this->_dependencyDB)) { static $initializing = false; if (!$initializing) { $initializing = true; if (!$this->_config) { // never used? $file = OS_WINDOWS ? 'pear.ini' : '.pearrc'; $this->_config = new PEAR_Config($this->statedir . DIRECTORY_SEPARATOR . $file); $this->_config->setRegistry($this); $this->_config->set('php_dir', $this->install_dir); } $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config); if (PEAR::isError($this->_dependencyDB)) { // attempt to recover by removing the dep db if (file_exists($this->_config->get('metadata_dir', null, 'pear.php.net') . DIRECTORY_SEPARATOR . '.depdb')) { @unlink($this->_config->get('metadata_dir', null, 'pear.php.net') . DIRECTORY_SEPARATOR . '.depdb'); } $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config); if (PEAR::isError($this->_dependencyDB)) { echo $this->_dependencyDB->getMessage(); echo 'Unrecoverable error'; exit(1); } } $initializing = false; } } } /** * PEAR_Registry destructor. Makes sure no locks are forgotten. * * @access private */ function _PEAR_Registry() { parent::_PEAR(); if (is_resource($this->lock_fp)) { $this->_unlock(); } } /** * Make sure the directory where we keep registry files exists. * * @return bool TRUE if directory exists, FALSE if it could not be * created * * @access private */ function _assertStateDir($channel = false) { if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') { return $this->_assertChannelStateDir($channel); } static $init = false; if (!file_exists($this->statedir)) { if (!$this->hasWriteAccess()) { return false; } require_once 'System.php'; if (!System::mkdir(array('-p', $this->statedir))) { return $this->raiseError("could not create directory '{$this->statedir}'"); } $init = true; } elseif (!is_dir($this->statedir)) { return $this->raiseError('Cannot create directory ' . $this->statedir . ', ' . 'it already exists and is not a directory'); } $ds = DIRECTORY_SEPARATOR; if (!file_exists($this->channelsdir)) { if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg') || !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') || !file_exists($this->channelsdir . $ds . 'doc.php.net.reg') || !file_exists($this->channelsdir . $ds . '__uri.reg')) { $init = true; } } elseif (!is_dir($this->channelsdir)) { return $this->raiseError('Cannot create directory ' . $this->channelsdir . ', ' . 'it already exists and is not a directory'); } if ($init) { static $running = false; if (!$running) { $running = true; $this->_initializeDirs(); $running = false; $init = false; } } else { $this->_initializeDepDB(); } return true; } /** * Make sure the directory where we keep registry files exists for a non-standard channel. * * @param string channel name * @return bool TRUE if directory exists, FALSE if it could not be * created * * @access private */ function _assertChannelStateDir($channel) { $ds = DIRECTORY_SEPARATOR; if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') { if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) { $this->_initializeChannelDirs(); } return $this->_assertStateDir($channel); } $channelDir = $this->_channelDirectoryName($channel); if (!is_dir($this->channelsdir) || !file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) { $this->_initializeChannelDirs(); } if (!file_exists($channelDir)) { if (!$this->hasWriteAccess()) { return false; } require_once 'System.php'; if (!System::mkdir(array('-p', $channelDir))) { return $this->raiseError("could not create directory '" . $channelDir . "'"); } } elseif (!is_dir($channelDir)) { return $this->raiseError("could not create directory '" . $channelDir . "', already exists and is not a directory"); } return true; } /** * Make sure the directory where we keep registry files for channels exists * * @return bool TRUE if directory exists, FALSE if it could not be * created * * @access private */ function _assertChannelDir() { if (!file_exists($this->channelsdir)) { if (!$this->hasWriteAccess()) { return false; } require_once 'System.php'; if (!System::mkdir(array('-p', $this->channelsdir))) { return $this->raiseError("could not create directory '{$this->channelsdir}'"); } } elseif (!is_dir($this->channelsdir)) { return $this->raiseError("could not create directory '{$this->channelsdir}" . "', it already exists and is not a directory"); } if (!file_exists($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) { if (!$this->hasWriteAccess()) { return false; } require_once 'System.php'; if (!System::mkdir(array('-p', $this->channelsdir . DIRECTORY_SEPARATOR . '.alias'))) { return $this->raiseError("could not create directory '{$this->channelsdir}/.alias'"); } } elseif (!is_dir($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) { return $this->raiseError("could not create directory '{$this->channelsdir}" . "/.alias', it already exists and is not a directory"); } return true; } /** * Get the name of the file where data for a given package is stored. * * @param string channel name, or false if this is a PEAR package * @param string package name * * @return string registry file name * * @access public */ function _packageFileName($package, $channel = false) { if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') { return $this->_channelDirectoryName($channel) . DIRECTORY_SEPARATOR . strtolower($package) . '.reg'; } return $this->statedir . DIRECTORY_SEPARATOR . strtolower($package) . '.reg'; } /** * Get the name of the file where data for a given channel is stored. * @param string channel name * @return string registry file name */ function _channelFileName($channel, $noaliases = false) { if (!$noaliases) { if (file_exists($this->_getChannelAliasFileName($channel))) { $channel = implode('', file($this->_getChannelAliasFileName($channel))); } } return $this->channelsdir . DIRECTORY_SEPARATOR . str_replace('/', '_', strtolower($channel)) . '.reg'; } /** * @param string * @return string */ function _getChannelAliasFileName($alias) { return $this->channelsdir . DIRECTORY_SEPARATOR . '.alias' . DIRECTORY_SEPARATOR . str_replace('/', '_', strtolower($alias)) . '.txt'; } /** * Get the name of a channel from its alias */ function _getChannelFromAlias($channel) { if (!$this->_channelExists($channel)) { if ($channel == 'pear.php.net') { return 'pear.php.net'; } if ($channel == 'pecl.php.net') { return 'pecl.php.net'; } if ($channel == 'doc.php.net') { return 'doc.php.net'; } if ($channel == '__uri') { return '__uri'; } return false; } $channel = strtolower($channel); if (file_exists($this->_getChannelAliasFileName($channel))) { // translate an alias to an actual channel return implode('', file($this->_getChannelAliasFileName($channel))); } return $channel; } /** * Get the alias of a channel from its alias or its name */ function _getAlias($channel) { if (!$this->_channelExists($channel)) { if ($channel == 'pear.php.net') { return 'pear'; } if ($channel == 'pecl.php.net') { return 'pecl'; } if ($channel == 'doc.php.net') { return 'phpdocs'; } return false; } $channel = $this->_getChannel($channel); if (PEAR::isError($channel)) { return $channel; } return $channel->getAlias(); } /** * Get the name of the file where data for a given package is stored. * * @param string channel name, or false if this is a PEAR package * @param string package name * * @return string registry file name * * @access public */ function _channelDirectoryName($channel) { if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') { return $this->statedir; } $ch = $this->_getChannelFromAlias($channel); if (!$ch) { $ch = $channel; } return $this->statedir . DIRECTORY_SEPARATOR . strtolower('.channel.' . str_replace('/', '_', $ch)); } function _openPackageFile($package, $mode, $channel = false) { if (!$this->_assertStateDir($channel)) { return null; } if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) { return null; } $file = $this->_packageFileName($package, $channel); if (!file_exists($file) && $mode == 'r' || $mode == 'rb') { return null; } $fp = @fopen($file, $mode); if (!$fp) { return null; } return $fp; } function _closePackageFile($fp) { fclose($fp); } function _openChannelFile($channel, $mode) { if (!$this->_assertChannelDir()) { return null; } if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) { return null; } $file = $this->_channelFileName($channel); if (!file_exists($file) && $mode == 'r' || $mode == 'rb') { return null; } $fp = @fopen($file, $mode); if (!$fp) { return null; } return $fp; } function _closeChannelFile($fp) { fclose($fp); } function _rebuildFileMap() { if (!class_exists('PEAR_Installer_Role')) { require_once 'PEAR/Installer/Role.php'; } $channels = $this->_listAllPackages(); $files = array(); foreach ($channels as $channel => $packages) { foreach ($packages as $package) { $version = $this->_packageInfo($package, 'version', $channel); $filelist = $this->_packageInfo($package, 'filelist', $channel); if (!is_array($filelist)) { continue; } foreach ($filelist as $name => $attrs) { if (isset($attrs['attribs'])) { $attrs = $attrs['attribs']; } // it is possible for conflicting packages in different channels to // conflict with data files/doc files if ($name == 'dirtree') { continue; } if (isset($attrs['role']) && !in_array($attrs['role'], PEAR_Installer_Role::getInstallableRoles())) { // these are not installed continue; } if (isset($attrs['role']) && !in_array($attrs['role'], PEAR_Installer_Role::getBaseinstallRoles())) { $attrs['baseinstalldir'] = $package; } if (isset($attrs['baseinstalldir'])) { $file = $attrs['baseinstalldir'].DIRECTORY_SEPARATOR.$name; } else { $file = $name; } $file = preg_replace(',^/+,', '', $file); if ($channel != 'pear.php.net') { if (!isset($files[$attrs['role']])) { $files[$attrs['role']] = array(); } $files[$attrs['role']][$file] = array(strtolower($channel), strtolower($package)); } else { if (!isset($files[$attrs['role']])) { $files[$attrs['role']] = array(); } $files[$attrs['role']][$file] = strtolower($package); } } } } $this->_assertStateDir(); if (!$this->hasWriteAccess()) { return false; } $fp = @fopen($this->filemap, 'wb'); if (!$fp) { return false; } $this->filemap_cache = $files; fwrite($fp, serialize($files)); fclose($fp); return true; } function _readFileMap() { if (!file_exists($this->filemap)) { return array(); } $fp = @fopen($this->filemap, 'r'); if (!$fp) { $last_errormsg = ''; $last_error = error_get_last(); if (!empty($last_error['message'])) { $last_errormsg = $last_error['message']; } return $this->raiseError('PEAR_Registry: could not open filemap "' . $this->filemap . '"', PEAR_REGISTRY_ERROR_FILE, null, null, $last_errormsg); } clearstatcache(); $fsize = filesize($this->filemap); fclose($fp); $data = file_get_contents($this->filemap); $tmp = unserialize($data); if (!$tmp && $fsize > 7) { return $this->raiseError('PEAR_Registry: invalid filemap data', PEAR_REGISTRY_ERROR_FORMAT, null, null, $data); } $this->filemap_cache = $tmp; return true; } /** * Lock the registry. * * @param integer lock mode, one of LOCK_EX, LOCK_SH or LOCK_UN. * See flock manual for more information. * * @return bool TRUE on success, FALSE if locking failed, or a * PEAR error if some other error occurs (such as the * lock file not being writable). * * @access private */ function _lock($mode = LOCK_EX) { if (stristr(php_uname(), 'Windows 9')) { return true; } if ($mode != LOCK_UN && is_resource($this->lock_fp)) { // XXX does not check type of lock (LOCK_SH/LOCK_EX) return true; } if (!$this->_assertStateDir()) { if ($mode == LOCK_EX) { return $this->raiseError('Registry directory is not writeable by the current user'); } return true; } $open_mode = 'w'; // XXX People reported problems with LOCK_SH and 'w' if ($mode === LOCK_SH || $mode === LOCK_UN) { if (!file_exists($this->lockfile)) { touch($this->lockfile); } $open_mode = 'r'; } if (!is_resource($this->lock_fp)) { $this->lock_fp = @fopen($this->lockfile, $open_mode); } if (!is_resource($this->lock_fp)) { $this->lock_fp = null; return $this->raiseError("could not create lock file" . (isset($php_errormsg) ? ": " . $php_errormsg : "")); } if (!(int)flock($this->lock_fp, $mode)) { switch ($mode) { case LOCK_SH: $str = 'shared'; break; case LOCK_EX: $str = 'exclusive'; break; case LOCK_UN: $str = 'unlock'; break; default: $str = 'unknown'; break; } //is resource at this point, close it on error. fclose($this->lock_fp); $this->lock_fp = null; return $this->raiseError("could not acquire $str lock ($this->lockfile)", PEAR_REGISTRY_ERROR_LOCK); } return true; } function _unlock() { $ret = $this->_lock(LOCK_UN); if (is_resource($this->lock_fp)) { fclose($this->lock_fp); } $this->lock_fp = null; return $ret; } function _packageExists($package, $channel = false) { return file_exists($this->_packageFileName($package, $channel)); } /** * Determine whether a channel exists in the registry * * @param string Channel name * @param bool if true, then aliases will be ignored * @return boolean */ function _channelExists($channel, $noaliases = false) { $a = file_exists($this->_channelFileName($channel, $noaliases)); if (!$a && $channel == 'pear.php.net') { return true; } if (!$a && $channel == 'pecl.php.net') { return true; } if (!$a && $channel == 'doc.php.net') { return true; } return $a; } /** * Determine whether a mirror exists within the default channel in the registry * * @param string Channel name * @param string Mirror name * * @return boolean */ function _mirrorExists($channel, $mirror) { $data = $this->_channelInfo($channel); if (!isset($data['servers']['mirror'])) { return false; } foreach ($data['servers']['mirror'] as $m) { if ($m['attribs']['host'] == $mirror) { return true; } } return false; } /** * @param PEAR_ChannelFile Channel object * @param donotuse * @param string Last-Modified HTTP tag from remote request * @return boolean|PEAR_Error True on creation, false if it already exists */ function _addChannel($channel, $update = false, $lastmodified = false) { if (!is_a($channel, 'PEAR_ChannelFile')) { return false; } if (!$channel->validate()) { return false; } if (file_exists($this->_channelFileName($channel->getName()))) { if (!$update) { return false; } $checker = $this->_getChannel($channel->getName()); if (PEAR::isError($checker)) { return $checker; } if ($channel->getAlias() != $checker->getAlias()) { if (file_exists($this->_getChannelAliasFileName($checker->getAlias()))) { @unlink($this->_getChannelAliasFileName($checker->getAlias())); } } } else { if ($update && !in_array($channel->getName(), array('pear.php.net', 'pecl.php.net', 'doc.php.net'))) { return false; } } $ret = $this->_assertChannelDir(); if (PEAR::isError($ret)) { return $ret; } $ret = $this->_assertChannelStateDir($channel->getName()); if (PEAR::isError($ret)) { return $ret; } if ($channel->getAlias() != $channel->getName()) { if (file_exists($this->_getChannelAliasFileName($channel->getAlias())) && $this->_getChannelFromAlias($channel->getAlias()) != $channel->getName()) { $channel->setAlias($channel->getName()); } if (!$this->hasWriteAccess()) { return false; } $fp = @fopen($this->_getChannelAliasFileName($channel->getAlias()), 'w'); if (!$fp) { return false; } fwrite($fp, $channel->getName()); fclose($fp); } if (!$this->hasWriteAccess()) { return false; } $fp = @fopen($this->_channelFileName($channel->getName()), 'wb'); if (!$fp) { return false; } $info = $channel->toArray(); if ($lastmodified) { $info['_lastmodified'] = $lastmodified; } else { $info['_lastmodified'] = self::getSourceDateEpoch(); } fwrite($fp, serialize($info)); fclose($fp); return true; } /** * Deletion fails if there are any packages installed from the channel * @param string|PEAR_ChannelFile channel name * @return boolean|PEAR_Error True on deletion, false if it doesn't exist */ function _deleteChannel($channel) { if (!is_string($channel)) { if (!is_a($channel, 'PEAR_ChannelFile')) { return false; } if (!$channel->validate()) { return false; } $channel = $channel->getName(); } if ($this->_getChannelFromAlias($channel) == '__uri') { return false; } if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') { return false; } if ($this->_getChannelFromAlias($channel) == 'doc.php.net') { return false; } if (!$this->_channelExists($channel)) { return false; } if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') { return false; } $channel = $this->_getChannelFromAlias($channel); if ($channel == 'pear.php.net') { return false; } $test = $this->_listChannelPackages($channel); if (count($test)) { return false; } $test = @rmdir($this->_channelDirectoryName($channel)); if (!$test) { return false; } $file = $this->_getChannelAliasFileName($this->_getAlias($channel)); if (file_exists($file)) { $test = @unlink($file); if (!$test) { return false; } } $file = $this->_channelFileName($channel); $ret = true; if (file_exists($file)) { $ret = @unlink($file); } return $ret; } /** * Determine whether a channel exists in the registry * @param string Channel Alias * @return boolean */ function _isChannelAlias($alias) { return file_exists($this->_getChannelAliasFileName($alias)); } /** * @param string|null * @param string|null * @param string|null * @return array|null * @access private */ function _packageInfo($package = null, $key = null, $channel = 'pear.php.net') { if ($package === null) { if ($channel === null) { $channels = $this->_listChannels(); $ret = array(); foreach ($channels as $channel) { $channel = strtolower($channel); $ret[$channel] = array(); $packages = $this->_listPackages($channel); foreach ($packages as $package) { $ret[$channel][] = $this->_packageInfo($package, null, $channel); } } return $ret; } $ps = $this->_listPackages($channel); if (!count($ps)) { return array(); } return array_map(array(&$this, '_packageInfo'), $ps, array_fill(0, count($ps), null), array_fill(0, count($ps), $channel)); } $fp = $this->_openPackageFile($package, 'r', $channel); if ($fp === null) { return null; } clearstatcache(); $this->_closePackageFile($fp); $data = file_get_contents($this->_packageFileName($package, $channel)); $data = @unserialize($data); if ($key === null) { return $data; } // compatibility for package.xml version 2.0 if (isset($data['old'][$key])) { return $data['old'][$key]; } if (isset($data[$key])) { return $data[$key]; } return null; } /** * @param string Channel name * @param bool whether to strictly retrieve info of channels, not just aliases * @return array|null */ function _channelInfo($channel, $noaliases = false) { if (!$this->_channelExists($channel, $noaliases)) { return null; } $fp = $this->_openChannelFile($channel, 'r'); if ($fp === null) { return null; } clearstatcache(); $this->_closeChannelFile($fp); $data = file_get_contents($this->_channelFileName($channel)); $data = unserialize($data); return $data; } function _listChannels() { $channellist = array(); if (!file_exists($this->channelsdir) || !is_dir($this->channelsdir)) { return array('pear.php.net', 'pecl.php.net', 'doc.php.net', '__uri'); } $dp = opendir($this->channelsdir); while ($ent = readdir($dp)) { if ($ent[0] == '.' || substr($ent, -4) != '.reg') { continue; } if ($ent == '__uri.reg') { $channellist[] = '__uri'; continue; } $channellist[] = str_replace('_', '/', substr($ent, 0, -4)); } closedir($dp); if (!in_array('pear.php.net', $channellist)) { $channellist[] = 'pear.php.net'; } if (!in_array('pecl.php.net', $channellist)) { $channellist[] = 'pecl.php.net'; } if (!in_array('doc.php.net', $channellist)) { $channellist[] = 'doc.php.net'; } if (!in_array('__uri', $channellist)) { $channellist[] = '__uri'; } natsort($channellist); return $channellist; } function _listPackages($channel = false) { if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') { return $this->_listChannelPackages($channel); } if (!file_exists($this->statedir) || !is_dir($this->statedir)) { return array(); } $pkglist = array(); $dp = opendir($this->statedir); if (!$dp) { return $pkglist; } while ($ent = readdir($dp)) { if ($ent[0] == '.' || substr($ent, -4) != '.reg') { continue; } $pkglist[] = substr($ent, 0, -4); } closedir($dp); sort($pkglist); return $pkglist; } function _listChannelPackages($channel) { $pkglist = array(); if (!file_exists($this->_channelDirectoryName($channel)) || !is_dir($this->_channelDirectoryName($channel))) { return array(); } $dp = opendir($this->_channelDirectoryName($channel)); if (!$dp) { return $pkglist; } while ($ent = readdir($dp)) { if ($ent[0] == '.' || substr($ent, -4) != '.reg') { continue; } $pkglist[] = substr($ent, 0, -4); } closedir($dp); return $pkglist; } function _listAllPackages() { $ret = array(); foreach ($this->_listChannels() as $channel) { $ret[$channel] = $this->_listPackages($channel); } return $ret; } /** * Add an installed package to the registry * @param string package name * @param array package info (parsed by PEAR_Common::infoFrom*() methods) * @return bool success of saving * @access private */ function _addPackage($package, $info) { if ($this->_packageExists($package)) { return false; } $fp = $this->_openPackageFile($package, 'wb'); if ($fp === null) { return false; } $info['_lastmodified'] = self::getSourceDateEpoch(); fwrite($fp, serialize($info)); $this->_closePackageFile($fp); if (isset($info['filelist'])) { $this->_rebuildFileMap(); } return true; } /** * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 * @return bool * @access private */ function _addPackage2($info) { if (!is_a($info, 'PEAR_PackageFile_v1') && !is_a($info, 'PEAR_PackageFile_v2')) { return false; } if (!$info->validate()) { if (class_exists('PEAR_Common')) { $ui = PEAR_Frontend::singleton(); if ($ui) { foreach ($info->getValidationWarnings() as $err) { $ui->log($err['message'], true); } } } return false; } $channel = $info->getChannel(); $package = $info->getPackage(); $save = $info; if ($this->_packageExists($package, $channel)) { return false; } if (!$this->_channelExists($channel, true)) { return false; } $info = $info->toArray(true); if (!$info) { return false; } $fp = $this->_openPackageFile($package, 'wb', $channel); if ($fp === null) { return false; } $info['_lastmodified'] = self::getSourceDateEpoch(); fwrite($fp, serialize($info)); $this->_closePackageFile($fp); $this->_rebuildFileMap(); return true; } /** * @param string Package name * @param array parsed package.xml 1.0 * @param bool this parameter is only here for BC. Don't use it. * @access private */ function _updatePackage($package, $info, $merge = true) { $oldinfo = $this->_packageInfo($package); if (empty($oldinfo)) { return false; } $fp = $this->_openPackageFile($package, 'w'); if ($fp === null) { return false; } if (is_object($info)) { $info = $info->toArray(); } $info['_lastmodified'] = self::getSourceDateEpoch(); $newinfo = $info; if ($merge) { $info = array_merge($oldinfo, $info); } else { $diff = $info; } fwrite($fp, serialize($info)); $this->_closePackageFile($fp); if (isset($newinfo['filelist'])) { $this->_rebuildFileMap(); } return true; } /** * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 * @return bool * @access private */ function _updatePackage2($info) { if (!$this->_packageExists($info->getPackage(), $info->getChannel())) { return false; } $fp = $this->_openPackageFile($info->getPackage(), 'w', $info->getChannel()); if ($fp === null) { return false; } $save = $info; $info = $save->getArray(true); $info['_lastmodified'] = self::getSourceDateEpoch(); fwrite($fp, serialize($info)); $this->_closePackageFile($fp); $this->_rebuildFileMap(); return true; } /** * @param string Package name * @param string Channel name * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null * @access private */ function &_getPackage($package, $channel = 'pear.php.net') { $info = $this->_packageInfo($package, null, $channel); if ($info === null) { return $info; } $a = $this->_config; if (!$a) { $this->_config = new PEAR_Config; $this->_config->set('php_dir', $this->statedir); } if (!class_exists('PEAR_PackageFile')) { require_once 'PEAR/PackageFile.php'; } $pkg = new PEAR_PackageFile($this->_config); $pf = &$pkg->fromArray($info); return $pf; } /** * @param string channel name * @param bool whether to strictly retrieve channel names * @return PEAR_ChannelFile|PEAR_Error * @access private */ function &_getChannel($channel, $noaliases = false) { $ch = false; if ($this->_channelExists($channel, $noaliases)) { $chinfo = $this->_channelInfo($channel, $noaliases); if ($chinfo) { if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } $ch = &PEAR_ChannelFile::fromArrayWithErrors($chinfo); } } if ($ch) { if ($ch->validate()) { return $ch; } foreach ($ch->getErrors(true) as $err) { $message = $err['message'] . "\n"; } $ch = PEAR::raiseError($message); return $ch; } if ($this->_getChannelFromAlias($channel) == 'pear.php.net') { // the registry is not properly set up, so use defaults if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } $pear_channel = new PEAR_ChannelFile; $pear_channel->setServer('pear.php.net'); $pear_channel->setAlias('pear'); $pear_channel->setSummary('PHP Extension and Application Repository'); $pear_channel->setDefaultPEARProtocols(); $pear_channel->setBaseURL('REST1.0', 'https://pear.php.net/rest/'); $pear_channel->setBaseURL('REST1.1', 'https://pear.php.net/rest/'); $pear_channel->setBaseURL('REST1.3', 'https://pear.php.net/rest/'); return $pear_channel; } if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') { // the registry is not properly set up, so use defaults if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } $pear_channel = new PEAR_ChannelFile; $pear_channel->setServer('pecl.php.net'); $pear_channel->setAlias('pecl'); $pear_channel->setSummary('PHP Extension Community Library'); $pear_channel->setDefaultPEARProtocols(); $pear_channel->setBaseURL('REST1.0', 'https://pecl.php.net/rest/'); $pear_channel->setBaseURL('REST1.1', 'https://pecl.php.net/rest/'); $pear_channel->setValidationPackage('PEAR_Validator_PECL', '1.0'); return $pear_channel; } if ($this->_getChannelFromAlias($channel) == 'doc.php.net') { // the registry is not properly set up, so use defaults if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } $doc_channel = new PEAR_ChannelFile; $doc_channel->setServer('doc.php.net'); $doc_channel->setAlias('phpdocs'); $doc_channel->setSummary('PHP Documentation Team'); $doc_channel->setDefaultPEARProtocols(); $doc_channel->setBaseURL('REST1.0', 'https://doc.php.net/rest/'); $doc_channel->setBaseURL('REST1.1', 'https://doc.php.net/rest/'); $doc_channel->setBaseURL('REST1.3', 'https://doc.php.net/rest/'); return $doc_channel; } if ($this->_getChannelFromAlias($channel) == '__uri') { // the registry is not properly set up, so use defaults if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } $private = new PEAR_ChannelFile; $private->setName('__uri'); $private->setDefaultPEARProtocols(); $private->setBaseURL('REST1.0', '****'); $private->setSummary('Pseudo-channel for static packages'); return $private; } return $ch; } /** * @param string Package name * @param string Channel name * @return bool */ function packageExists($package, $channel = 'pear.php.net') { if (PEAR::isError($e = $this->_lock(LOCK_SH))) { return $e; } $ret = $this->_packageExists($package, $channel); $this->_unlock(); return $ret; } // }}} // {{{ channelExists() /** * @param string channel name * @param bool if true, then aliases will be ignored * @return bool */ function channelExists($channel, $noaliases = false) { if (PEAR::isError($e = $this->_lock(LOCK_SH))) { return $e; } $ret = $this->_channelExists($channel, $noaliases); $this->_unlock(); return $ret; } // }}} /** * @param string channel name mirror is in * @param string mirror name * * @return bool */ function mirrorExists($channel, $mirror) { if (PEAR::isError($e = $this->_lock(LOCK_SH))) { return $e; } $ret = $this->_mirrorExists($channel, $mirror); $this->_unlock(); return $ret; } // {{{ isAlias() /** * Determines whether the parameter is an alias of a channel * @param string * @return bool */ function isAlias($alias) { if (PEAR::isError($e = $this->_lock(LOCK_SH))) { return $e; } $ret = $this->_isChannelAlias($alias); $this->_unlock(); return $ret; } // }}} // {{{ packageInfo() /** * @param string|null * @param string|null * @param string * @return array|null */ function packageInfo($package = null, $key = null, $channel = 'pear.php.net') { if (PEAR::isError($e = $this->_lock(LOCK_SH))) { return $e; } $ret = $this->_packageInfo($package, $key, $channel); $this->_unlock(); return $ret; } // }}} // {{{ channelInfo() /** * Retrieve a raw array of channel data. * * Do not use this, instead use {@link getChannel()} for normal * operations. Array structure is undefined in this method * @param string channel name * @param bool whether to strictly retrieve information only on non-aliases * @return array|null|PEAR_Error */ function channelInfo($channel = null, $noaliases = false) { if (PEAR::isError($e = $this->_lock(LOCK_SH))) { return $e; } $ret = $this->_channelInfo($channel, $noaliases); $this->_unlock(); return $ret; } // }}} /** * @param string */ function channelName($channel) { if (PEAR::isError($e = $this->_lock(LOCK_SH))) { return $e; } $ret = $this->_getChannelFromAlias($channel); $this->_unlock(); return $ret; } /** * @param string */ function channelAlias($channel) { if (PEAR::isError($e = $this->_lock(LOCK_SH))) { return $e; } $ret = $this->_getAlias($channel); $this->_unlock(); return $ret; } // {{{ listPackages() function listPackages($channel = false) { if (PEAR::isError($e = $this->_lock(LOCK_SH))) { return $e; } $ret = $this->_listPackages($channel); $this->_unlock(); return $ret; } // }}} // {{{ listAllPackages() function listAllPackages() { if (PEAR::isError($e = $this->_lock(LOCK_SH))) { return $e; } $ret = $this->_listAllPackages(); $this->_unlock(); return $ret; } // }}} // {{{ listChannel() function listChannels() { if (PEAR::isError($e = $this->_lock(LOCK_SH))) { return $e; } $ret = $this->_listChannels(); $this->_unlock(); return $ret; } // }}} // {{{ addPackage() /** * Add an installed package to the registry * @param string|PEAR_PackageFile_v1|PEAR_PackageFile_v2 package name or object * that will be passed to {@link addPackage2()} * @param array package info (parsed by PEAR_Common::infoFrom*() methods) * @return bool success of saving */ function addPackage($package, $info) { if (is_object($info)) { return $this->addPackage2($info); } if (PEAR::isError($e = $this->_lock(LOCK_EX))) { return $e; } $ret = $this->_addPackage($package, $info); $this->_unlock(); if ($ret) { if (!class_exists('PEAR_PackageFile_v1')) { require_once 'PEAR/PackageFile/v1.php'; } $pf = new PEAR_PackageFile_v1; $pf->setConfig($this->_config); $pf->fromArray($info); $this->_dependencyDB->uninstallPackage($pf); $this->_dependencyDB->installPackage($pf); } return $ret; } // }}} // {{{ addPackage2() function addPackage2($info) { if (!is_object($info)) { return $this->addPackage($info['package'], $info); } if (PEAR::isError($e = $this->_lock(LOCK_EX))) { return $e; } $ret = $this->_addPackage2($info); $this->_unlock(); if ($ret) { $this->_dependencyDB->uninstallPackage($info); $this->_dependencyDB->installPackage($info); } return $ret; } // }}} // {{{ updateChannel() /** * For future expandibility purposes, separate this * @param PEAR_ChannelFile */ function updateChannel($channel, $lastmodified = null) { if ($channel->getName() == '__uri') { return false; } return $this->addChannel($channel, $lastmodified, true); } // }}} // {{{ deleteChannel() /** * Deletion fails if there are any packages installed from the channel * @param string|PEAR_ChannelFile channel name * @return boolean|PEAR_Error True on deletion, false if it doesn't exist */ function deleteChannel($channel) { if (PEAR::isError($e = $this->_lock(LOCK_EX))) { return $e; } $ret = $this->_deleteChannel($channel); $this->_unlock(); if ($ret && is_a($this->_config, 'PEAR_Config')) { $this->_config->setChannels($this->listChannels()); } return $ret; } // }}} // {{{ addChannel() /** * @param PEAR_ChannelFile Channel object * @param string Last-Modified header from HTTP for caching * @return boolean|PEAR_Error True on creation, false if it already exists */ function addChannel($channel, $lastmodified = false, $update = false) { if (!is_a($channel, 'PEAR_ChannelFile') || !$channel->validate()) { return false; } if (PEAR::isError($e = $this->_lock(LOCK_EX))) { return $e; } $ret = $this->_addChannel($channel, $update, $lastmodified); $this->_unlock(); if (!$update && $ret && is_a($this->_config, 'PEAR_Config')) { $this->_config->setChannels($this->listChannels()); } return $ret; } // }}} // {{{ deletePackage() function deletePackage($package, $channel = 'pear.php.net') { if (PEAR::isError($e = $this->_lock(LOCK_EX))) { return $e; } $file = $this->_packageFileName($package, $channel); $ret = file_exists($file) ? @unlink($file) : false; $this->_rebuildFileMap(); $this->_unlock(); $p = array('channel' => $channel, 'package' => $package); $this->_dependencyDB->uninstallPackage($p); return $ret; } // }}} // {{{ updatePackage() function updatePackage($package, $info, $merge = true) { if (is_object($info)) { return $this->updatePackage2($info, $merge); } if (PEAR::isError($e = $this->_lock(LOCK_EX))) { return $e; } $ret = $this->_updatePackage($package, $info, $merge); $this->_unlock(); if ($ret) { if (!class_exists('PEAR_PackageFile_v1')) { require_once 'PEAR/PackageFile/v1.php'; } $pf = new PEAR_PackageFile_v1; $pf->setConfig($this->_config); $pf->fromArray($this->packageInfo($package)); $this->_dependencyDB->uninstallPackage($pf); $this->_dependencyDB->installPackage($pf); } return $ret; } // }}} // {{{ updatePackage2() function updatePackage2($info) { if (!is_object($info)) { return $this->updatePackage($info['package'], $info, $merge); } if (!$info->validate(PEAR_VALIDATE_DOWNLOADING)) { return false; } if (PEAR::isError($e = $this->_lock(LOCK_EX))) { return $e; } $ret = $this->_updatePackage2($info); $this->_unlock(); if ($ret) { $this->_dependencyDB->uninstallPackage($info); $this->_dependencyDB->installPackage($info); } return $ret; } // }}} // {{{ getChannel() /** * @param string channel name * @param bool whether to strictly return raw channels (no aliases) * @return PEAR_ChannelFile|PEAR_Error */ function getChannel($channel, $noaliases = false) { if (PEAR::isError($e = $this->_lock(LOCK_SH))) { return $e; } $ret = $this->_getChannel($channel, $noaliases); $this->_unlock(); if (!$ret) { return PEAR::raiseError('Unknown channel: ' . $channel); } return $ret; } // }}} // {{{ getPackage() /** * @param string package name * @param string channel name * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null */ function &getPackage($package, $channel = 'pear.php.net') { if (PEAR::isError($e = $this->_lock(LOCK_SH))) { return $e; } $pf = &$this->_getPackage($package, $channel); $this->_unlock(); return $pf; } // }}} /** * Get PEAR_PackageFile_v[1/2] objects representing the contents of * a dependency group that are installed. * * This is used at uninstall-time * @param array * @return array|false */ function getInstalledGroup($group) { $ret = array(); if (isset($group['package'])) { if (!isset($group['package'][0])) { $group['package'] = array($group['package']); } foreach ($group['package'] as $package) { $depchannel = isset($package['channel']) ? $package['channel'] : '__uri'; $p = &$this->getPackage($package['name'], $depchannel); if ($p) { $save = &$p; $ret[] = &$save; } } } if (isset($group['subpackage'])) { if (!isset($group['subpackage'][0])) { $group['subpackage'] = array($group['subpackage']); } foreach ($group['subpackage'] as $package) { $depchannel = isset($package['channel']) ? $package['channel'] : '__uri'; $p = &$this->getPackage($package['name'], $depchannel); if ($p) { $save = &$p; $ret[] = &$save; } } } if (!count($ret)) { return false; } return $ret; } // {{{ getChannelValidator() /** * @param string channel name * @return PEAR_Validate|false */ function &getChannelValidator($channel) { $chan = $this->getChannel($channel); if (PEAR::isError($chan)) { return $chan; } $val = $chan->getValidationObject(); return $val; } // }}} // {{{ getChannels() /** * @param string channel name * @return array an array of PEAR_ChannelFile objects representing every installed channel */ function &getChannels() { $ret = array(); if (PEAR::isError($e = $this->_lock(LOCK_SH))) { return $e; } foreach ($this->_listChannels() as $channel) { $e = &$this->_getChannel($channel); if (!$e || PEAR::isError($e)) { continue; } $ret[] = $e; } $this->_unlock(); return $ret; } // }}} // {{{ checkFileMap() /** * Test whether a file or set of files belongs to a package. * * If an array is passed in * @param string|array file path, absolute or relative to the pear * install dir * @param string|array name of PEAR package or array('package' => name, 'channel' => * channel) of a package that will be ignored * @param string API version - 1.1 will exclude any files belonging to a package * @param array private recursion variable * @return array|false which package and channel the file belongs to, or an empty * string if the file does not belong to an installed package, * or belongs to the second parameter's package */ function checkFileMap($path, $package = false, $api = '1.0', $attrs = false) { if (is_array($path)) { static $notempty; if (empty($notempty)) { if (!class_exists('PEAR_Installer_Role')) { require_once 'PEAR/Installer/Role.php'; } $notempty = function($a) { return !empty($a); }; } $package = is_array($package) ? array(strtolower($package[0]), strtolower($package[1])) : strtolower($package); $pkgs = array(); foreach ($path as $name => $attrs) { if (is_array($attrs)) { if (isset($attrs['install-as'])) { $name = $attrs['install-as']; } if (!in_array($attrs['role'], PEAR_Installer_Role::getInstallableRoles())) { // these are not installed continue; } if (!in_array($attrs['role'], PEAR_Installer_Role::getBaseinstallRoles())) { $attrs['baseinstalldir'] = is_array($package) ? $package[1] : $package; } if (isset($attrs['baseinstalldir'])) { $name = $attrs['baseinstalldir'] . DIRECTORY_SEPARATOR . $name; } } $pkgs[$name] = $this->checkFileMap($name, $package, $api, $attrs); if (PEAR::isError($pkgs[$name])) { return $pkgs[$name]; } } return array_filter($pkgs, $notempty); } if (empty($this->filemap_cache)) { if (PEAR::isError($e = $this->_lock(LOCK_SH))) { return $e; } $err = $this->_readFileMap(); $this->_unlock(); if (PEAR::isError($err)) { return $err; } } if (!$attrs) { $attrs = array('role' => 'php'); // any old call would be for PHP role only } if (isset($this->filemap_cache[$attrs['role']][$path])) { if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) { return false; } return $this->filemap_cache[$attrs['role']][$path]; } $l = strlen($this->install_dir); if (substr($path, 0, $l) == $this->install_dir) { $path = preg_replace('!^'.DIRECTORY_SEPARATOR.'+!', '', substr($path, $l)); } if (isset($this->filemap_cache[$attrs['role']][$path])) { if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) { return false; } return $this->filemap_cache[$attrs['role']][$path]; } return false; } // }}} // {{{ flush() /** * Force a reload of the filemap * @since 1.5.0RC3 */ function flushFileMap() { $this->filemap_cache = null; clearstatcache(); // ensure that the next read gets the full, current filemap } // }}} // {{{ apiVersion() /** * Get the expected API version. Channels API is version 1.1, as it is backwards * compatible with 1.0 * @return string */ function apiVersion() { return '1.1'; } // }}} /** * Parse a package name, or validate a parsed package name array * @param string|array pass in an array of format * array( * 'package' => 'pname', * ['channel' => 'channame',] * ['version' => 'version',] * ['state' => 'state',] * ['group' => 'groupname']) * or a string of format * [channel://][channame/]pname[-version|-state][/group=groupname] * @return array|PEAR_Error */ function parsePackageName($param, $defaultchannel = 'pear.php.net') { $saveparam = $param; if (is_array($param)) { // convert to string for error messages $saveparam = $this->parsedPackageNameToString($param); // process the array if (!isset($param['package'])) { return PEAR::raiseError('parsePackageName(): array $param ' . 'must contain a valid package name in index "param"', 'package', null, null, $param); } if (!isset($param['uri'])) { if (!isset($param['channel'])) { $param['channel'] = $defaultchannel; } } else { $param['channel'] = '__uri'; } } else { $components = @parse_url((string) $param); if (isset($components['scheme'])) { if ($components['scheme'] == 'http') { // uri package $param = array('uri' => $param, 'channel' => '__uri'); } elseif($components['scheme'] != 'channel') { return PEAR::raiseError('parsePackageName(): only channel:// uris may ' . 'be downloaded, not "' . $param . '"', 'invalid', null, null, $param); } } if (!isset($components['path'])) { return PEAR::raiseError('parsePackageName(): array $param ' . 'must contain a valid package name in "' . $param . '"', 'package', null, null, $param); } if (isset($components['host'])) { // remove the leading "/" $components['path'] = substr($components['path'], 1); } if (!isset($components['scheme'])) { if (strpos($components['path'], '/') !== false) { if ($components['path'][0] == '/') { return PEAR::raiseError('parsePackageName(): this is not ' . 'a package name, it begins with "/" in "' . $param . '"', 'invalid', null, null, $param); } $parts = explode('/', $components['path']); $components['host'] = array_shift($parts); if (count($parts) > 1) { $components['path'] = array_pop($parts); $components['host'] .= '/' . implode('/', $parts); } else { $components['path'] = implode('/', $parts); } } else { $components['host'] = $defaultchannel; } } else { if (strpos($components['path'], '/')) { $parts = explode('/', $components['path']); $components['path'] = array_pop($parts); $components['host'] .= '/' . implode('/', $parts); } } if (is_array($param)) { $param['package'] = $components['path']; } else { $param = array( 'package' => $components['path'] ); if (isset($components['host'])) { $param['channel'] = $components['host']; } } if (isset($components['fragment'])) { $param['group'] = $components['fragment']; } if (isset($components['user'])) { $param['user'] = $components['user']; } if (isset($components['pass'])) { $param['pass'] = $components['pass']; } if (isset($components['query'])) { parse_str($components['query'], $param['opts']); } // check for extension $pathinfo = pathinfo($param['package']); if (isset($pathinfo['extension']) && in_array(strtolower($pathinfo['extension']), array('tgz', 'tar'))) { $param['extension'] = $pathinfo['extension']; $param['package'] = substr($pathinfo['basename'], 0, strlen($pathinfo['basename']) - 4); } // check for version if (strpos($param['package'], '-')) { $test = explode('-', $param['package']); if (count($test) != 2) { return PEAR::raiseError('parsePackageName(): only one version/state ' . 'delimiter "-" is allowed in "' . $saveparam . '"', 'version', null, null, $param); } list($param['package'], $param['version']) = $test; } } // validation $info = $this->channelExists($param['channel']); if (PEAR::isError($info)) { return $info; } if (!$info) { return PEAR::raiseError('unknown channel "' . $param['channel'] . '" in "' . $saveparam . '"', 'channel', null, null, $param); } $chan = $this->getChannel($param['channel']); if (PEAR::isError($chan)) { return $chan; } if (!$chan) { return PEAR::raiseError("Exception: corrupt registry, could not " . "retrieve channel " . $param['channel'] . " information", 'registry', null, null, $param); } $param['channel'] = $chan->getName(); $validate = $chan->getValidationObject(); $vpackage = $chan->getValidationPackage(); // validate package name if (!$validate->validPackageName($param['package'], $vpackage['_content'])) { return PEAR::raiseError('parsePackageName(): invalid package name "' . $param['package'] . '" in "' . $saveparam . '"', 'package', null, null, $param); } if (isset($param['group'])) { if (!PEAR_Validate::validGroupName($param['group'])) { return PEAR::raiseError('parsePackageName(): dependency group "' . $param['group'] . '" is not a valid group name in "' . $saveparam . '"', 'group', null, null, $param); } } if (isset($param['state'])) { if (!in_array(strtolower($param['state']), $validate->getValidStates())) { return PEAR::raiseError('parsePackageName(): state "' . $param['state'] . '" is not a valid state in "' . $saveparam . '"', 'state', null, null, $param); } } if (isset($param['version'])) { if (isset($param['state'])) { return PEAR::raiseError('parsePackageName(): cannot contain both ' . 'a version and a stability (state) in "' . $saveparam . '"', 'version/state', null, null, $param); } // check whether version is actually a state if (in_array(strtolower($param['version']), $validate->getValidStates())) { $param['state'] = strtolower($param['version']); unset($param['version']); } else { if (!$validate->validVersion($param['version'])) { return PEAR::raiseError('parsePackageName(): "' . $param['version'] . '" is neither a valid version nor a valid state in "' . $saveparam . '"', 'version/state', null, null, $param); } } } return $param; } /** * @param array * @return string */ function parsedPackageNameToString($parsed, $brief = false) { if (is_string($parsed)) { return $parsed; } if (is_object($parsed)) { $p = $parsed; $parsed = array( 'package' => $p->getPackage(), 'channel' => $p->getChannel(), 'version' => $p->getVersion(), ); } if (isset($parsed['uri'])) { return $parsed['uri']; } if ($brief) { if ($channel = $this->channelAlias($parsed['channel'])) { return $channel . '/' . $parsed['package']; } } $upass = ''; if (isset($parsed['user'])) { $upass = $parsed['user']; if (isset($parsed['pass'])) { $upass .= ':' . $parsed['pass']; } $upass = "$upass@"; } $ret = 'channel://' . $upass . $parsed['channel'] . '/' . $parsed['package']; if (isset($parsed['version']) || isset($parsed['state'])) { $ver = isset($parsed['version']) ? $parsed['version'] : ''; $ver .= isset($parsed['state']) ? $parsed['state'] : ''; $ret .= '-' . $ver; } if (isset($parsed['extension'])) { $ret .= '.' . $parsed['extension']; } if (isset($parsed['opts'])) { $ret .= '?'; foreach ($parsed['opts'] as $name => $value) { $parsed['opts'][$name] = "$name=$value"; } $ret .= implode('&', $parsed['opts']); } if (isset($parsed['group'])) { $ret .= '#' . $parsed['group']; } return $ret; } } PK=1]4-^.++pear/PEAR/REST/11.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.3 */ /** * For downloading REST xml/txt files */ require_once 'PEAR/REST.php'; /** * Implement REST 1.1 * * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.3 */ class PEAR_REST_11 { /** * @var PEAR_REST */ var $_rest; function __construct($config, $options = array()) { $this->_rest = new PEAR_REST($config, $options); } function listAll($base, $dostable, $basic = true, $searchpackage = false, $searchsummary = false, $channel = false) { $categorylist = $this->_rest->retrieveData($base . 'c/categories.xml', false, false, $channel); if (PEAR::isError($categorylist)) { return $categorylist; } $ret = array(); if (!is_array($categorylist['c']) || !isset($categorylist['c'][0])) { $categorylist['c'] = array($categorylist['c']); } PEAR::pushErrorHandling(PEAR_ERROR_RETURN); foreach ($categorylist['c'] as $progress => $category) { $category = $category['_content']; $packagesinfo = $this->_rest->retrieveData($base . 'c/' . urlencode($category) . '/packagesinfo.xml', false, false, $channel); if (PEAR::isError($packagesinfo)) { continue; } if (!is_array($packagesinfo) || !isset($packagesinfo['pi'])) { continue; } if (!is_array($packagesinfo['pi']) || !isset($packagesinfo['pi'][0])) { $packagesinfo['pi'] = array($packagesinfo['pi']); } foreach ($packagesinfo['pi'] as $packageinfo) { if (empty($packageinfo)) { continue; } $info = $packageinfo['p']; $package = $info['n']; $releases = isset($packageinfo['a']) ? $packageinfo['a'] : false; unset($latest); unset($unstable); unset($stable); unset($state); if ($releases) { if (!isset($releases['r'][0])) { $releases['r'] = array($releases['r']); } foreach ($releases['r'] as $release) { if (!isset($latest)) { if ($dostable && $release['s'] == 'stable') { $latest = $release['v']; $state = 'stable'; } if (!$dostable) { $latest = $release['v']; $state = $release['s']; } } if (!isset($stable) && $release['s'] == 'stable') { $stable = $release['v']; if (!isset($unstable)) { $unstable = $stable; } } if (!isset($unstable) && $release['s'] != 'stable') { $unstable = $release['v']; $state = $release['s']; } if (isset($latest) && !isset($state)) { $state = $release['s']; } if (isset($latest) && isset($stable) && isset($unstable)) { break; } } } if ($basic) { // remote-list command if (!isset($latest)) { $latest = false; } if ($dostable) { // $state is not set if there are no releases if (isset($state) && $state == 'stable') { $ret[$package] = array('stable' => $latest); } else { $ret[$package] = array('stable' => '-n/a-'); } } else { $ret[$package] = array('stable' => $latest); } continue; } // list-all command if (!isset($unstable)) { $unstable = false; $state = 'stable'; if (isset($stable)) { $latest = $unstable = $stable; } } else { $latest = $unstable; } if (!isset($latest)) { $latest = false; } $deps = array(); if ($latest && isset($packageinfo['deps'])) { if (!is_array($packageinfo['deps']) || !isset($packageinfo['deps'][0]) ) { $packageinfo['deps'] = array($packageinfo['deps']); } $d = false; foreach ($packageinfo['deps'] as $dep) { if ($dep['v'] == $latest) { $d = unserialize($dep['d']); } } if ($d) { if (isset($d['required'])) { if (!class_exists('PEAR_PackageFile_v2')) { require_once 'PEAR/PackageFile/v2.php'; } if (!isset($pf)) { $pf = new PEAR_PackageFile_v2; } $pf->setDeps($d); $tdeps = $pf->getDeps(); } else { $tdeps = $d; } foreach ($tdeps as $dep) { if ($dep['type'] !== 'pkg') { continue; } $deps[] = $dep; } } } $info = array( 'stable' => $latest, 'summary' => $info['s'], 'description' => $info['d'], 'deps' => $deps, 'category' => $info['ca']['_content'], 'unstable' => $unstable, 'state' => $state ); $ret[$package] = $info; } } PEAR::popErrorHandling(); return $ret; } /** * List all categories of a REST server * * @param string $base base URL of the server * @return array of categorynames */ function listCategories($base, $channel = false) { $categorylist = $this->_rest->retrieveData($base . 'c/categories.xml', false, false, $channel); if (PEAR::isError($categorylist)) { return $categorylist; } if (!is_array($categorylist) || !isset($categorylist['c'])) { return array(); } if (isset($categorylist['c']['_content'])) { // only 1 category $categorylist['c'] = array($categorylist['c']); } return $categorylist['c']; } /** * List packages in a category of a REST server * * @param string $base base URL of the server * @param string $category name of the category * @param boolean $info also download full package info * @return array of packagenames */ function listCategory($base, $category, $info = false, $channel = false) { if ($info == false) { $url = '%s'.'c/%s/packages.xml'; } else { $url = '%s'.'c/%s/packagesinfo.xml'; } $url = sprintf($url, $base, urlencode($category)); // gives '404 Not Found' error when category doesn't exist $packagelist = $this->_rest->retrieveData($url, false, false, $channel); if (PEAR::isError($packagelist)) { return $packagelist; } if (!is_array($packagelist)) { return array(); } if ($info == false) { if (!isset($packagelist['p'])) { return array(); } if (!is_array($packagelist['p']) || !isset($packagelist['p'][0])) { // only 1 pkg $packagelist = array($packagelist['p']); } else { $packagelist = $packagelist['p']; } return $packagelist; } // info == true if (!isset($packagelist['pi'])) { return array(); } if (!is_array($packagelist['pi']) || !isset($packagelist['pi'][0])) { // only 1 pkg $packagelist_pre = array($packagelist['pi']); } else { $packagelist_pre = $packagelist['pi']; } $packagelist = array(); foreach ($packagelist_pre as $i => $item) { // compatibility with r/.xml if (isset($item['a']['r'][0])) { // multiple releases $item['p']['v'] = $item['a']['r'][0]['v']; $item['p']['st'] = $item['a']['r'][0]['s']; } elseif (isset($item['a'])) { // first and only release $item['p']['v'] = $item['a']['r']['v']; $item['p']['st'] = $item['a']['r']['s']; } $packagelist[$i] = array('attribs' => $item['p']['r'], '_content' => $item['p']['n'], 'info' => $item['p']); } return $packagelist; } /** * Return an array containing all of the states that are more stable than * or equal to the passed in state * * @param string Release state * @param boolean Determines whether to include $state in the list * @return false|array False if $state is not a valid release state */ function betterStates($state, $include = false) { static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable'); $i = array_search($state, $states); if ($i === false) { return false; } if ($include) { $i--; } return array_slice($states, $i + 1); } } ?> PK=1]"Fّpear/PEAR/REST/10.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a12 */ /** * For downloading REST xml/txt files */ require_once 'PEAR/REST.php'; /** * Implement REST 1.0 * * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a12 */ class PEAR_REST_10 { /** * @var PEAR_REST */ var $_rest; function __construct($config, $options = array()) { $this->_rest = new PEAR_REST($config, $options); } /** * Retrieve information about a remote package to be downloaded from a REST server * * @param string $base The uri to prepend to all REST calls * @param array $packageinfo an array of format: *
     *  array(
     *   'package' => 'packagename',
     *   'channel' => 'channelname',
     *  ['state' => 'alpha' (or valid state),]
     *  -or-
     *  ['version' => '1.whatever']
     * 
* @param string $prefstate Current preferred_state config variable value * @param bool $installed the installed version of this package to compare against * @return array|false|PEAR_Error see {@link _returnDownloadURL()} */ function getDownloadURL($base, $packageinfo, $prefstate, $installed, $channel = false) { $states = $this->betterStates($prefstate, true); if (!$states) { return PEAR::raiseError('"' . $prefstate . '" is not a valid state'); } $channel = $packageinfo['channel']; $package = $packageinfo['package']; $state = isset($packageinfo['state']) ? $packageinfo['state'] : null; $version = isset($packageinfo['version']) ? $packageinfo['version'] : null; $restFile = $base . 'r/' . strtolower($package) . '/allreleases.xml'; $info = $this->_rest->retrieveData($restFile, false, false, $channel); if (PEAR::isError($info)) { return PEAR::raiseError('No releases available for package "' . $channel . '/' . $package . '"'); } if (!isset($info['r'])) { return false; } $release = $found = false; if (!is_array($info['r']) || !isset($info['r'][0])) { $info['r'] = array($info['r']); } foreach ($info['r'] as $release) { if (!isset($this->_rest->_options['force']) && ($installed && version_compare($release['v'], $installed, '<'))) { continue; } if (isset($state)) { // try our preferred state first if ($release['s'] == $state) { $found = true; break; } // see if there is something newer and more stable // bug #7221 if (in_array($release['s'], $this->betterStates($state), true)) { $found = true; break; } } elseif (isset($version)) { if ($release['v'] == $version) { $found = true; break; } } else { if (in_array($release['s'], $states)) { $found = true; break; } } } return $this->_returnDownloadURL($base, $package, $release, $info, $found, false, $channel); } function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage, $prefstate = 'stable', $installed = false, $channel = false) { $states = $this->betterStates($prefstate, true); if (!$states) { return PEAR::raiseError('"' . $prefstate . '" is not a valid state'); } $channel = $dependency['channel']; $package = $dependency['name']; $state = isset($dependency['state']) ? $dependency['state'] : null; $version = isset($dependency['version']) ? $dependency['version'] : null; $restFile = $base . 'r/' . strtolower($package) . '/allreleases.xml'; $info = $this->_rest->retrieveData($restFile, false, false, $channel); if (PEAR::isError($info)) { return PEAR::raiseError('Package "' . $deppackage['channel'] . '/' . $deppackage['package'] . '" dependency "' . $channel . '/' . $package . '" has no releases'); } if (!is_array($info) || !isset($info['r'])) { return false; } $exclude = array(); $min = $max = $recommended = false; if ($xsdversion == '1.0') { switch ($dependency['rel']) { case 'ge' : $min = $dependency['version']; break; case 'gt' : $min = $dependency['version']; $exclude = array($dependency['version']); break; case 'eq' : $recommended = $dependency['version']; break; case 'lt' : $max = $dependency['version']; $exclude = array($dependency['version']); break; case 'le' : $max = $dependency['version']; break; case 'ne' : $exclude = array($dependency['version']); break; } } else { $min = isset($dependency['min']) ? $dependency['min'] : false; $max = isset($dependency['max']) ? $dependency['max'] : false; $recommended = isset($dependency['recommended']) ? $dependency['recommended'] : false; if (isset($dependency['exclude'])) { if (!isset($dependency['exclude'][0])) { $exclude = array($dependency['exclude']); } } } $release = $found = false; if (!is_array($info['r']) || !isset($info['r'][0])) { $info['r'] = array($info['r']); } foreach ($info['r'] as $release) { if (!isset($this->_rest->_options['force']) && ($installed && version_compare($release['v'], $installed, '<'))) { continue; } if (in_array($release['v'], $exclude)) { // skip excluded versions continue; } // allow newer releases to say "I'm OK with the dependent package" if ($xsdversion == '2.0' && isset($release['co'])) { if (!is_array($release['co']) || !isset($release['co'][0])) { $release['co'] = array($release['co']); } foreach ($release['co'] as $entry) { if (isset($entry['x']) && !is_array($entry['x'])) { $entry['x'] = array($entry['x']); } elseif (!isset($entry['x'])) { $entry['x'] = array(); } if ($entry['c'] == $deppackage['channel'] && strtolower($entry['p']) == strtolower($deppackage['package']) && version_compare($deppackage['version'], $entry['min'], '>=') && version_compare($deppackage['version'], $entry['max'], '<=') && !in_array($release['v'], $entry['x'])) { $recommended = $release['v']; break; } } } if ($recommended) { if ($release['v'] != $recommended) { // if we want a specific // version, then skip all others continue; } else { if (!in_array($release['s'], $states)) { // the stability is too low, but we must return the // recommended version if possible return $this->_returnDownloadURL($base, $package, $release, $info, true, false, $channel); } } } if ($min && version_compare($release['v'], $min, 'lt')) { // skip too old versions continue; } if ($max && version_compare($release['v'], $max, 'gt')) { // skip too new versions continue; } if ($installed && version_compare($release['v'], $installed, '<')) { continue; } if (in_array($release['s'], $states)) { // if in the preferred state... $found = true; // ... then use it break; } } return $this->_returnDownloadURL($base, $package, $release, $info, $found, false, $channel); } /** * Take raw data and return the array needed for processing a download URL * * @param string $base REST base uri * @param string $package Package name * @param array $release an array of format array('v' => version, 's' => state) * describing the release to download * @param array $info list of all releases as defined by allreleases.xml * @param bool|null $found determines whether the release was found or this is the next * best alternative. If null, then versions were skipped because * of PHP dependency * @return array|PEAR_Error * @access private */ function _returnDownloadURL($base, $package, $release, $info, $found, $phpversion = false, $channel = false) { if (!$found) { $release = $info['r'][0]; } $packageLower = strtolower($package); $pinfo = $this->_rest->retrieveCacheFirst($base . 'p/' . $packageLower . '/' . 'info.xml', false, false, $channel); if (PEAR::isError($pinfo)) { return PEAR::raiseError('Package "' . $package . '" does not have REST info xml available'); } $releaseinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . $packageLower . '/' . $release['v'] . '.xml', false, false, $channel); if (PEAR::isError($releaseinfo)) { return PEAR::raiseError('Package "' . $package . '" Version "' . $release['v'] . '" does not have REST xml available'); } $packagexml = $this->_rest->retrieveCacheFirst($base . 'r/' . $packageLower . '/' . 'deps.' . $release['v'] . '.txt', false, true, $channel); if (PEAR::isError($packagexml)) { return PEAR::raiseError('Package "' . $package . '" Version "' . $release['v'] . '" does not have REST dependency information available'); } $packagexml = unserialize($packagexml); if (!$packagexml) { $packagexml = array(); } $allinfo = $this->_rest->retrieveData($base . 'r/' . $packageLower . '/allreleases.xml', false, false, $channel); if (PEAR::isError($allinfo)) { return $allinfo; } if (!is_array($allinfo['r']) || !isset($allinfo['r'][0])) { $allinfo['r'] = array($allinfo['r']); } $compatible = false; foreach ($allinfo['r'] as $release) { if ($release['v'] != $releaseinfo['v']) { continue; } if (!isset($release['co'])) { break; } $compatible = array(); if (!is_array($release['co']) || !isset($release['co'][0])) { $release['co'] = array($release['co']); } foreach ($release['co'] as $entry) { $comp = array(); $comp['name'] = $entry['p']; $comp['channel'] = $entry['c']; $comp['min'] = $entry['min']; $comp['max'] = $entry['max']; if (isset($entry['x']) && !is_array($entry['x'])) { $comp['exclude'] = $entry['x']; } $compatible[] = $comp; } if (count($compatible) == 1) { $compatible = $compatible[0]; } break; } $deprecated = false; if (isset($pinfo['dc']) && isset($pinfo['dp'])) { if (is_array($pinfo['dp'])) { $deprecated = array('channel' => (string) $pinfo['dc'], 'package' => trim($pinfo['dp']['_content'])); } else { $deprecated = array('channel' => (string) $pinfo['dc'], 'package' => trim($pinfo['dp'])); } } $return = array( 'version' => $releaseinfo['v'], 'info' => $packagexml, 'package' => $releaseinfo['p']['_content'], 'stability' => $releaseinfo['st'], 'compatible' => $compatible, 'deprecated' => $deprecated, ); if ($found) { $return['url'] = $releaseinfo['g']; return $return; } $return['php'] = $phpversion; return $return; } function listPackages($base, $channel = false) { $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel); if (PEAR::isError($packagelist)) { return $packagelist; } if (!is_array($packagelist) || !isset($packagelist['p'])) { return array(); } if (!is_array($packagelist['p'])) { $packagelist['p'] = array($packagelist['p']); } return $packagelist['p']; } /** * List all categories of a REST server * * @param string $base base URL of the server * @return array of categorynames */ function listCategories($base, $channel = false) { $categories = array(); // c/categories.xml does not exist; // check for every package its category manually // This is SLOOOWWWW : /// $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel); if (PEAR::isError($packagelist)) { return $packagelist; } if (!is_array($packagelist) || !isset($packagelist['p'])) { $ret = array(); return $ret; } if (!is_array($packagelist['p'])) { $packagelist['p'] = array($packagelist['p']); } PEAR::pushErrorHandling(PEAR_ERROR_RETURN); foreach ($packagelist['p'] as $package) { $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel); if (PEAR::isError($inf)) { PEAR::popErrorHandling(); return $inf; } $cat = $inf['ca']['_content']; if (!isset($categories[$cat])) { $categories[$cat] = $inf['ca']; } } return array_values($categories); } /** * List a category of a REST server * * @param string $base base URL of the server * @param string $category name of the category * @param boolean $info also download full package info * @return array of packagenames */ function listCategory($base, $category, $info = false, $channel = false) { // gives '404 Not Found' error when category doesn't exist $packagelist = $this->_rest->retrieveData($base.'c/'.urlencode($category).'/packages.xml', false, false, $channel); if (PEAR::isError($packagelist)) { return $packagelist; } if (!is_array($packagelist) || !isset($packagelist['p'])) { return array(); } if (!is_array($packagelist['p']) || !isset($packagelist['p'][0])) { // only 1 pkg $packagelist = array($packagelist['p']); } else { $packagelist = $packagelist['p']; } if ($info == true) { // get individual package info PEAR::pushErrorHandling(PEAR_ERROR_RETURN); foreach ($packagelist as $i => $packageitem) { $url = sprintf('%s'.'r/%s/latest.txt', $base, strtolower($packageitem['_content'])); $version = $this->_rest->retrieveData($url, false, false, $channel); if (PEAR::isError($version)) { break; // skipit } $url = sprintf('%s'.'r/%s/%s.xml', $base, strtolower($packageitem['_content']), $version); $info = $this->_rest->retrieveData($url, false, false, $channel); if (PEAR::isError($info)) { break; // skipit } $packagelist[$i]['info'] = $info; } PEAR::popErrorHandling(); } return $packagelist; } function listAll($base, $dostable, $basic = true, $searchpackage = false, $searchsummary = false, $channel = false) { $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel); if (PEAR::isError($packagelist)) { return $packagelist; } if ($this->_rest->config->get('verbose') > 0) { $ui = &PEAR_Frontend::singleton(); $ui->log('Retrieving data...0%', true); } $ret = array(); if (!is_array($packagelist) || !isset($packagelist['p'])) { return $ret; } if (!is_array($packagelist['p'])) { $packagelist['p'] = array($packagelist['p']); } // only search-packagename = quicksearch ! if ($searchpackage && (!$searchsummary || empty($searchpackage))) { $newpackagelist = array(); foreach ($packagelist['p'] as $package) { if (!empty($searchpackage) && stristr($package, $searchpackage) !== false) { $newpackagelist[] = $package; } } $packagelist['p'] = $newpackagelist; } PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $next = .1; foreach ($packagelist['p'] as $progress => $package) { if ($this->_rest->config->get('verbose') > 0) { if ($progress / count($packagelist['p']) >= $next) { if ($next == .5) { $ui->log('50%', false); } else { $ui->log('.', false); } $next += .1; } } if ($basic) { // remote-list command if ($dostable) { $latest = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . '/stable.txt', false, false, $channel); } else { $latest = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . '/latest.txt', false, false, $channel); } if (PEAR::isError($latest)) { $latest = false; } $info = array('stable' => $latest); } else { // list-all command $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel); if (PEAR::isError($inf)) { PEAR::popErrorHandling(); return $inf; } if ($searchpackage) { $found = (!empty($searchpackage) && stristr($package, $searchpackage) !== false); if (!$found && !(isset($searchsummary) && !empty($searchsummary) && (stristr($inf['s'], $searchsummary) !== false || stristr($inf['d'], $searchsummary) !== false))) { continue; }; } $releases = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . '/allreleases.xml', false, false, $channel); if (PEAR::isError($releases)) { continue; } if (!isset($releases['r'][0])) { $releases['r'] = array($releases['r']); } unset($latest); unset($unstable); unset($stable); unset($state); foreach ($releases['r'] as $release) { if (!isset($latest)) { if ($dostable && $release['s'] == 'stable') { $latest = $release['v']; $state = 'stable'; } if (!$dostable) { $latest = $release['v']; $state = $release['s']; } } if (!isset($stable) && $release['s'] == 'stable') { $stable = $release['v']; if (!isset($unstable)) { $unstable = $stable; } } if (!isset($unstable) && $release['s'] != 'stable') { $latest = $unstable = $release['v']; $state = $release['s']; } if (isset($latest) && !isset($state)) { $state = $release['s']; } if (isset($latest) && isset($stable) && isset($unstable)) { break; } } $deps = array(); if (!isset($unstable)) { $unstable = false; $state = 'stable'; if (isset($stable)) { $latest = $unstable = $stable; } } else { $latest = $unstable; } if (!isset($latest)) { $latest = false; } if ($latest) { $d = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/deps.' . $latest . '.txt', false, false, $channel); if (!PEAR::isError($d)) { $d = unserialize($d); if ($d) { if (isset($d['required'])) { if (!class_exists('PEAR_PackageFile_v2')) { require_once 'PEAR/PackageFile/v2.php'; } if (!isset($pf)) { $pf = new PEAR_PackageFile_v2; } $pf->setDeps($d); $tdeps = $pf->getDeps(); } else { $tdeps = $d; } foreach ($tdeps as $dep) { if ($dep['type'] !== 'pkg') { continue; } $deps[] = $dep; } } } } if (!isset($stable)) { $stable = '-n/a-'; } if (!$searchpackage) { $info = array('stable' => $latest, 'summary' => $inf['s'], 'description' => $inf['d'], 'deps' => $deps, 'category' => $inf['ca']['_content'], 'unstable' => $unstable, 'state' => $state); } else { $info = array('stable' => $stable, 'summary' => $inf['s'], 'description' => $inf['d'], 'deps' => $deps, 'category' => $inf['ca']['_content'], 'unstable' => $unstable, 'state' => $state); } } $ret[$package] = $info; } PEAR::popErrorHandling(); return $ret; } function listLatestUpgrades($base, $pref_state, $installed, $channel, &$reg) { $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel); if (PEAR::isError($packagelist)) { return $packagelist; } $ret = array(); if (!is_array($packagelist) || !isset($packagelist['p'])) { return $ret; } if (!is_array($packagelist['p'])) { $packagelist['p'] = array($packagelist['p']); } foreach ($packagelist['p'] as $package) { if (!isset($installed[strtolower($package)])) { continue; } $inst_version = $reg->packageInfo($package, 'version', $channel); $inst_state = $reg->packageInfo($package, 'release_state', $channel); PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $info = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . '/allreleases.xml', false, false, $channel); PEAR::popErrorHandling(); if (PEAR::isError($info)) { continue; // no remote releases } if (!isset($info['r'])) { continue; } $release = $found = false; if (!is_array($info['r']) || !isset($info['r'][0])) { $info['r'] = array($info['r']); } // $info['r'] is sorted by version number usort($info['r'], array($this, '_sortReleasesByVersionNumber')); foreach ($info['r'] as $release) { if ($inst_version && version_compare($release['v'], $inst_version, '<=')) { // not newer than the one installed break; } // new version > installed version if (!$pref_state) { // every state is a good state $found = true; break; } else { $new_state = $release['s']; // if new state >= installed state: go if (in_array($new_state, $this->betterStates($inst_state, true))) { $found = true; break; } else { // only allow to lower the state of package, // if new state >= preferred state: go if (in_array($new_state, $this->betterStates($pref_state, true))) { $found = true; break; } } } } if (!$found) { continue; } $relinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/' . $release['v'] . '.xml', false, false, $channel); if (PEAR::isError($relinfo)) { return $relinfo; } $ret[$package] = array( 'version' => $release['v'], 'state' => $release['s'], 'filesize' => $relinfo['f'], ); } return $ret; } function packageInfo($base, $package, $channel = false) { PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $pinfo = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel); if (PEAR::isError($pinfo)) { PEAR::popErrorHandling(); return PEAR::raiseError('Unknown package: "' . $package . '" in channel "' . $channel . '"' . "\n". 'Debug: ' . $pinfo->getMessage()); } $releases = array(); $allreleases = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . '/allreleases.xml', false, false, $channel); if (!PEAR::isError($allreleases)) { if (!class_exists('PEAR_PackageFile_v2')) { require_once 'PEAR/PackageFile/v2.php'; } if (!is_array($allreleases['r']) || !isset($allreleases['r'][0])) { $allreleases['r'] = array($allreleases['r']); } $pf = new PEAR_PackageFile_v2; foreach ($allreleases['r'] as $release) { $ds = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/deps.' . $release['v'] . '.txt', false, false, $channel); if (PEAR::isError($ds)) { continue; } if (!isset($latest)) { $latest = $release['v']; } $pf->setDeps(unserialize($ds)); $ds = $pf->getDeps(); $info = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/' . $release['v'] . '.xml', false, false, $channel); if (PEAR::isError($info)) { continue; } $releases[$release['v']] = array( 'doneby' => $info['m'], 'license' => $info['l'], 'summary' => $info['s'], 'description' => $info['d'], 'releasedate' => $info['da'], 'releasenotes' => $info['n'], 'state' => $release['s'], 'deps' => $ds ? $ds : array(), ); } } else { $latest = ''; } PEAR::popErrorHandling(); if (isset($pinfo['dc']) && isset($pinfo['dp'])) { if (is_array($pinfo['dp'])) { $deprecated = array('channel' => (string) $pinfo['dc'], 'package' => trim($pinfo['dp']['_content'])); } else { $deprecated = array('channel' => (string) $pinfo['dc'], 'package' => trim($pinfo['dp'])); } } else { $deprecated = false; } if (!isset($latest)) { $latest = ''; } return array( 'name' => $pinfo['n'], 'channel' => $pinfo['c'], 'category' => $pinfo['ca']['_content'], 'stable' => $latest, 'license' => $pinfo['l'], 'summary' => $pinfo['s'], 'description' => $pinfo['d'], 'releases' => $releases, 'deprecated' => $deprecated, ); } /** * Return an array containing all of the states that are more stable than * or equal to the passed in state * * @param string Release state * @param boolean Determines whether to include $state in the list * @return false|array False if $state is not a valid release state */ function betterStates($state, $include = false) { static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable'); $i = array_search($state, $states); if ($i === false) { return false; } if ($include) { $i--; } return array_slice($states, $i + 1); } /** * Sort releases by version number * * @access private */ function _sortReleasesByVersionNumber($a, $b) { if (version_compare($a['v'], $b['v'], '=')) { return 0; } if (version_compare($a['v'], $b['v'], '>')) { return -1; } if (version_compare($a['v'], $b['v'], '<')) { return 1; } } } PK=1]Wď;;pear/PEAR/REST/13.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a12 */ /** * For downloading REST xml/txt files */ require_once 'PEAR/REST.php'; require_once 'PEAR/REST/10.php'; /** * Implement REST 1.3 * * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a12 */ class PEAR_REST_13 extends PEAR_REST_10 { /** * Retrieve information about a remote package to be downloaded from a REST server * * This is smart enough to resolve the minimum PHP version dependency prior to download * @param string $base The uri to prepend to all REST calls * @param array $packageinfo an array of format: *
     *  array(
     *   'package' => 'packagename',
     *   'channel' => 'channelname',
     *  ['state' => 'alpha' (or valid state),]
     *  -or-
     *  ['version' => '1.whatever']
     * 
* @param string $prefstate Current preferred_state config variable value * @param bool $installed the installed version of this package to compare against * @return array|false|PEAR_Error see {@link _returnDownloadURL()} */ function getDownloadURL($base, $packageinfo, $prefstate, $installed, $channel = false) { $states = $this->betterStates($prefstate, true); if (!$states) { return PEAR::raiseError('"' . $prefstate . '" is not a valid state'); } $channel = $packageinfo['channel']; $package = $packageinfo['package']; $state = isset($packageinfo['state']) ? $packageinfo['state'] : null; $version = isset($packageinfo['version']) ? $packageinfo['version'] : null; $restFile = $base . 'r/' . strtolower($package) . '/allreleases2.xml'; $info = $this->_rest->retrieveData($restFile, false, false, $channel); if (PEAR::isError($info)) { return PEAR::raiseError('No releases available for package "' . $channel . '/' . $package . '"'); } if (!isset($info['r'])) { return false; } $release = $found = false; if (!is_array($info['r']) || !isset($info['r'][0])) { $info['r'] = array($info['r']); } $skippedphp = false; foreach ($info['r'] as $release) { if (!isset($this->_rest->_options['force']) && ($installed && version_compare($release['v'], $installed, '<'))) { continue; } if (isset($state)) { // try our preferred state first if ($release['s'] == $state) { if (!isset($version) && version_compare($release['m'], phpversion(), '>')) { // skip releases that require a PHP version newer than our PHP version $skippedphp = $release; continue; } $found = true; break; } // see if there is something newer and more stable // bug #7221 if (in_array($release['s'], $this->betterStates($state), true)) { if (!isset($version) && version_compare($release['m'], phpversion(), '>')) { // skip releases that require a PHP version newer than our PHP version $skippedphp = $release; continue; } $found = true; break; } } elseif (isset($version)) { if ($release['v'] == $version) { if (!isset($this->_rest->_options['force']) && !isset($version) && version_compare($release['m'], phpversion(), '>')) { // skip releases that require a PHP version newer than our PHP version $skippedphp = $release; continue; } $found = true; break; } } else { if (in_array($release['s'], $states)) { if (version_compare($release['m'], phpversion(), '>')) { // skip releases that require a PHP version newer than our PHP version $skippedphp = $release; continue; } $found = true; break; } } } if (!$found && $skippedphp) { $found = null; } return $this->_returnDownloadURL($base, $package, $release, $info, $found, $skippedphp, $channel); } function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage, $prefstate = 'stable', $installed = false, $channel = false) { $states = $this->betterStates($prefstate, true); if (!$states) { return PEAR::raiseError('"' . $prefstate . '" is not a valid state'); } $channel = $dependency['channel']; $package = $dependency['name']; $state = isset($dependency['state']) ? $dependency['state'] : null; $version = isset($dependency['version']) ? $dependency['version'] : null; $restFile = $base . 'r/' . strtolower($package) .'/allreleases2.xml'; $info = $this->_rest->retrieveData($restFile, false, false, $channel); if (PEAR::isError($info)) { return PEAR::raiseError('Package "' . $deppackage['channel'] . '/' . $deppackage['package'] . '" dependency "' . $channel . '/' . $package . '" has no releases'); } if (!is_array($info) || !isset($info['r'])) { return false; } $exclude = array(); $min = $max = $recommended = false; if ($xsdversion == '1.0') { $pinfo['package'] = $dependency['name']; $pinfo['channel'] = 'pear.php.net'; // this is always true - don't change this switch ($dependency['rel']) { case 'ge' : $min = $dependency['version']; break; case 'gt' : $min = $dependency['version']; $exclude = array($dependency['version']); break; case 'eq' : $recommended = $dependency['version']; break; case 'lt' : $max = $dependency['version']; $exclude = array($dependency['version']); break; case 'le' : $max = $dependency['version']; break; case 'ne' : $exclude = array($dependency['version']); break; } } else { $pinfo['package'] = $dependency['name']; $min = isset($dependency['min']) ? $dependency['min'] : false; $max = isset($dependency['max']) ? $dependency['max'] : false; $recommended = isset($dependency['recommended']) ? $dependency['recommended'] : false; if (isset($dependency['exclude'])) { if (!isset($dependency['exclude'][0])) { $exclude = array($dependency['exclude']); } } } $skippedphp = $found = $release = false; if (!is_array($info['r']) || !isset($info['r'][0])) { $info['r'] = array($info['r']); } foreach ($info['r'] as $release) { if (!isset($this->_rest->_options['force']) && ($installed && version_compare($release['v'], $installed, '<'))) { continue; } if (in_array($release['v'], $exclude)) { // skip excluded versions continue; } // allow newer releases to say "I'm OK with the dependent package" if ($xsdversion == '2.0' && isset($release['co'])) { if (!is_array($release['co']) || !isset($release['co'][0])) { $release['co'] = array($release['co']); } foreach ($release['co'] as $entry) { if (isset($entry['x']) && !is_array($entry['x'])) { $entry['x'] = array($entry['x']); } elseif (!isset($entry['x'])) { $entry['x'] = array(); } if ($entry['c'] == $deppackage['channel'] && strtolower($entry['p']) == strtolower($deppackage['package']) && version_compare($deppackage['version'], $entry['min'], '>=') && version_compare($deppackage['version'], $entry['max'], '<=') && !in_array($release['v'], $entry['x'])) { if (version_compare($release['m'], phpversion(), '>')) { // skip dependency releases that require a PHP version // newer than our PHP version $skippedphp = $release; continue; } $recommended = $release['v']; break; } } } if ($recommended) { if ($release['v'] != $recommended) { // if we want a specific // version, then skip all others continue; } if (!in_array($release['s'], $states)) { // the stability is too low, but we must return the // recommended version if possible return $this->_returnDownloadURL($base, $package, $release, $info, true, false, $channel); } } if ($min && version_compare($release['v'], $min, 'lt')) { // skip too old versions continue; } if ($max && version_compare($release['v'], $max, 'gt')) { // skip too new versions continue; } if ($installed && version_compare($release['v'], $installed, '<')) { continue; } if (in_array($release['s'], $states)) { // if in the preferred state... if (version_compare($release['m'], phpversion(), '>')) { // skip dependency releases that require a PHP version // newer than our PHP version $skippedphp = $release; continue; } $found = true; // ... then use it break; } } if (!$found && $skippedphp) { $found = null; } return $this->_returnDownloadURL($base, $package, $release, $info, $found, $skippedphp, $channel); } /** * List package upgrades but take the PHP version into account. */ function listLatestUpgrades($base, $pref_state, $installed, $channel, &$reg) { $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel); if (PEAR::isError($packagelist)) { return $packagelist; } $ret = array(); if (!is_array($packagelist) || !isset($packagelist['p'])) { return $ret; } if (!is_array($packagelist['p'])) { $packagelist['p'] = array($packagelist['p']); } foreach ($packagelist['p'] as $package) { if (!isset($installed[strtolower($package)])) { continue; } $inst_version = $reg->packageInfo($package, 'version', $channel); $inst_state = $reg->packageInfo($package, 'release_state', $channel); PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $info = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . '/allreleases2.xml', false, false, $channel); PEAR::popErrorHandling(); if (PEAR::isError($info)) { continue; // no remote releases } if (!isset($info['r'])) { continue; } $release = $found = false; if (!is_array($info['r']) || !isset($info['r'][0])) { $info['r'] = array($info['r']); } // $info['r'] is sorted by version number usort($info['r'], array($this, '_sortReleasesByVersionNumber')); foreach ($info['r'] as $release) { if ($inst_version && version_compare($release['v'], $inst_version, '<=')) { // not newer than the one installed break; } if (version_compare($release['m'], phpversion(), '>')) { // skip dependency releases that require a PHP version // newer than our PHP version continue; } // new version > installed version if (!$pref_state) { // every state is a good state $found = true; break; } else { $new_state = $release['s']; // if new state >= installed state: go if (in_array($new_state, $this->betterStates($inst_state, true))) { $found = true; break; } else { // only allow to lower the state of package, // if new state >= preferred state: go if (in_array($new_state, $this->betterStates($pref_state, true))) { $found = true; break; } } } } if (!$found) { continue; } $relinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/' . $release['v'] . '.xml', false, false, $channel); if (PEAR::isError($relinfo)) { return $relinfo; } $ret[$package] = array( 'version' => $release['v'], 'state' => $release['s'], 'filesize' => $relinfo['f'], ); } return $ret; } }PK=1]fS__pear/PEAR/DependencyDB.phpnu[ * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * Needed for error handling */ require_once 'PEAR.php'; require_once 'PEAR/Config.php'; $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'] = array(); /** * Track dependency relationships between installed packages * @category pear * @package PEAR * @author Greg Beaver * @author Tomas V.V.Cox * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_DependencyDB { // {{{ properties /** * This is initialized by {@link setConfig()} * @var PEAR_Config * @access private */ var $_config; /** * This is initialized by {@link setConfig()} * @var PEAR_Registry * @access private */ var $_registry; /** * Filename of the dependency DB (usually .depdb) * @var string * @access private */ var $_depdb = false; /** * File name of the lockfile (usually .depdblock) * @var string * @access private */ var $_lockfile = false; /** * Open file resource for locking the lockfile * @var resource|false * @access private */ var $_lockFp = false; /** * API version of this class, used to validate a file on-disk * @var string * @access private */ var $_version = '1.0'; /** * Cached dependency database file * @var array|null * @access private */ var $_cache; // }}} // {{{ & singleton() /** * Get a raw dependency database. Calls setConfig() and assertDepsDB() * @param PEAR_Config * @param string|false full path to the dependency database, or false to use default * @return PEAR_DependencyDB|PEAR_Error */ public static function &singleton(&$config, $depdb = false) { $phpdir = $config->get('php_dir', null, 'pear.php.net'); if (!isset($GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir])) { $a = new PEAR_DependencyDB; $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir] = &$a; $a->setConfig($config, $depdb); $e = $a->assertDepsDB(); if (PEAR::isError($e)) { return $e; } } return $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir]; } /** * Set up the registry/location of dependency DB * @param PEAR_Config|false * @param string|false full path to the dependency database, or false to use default */ function setConfig(&$config, $depdb = false) { if (!$config) { $this->_config = &PEAR_Config::singleton(); } else { $this->_config = &$config; } $this->_registry = &$this->_config->getRegistry(); if (!$depdb) { $dir = $this->_config->get('metadata_dir', null, 'pear.php.net'); if (!$dir) { $dir = $this->_config->get('php_dir', null, 'pear.php.net'); } $this->_depdb = $dir . DIRECTORY_SEPARATOR . '.depdb'; } else { $this->_depdb = $depdb; } $this->_lockfile = dirname($this->_depdb) . DIRECTORY_SEPARATOR . '.depdblock'; } // }}} function hasWriteAccess() { if (!file_exists($this->_depdb)) { $dir = $this->_depdb; while ($dir && $dir != '.') { $dir = dirname($dir); // cd .. if ($dir != '.' && file_exists($dir)) { if (is_writeable($dir)) { return true; } return false; } } return false; } return is_writeable($this->_depdb); } // {{{ assertDepsDB() /** * Create the dependency database, if it doesn't exist. Error if the database is * newer than the code reading it. * @return void|PEAR_Error */ function assertDepsDB() { if (!is_file($this->_depdb)) { $this->rebuildDB(); return; } $depdb = $this->_getDepDB(); // Datatype format has been changed, rebuild the Deps DB if ($depdb['_version'] < $this->_version) { $this->rebuildDB(); } if ($depdb['_version'][0] > $this->_version[0]) { return PEAR::raiseError('Dependency database is version ' . $depdb['_version'] . ', and we are version ' . $this->_version . ', cannot continue'); } } /** * Get a list of installed packages that depend on this package * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array * @return array|false */ function getDependentPackages(&$pkg) { $data = $this->_getDepDB(); if (is_object($pkg)) { $channel = strtolower($pkg->getChannel()); $package = strtolower($pkg->getPackage()); } else { $channel = strtolower($pkg['channel']); $package = strtolower($pkg['package']); } if (isset($data['packages'][$channel][$package])) { return $data['packages'][$channel][$package]; } return false; } /** * Get a list of the actual dependencies of installed packages that depend on * a package. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array * @return array|false */ function getDependentPackageDependencies(&$pkg) { $data = $this->_getDepDB(); if (is_object($pkg)) { $channel = strtolower($pkg->getChannel()); $package = strtolower($pkg->getPackage()); } else if (is_array($pkg)) { $channel = strtolower($pkg['channel']); $package = strtolower($pkg['package']); } else { return false; } $depend = $this->getDependentPackages($pkg); if (!$depend) { return false; } $dependencies = array(); foreach ($depend as $info) { $temp = $this->getDependencies($info); foreach ($temp as $dep) { if ( isset($dep['dep'], $dep['dep']['channel'], $dep['dep']['name']) && strtolower($dep['dep']['channel']) == $channel && strtolower($dep['dep']['name']) == $package ) { if (!isset($dependencies[$info['channel']])) { $dependencies[$info['channel']] = array(); } if (!isset($dependencies[$info['channel']][$info['package']])) { $dependencies[$info['channel']][$info['package']] = array(); } $dependencies[$info['channel']][$info['package']][] = $dep; } } } return $dependencies; } /** * Get a list of dependencies of this installed package * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array * @return array|false */ function getDependencies(&$pkg) { if (is_object($pkg)) { $channel = strtolower($pkg->getChannel()); $package = strtolower($pkg->getPackage()); } else { $channel = strtolower($pkg['channel']); $package = strtolower($pkg['package']); } $data = $this->_getDepDB(); if (isset($data['dependencies'][$channel][$package])) { return $data['dependencies'][$channel][$package]; } return false; } /** * Determine whether $parent depends on $child, near or deep * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2 * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2 */ function dependsOn($parent, $child) { $c = array(); $this->_getDepDB(); return $this->_dependsOn($parent, $child, $c); } function _dependsOn($parent, $child, &$checked) { if (is_object($parent)) { $channel = strtolower($parent->getChannel()); $package = strtolower($parent->getPackage()); } else { $channel = strtolower($parent['channel']); $package = strtolower($parent['package']); } if (is_object($child)) { $depchannel = strtolower($child->getChannel()); $deppackage = strtolower($child->getPackage()); } else { $depchannel = strtolower($child['channel']); $deppackage = strtolower($child['package']); } if (isset($checked[$channel][$package][$depchannel][$deppackage])) { return false; // avoid endless recursion } $checked[$channel][$package][$depchannel][$deppackage] = true; if (!isset($this->_cache['dependencies'][$channel][$package])) { return false; } foreach ($this->_cache['dependencies'][$channel][$package] as $info) { if (isset($info['dep']['uri'])) { if (is_object($child)) { if ($info['dep']['uri'] == $child->getURI()) { return true; } } elseif (isset($child['uri'])) { if ($info['dep']['uri'] == $child['uri']) { return true; } } return false; } if (strtolower($info['dep']['channel']) == $depchannel && strtolower($info['dep']['name']) == $deppackage) { return true; } } foreach ($this->_cache['dependencies'][$channel][$package] as $info) { if (isset($info['dep']['uri'])) { if ($this->_dependsOn(array( 'uri' => $info['dep']['uri'], 'package' => $info['dep']['name']), $child, $checked)) { return true; } } else { if ($this->_dependsOn(array( 'channel' => $info['dep']['channel'], 'package' => $info['dep']['name']), $child, $checked)) { return true; } } } return false; } /** * Register dependencies of a package that is being installed or upgraded * @param PEAR_PackageFile_v2|PEAR_PackageFile_v2 */ function installPackage(&$package) { $data = $this->_getDepDB(); unset($this->_cache); $this->_setPackageDeps($data, $package); $this->_writeDepDB($data); } /** * Remove dependencies of a package that is being uninstalled, or upgraded. * * Upgraded packages first uninstall, then install * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array If an array, then it must have * indices 'channel' and 'package' */ function uninstallPackage(&$pkg) { $data = $this->_getDepDB(); unset($this->_cache); if (is_object($pkg)) { $channel = strtolower($pkg->getChannel()); $package = strtolower($pkg->getPackage()); } else { $channel = strtolower($pkg['channel']); $package = strtolower($pkg['package']); } if (!isset($data['dependencies'][$channel][$package])) { return true; } foreach ($data['dependencies'][$channel][$package] as $dep) { $found = false; $depchannel = isset($dep['dep']['uri']) ? '__uri' : strtolower($dep['dep']['channel']); $depname = strtolower($dep['dep']['name']); if (isset($data['packages'][$depchannel][$depname])) { foreach ($data['packages'][$depchannel][$depname] as $i => $info) { if ($info['channel'] == $channel && $info['package'] == $package) { $found = true; break; } } } if ($found) { unset($data['packages'][$depchannel][$depname][$i]); if (!count($data['packages'][$depchannel][$depname])) { unset($data['packages'][$depchannel][$depname]); if (!count($data['packages'][$depchannel])) { unset($data['packages'][$depchannel]); } } else { $data['packages'][$depchannel][$depname] = array_values($data['packages'][$depchannel][$depname]); } } } unset($data['dependencies'][$channel][$package]); if (!count($data['dependencies'][$channel])) { unset($data['dependencies'][$channel]); } if (!count($data['dependencies'])) { unset($data['dependencies']); } if (!count($data['packages'])) { unset($data['packages']); } $this->_writeDepDB($data); } /** * Rebuild the dependency DB by reading registry entries. * @return true|PEAR_Error */ function rebuildDB() { $depdb = array('_version' => $this->_version); if (!$this->hasWriteAccess()) { // allow startup for read-only with older Registry return $depdb; } $packages = $this->_registry->listAllPackages(); if (PEAR::isError($packages)) { return $packages; } foreach ($packages as $channel => $ps) { foreach ($ps as $package) { $package = $this->_registry->getPackage($package, $channel); if (PEAR::isError($package)) { return $package; } $this->_setPackageDeps($depdb, $package); } } $error = $this->_writeDepDB($depdb); if (PEAR::isError($error)) { return $error; } $this->_cache = $depdb; return true; } /** * Register usage of the dependency DB to prevent race conditions * @param int one of the LOCK_* constants * @return true|PEAR_Error * @access private */ function _lock($mode = LOCK_EX) { if (stristr(php_uname(), 'Windows 9')) { return true; } if ($mode != LOCK_UN && is_resource($this->_lockFp)) { // XXX does not check type of lock (LOCK_SH/LOCK_EX) return true; } $open_mode = 'w'; // XXX People reported problems with LOCK_SH and 'w' if ($mode === LOCK_SH) { if (!file_exists($this->_lockfile)) { touch($this->_lockfile); } elseif (!is_file($this->_lockfile)) { return PEAR::raiseError('could not create Dependency lock file, ' . 'it exists and is not a regular file'); } $open_mode = 'r'; } if (!is_resource($this->_lockFp)) { $this->_lockFp = @fopen($this->_lockfile, $open_mode); } if (!is_resource($this->_lockFp)) { $last_errormsg = ''; $last_error = error_get_last(); if (!empty($last_error['message'])) { $last_errormsg = $last_error['message']; } return PEAR::raiseError("could not create Dependency lock file" . (isset($last_errormsg) ? ": " . $last_errormsg : "")); } if (!(int)flock($this->_lockFp, $mode)) { switch ($mode) { case LOCK_SH: $str = 'shared'; break; case LOCK_EX: $str = 'exclusive'; break; case LOCK_UN: $str = 'unlock'; break; default: $str = 'unknown'; break; } return PEAR::raiseError("could not acquire $str lock ($this->_lockfile)"); } return true; } /** * Release usage of dependency DB * @return true|PEAR_Error * @access private */ function _unlock() { $ret = $this->_lock(LOCK_UN); if (is_resource($this->_lockFp)) { fclose($this->_lockFp); } $this->_lockFp = null; return $ret; } /** * Load the dependency database from disk, or return the cache * @return array|PEAR_Error */ function _getDepDB() { if (!$this->hasWriteAccess()) { return array('_version' => $this->_version); } if (isset($this->_cache)) { return $this->_cache; } if (!$fp = fopen($this->_depdb, 'r')) { $err = PEAR::raiseError("Could not open dependencies file `".$this->_depdb."'"); return $err; } clearstatcache(); fclose($fp); $data = @unserialize(file_get_contents($this->_depdb)); $this->_cache = $data; return $data; } /** * Write out the dependency database to disk * @param array the database * @return true|PEAR_Error * @access private */ function _writeDepDB(&$deps) { if (PEAR::isError($e = $this->_lock(LOCK_EX))) { return $e; } if (!$fp = fopen($this->_depdb, 'wb')) { $this->_unlock(); return PEAR::raiseError("Could not open dependencies file `".$this->_depdb."' for writing"); } fwrite($fp, serialize($deps)); fclose($fp); $this->_unlock(); $this->_cache = $deps; return true; } /** * Register all dependencies from a package in the dependencies database, in essence * "installing" the package's dependency information * @param array the database * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 * @access private */ function _setPackageDeps(&$data, &$pkg) { $pkg->setConfig($this->_config); if ($pkg->getPackagexmlVersion() == '1.0') { $gen = &$pkg->getDefaultGenerator(); $deps = $gen->dependenciesToV2(); } else { $deps = $pkg->getDeps(true); } if (!$deps) { return; } if (!is_array($data)) { $data = array(); } if (!isset($data['dependencies'])) { $data['dependencies'] = array(); } $channel = strtolower($pkg->getChannel()); $package = strtolower($pkg->getPackage()); if (!isset($data['dependencies'][$channel])) { $data['dependencies'][$channel] = array(); } $data['dependencies'][$channel][$package] = array(); if (isset($deps['required']['package'])) { if (!isset($deps['required']['package'][0])) { $deps['required']['package'] = array($deps['required']['package']); } foreach ($deps['required']['package'] as $dep) { $this->_registerDep($data, $pkg, $dep, 'required'); } } if (isset($deps['optional']['package'])) { if (!isset($deps['optional']['package'][0])) { $deps['optional']['package'] = array($deps['optional']['package']); } foreach ($deps['optional']['package'] as $dep) { $this->_registerDep($data, $pkg, $dep, 'optional'); } } if (isset($deps['required']['subpackage'])) { if (!isset($deps['required']['subpackage'][0])) { $deps['required']['subpackage'] = array($deps['required']['subpackage']); } foreach ($deps['required']['subpackage'] as $dep) { $this->_registerDep($data, $pkg, $dep, 'required'); } } if (isset($deps['optional']['subpackage'])) { if (!isset($deps['optional']['subpackage'][0])) { $deps['optional']['subpackage'] = array($deps['optional']['subpackage']); } foreach ($deps['optional']['subpackage'] as $dep) { $this->_registerDep($data, $pkg, $dep, 'optional'); } } if (isset($deps['group'])) { if (!isset($deps['group'][0])) { $deps['group'] = array($deps['group']); } foreach ($deps['group'] as $group) { if (isset($group['package'])) { if (!isset($group['package'][0])) { $group['package'] = array($group['package']); } foreach ($group['package'] as $dep) { $this->_registerDep($data, $pkg, $dep, 'optional', $group['attribs']['name']); } } if (isset($group['subpackage'])) { if (!isset($group['subpackage'][0])) { $group['subpackage'] = array($group['subpackage']); } foreach ($group['subpackage'] as $dep) { $this->_registerDep($data, $pkg, $dep, 'optional', $group['attribs']['name']); } } } } if ($data['dependencies'][$channel][$package] == array()) { unset($data['dependencies'][$channel][$package]); if (!count($data['dependencies'][$channel])) { unset($data['dependencies'][$channel]); } } } /** * @param array the database * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 * @param array the specific dependency * @param required|optional whether this is a required or an optional dep * @param string|false dependency group this dependency is from, or false for ordinary dep */ function _registerDep(&$data, &$pkg, $dep, $type, $group = false) { $info = array( 'dep' => $dep, 'type' => $type, 'group' => $group ); $dep = array_map('strtolower', $dep); $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri'; if (!isset($data['dependencies'])) { $data['dependencies'] = array(); } $channel = strtolower($pkg->getChannel()); $package = strtolower($pkg->getPackage()); if (!isset($data['dependencies'][$channel])) { $data['dependencies'][$channel] = array(); } if (!isset($data['dependencies'][$channel][$package])) { $data['dependencies'][$channel][$package] = array(); } $data['dependencies'][$channel][$package][] = $info; if (isset($data['packages'][$depchannel][$dep['name']])) { $found = false; foreach ($data['packages'][$depchannel][$dep['name']] as $i => $p) { if ($p['channel'] == $channel && $p['package'] == $package) { $found = true; break; } } } else { if (!isset($data['packages'])) { $data['packages'] = array(); } if (!isset($data['packages'][$depchannel])) { $data['packages'][$depchannel] = array(); } if (!isset($data['packages'][$depchannel][$dep['name']])) { $data['packages'][$depchannel][$dep['name']] = array(); } $found = false; } if (!$found) { $data['packages'][$depchannel][$dep['name']][] = array( 'channel' => $channel, 'package' => $package ); } } } PK=1]#Cpear/PEAR/Installer.phpnu[ * @author Tomas V.V. Cox * @author Martin Jansen * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ /** * Used for installation groups in package.xml 2.0 and platform exceptions */ require_once 'OS/Guess.php'; require_once 'PEAR/Downloader.php'; define('PEAR_INSTALLER_NOBINARY', -240); /** * Administration class used to install PEAR packages and maintain the * installed package database. * * @category pear * @package PEAR * @author Stig Bakken * @author Tomas V.V. Cox * @author Martin Jansen * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ class PEAR_Installer extends PEAR_Downloader { // {{{ properties /** name of the package directory, for example Foo-1.0 * @var string */ var $pkgdir; /** directory where PHP code files go * @var string */ var $phpdir; /** directory where PHP extension files go * @var string */ var $extdir; /** directory where documentation goes * @var string */ var $docdir; /** installation root directory (ala PHP's INSTALL_ROOT or * automake's DESTDIR * @var string */ var $installroot = ''; /** debug level * @var int */ var $debug = 1; /** temporary directory * @var string */ var $tmpdir; /** * PEAR_Registry object used by the installer * @var PEAR_Registry */ var $registry; /** * array of PEAR_Downloader_Packages * @var array */ var $_downloadedPackages; /** List of file transactions queued for an install/upgrade/uninstall. * * Format: * array( * 0 => array("rename => array("from-file", "to-file")), * 1 => array("delete" => array("file-to-delete")), * ... * ) * * @var array */ var $file_operations = array(); // }}} // {{{ constructor /** * PEAR_Installer constructor. * * @param object $ui user interface object (instance of PEAR_Frontend_*) * * @access public */ function __construct(&$ui) { parent::__construct($ui, array(), null); $this->setFrontendObject($ui); $this->debug = $this->config->get('verbose'); } function setOptions($options) { $this->_options = $options; } function setConfig(&$config) { $this->config = &$config; $this->_registry = &$config->getRegistry(); } // }}} function _removeBackups($files) { foreach ($files as $path) { $this->addFileOperation('removebackup', array($path)); } } // {{{ _deletePackageFiles() /** * Delete a package's installed files, does not remove empty directories. * * @param string package name * @param string channel name * @param bool if true, then files are backed up first * @return bool TRUE on success, or a PEAR error on failure * @access protected */ function _deletePackageFiles($package, $channel = false, $backup = false) { if (!$channel) { $channel = 'pear.php.net'; } if (!strlen($package)) { return $this->raiseError("No package to uninstall given"); } if (strtolower($package) == 'pear' && $channel == 'pear.php.net') { // to avoid race conditions, include all possible needed files require_once 'PEAR/Task/Common.php'; require_once 'PEAR/Task/Replace.php'; require_once 'PEAR/Task/Unixeol.php'; require_once 'PEAR/Task/Windowseol.php'; require_once 'PEAR/PackageFile/v1.php'; require_once 'PEAR/PackageFile/v2.php'; require_once 'PEAR/PackageFile/Generator/v1.php'; require_once 'PEAR/PackageFile/Generator/v2.php'; } $filelist = $this->_registry->packageInfo($package, 'filelist', $channel); if ($filelist == null) { return $this->raiseError("$channel/$package not installed"); } $ret = array(); foreach ($filelist as $file => $props) { if (empty($props['installed_as'])) { continue; } $path = $props['installed_as']; if ($backup) { $this->addFileOperation('backup', array($path)); $ret[] = $path; } $this->addFileOperation('delete', array($path)); } if ($backup) { return $ret; } return true; } // }}} // {{{ _installFile() /** * @param string filename * @param array attributes from tag in package.xml * @param string path to install the file in * @param array options from command-line * @access private */ function _installFile($file, $atts, $tmp_path, $options) { // {{{ return if this file is meant for another platform static $os; if (!isset($this->_registry)) { $this->_registry = &$this->config->getRegistry(); } if (isset($atts['platform'])) { if (empty($os)) { $os = new OS_Guess(); } if (strlen($atts['platform']) && $atts['platform'][0] == '!') { $negate = true; $platform = substr($atts['platform'], 1); } else { $negate = false; $platform = $atts['platform']; } if ((bool) $os->matchSignature($platform) === $negate) { $this->log(3, "skipped $file (meant for $atts[platform], we are ".$os->getSignature().")"); return PEAR_INSTALLER_SKIPPED; } } // }}} $channel = $this->pkginfo->getChannel(); // {{{ assemble the destination paths switch ($atts['role']) { case 'src': case 'extsrc': $this->source_files++; return; case 'doc': case 'data': case 'test': $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel) . DIRECTORY_SEPARATOR . $this->pkginfo->getPackage(); unset($atts['baseinstalldir']); break; case 'ext': case 'php': $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel); break; case 'script': $dest_dir = $this->config->get('bin_dir', null, $channel); break; default: return $this->raiseError("Invalid role `$atts[role]' for file $file"); } $save_destdir = $dest_dir; if (!empty($atts['baseinstalldir'])) { $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir']; } if (dirname($file) != '.' && empty($atts['install-as'])) { $dest_dir .= DIRECTORY_SEPARATOR . dirname($file); } if (empty($atts['install-as'])) { $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file); } else { $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as']; } $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file; // Clean up the DIRECTORY_SEPARATOR mess $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR; list($dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"), array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), array($dest_file, $orig_file)); $final_dest_file = $installed_as = $dest_file; if (isset($this->_options['packagingroot'])) { $installedas_dest_dir = dirname($final_dest_file); $installedas_dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); $final_dest_file = $this->_prependPath($final_dest_file, $this->_options['packagingroot']); } else { $installedas_dest_dir = dirname($final_dest_file); $installedas_dest_file = $installedas_dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); } $dest_dir = dirname($final_dest_file); $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) { return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED); } // }}} if (empty($this->_options['register-only']) && (!file_exists($dest_dir) || !is_dir($dest_dir))) { if (!$this->mkDirHier($dest_dir)) { return $this->raiseError("failed to mkdir $dest_dir", PEAR_INSTALLER_FAILED); } $this->log(3, "+ mkdir $dest_dir"); } // pretty much nothing happens if we are only registering the install if (empty($this->_options['register-only'])) { if (empty($atts['replacements'])) { if (!file_exists($orig_file)) { return $this->raiseError("file $orig_file does not exist", PEAR_INSTALLER_FAILED); } if (!@copy($orig_file, $dest_file)) { return $this->raiseError( "failed to write $dest_file: " . error_get_last()["message"], PEAR_INSTALLER_FAILED); } $this->log(3, "+ cp $orig_file $dest_file"); if (isset($atts['md5sum'])) { $md5sum = md5_file($dest_file); } } else { // {{{ file with replacements if (!file_exists($orig_file)) { return $this->raiseError("file does not exist", PEAR_INSTALLER_FAILED); } $contents = file_get_contents($orig_file); if ($contents === false) { $contents = ''; } if (isset($atts['md5sum'])) { $md5sum = md5($contents); } $subst_from = $subst_to = array(); foreach ($atts['replacements'] as $a) { $to = ''; if ($a['type'] == 'php-const') { if (preg_match('/^[a-z0-9_]+\\z/i', $a['to'])) { eval("\$to = $a[to];"); } else { if (!isset($options['soft'])) { $this->log(0, "invalid php-const replacement: $a[to]"); } continue; } } elseif ($a['type'] == 'pear-config') { if ($a['to'] == 'master_server') { $chan = $this->_registry->getChannel($channel); if (!PEAR::isError($chan)) { $to = $chan->getServer(); } else { $to = $this->config->get($a['to'], null, $channel); } } else { $to = $this->config->get($a['to'], null, $channel); } if (is_null($to)) { if (!isset($options['soft'])) { $this->log(0, "invalid pear-config replacement: $a[to]"); } continue; } } elseif ($a['type'] == 'package-info') { if ($t = $this->pkginfo->packageInfo($a['to'])) { $to = $t; } else { if (!isset($options['soft'])) { $this->log(0, "invalid package-info replacement: $a[to]"); } continue; } } if (!is_null($to)) { $subst_from[] = $a['from']; $subst_to[] = $to; } } $this->log(3, "doing ".sizeof($subst_from)." substitution(s) for $final_dest_file"); if (sizeof($subst_from)) { $contents = str_replace($subst_from, $subst_to, $contents); } $wp = @fopen($dest_file, "wb"); if (!is_resource($wp)) { return $this->raiseError( "failed to create $dest_file: " . error_get_last()["message"], PEAR_INSTALLER_FAILED); } if (@fwrite($wp, $contents) === false) { return $this->raiseError( "failed writing to $dest_file: " . error_get_last()["message"], PEAR_INSTALLER_FAILED); } fclose($wp); // }}} } // {{{ check the md5 if (isset($md5sum)) { if (strtolower($md5sum) === strtolower($atts['md5sum'])) { $this->log(2, "md5sum ok: $final_dest_file"); } else { if (empty($options['force'])) { // delete the file if (file_exists($dest_file)) { unlink($dest_file); } if (!isset($options['ignore-errors'])) { return $this->raiseError("bad md5sum for file $final_dest_file", PEAR_INSTALLER_FAILED); } if (!isset($options['soft'])) { $this->log(0, "warning : bad md5sum for file $final_dest_file"); } } else { if (!isset($options['soft'])) { $this->log(0, "warning : bad md5sum for file $final_dest_file"); } } } } // }}} // {{{ set file permissions if (!OS_WINDOWS) { if ($atts['role'] == 'script') { $mode = 0777 & ~(int)octdec($this->config->get('umask')); $this->log(3, "+ chmod +x $dest_file"); } else { $mode = 0666 & ~(int)octdec($this->config->get('umask')); } if ($atts['role'] != 'src') { $this->addFileOperation("chmod", array($mode, $dest_file)); if (!@chmod($dest_file, $mode)) { if (!isset($options['soft'])) { $this->log(0, "failed to change mode of $dest_file: " . error_get_last()["message"]); } } } } // }}} if ($atts['role'] == 'src') { rename($dest_file, $final_dest_file); $this->log(2, "renamed source file $dest_file to $final_dest_file"); } else { $this->addFileOperation("rename", array($dest_file, $final_dest_file, $atts['role'] == 'ext')); } } // Store the full path where the file was installed for easy unistall if ($atts['role'] != 'script') { $loc = $this->config->get($atts['role'] . '_dir'); } else { $loc = $this->config->get('bin_dir'); } if ($atts['role'] != 'src') { $this->addFileOperation("installed_as", array($file, $installed_as, $loc, dirname(substr($installedas_dest_file, strlen($loc))))); } //$this->log(2, "installed: $dest_file"); return PEAR_INSTALLER_OK; } // }}} // {{{ _installFile2() /** * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 * @param string filename * @param array attributes from tag in package.xml * @param string path to install the file in * @param array options from command-line * @access private */ function _installFile2(&$pkg, $file, &$real_atts, $tmp_path, $options) { $atts = $real_atts; if (!isset($this->_registry)) { $this->_registry = &$this->config->getRegistry(); } $channel = $pkg->getChannel(); // {{{ assemble the destination paths if (!in_array($atts['attribs']['role'], PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) { return $this->raiseError('Invalid role `' . $atts['attribs']['role'] . "' for file $file"); } $role = &PEAR_Installer_Role::factory($pkg, $atts['attribs']['role'], $this->config); $err = $role->setup($this, $pkg, $atts['attribs'], $file); if (PEAR::isError($err)) { return $err; } if (!$role->isInstallable()) { return; } $info = $role->processInstallation($pkg, $atts['attribs'], $file, $tmp_path); if (PEAR::isError($info)) { return $info; } list($save_destdir, $dest_dir, $dest_file, $orig_file) = $info; if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) { return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED); } $final_dest_file = $installed_as = $dest_file; if (isset($this->_options['packagingroot'])) { $final_dest_file = $this->_prependPath($final_dest_file, $this->_options['packagingroot']); } $dest_dir = dirname($final_dest_file); $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); // }}} if (empty($this->_options['register-only'])) { if (!file_exists($dest_dir) || !is_dir($dest_dir)) { if (!$this->mkDirHier($dest_dir)) { return $this->raiseError("failed to mkdir $dest_dir", PEAR_INSTALLER_FAILED); } $this->log(3, "+ mkdir $dest_dir"); } } $attribs = $atts['attribs']; unset($atts['attribs']); // pretty much nothing happens if we are only registering the install if (empty($this->_options['register-only'])) { if (!count($atts)) { // no tasks if (!file_exists($orig_file)) { return $this->raiseError("file $orig_file does not exist", PEAR_INSTALLER_FAILED); } if (!@copy($orig_file, $dest_file)) { return $this->raiseError( "failed to write $dest_file: " . error_get_last()["message"], PEAR_INSTALLER_FAILED); } $this->log(3, "+ cp $orig_file $dest_file"); if (isset($attribs['md5sum'])) { $md5sum = md5_file($dest_file); } } else { // file with tasks if (!file_exists($orig_file)) { return $this->raiseError("file $orig_file does not exist", PEAR_INSTALLER_FAILED); } $contents = file_get_contents($orig_file); if ($contents === false) { $contents = ''; } if (isset($attribs['md5sum'])) { $md5sum = md5($contents); } foreach ($atts as $tag => $raw) { $tag = str_replace(array($pkg->getTasksNs() . ':', '-'), array('', '_'), $tag); $task = "PEAR_Task_$tag"; $task = new $task($this->config, $this, PEAR_TASK_INSTALL); if (!$task->isScript()) { // scripts are only handled after installation $task->init($raw, $attribs, $pkg->getLastInstalledVersion()); $res = $task->startSession($pkg, $contents, $final_dest_file); if ($res === false) { continue; // skip this file } if (PEAR::isError($res)) { return $res; } $contents = $res; // save changes } $wp = @fopen($dest_file, "wb"); if (!is_resource($wp)) { return $this->raiseError( "failed to create $dest_file: " . error_get_last()["message"], PEAR_INSTALLER_FAILED); } if (fwrite($wp, $contents) === false) { return $this->raiseError( "failed writing to $dest_file: " . error_get_last()["message"], PEAR_INSTALLER_FAILED); } fclose($wp); } } // {{{ check the md5 if (isset($md5sum)) { // Make sure the original md5 sum matches with expected if (strtolower($md5sum) === strtolower($attribs['md5sum'])) { $this->log(2, "md5sum ok: $final_dest_file"); if (isset($contents)) { // set md5 sum based on $content in case any tasks were run. $real_atts['attribs']['md5sum'] = md5($contents); } } else { if (empty($options['force'])) { // delete the file if (file_exists($dest_file)) { unlink($dest_file); } if (!isset($options['ignore-errors'])) { return $this->raiseError("bad md5sum for file $final_dest_file", PEAR_INSTALLER_FAILED); } if (!isset($options['soft'])) { $this->log(0, "warning : bad md5sum for file $final_dest_file"); } } else { if (!isset($options['soft'])) { $this->log(0, "warning : bad md5sum for file $final_dest_file"); } } } } else { $real_atts['attribs']['md5sum'] = md5_file($dest_file); } // }}} // {{{ set file permissions if (!OS_WINDOWS) { if ($role->isExecutable()) { $mode = 0777 & ~(int)octdec($this->config->get('umask')); $this->log(3, "+ chmod +x $dest_file"); } else { $mode = 0666 & ~(int)octdec($this->config->get('umask')); } if ($attribs['role'] != 'src') { $this->addFileOperation("chmod", array($mode, $dest_file)); if (!@chmod($dest_file, $mode)) { if (!isset($options['soft'])) { $this->log(0, "failed to change mode of $dest_file: " . error_get_last()["message"]); } } } } // }}} if ($attribs['role'] == 'src') { rename($dest_file, $final_dest_file); $this->log(2, "renamed source file $dest_file to $final_dest_file"); } else { $this->addFileOperation("rename", array($dest_file, $final_dest_file, $role->isExtension())); } } // Store the full path where the file was installed for easy uninstall if ($attribs['role'] != 'src') { $loc = $this->config->get($role->getLocationConfig(), null, $channel); $this->addFileOperation('installed_as', array($file, $installed_as, $loc, dirname(substr($installed_as, strlen($loc))))); } //$this->log(2, "installed: $dest_file"); return PEAR_INSTALLER_OK; } // }}} // {{{ addFileOperation() /** * Add a file operation to the current file transaction. * * @see startFileTransaction() * @param string $type This can be one of: * - rename: rename a file ($data has 3 values) * - backup: backup an existing file ($data has 1 value) * - removebackup: clean up backups created during install ($data has 1 value) * - chmod: change permissions on a file ($data has 2 values) * - delete: delete a file ($data has 1 value) * - rmdir: delete a directory if empty ($data has 1 value) * - installed_as: mark a file as installed ($data has 4 values). * @param array $data For all file operations, this array must contain the * full path to the file or directory that is being operated on. For * the rename command, the first parameter must be the file to rename, * the second its new name, the third whether this is a PHP extension. * * The installed_as operation contains 4 elements in this order: * 1. Filename as listed in the filelist element from package.xml * 2. Full path to the installed file * 3. Full path from the php_dir configuration variable used in this * installation * 4. Relative path from the php_dir that this file is installed in */ function addFileOperation($type, $data) { if (!is_array($data)) { return $this->raiseError('Internal Error: $data in addFileOperation' . ' must be an array, was ' . gettype($data)); } if ($type == 'chmod') { $octmode = decoct($data[0]); $this->log(3, "adding to transaction: $type $octmode $data[1]"); } else { $this->log(3, "adding to transaction: $type " . implode(" ", $data)); } $this->file_operations[] = array($type, $data); } // }}} // {{{ startFileTransaction() function startFileTransaction($rollback_in_case = false) { if (count($this->file_operations) && $rollback_in_case) { $this->rollbackFileTransaction(); } $this->file_operations = array(); } // }}} // {{{ commitFileTransaction() function commitFileTransaction() { // {{{ first, check permissions and such manually $errors = array(); foreach ($this->file_operations as $key => $tr) { list($type, $data) = $tr; switch ($type) { case 'rename': if (!file_exists($data[0])) { $errors[] = "cannot rename file $data[0], doesn't exist"; } // check that dest dir. is writable if (!is_writable(dirname($data[1]))) { $errors[] = "permission denied ($type): $data[1]"; } break; case 'chmod': // check that file is writable if (!is_writable($data[1])) { $errors[] = "permission denied ($type): $data[1] " . decoct($data[0]); } break; case 'delete': if (!file_exists($data[0])) { $this->log(2, "warning: file $data[0] doesn't exist, can't be deleted"); } // check that directory is writable if (file_exists($data[0])) { if (!is_writable(dirname($data[0]))) { $errors[] = "permission denied ($type): $data[0]"; } else { // make sure the file to be deleted can be opened for writing $fp = false; if (!is_dir($data[0]) && (!is_writable($data[0]) || !($fp = @fopen($data[0], 'a')))) { $errors[] = "permission denied ($type): $data[0]"; } elseif ($fp) { fclose($fp); } } /* Verify we are not deleting a file owned by another package * This can happen when a file moves from package A to B in * an upgrade ala http://pear.php.net/17986 */ $info = array( 'package' => strtolower($this->pkginfo->getName()), 'channel' => strtolower($this->pkginfo->getChannel()), ); $result = $this->_registry->checkFileMap($data[0], $info, '1.1'); if (is_array($result)) { $res = array_diff($result, $info); if (!empty($res)) { $new = $this->_registry->getPackage($result[1], $result[0]); $this->file_operations[$key] = false; $pkginfoName = $this->pkginfo->getName(); $newChannel = $new->getChannel(); $newPackage = $new->getName(); $this->log(3, "file $data[0] was scheduled for removal from $pkginfoName but is owned by $newChannel/$newPackage, removal has been cancelled."); } } } break; } } // }}} $n = count($this->file_operations); $this->log(2, "about to commit $n file operations for " . $this->pkginfo->getName()); $m = count($errors); if ($m > 0) { foreach ($errors as $error) { if (!isset($this->_options['soft'])) { $this->log(1, $error); } } if (!isset($this->_options['ignore-errors'])) { return false; } } $this->_dirtree = array(); // {{{ really commit the transaction foreach ($this->file_operations as $i => $tr) { if (!$tr) { // support removal of non-existing backups continue; } list($type, $data) = $tr; switch ($type) { case 'backup': if (!file_exists($data[0])) { $this->file_operations[$i] = false; break; } if (!@copy($data[0], $data[0] . '.bak')) { $this->log(1, 'Could not copy ' . $data[0] . ' to ' . $data[0] . '.bak ' . error_get_last()["message"]); return false; } $this->log(3, "+ backup $data[0] to $data[0].bak"); break; case 'removebackup': if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) { unlink($data[0] . '.bak'); $this->log(3, "+ rm backup of $data[0] ($data[0].bak)"); } break; case 'rename': $test = file_exists($data[1]) ? @unlink($data[1]) : null; if (!$test && file_exists($data[1])) { if ($data[2]) { $extra = ', this extension must be installed manually. Rename to "' . basename($data[1]) . '"'; } else { $extra = ''; } if (!isset($this->_options['soft'])) { $this->log(1, 'Could not delete ' . $data[1] . ', cannot rename ' . $data[0] . $extra); } if (!isset($this->_options['ignore-errors'])) { return false; } } // permissions issues with rename - copy() is far superior $perms = @fileperms($data[0]); if (!@copy($data[0], $data[1])) { $this->log(1, 'Could not rename ' . $data[0] . ' to ' . $data[1] . ' ' . error_get_last()["message"]); return false; } // copy over permissions, otherwise they are lost @chmod($data[1], $perms); @unlink($data[0]); $this->log(3, "+ mv $data[0] $data[1]"); break; case 'chmod': if (!@chmod($data[1], $data[0])) { $this->log(1, 'Could not chmod ' . $data[1] . ' to ' . decoct($data[0]) . ' ' . error_get_last()["message"]); return false; } $octmode = decoct($data[0]); $this->log(3, "+ chmod $octmode $data[1]"); break; case 'delete': if (file_exists($data[0])) { if (!@unlink($data[0])) { $this->log(1, 'Could not delete ' . $data[0] . ' ' . error_get_last()["message"]); return false; } $this->log(3, "+ rm $data[0]"); } break; case 'rmdir': if (file_exists($data[0])) { do { $testme = opendir($data[0]); while (false !== ($entry = readdir($testme))) { if ($entry == '.' || $entry == '..') { continue; } closedir($testme); break 2; // this directory is not empty and can't be // deleted } closedir($testme); if (!@rmdir($data[0])) { $this->log(1, 'Could not rmdir ' . $data[0] . ' ' . error_get_last()["message"]); return false; } $this->log(3, "+ rmdir $data[0]"); } while (false); } break; case 'installed_as': $this->pkginfo->setInstalledAs($data[0], $data[1]); if (!isset($this->_dirtree[dirname($data[1])])) { $this->_dirtree[dirname($data[1])] = true; $this->pkginfo->setDirtree(dirname($data[1])); while(!empty($data[3]) && dirname($data[3]) != $data[3] && $data[3] != '/' && $data[3] != '\\') { $this->pkginfo->setDirtree($pp = $this->_prependPath($data[3], $data[2])); $this->_dirtree[$pp] = true; $data[3] = dirname($data[3]); } } break; } } // }}} $this->log(2, "successfully committed $n file operations"); $this->file_operations = array(); return true; } // }}} // {{{ rollbackFileTransaction() function rollbackFileTransaction() { $n = count($this->file_operations); $this->log(2, "rolling back $n file operations"); foreach ($this->file_operations as $tr) { list($type, $data) = $tr; switch ($type) { case 'backup': if (file_exists($data[0] . '.bak')) { if (file_exists($data[0] && is_writable($data[0]))) { unlink($data[0]); } @copy($data[0] . '.bak', $data[0]); $this->log(3, "+ restore $data[0] from $data[0].bak"); } break; case 'removebackup': if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) { unlink($data[0] . '.bak'); $this->log(3, "+ rm backup of $data[0] ($data[0].bak)"); } break; case 'rename': @unlink($data[0]); $this->log(3, "+ rm $data[0]"); break; case 'mkdir': @rmdir($data[0]); $this->log(3, "+ rmdir $data[0]"); break; case 'chmod': break; case 'delete': break; case 'installed_as': $this->pkginfo->setInstalledAs($data[0], false); break; } } $this->pkginfo->resetDirtree(); $this->file_operations = array(); } // }}} // {{{ mkDirHier($dir) function mkDirHier($dir) { $this->addFileOperation('mkdir', array($dir)); return parent::mkDirHier($dir); } // }}} // {{{ _parsePackageXml() function _parsePackageXml(&$descfile) { // Parse xml file ----------------------------------------------- $pkg = new PEAR_PackageFile($this->config, $this->debug); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $p = &$pkg->fromAnyFile($descfile, PEAR_VALIDATE_INSTALLING); PEAR::staticPopErrorHandling(); if (PEAR::isError($p)) { if (is_array($p->getUserInfo())) { foreach ($p->getUserInfo() as $err) { $loglevel = $err['level'] == 'error' ? 0 : 1; if (!isset($this->_options['soft'])) { $this->log($loglevel, ucfirst($err['level']) . ': ' . $err['message']); } } } return $this->raiseError('Installation failed: invalid package file'); } $descfile = $p->getPackageFile(); return $p; } // }}} /** * Set the list of PEAR_Downloader_Package objects to allow more sane * dependency validation * @param array */ function setDownloadedPackages(&$pkgs) { PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $err = $this->analyzeDependencies($pkgs); PEAR::popErrorHandling(); if (PEAR::isError($err)) { return $err; } $this->_downloadedPackages = &$pkgs; } /** * Set the list of PEAR_Downloader_Package objects to allow more sane * dependency validation * @param array */ function setUninstallPackages(&$pkgs) { $this->_downloadedPackages = &$pkgs; } function getInstallPackages() { return $this->_downloadedPackages; } // {{{ install() /** * Installs the files within the package file specified. * * @param string|PEAR_Downloader_Package $pkgfile path to the package file, * or a pre-initialized packagefile object * @param array $options * recognized options: * - installroot : optional prefix directory for installation * - force : force installation * - register-only : update registry but don't install files * - upgrade : upgrade existing install * - soft : fail silently * - nodeps : ignore dependency conflicts/missing dependencies * - alldeps : install all dependencies * - onlyreqdeps : install only required dependencies * * @return array|PEAR_Error package info if successful */ function install($pkgfile, $options = array()) { $this->_options = $options; $this->_registry = &$this->config->getRegistry(); if (is_object($pkgfile)) { $dlpkg = &$pkgfile; $pkg = $pkgfile->getPackageFile(); $pkgfile = $pkg->getArchiveFile(); $descfile = $pkg->getPackageFile(); } else { $descfile = $pkgfile; $pkg = $this->_parsePackageXml($descfile); if (PEAR::isError($pkg)) { return $pkg; } } $tmpdir = dirname($descfile); if (realpath($descfile) != realpath($pkgfile)) { // Use the temp_dir since $descfile can contain the download dir path $tmpdir = $this->config->get('temp_dir', null, 'pear.php.net'); $tmpdir = System::mktemp('-d -t "' . $tmpdir . '"'); $tar = new Archive_Tar($pkgfile); if (!$tar->extract($tmpdir)) { return $this->raiseError("unable to unpack $pkgfile"); } } $pkgname = $pkg->getName(); $channel = $pkg->getChannel(); if (isset($options['installroot'])) { $this->config->setInstallRoot($options['installroot']); $this->_registry = &$this->config->getRegistry(); $installregistry = &$this->_registry; $this->installroot = ''; // all done automagically now $php_dir = $this->config->get('php_dir', null, $channel); } else { $this->config->setInstallRoot(false); $this->_registry = &$this->config->getRegistry(); if (isset($this->_options['packagingroot'])) { $regdir = $this->_prependPath( $this->config->get('php_dir', null, 'pear.php.net'), $this->_options['packagingroot']); $metadata_dir = $this->config->get('metadata_dir', null, 'pear.php.net'); if ($metadata_dir) { $metadata_dir = $this->_prependPath( $metadata_dir, $this->_options['packagingroot']); } $packrootphp_dir = $this->_prependPath( $this->config->get('php_dir', null, $channel), $this->_options['packagingroot']); $installregistry = new PEAR_Registry($regdir, false, false, $metadata_dir); if (!$installregistry->channelExists($channel, true)) { // we need to fake a channel-discover of this channel $chanobj = $this->_registry->getChannel($channel, true); $installregistry->addChannel($chanobj); } $php_dir = $packrootphp_dir; } else { $installregistry = &$this->_registry; $php_dir = $this->config->get('php_dir', null, $channel); } $this->installroot = ''; } // {{{ checks to do when not in "force" mode if (empty($options['force']) && (file_exists($this->config->get('php_dir')) && is_dir($this->config->get('php_dir')))) { $testp = $channel == 'pear.php.net' ? $pkgname : array($channel, $pkgname); $instfilelist = $pkg->getInstallationFileList(true); if (PEAR::isError($instfilelist)) { return $instfilelist; } // ensure we have the most accurate registry $installregistry->flushFileMap(); $test = $installregistry->checkFileMap($instfilelist, $testp, '1.1'); if (PEAR::isError($test)) { return $test; } if (sizeof($test)) { $pkgs = $this->getInstallPackages(); $found = false; foreach ($pkgs as $param) { if ($pkg->isSubpackageOf($param)) { $found = true; break; } } if ($found) { // subpackages can conflict with earlier versions of parent packages $parentreg = $installregistry->packageInfo($param->getPackage(), null, $param->getChannel()); $tmp = $test; foreach ($tmp as $file => $info) { if (is_array($info)) { if (strtolower($info[1]) == strtolower($param->getPackage()) && strtolower($info[0]) == strtolower($param->getChannel()) ) { if (isset($parentreg['filelist'][$file])) { unset($parentreg['filelist'][$file]); } else{ $pos = strpos($file, '/'); $basedir = substr($file, 0, $pos); $file2 = substr($file, $pos + 1); if (isset($parentreg['filelist'][$file2]['baseinstalldir']) && $parentreg['filelist'][$file2]['baseinstalldir'] === $basedir ) { unset($parentreg['filelist'][$file2]); } } unset($test[$file]); } } else { if (strtolower($param->getChannel()) != 'pear.php.net') { continue; } if (strtolower($info) == strtolower($param->getPackage())) { if (isset($parentreg['filelist'][$file])) { unset($parentreg['filelist'][$file]); } else{ $pos = strpos($file, '/'); $basedir = substr($file, 0, $pos); $file2 = substr($file, $pos + 1); if (isset($parentreg['filelist'][$file2]['baseinstalldir']) && $parentreg['filelist'][$file2]['baseinstalldir'] === $basedir ) { unset($parentreg['filelist'][$file2]); } } unset($test[$file]); } } } $pfk = new PEAR_PackageFile($this->config); $parentpkg = &$pfk->fromArray($parentreg); $installregistry->updatePackage2($parentpkg); } if ($param->getChannel() == 'pecl.php.net' && isset($options['upgrade'])) { $tmp = $test; foreach ($tmp as $file => $info) { if (is_string($info)) { // pear.php.net packages are always stored as strings if (strtolower($info) == strtolower($param->getPackage())) { // upgrading existing package unset($test[$file]); } } } } if (count($test)) { $msg = "$channel/$pkgname: conflicting files found:\n"; $longest = max(array_map("strlen", array_keys($test))); $fmt = "%{$longest}s (%s)\n"; foreach ($test as $file => $info) { if (!is_array($info)) { $info = array('pear.php.net', $info); } $info = $info[0] . '/' . $info[1]; $msg .= sprintf($fmt, $file, $info); } if (!isset($options['ignore-errors'])) { return $this->raiseError($msg); } if (!isset($options['soft'])) { $this->log(0, "WARNING: $msg"); } } } } // }}} $this->startFileTransaction(); $usechannel = $channel; if ($channel == 'pecl.php.net') { $test = $installregistry->packageExists($pkgname, $channel); if (!$test) { $test = $installregistry->packageExists($pkgname, 'pear.php.net'); $usechannel = 'pear.php.net'; } } else { $test = $installregistry->packageExists($pkgname, $channel); } if (empty($options['upgrade']) && empty($options['soft'])) { // checks to do only when installing new packages if (empty($options['force']) && $test) { return $this->raiseError("$channel/$pkgname is already installed"); } } else { // Upgrade if ($test) { $v1 = $installregistry->packageInfo($pkgname, 'version', $usechannel); $v2 = $pkg->getVersion(); $cmp = version_compare("$v1", "$v2", 'gt'); if (empty($options['force']) && !version_compare("$v2", "$v1", 'gt')) { return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)"); } } } // Do cleanups for upgrade and install, remove old release's files first if ($test && empty($options['register-only'])) { // when upgrading, remove old release's files first: if (PEAR::isError($err = $this->_deletePackageFiles($pkgname, $usechannel, true))) { if (!isset($options['ignore-errors'])) { return $this->raiseError($err); } if (!isset($options['soft'])) { $this->log(0, 'WARNING: ' . $err->getMessage()); } } else { $backedup = $err; } } // {{{ Copy files to dest dir --------------------------------------- // info from the package it self we want to access from _installFile $this->pkginfo = &$pkg; // used to determine whether we should build any C code $this->source_files = 0; $savechannel = $this->config->get('default_channel'); if (empty($options['register-only']) && !is_dir($php_dir)) { if (PEAR::isError(System::mkdir(array('-p'), $php_dir))) { return $this->raiseError("no installation destination directory '$php_dir'\n"); } } if (substr($pkgfile, -4) != '.xml') { $tmpdir .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkg->getVersion(); } $this->configSet('default_channel', $channel); // {{{ install files $ver = $pkg->getPackagexmlVersion(); if (version_compare($ver, '2.0', '>=')) { $filelist = $pkg->getInstallationFilelist(); } else { $filelist = $pkg->getFileList(); } if (PEAR::isError($filelist)) { return $filelist; } $p = &$installregistry->getPackage($pkgname, $channel); $dirtree = (empty($options['register-only']) && $p) ? $p->getDirTree() : false; $pkg->resetFilelist(); $pkg->setLastInstalledVersion($installregistry->packageInfo($pkg->getPackage(), 'version', $pkg->getChannel())); foreach ($filelist as $file => $atts) { $this->expectError(PEAR_INSTALLER_FAILED); if ($pkg->getPackagexmlVersion() == '1.0') { $res = $this->_installFile($file, $atts, $tmpdir, $options); } else { $res = $this->_installFile2($pkg, $file, $atts, $tmpdir, $options); } $this->popExpect(); if (PEAR::isError($res)) { if (empty($options['ignore-errors'])) { $this->rollbackFileTransaction(); if ($res->getMessage() == "file does not exist") { $this->raiseError("file $file in package.xml does not exist"); } return $this->raiseError($res); } if (!isset($options['soft'])) { $this->log(0, "Warning: " . $res->getMessage()); } } $real = isset($atts['attribs']) ? $atts['attribs'] : $atts; if ($res == PEAR_INSTALLER_OK && $real['role'] != 'src') { // Register files that were installed $pkg->installedFile($file, $atts); } } // }}} // {{{ compile and install source files if ($this->source_files > 0 && empty($options['nobuild'])) { $configureoptions = empty($options['configureoptions']) ? '' : $options['configureoptions']; if (PEAR::isError($err = $this->_compileSourceFiles($savechannel, $pkg, $configureoptions))) { return $err; } } // }}} if (isset($backedup)) { $this->_removeBackups($backedup); } if (!$this->commitFileTransaction()) { $this->rollbackFileTransaction(); $this->configSet('default_channel', $savechannel); return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED); } // }}} $ret = false; $installphase = 'install'; $oldversion = false; // {{{ Register that the package is installed ----------------------- if (empty($options['upgrade'])) { // if 'force' is used, replace the info in registry $usechannel = $channel; if ($channel == 'pecl.php.net') { $test = $installregistry->packageExists($pkgname, $channel); if (!$test) { $test = $installregistry->packageExists($pkgname, 'pear.php.net'); $usechannel = 'pear.php.net'; } } else { $test = $installregistry->packageExists($pkgname, $channel); } if (!empty($options['force']) && $test) { $oldversion = $installregistry->packageInfo($pkgname, 'version', $usechannel); $installregistry->deletePackage($pkgname, $usechannel); } $ret = $installregistry->addPackage2($pkg); } else { if ($dirtree) { $this->startFileTransaction(); // attempt to delete empty directories uksort($dirtree, array($this, '_sortDirs')); foreach($dirtree as $dir => $notused) { $this->addFileOperation('rmdir', array($dir)); } $this->commitFileTransaction(); } $usechannel = $channel; if ($channel == 'pecl.php.net') { $test = $installregistry->packageExists($pkgname, $channel); if (!$test) { $test = $installregistry->packageExists($pkgname, 'pear.php.net'); $usechannel = 'pear.php.net'; } } else { $test = $installregistry->packageExists($pkgname, $channel); } // new: upgrade installs a package if it isn't installed if (!$test) { $ret = $installregistry->addPackage2($pkg); } else { if ($usechannel != $channel) { $installregistry->deletePackage($pkgname, $usechannel); $ret = $installregistry->addPackage2($pkg); } else { $ret = $installregistry->updatePackage2($pkg); } $installphase = 'upgrade'; } } if (!$ret) { $this->configSet('default_channel', $savechannel); return $this->raiseError("Adding package $channel/$pkgname to registry failed"); } // }}} $this->configSet('default_channel', $savechannel); if (class_exists('PEAR_Task_Common')) { // this is auto-included if any tasks exist if (PEAR_Task_Common::hasPostinstallTasks()) { PEAR_Task_Common::runPostinstallTasks($installphase); } } return $pkg->toArray(true); } // }}} // {{{ _compileSourceFiles() /** * @param string * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 * @param mixed[] $configureoptions */ function _compileSourceFiles($savechannel, &$filelist, $configureoptions) { require_once 'PEAR/Builder.php'; $this->log(1, "$this->source_files source files, building"); $bob = new PEAR_Builder($configureoptions, $this->ui); $bob->debug = $this->debug; $built = $bob->build($filelist, array(&$this, '_buildCallback')); if (PEAR::isError($built)) { $this->rollbackFileTransaction(); $this->configSet('default_channel', $savechannel); return $built; } $this->log(1, "\nBuild process completed successfully"); foreach ($built as $ext) { $bn = basename($ext['file']); list($_ext_name, $_ext_suff) = explode('.', $bn); if ($_ext_suff == 'so' || $_ext_suff == 'dll') { if (extension_loaded($_ext_name)) { return $this->raiseError("Extension '$_ext_name' already loaded. " . 'Please unload it in your php.ini file ' . 'prior to install or upgrade'); } $role = 'ext'; } else { $role = 'src'; } $dest = $ext['dest']; $packagingroot = ''; if (isset($this->_options['packagingroot'])) { $packagingroot = $this->_options['packagingroot']; } $copyto = $this->_prependPath($dest, $packagingroot); $extra = $copyto != $dest ? " as '$copyto'" : ''; $this->log(1, "Installing '$dest'$extra"); $copydir = dirname($copyto); // pretty much nothing happens if we are only registering the install if (empty($this->_options['register-only'])) { if (!file_exists($copydir) || !is_dir($copydir)) { if (!$this->mkDirHier($copydir)) { return $this->raiseError("failed to mkdir $copydir", PEAR_INSTALLER_FAILED); } $this->log(3, "+ mkdir $copydir"); } if (!@copy($ext['file'], $copyto)) { return $this->raiseError( "failed to write $copyto (" . error_get_last()["message"] . ")", PEAR_INSTALLER_FAILED); } $this->log(3, "+ cp $ext[file] $copyto"); $this->addFileOperation('rename', array($ext['file'], $copyto)); if (!OS_WINDOWS) { $mode = 0666 & ~(int)octdec($this->config->get('umask')); $this->addFileOperation('chmod', array($mode, $copyto)); if (!@chmod($copyto, $mode)) { $this->log(0, "failed to change mode of $copyto (" . error_get_last()["message"] . ")"); } } } $data = array( 'role' => $role, 'name' => $bn, 'installed_as' => $dest, 'php_api' => $ext['php_api'], 'zend_mod_api' => $ext['zend_mod_api'], 'zend_ext_api' => $ext['zend_ext_api'], ); if ($filelist->getPackageXmlVersion() == '1.0') { $filelist->installedFile($bn, $data); } else { $filelist->installedFile($bn, array('attribs' => $data)); } } } // }}} function &getUninstallPackages() { return $this->_downloadedPackages; } // {{{ uninstall() /** * Uninstall a package * * This method removes all files installed by the application, and then * removes any empty directories. * @param string package name * @param array Command-line options. Possibilities include: * * - installroot: base installation dir, if not the default * - register-only : update registry but don't remove files * - nodeps: do not process dependencies of other packages to ensure * uninstallation does not break things */ function uninstall($package, $options = array()) { $installRoot = isset($options['installroot']) ? $options['installroot'] : ''; $this->config->setInstallRoot($installRoot); $this->installroot = ''; $this->_registry = &$this->config->getRegistry(); if (is_object($package)) { $channel = $package->getChannel(); $pkg = $package; $package = $pkg->getPackage(); } else { $pkg = false; $info = $this->_registry->parsePackageName($package, $this->config->get('default_channel')); $channel = $info['channel']; $package = $info['package']; } $savechannel = $this->config->get('default_channel'); $this->configSet('default_channel', $channel); if (!is_object($pkg)) { $pkg = $this->_registry->getPackage($package, $channel); } if (!$pkg) { $this->configSet('default_channel', $savechannel); return $this->raiseError($this->_registry->parsedPackageNameToString( array( 'channel' => $channel, 'package' => $package ), true) . ' not installed'); } if ($pkg->getInstalledBinary()) { // this is just an alias for a binary package return $this->_registry->deletePackage($package, $channel); } $filelist = $pkg->getFilelist(); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); if (!class_exists('PEAR_Dependency2')) { require_once 'PEAR/Dependency2.php'; } $depchecker = new PEAR_Dependency2($this->config, $options, array('channel' => $channel, 'package' => $package), PEAR_VALIDATE_UNINSTALLING); $e = $depchecker->validatePackageUninstall($this); PEAR::staticPopErrorHandling(); if (PEAR::isError($e)) { if (!isset($options['ignore-errors'])) { return $this->raiseError($e); } if (!isset($options['soft'])) { $this->log(0, 'WARNING: ' . $e->getMessage()); } } elseif (is_array($e)) { if (!isset($options['soft'])) { $this->log(0, $e[0]); } } $this->pkginfo = &$pkg; // pretty much nothing happens if we are only registering the uninstall if (empty($options['register-only'])) { // {{{ Delete the files $this->startFileTransaction(); PEAR::pushErrorHandling(PEAR_ERROR_RETURN); if (PEAR::isError($err = $this->_deletePackageFiles($package, $channel))) { PEAR::popErrorHandling(); $this->rollbackFileTransaction(); $this->configSet('default_channel', $savechannel); if (!isset($options['ignore-errors'])) { return $this->raiseError($err); } if (!isset($options['soft'])) { $this->log(0, 'WARNING: ' . $err->getMessage()); } } else { PEAR::popErrorHandling(); } if (!$this->commitFileTransaction()) { $this->rollbackFileTransaction(); if (!isset($options['ignore-errors'])) { return $this->raiseError("uninstall failed"); } if (!isset($options['soft'])) { $this->log(0, 'WARNING: uninstall failed'); } } else { $this->startFileTransaction(); $dirtree = $pkg->getDirTree(); if ($dirtree === false) { $this->configSet('default_channel', $savechannel); return $this->_registry->deletePackage($package, $channel); } // attempt to delete empty directories uksort($dirtree, array($this, '_sortDirs')); foreach($dirtree as $dir => $notused) { $this->addFileOperation('rmdir', array($dir)); } if (!$this->commitFileTransaction()) { $this->rollbackFileTransaction(); if (!isset($options['ignore-errors'])) { return $this->raiseError("uninstall failed"); } if (!isset($options['soft'])) { $this->log(0, 'WARNING: uninstall failed'); } } } // }}} } $this->configSet('default_channel', $savechannel); // Register that the package is no longer installed return $this->_registry->deletePackage($package, $channel); } /** * Sort a list of arrays of array(downloaded packagefilename) by dependency. * * It also removes duplicate dependencies * @param array an array of PEAR_PackageFile_v[1/2] objects * @return array|PEAR_Error array of array(packagefilename, package.xml contents) */ function sortPackagesForUninstall(&$packages) { $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->config); if (PEAR::isError($this->_dependencyDB)) { return $this->_dependencyDB; } usort($packages, array(&$this, '_sortUninstall')); } function _sortUninstall($a, $b) { if (!$a->getDeps() && !$b->getDeps()) { return 0; // neither package has dependencies, order is insignificant } if ($a->getDeps() && !$b->getDeps()) { return -1; // $a must be installed after $b because $a has dependencies } if (!$a->getDeps() && $b->getDeps()) { return 1; // $b must be installed after $a because $b has dependencies } // both packages have dependencies if ($this->_dependencyDB->dependsOn($a, $b)) { return -1; } if ($this->_dependencyDB->dependsOn($b, $a)) { return 1; } return 0; } // }}} // {{{ _sortDirs() function _sortDirs($a, $b) { if (strnatcmp($a, $b) == -1) return 1; if (strnatcmp($a, $b) == 1) return -1; return 0; } // }}} // {{{ _buildCallback() function _buildCallback($what, $data) { if (($what == 'cmdoutput' && $this->debug > 1) || ($what == 'output' && $this->debug > 0)) { $this->ui->outputData(rtrim($data), 'build'); } } // }}} } PK=1]r\IIpear/PEAR/Builder.phpnu[ * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 * * TODO: log output parameters in PECL command line * TODO: msdev path in configuration */ /** * Needed for extending PEAR_Builder */ require_once 'PEAR/Common.php'; require_once 'PEAR/PackageFile.php'; require_once 'System.php'; /** * Class to handle building (compiling) extensions. * * @category pear * @package PEAR * @author Stig Bakken * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since PHP 4.0.2 * @see http://pear.php.net/manual/en/core.ppm.pear-builder.php */ class PEAR_Builder extends PEAR_Common { var $php_api_version = 0; var $zend_module_api_no = 0; var $zend_extension_api_no = 0; var $extensions_built = array(); /** * @var string Used for reporting when it is not possible to pass function * via extra parameter, e.g. log, msdevCallback */ var $current_callback = null; // used for msdev builds var $_lastline = null; var $_firstline = null; /** * Parsed --configureoptions. * * @var mixed[] */ var $_parsed_configure_options; /** * PEAR_Builder constructor. * * @param mixed[] $configureoptions * @param object $ui user interface object (instance of PEAR_Frontend_*) * * @access public */ function __construct($configureoptions, &$ui) { parent::__construct(); $this->setFrontendObject($ui); $this->_parseConfigureOptions($configureoptions); } /** * Parse --configureoptions string. * * @param string Options, in the form "X=1 Y=2 Z='there\'s always one'" */ function _parseConfigureOptions($options) { $data = ''; $parser = xml_parser_create('ISO-8859-1'); xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); xml_set_element_handler( $parser, array($this, '_parseConfigureOptionsStartElement'), array($this, '_parseConfigureOptionsEndElement')); xml_parse($parser, $data, true); xml_parser_free($parser); } /** * Handle element start. * * @see PEAR_Builder::_parseConfigureOptions() * * @param resource $parser * @param string $tagName * @param mixed[] $attribs */ function _parseConfigureOptionsStartElement($parser, $tagName, $attribs) { if ($tagName !== 'PROPERTIES') { return; } $this->_parsed_configure_options = $attribs; } /** * Handle element end. * * @see PEAR_Builder::_parseConfigureOptions() * * @param resource * @param string $element */ function _parseConfigureOptionsEndElement($parser, $element) { } /** * Build an extension from source on windows. * requires msdev */ function _build_win32($descfile, $callback = null) { if (is_object($descfile)) { $pkg = $descfile; $descfile = $pkg->getPackageFile(); } else { $pf = new PEAR_PackageFile($this->config, $this->debug); $pkg = &$pf->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL); if (PEAR::isError($pkg)) { return $pkg; } } $dir = dirname($descfile); $old_cwd = getcwd(); if (!file_exists($dir) || !is_dir($dir) || !chdir($dir)) { return $this->raiseError("could not chdir to $dir"); } // packages that were in a .tar have the packagefile in this directory $vdir = $pkg->getPackage() . '-' . $pkg->getVersion(); if (file_exists($dir) && is_dir($vdir)) { if (!chdir($vdir)) { return $this->raiseError("could not chdir to " . realpath($vdir)); } $dir = getcwd(); } $this->log(2, "building in $dir"); $dsp = $pkg->getPackage().'.dsp'; if (!file_exists("$dir/$dsp")) { return $this->raiseError("The DSP $dsp does not exist."); } // XXX TODO: make release build type configurable $command = 'msdev '.$dsp.' /MAKE "'.$pkg->getPackage(). ' - Release"'; $err = $this->_runCommand($command, array(&$this, 'msdevCallback')); if (PEAR::isError($err)) { return $err; } // figure out the build platform and type $platform = 'Win32'; $buildtype = 'Release'; if (preg_match('/.*?'.$pkg->getPackage().'\s-\s(\w+)\s(.*?)-+/i',$this->_firstline,$matches)) { $platform = $matches[1]; $buildtype = $matches[2]; } if (preg_match('/(.*)?\s-\s(\d+).*?(\d+)/', $this->_lastline, $matches)) { if ($matches[2]) { // there were errors in the build return $this->raiseError("There were errors during compilation."); } $out = $matches[1]; } else { return $this->raiseError("Did not understand the completion status returned from msdev.exe."); } // msdev doesn't tell us the output directory :/ // open the dsp, find /out and use that directory $dsptext = join('', file($dsp)); // this regex depends on the build platform and type having been // correctly identified above. $regex ='/.*?!IF\s+"\$\(CFG\)"\s+==\s+("'. $pkg->getPackage().'\s-\s'. $platform.'\s'. $buildtype.'").*?'. '\/out:"(.*?)"/is'; if ($dsptext && preg_match($regex, $dsptext, $matches)) { // what we get back is a relative path to the output file itself. $outfile = realpath($matches[2]); } else { return $this->raiseError("Could not retrieve output information from $dsp."); } // realpath returns false if the file doesn't exist if ($outfile && copy($outfile, "$dir/$out")) { $outfile = "$dir/$out"; } $built_files[] = array( 'file' => "$outfile", 'php_api' => $this->php_api_version, 'zend_mod_api' => $this->zend_module_api_no, 'zend_ext_api' => $this->zend_extension_api_no, ); return $built_files; } // }}} // {{{ msdevCallback() function msdevCallback($what, $data) { if (!$this->_firstline) $this->_firstline = $data; $this->_lastline = $data; call_user_func($this->current_callback, $what, $data); } /** * @param string * @param string * @param array * @access private */ function _harvestInstDir($dest_prefix, $dirname, &$built_files) { $d = opendir($dirname); if (!$d) return false; $ret = true; while (($ent = readdir($d)) !== false) { if ($ent[0] == '.') continue; $full = $dirname . DIRECTORY_SEPARATOR . $ent; if (is_dir($full)) { if (!$this->_harvestInstDir( $dest_prefix . DIRECTORY_SEPARATOR . $ent, $full, $built_files)) { $ret = false; break; } } else { $dest = $dest_prefix . DIRECTORY_SEPARATOR . $ent; $built_files[] = array( 'file' => $full, 'dest' => $dest, 'php_api' => $this->php_api_version, 'zend_mod_api' => $this->zend_module_api_no, 'zend_ext_api' => $this->zend_extension_api_no, ); } } closedir($d); return $ret; } /** * Build an extension from source. Runs "phpize" in the source * directory, but compiles in a temporary directory * (TMPDIR/pear-build-USER/PACKAGE-VERSION). * * @param string|PEAR_PackageFile_v* $descfile path to XML package description file, or * a PEAR_PackageFile object * * @param mixed $callback callback function used to report output, * see PEAR_Builder::_runCommand for details * * @return array an array of associative arrays with built files, * format: * array( array( 'file' => '/path/to/ext.so', * 'php_api' => YYYYMMDD, * 'zend_mod_api' => YYYYMMDD, * 'zend_ext_api' => YYYYMMDD ), * ... ) * * @access public * * @see PEAR_Builder::_runCommand */ function build($descfile, $callback = null) { if (preg_match('/(\\/|\\\\|^)([^\\/\\\\]+)?php([^\\/\\\\]+)?$/', $this->config->get('php_bin'), $matches)) { if (isset($matches[2]) && strlen($matches[2]) && trim($matches[2]) != trim($this->config->get('php_prefix'))) { $this->log(0, 'WARNING: php_bin ' . $this->config->get('php_bin') . ' appears to have a prefix ' . $matches[2] . ', but' . ' config variable php_prefix does not match'); } if (isset($matches[3]) && strlen($matches[3]) && trim($matches[3]) != trim($this->config->get('php_suffix'))) { $this->log(0, 'WARNING: php_bin ' . $this->config->get('php_bin') . ' appears to have a suffix ' . $matches[3] . ', but' . ' config variable php_suffix does not match'); } } $this->current_callback = $callback; if (PEAR_OS == "Windows") { return $this->_build_win32($descfile, $callback); } if (PEAR_OS != 'Unix') { return $this->raiseError("building extensions not supported on this platform"); } if (is_object($descfile)) { $pkg = $descfile; $descfile = $pkg->getPackageFile(); if (is_a($pkg, 'PEAR_PackageFile_v1')) { $dir = dirname($descfile); } else { $dir = $pkg->_config->get('temp_dir') . '/' . $pkg->getName(); // automatically delete at session end self::addTempFile($dir); } } else { $pf = new PEAR_PackageFile($this->config); $pkg = &$pf->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL); if (PEAR::isError($pkg)) { return $pkg; } $dir = dirname($descfile); } // Find config. outside of normal path - e.g. config.m4 foreach (array_keys($pkg->getInstallationFileList()) as $item) { if (stristr(basename($item), 'config.m4') && dirname($item) != '.') { $dir .= DIRECTORY_SEPARATOR . dirname($item); break; } } $old_cwd = getcwd(); if (!file_exists($dir) || !is_dir($dir) || !chdir($dir)) { return $this->raiseError("could not chdir to $dir"); } $vdir = $pkg->getPackage() . '-' . $pkg->getVersion(); if (is_dir($vdir)) { chdir($vdir); } $dir = getcwd(); $this->log(2, "building in $dir"); $binDir = $this->config->get('bin_dir'); if (!preg_match('@(^|:)' . preg_quote($binDir, '@') . '(:|$)@', getenv('PATH'))) { putenv('PATH=' . $binDir . ':' . getenv('PATH')); } $err = $this->_runCommand($this->config->get('php_prefix') . "phpize" . $this->config->get('php_suffix'), array(&$this, 'phpizeCallback')); if (PEAR::isError($err)) { return $err; } if (!$err) { return $this->raiseError("`phpize' failed"); } // {{{ start of interactive part $configure_command = "$dir/configure"; $phpConfigName = $this->config->get('php_prefix') . 'php-config' . $this->config->get('php_suffix'); $phpConfigPath = System::which($phpConfigName); if ($phpConfigPath !== false) { $configure_command .= ' --with-php-config=' . $phpConfigPath; } $configure_options = $pkg->getConfigureOptions(); if ($configure_options) { foreach ($configure_options as $option) { $default = array_key_exists('default', $option) ? $option['default'] : null; if (array_key_exists($option['name'], $this->_parsed_configure_options)) { $response = $this->_parsed_configure_options[$option['name']]; } else { list($response) = $this->ui->userDialog( 'build', [$option['prompt']], ['text'], [$default]); } if (substr($option['name'], 0, 5) === 'with-' && ($response === 'yes' || $response === 'autodetect')) { $configure_command .= " --{$option['name']}"; } else { $configure_command .= " --{$option['name']}=".trim($response); } } } // }}} end of interactive part // FIXME make configurable if (!$user=getenv('USER')) { $user='defaultuser'; } $tmpdir = $this->config->get('temp_dir'); $build_basedir = System::mktemp(' -t "' . $tmpdir . '" -d "pear-build-' . $user . '"'); $build_dir = "$build_basedir/$vdir"; $inst_dir = "$build_basedir/install-$vdir"; $this->log(1, "building in $build_dir"); if (is_dir($build_dir)) { System::rm(array('-rf', $build_dir)); } if (!System::mkDir(array('-p', $build_dir))) { return $this->raiseError("could not create build dir: $build_dir"); } self::addTempFile($build_dir); if (!System::mkDir(array('-p', $inst_dir))) { return $this->raiseError("could not create temporary install dir: $inst_dir"); } self::addTempFile($inst_dir); $make_command = getenv('MAKE') ? getenv('MAKE') : 'make'; $to_run = array( $configure_command, $make_command, "$make_command INSTALL_ROOT=\"$inst_dir\" install", "find \"$inst_dir\" | xargs ls -dils" ); if (!file_exists($build_dir) || !is_dir($build_dir) || !chdir($build_dir)) { return $this->raiseError("could not chdir to $build_dir"); } putenv('PHP_PEAR_VERSION=1.10.18'); foreach ($to_run as $cmd) { $err = $this->_runCommand($cmd, $callback); if (PEAR::isError($err)) { chdir($old_cwd); return $err; } if (!$err) { chdir($old_cwd); return $this->raiseError("`$cmd' failed"); } } if (!($dp = opendir("modules"))) { chdir($old_cwd); return $this->raiseError("no `modules' directory found"); } $built_files = array(); $prefix = exec($this->config->get('php_prefix') . "php-config" . $this->config->get('php_suffix') . " --prefix"); $this->_harvestInstDir($prefix, $inst_dir . DIRECTORY_SEPARATOR . $prefix, $built_files); chdir($old_cwd); return $built_files; } /** * Message callback function used when running the "phpize" * program. Extracts the API numbers used. Ignores other message * types than "cmdoutput". * * @param string $what the type of message * @param mixed $data the message * * @return void * * @access public */ function phpizeCallback($what, $data) { if ($what != 'cmdoutput') { return; } $this->log(1, rtrim($data)); if (preg_match('/You should update your .aclocal.m4/', $data)) { return; } $matches = array(); if (preg_match('/^\s+(\S[^:]+):\s+(\d{8})/', $data, $matches)) { $member = preg_replace('/[^a-z]/', '_', strtolower($matches[1])); $apino = (int)$matches[2]; if (isset($this->$member)) { $this->$member = $apino; //$msg = sprintf("%-22s : %d", $matches[1], $apino); //$this->log(1, $msg); } } } /** * Run an external command, using a message callback to report * output. The command will be run through popen and output is * reported for every line with a "cmdoutput" message with the * line string, including newlines, as payload. * * @param string $command the command to run * * @param mixed $callback (optional) function to use as message * callback * * @return bool whether the command was successful (exit code 0 * means success, any other means failure) * * @access private */ function _runCommand($command, $callback = null) { $this->log(1, "running: $command"); $pp = popen("$command 2>&1", "r"); if (!$pp) { return $this->raiseError("failed to run `$command'"); } if ($callback && $callback[0]->debug == 1) { $olddbg = $callback[0]->debug; $callback[0]->debug = 2; } while ($line = fgets($pp, 1024)) { if ($callback) { call_user_func($callback, 'cmdoutput', $line); } else { $this->log(2, rtrim($line)); } } if ($callback && isset($olddbg)) { $callback[0]->debug = $olddbg; } $exitcode = is_resource($pp) ? pclose($pp) : -1; return ($exitcode == 0); } function log($level, $msg, $append_crlf = true) { if ($this->current_callback) { if ($this->debug >= $level) { call_user_func($this->current_callback, 'output', $msg); } return; } return parent::log($level, $msg, $append_crlf); } } PK=1]T^RUUpear/PEAR/Validate.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /**#@+ * Constants for install stage */ define('PEAR_VALIDATE_INSTALLING', 1); define('PEAR_VALIDATE_UNINSTALLING', 2); // this is not bit-mapped like the others define('PEAR_VALIDATE_NORMAL', 3); define('PEAR_VALIDATE_DOWNLOADING', 4); // this is not bit-mapped like the others define('PEAR_VALIDATE_PACKAGING', 7); /**#@-*/ require_once 'PEAR/Common.php'; require_once 'PEAR/Validator/PECL.php'; /** * Validation class for package.xml - channel-level advanced validation * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_Validate { var $packageregex = _PEAR_COMMON_PACKAGE_NAME_PREG; /** * @var PEAR_PackageFile_v1|PEAR_PackageFile_v2 */ var $_packagexml; /** * @var int one of the PEAR_VALIDATE_* constants */ var $_state = PEAR_VALIDATE_NORMAL; /** * Format: ('error' => array('field' => name, 'reason' => reason), 'warning' => same) * @var array * @access private */ var $_failures = array('error' => array(), 'warning' => array()); /** * Override this method to handle validation of normal package names * @param string * @return bool * @access protected */ function _validPackageName($name) { return (bool) preg_match('/^' . $this->packageregex . '\\z/', $name); } /** * @param string package name to validate * @param string name of channel-specific validation package * @final */ function validPackageName($name, $validatepackagename = false) { if ($validatepackagename) { if (strtolower($name) == strtolower($validatepackagename)) { return (bool) preg_match('/^[a-zA-Z0-9_]+(?:\.[a-zA-Z0-9_]+)*\\z/', $name); } } return $this->_validPackageName($name); } /** * This validates a bundle name, and bundle names must conform * to the PEAR naming convention, so the method is final and static. * @param string * @final */ public static function validGroupName($name) { return (bool) preg_match('/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '\\z/', $name); } /** * Determine whether $state represents a valid stability level * @param string * @return bool * @final */ public static function validState($state) { return in_array($state, array('snapshot', 'devel', 'alpha', 'beta', 'stable')); } /** * Get a list of valid stability levels * @return array * @final */ public static function getValidStates() { return array('snapshot', 'devel', 'alpha', 'beta', 'stable'); } /** * Determine whether a version is a properly formatted version number that can be used * by version_compare * @param string * @return bool * @final */ public static function validVersion($ver) { return (bool) preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver); } /** * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 */ function setPackageFile(&$pf) { $this->_packagexml = &$pf; } /** * @access private */ function _addFailure($field, $reason) { $this->_failures['errors'][] = array('field' => $field, 'reason' => $reason); } /** * @access private */ function _addWarning($field, $reason) { $this->_failures['warnings'][] = array('field' => $field, 'reason' => $reason); } function getFailures() { $failures = $this->_failures; $this->_failures = array('warnings' => array(), 'errors' => array()); return $failures; } /** * @param int one of the PEAR_VALIDATE_* constants */ function validate($state = null) { if (!isset($this->_packagexml)) { return false; } if ($state !== null) { $this->_state = $state; } $this->_failures = array('warnings' => array(), 'errors' => array()); $this->validatePackageName(); $this->validateVersion(); $this->validateMaintainers(); $this->validateDate(); $this->validateSummary(); $this->validateDescription(); $this->validateLicense(); $this->validateNotes(); if ($this->_packagexml->getPackagexmlVersion() == '1.0') { $this->validateState(); $this->validateFilelist(); } elseif ($this->_packagexml->getPackagexmlVersion() == '2.0' || $this->_packagexml->getPackagexmlVersion() == '2.1') { $this->validateTime(); $this->validateStability(); $this->validateDeps(); $this->validateMainFilelist(); $this->validateReleaseFilelist(); //$this->validateGlobalTasks(); $this->validateChangelog(); } return !((bool) count($this->_failures['errors'])); } /** * @access protected */ function validatePackageName() { if ($this->_state == PEAR_VALIDATE_PACKAGING || $this->_state == PEAR_VALIDATE_NORMAL) { if (($this->_packagexml->getPackagexmlVersion() == '2.0' || $this->_packagexml->getPackagexmlVersion() == '2.1') && $this->_packagexml->getExtends()) { $version = $this->_packagexml->getVersion() . ''; $name = $this->_packagexml->getPackage(); $a = explode('.', $version); $test = array_shift($a); if ($test == '0') { return true; } $vlen = strlen($test); $majver = substr($name, strlen($name) - $vlen); while ($majver && !is_numeric($majver[0])) { $majver = substr($majver, 1); } if ($majver != $test) { $this->_addWarning('package', "package $name extends package " . $this->_packagexml->getExtends() . ' and so the name should ' . 'have a postfix equal to the major version like "' . $this->_packagexml->getExtends() . $test . '"'); return true; } elseif (substr($name, 0, strlen($name) - $vlen) != $this->_packagexml->getExtends()) { $this->_addWarning('package', "package $name extends package " . $this->_packagexml->getExtends() . ' and so the name must ' . 'be an extension like "' . $this->_packagexml->getExtends() . $test . '"'); return true; } } } if (!$this->validPackageName($this->_packagexml->getPackage())) { $this->_addFailure('name', 'package name "' . $this->_packagexml->getPackage() . '" is invalid'); return false; } } /** * @access protected */ function validateVersion() { if ($this->_state != PEAR_VALIDATE_PACKAGING) { if (!$this->validVersion($this->_packagexml->getVersion())) { $this->_addFailure('version', 'Invalid version number "' . $this->_packagexml->getVersion() . '"'); } return false; } $version = $this->_packagexml->getVersion(); $versioncomponents = explode('.', $version); if (count($versioncomponents) != 3) { $this->_addWarning('version', 'A version number should have 3 decimals (x.y.z)'); return true; } $name = $this->_packagexml->getPackage(); // version must be based upon state switch ($this->_packagexml->getState()) { case 'snapshot' : return true; case 'devel' : if ($versioncomponents[0] . 'a' == '0a') { return true; } if ($versioncomponents[0] == 0) { $versioncomponents[0] = '0'; $this->_addWarning('version', 'version "' . $version . '" should be "' . implode('.' ,$versioncomponents) . '"'); } else { $this->_addWarning('version', 'packages with devel stability must be < version 1.0.0'); } return true; break; case 'alpha' : case 'beta' : // check for a package that extends a package, // like Foo and Foo2 if ($this->_state == PEAR_VALIDATE_PACKAGING) { if (substr($versioncomponents[2], 1, 2) == 'rc') { $this->_addFailure('version', 'Release Candidate versions ' . 'must have capital RC, not lower-case rc'); return false; } } if (!$this->_packagexml->getExtends()) { if ($versioncomponents[0] == '1') { if ($versioncomponents[2][0] == '0') { if ($versioncomponents[2] == '0') { // version 1.*.0000 $this->_addWarning('version', 'version 1.' . $versioncomponents[1] . '.0 probably should not be alpha or beta'); return true; } elseif (strlen($versioncomponents[2]) > 1) { // version 1.*.0RC1 or 1.*.0beta24 etc. return true; } else { // version 1.*.0 $this->_addWarning('version', 'version 1.' . $versioncomponents[1] . '.0 probably should not be alpha or beta'); return true; } } else { $this->_addWarning('version', 'bugfix versions (1.3.x where x > 0) probably should ' . 'not be alpha or beta'); return true; } } elseif ($versioncomponents[0] != '0') { $this->_addWarning('version', 'major versions greater than 1 are not allowed for packages ' . 'without an tag or an identical postfix (foo2 v2.0.0)'); return true; } if ($versioncomponents[0] . 'a' == '0a') { return true; } if ($versioncomponents[0] == 0) { $versioncomponents[0] = '0'; $this->_addWarning('version', 'version "' . $version . '" should be "' . implode('.' ,$versioncomponents) . '"'); } } else { $vlen = strlen($versioncomponents[0] . ''); $majver = substr($name, strlen($name) - $vlen); while ($majver && !is_numeric($majver[0])) { $majver = substr($majver, 1); } if (($versioncomponents[0] != 0) && $majver != $versioncomponents[0]) { $this->_addWarning('version', 'first version number "' . $versioncomponents[0] . '" must match the postfix of ' . 'package name "' . $name . '" (' . $majver . ')'); return true; } if ($versioncomponents[0] == $majver) { if ($versioncomponents[2][0] == '0') { if ($versioncomponents[2] == '0') { // version 2.*.0000 $this->_addWarning('version', "version $majver." . $versioncomponents[1] . '.0 probably should not be alpha or beta'); return false; } elseif (strlen($versioncomponents[2]) > 1) { // version 2.*.0RC1 or 2.*.0beta24 etc. return true; } else { // version 2.*.0 $this->_addWarning('version', "version $majver." . $versioncomponents[1] . '.0 cannot be alpha or beta'); return true; } } else { $this->_addWarning('version', "bugfix versions ($majver.x.y where y > 0) should " . 'not be alpha or beta'); return true; } } elseif ($versioncomponents[0] != '0') { $this->_addWarning('version', "only versions 0.x.y and $majver.x.y are allowed for alpha/beta releases"); return true; } if ($versioncomponents[0] . 'a' == '0a') { return true; } if ($versioncomponents[0] == 0) { $versioncomponents[0] = '0'; $this->_addWarning('version', 'version "' . $version . '" should be "' . implode('.' ,$versioncomponents) . '"'); } } return true; break; case 'stable' : if ($versioncomponents[0] == '0') { $this->_addWarning('version', 'versions less than 1.0.0 cannot ' . 'be stable'); return true; } if (!is_numeric($versioncomponents[2])) { if (preg_match('/\d+(rc|a|alpha|b|beta)\d*/i', $versioncomponents[2])) { $this->_addWarning('version', 'version "' . $version . '" or any ' . 'RC/beta/alpha version cannot be stable'); return true; } } // check for a package that extends a package, // like Foo and Foo2 if ($this->_packagexml->getExtends()) { $vlen = strlen($versioncomponents[0] . ''); $majver = substr($name, strlen($name) - $vlen); while ($majver && !is_numeric($majver[0])) { $majver = substr($majver, 1); } if (($versioncomponents[0] != 0) && $majver != $versioncomponents[0]) { $this->_addWarning('version', 'first version number "' . $versioncomponents[0] . '" must match the postfix of ' . 'package name "' . $name . '" (' . $majver . ')'); return true; } } elseif ($versioncomponents[0] > 1) { $this->_addWarning('version', 'major version x in x.y.z may not be greater than ' . '1 for any package that does not have an tag'); } return true; break; default : return false; break; } } /** * @access protected */ function validateMaintainers() { // maintainers can only be truly validated server-side for most channels // but allow this customization for those who wish it return true; } /** * @access protected */ function validateDate() { if ($this->_state == PEAR_VALIDATE_NORMAL || $this->_state == PEAR_VALIDATE_PACKAGING) { if (!preg_match('/(\d\d\d\d)\-(\d\d)\-(\d\d)/', $this->_packagexml->getDate(), $res) || count($res) < 4 || !checkdate($res[2], $res[3], $res[1]) ) { $this->_addFailure('date', 'invalid release date "' . $this->_packagexml->getDate() . '"'); return false; } if ($this->_state == PEAR_VALIDATE_PACKAGING && $this->_packagexml->getDate() != date('Y-m-d')) { $this->_addWarning('date', 'Release Date "' . $this->_packagexml->getDate() . '" is not today'); } } return true; } /** * @access protected */ function validateTime() { if (!$this->_packagexml->getTime()) { // default of no time value set return true; } // packager automatically sets time, so only validate if pear validate is called if ($this->_state = PEAR_VALIDATE_NORMAL) { if (!preg_match('/\d\d:\d\d:\d\d/', $this->_packagexml->getTime())) { $this->_addFailure('time', 'invalid release time "' . $this->_packagexml->getTime() . '"'); return false; } $result = preg_match('|\d{2}\:\d{2}\:\d{2}|', $this->_packagexml->getTime(), $matches); if ($result === false || empty($matches)) { $this->_addFailure('time', 'invalid release time "' . $this->_packagexml->getTime() . '"'); return false; } } return true; } /** * @access protected */ function validateState() { // this is the closest to "final" php4 can get if (!PEAR_Validate::validState($this->_packagexml->getState())) { if (strtolower($this->_packagexml->getState() == 'rc')) { $this->_addFailure('state', 'RC is not a state, it is a version ' . 'postfix, use ' . $this->_packagexml->getVersion() . 'RC1, state beta'); } $this->_addFailure('state', 'invalid release state "' . $this->_packagexml->getState() . '", must be one of: ' . implode(', ', PEAR_Validate::getValidStates())); return false; } return true; } /** * @access protected */ function validateStability() { $ret = true; $packagestability = $this->_packagexml->getState(); $apistability = $this->_packagexml->getState('api'); if (!PEAR_Validate::validState($packagestability)) { $this->_addFailure('state', 'invalid release stability "' . $this->_packagexml->getState() . '", must be one of: ' . implode(', ', PEAR_Validate::getValidStates())); $ret = false; } $apistates = PEAR_Validate::getValidStates(); array_shift($apistates); // snapshot is not allowed if (!in_array($apistability, $apistates)) { $this->_addFailure('state', 'invalid API stability "' . $this->_packagexml->getState('api') . '", must be one of: ' . implode(', ', $apistates)); $ret = false; } return $ret; } /** * @access protected */ function validateSummary() { return true; } /** * @access protected */ function validateDescription() { return true; } /** * @access protected */ function validateLicense() { return true; } /** * @access protected */ function validateNotes() { return true; } /** * for package.xml 2.0 only - channels can't use package.xml 1.0 * @access protected */ function validateDependencies() { return true; } /** * for package.xml 1.0 only * @access private */ function _validateFilelist() { return true; // placeholder for now } /** * for package.xml 2.0 only * @access protected */ function validateMainFilelist() { return true; // placeholder for now } /** * for package.xml 2.0 only * @access protected */ function validateReleaseFilelist() { return true; // placeholder for now } /** * @access protected */ function validateChangelog() { return true; } /** * @access protected */ function validateFilelist() { return true; } /** * @access protected */ function validateDeps() { return true; } }PK=1]99pear/PEAR/Validator/PECL.phpnu[ * @copyright 1997-2006 The PHP Group * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a5 */ /** * This is the parent class for all validators */ require_once 'PEAR/Validate.php'; /** * Channel Validator for the pecl.php.net channel * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a5 */ class PEAR_Validator_PECL extends PEAR_Validate { function validateVersion() { if ($this->_state == PEAR_VALIDATE_PACKAGING) { $version = $this->_packagexml->getVersion(); $versioncomponents = explode('.', $version); $last = array_pop($versioncomponents); if (substr($last, 1, 2) == 'rc') { $this->_addFailure('version', 'Release Candidate versions must have ' . 'upper-case RC, not lower-case rc'); return false; } } return true; } function validatePackageName() { $ret = parent::validatePackageName(); if ($this->_packagexml->getPackageType() == 'extsrc' || $this->_packagexml->getPackageType() == 'zendextsrc') { if (strtolower($this->_packagexml->getPackage()) != strtolower($this->_packagexml->getProvidesExtension())) { $this->_addWarning('providesextension', 'package name "' . $this->_packagexml->getPackage() . '" is different from extension name "' . $this->_packagexml->getProvidesExtension() . '"'); } } return $ret; } } ?>PK=1]" jddpear/PEAR/Frontend/CLI.phpnu[ * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ /** * base class */ require_once 'PEAR/Frontend.php'; /** * Command-line Frontend for the PEAR Installer * @category pear * @package PEAR * @author Stig Bakken * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ class PEAR_Frontend_CLI extends PEAR_Frontend { /** * What type of user interface this frontend is for. * @var string * @access public */ var $type = 'CLI'; var $lp = ''; // line prefix var $params = array(); var $term = array( 'bold' => '', 'normal' => '', ); function __construct() { parent::__construct(); $term = getenv('TERM'); //(cox) $_ENV is empty for me in 4.1.1 if (function_exists('posix_isatty') && !posix_isatty(1)) { // output is being redirected to a file or through a pipe } elseif ($term) { if (preg_match('/^(xterm|vt220|linux)/', $term)) { $this->term['bold'] = sprintf("%c%c%c%c", 27, 91, 49, 109); $this->term['normal'] = sprintf("%c%c%c", 27, 91, 109); } elseif (preg_match('/^vt100/', $term)) { $this->term['bold'] = sprintf("%c%c%c%c%c%c", 27, 91, 49, 109, 0, 0); $this->term['normal'] = sprintf("%c%c%c%c%c", 27, 91, 109, 0, 0); } } elseif (OS_WINDOWS) { // XXX add ANSI codes here } } /** * @param object PEAR_Error object */ function displayError($e) { return $this->_displayLine($e->getMessage()); } /** * @param object PEAR_Error object */ function displayFatalError($eobj) { $this->displayError($eobj); if (class_exists('PEAR_Config')) { $config = &PEAR_Config::singleton(); if ($config->get('verbose') > 5) { if (function_exists('debug_print_backtrace')) { debug_print_backtrace(); exit(1); } $raised = false; foreach (debug_backtrace() as $i => $frame) { if (!$raised) { if (isset($frame['class']) && strtolower($frame['class']) == 'pear' && strtolower($frame['function']) == 'raiseerror' ) { $raised = true; } else { continue; } } $frame['class'] = !isset($frame['class']) ? '' : $frame['class']; $frame['type'] = !isset($frame['type']) ? '' : $frame['type']; $frame['function'] = !isset($frame['function']) ? '' : $frame['function']; $frame['line'] = !isset($frame['line']) ? '' : $frame['line']; $this->_displayLine("#$i: $frame[class]$frame[type]$frame[function] $frame[line]"); } } } exit(1); } /** * Instruct the runInstallScript method to skip a paramgroup that matches the * id value passed in. * * This method is useful for dynamically configuring which sections of a post-install script * will be run based on the user's setup, which is very useful for making flexible * post-install scripts without losing the cross-Frontend ability to retrieve user input * @param string */ function skipParamgroup($id) { $this->_skipSections[$id] = true; } function runPostinstallScripts(&$scripts) { foreach ($scripts as $i => $script) { $this->runInstallScript($scripts[$i]->_params, $scripts[$i]->_obj); } } /** * @param array $xml contents of postinstallscript tag * @param object $script post-installation script * @param string install|upgrade */ function runInstallScript($xml, &$script) { $this->_skipSections = array(); if (!is_array($xml) || !isset($xml['paramgroup'])) { $script->run(array(), '_default'); return; } $completedPhases = array(); if (!isset($xml['paramgroup'][0])) { $xml['paramgroup'] = array($xml['paramgroup']); } foreach ($xml['paramgroup'] as $group) { if (isset($this->_skipSections[$group['id']])) { // the post-install script chose to skip this section dynamically continue; } if (isset($group['name'])) { $paramname = explode('::', $group['name']); if ($lastgroup['id'] != $paramname[0]) { continue; } $group['name'] = $paramname[1]; if (!isset($answers)) { return; } if (isset($answers[$group['name']])) { switch ($group['conditiontype']) { case '=' : if ($answers[$group['name']] != $group['value']) { continue 2; } break; case '!=' : if ($answers[$group['name']] == $group['value']) { continue 2; } break; case 'preg_match' : if (!@preg_match('/' . $group['value'] . '/', $answers[$group['name']])) { continue 2; } break; default : return; } } } $lastgroup = $group; if (isset($group['instructions'])) { $this->_display($group['instructions']); } if (!isset($group['param'][0])) { $group['param'] = array($group['param']); } if (isset($group['param'])) { if (method_exists($script, 'postProcessPrompts')) { $prompts = $script->postProcessPrompts($group['param'], $group['id']); if (!is_array($prompts) || count($prompts) != count($group['param'])) { $this->outputData('postinstall', 'Error: post-install script did not ' . 'return proper post-processed prompts'); $prompts = $group['param']; } else { foreach ($prompts as $i => $var) { if (!is_array($var) || !isset($var['prompt']) || !isset($var['name']) || ($var['name'] != $group['param'][$i]['name']) || ($var['type'] != $group['param'][$i]['type']) ) { $this->outputData('postinstall', 'Error: post-install script ' . 'modified the variables or prompts, severe security risk. ' . 'Will instead use the defaults from the package.xml'); $prompts = $group['param']; } } } $answers = $this->confirmDialog($prompts); } else { $answers = $this->confirmDialog($group['param']); } } if ((isset($answers) && $answers) || !isset($group['param'])) { if (!isset($answers)) { $answers = array(); } array_unshift($completedPhases, $group['id']); if (!$script->run($answers, $group['id'])) { $script->run($completedPhases, '_undoOnError'); return; } } else { $script->run($completedPhases, '_undoOnError'); return; } } } /** * Ask for user input, confirm the answers and continue until the user is satisfied * @param array an array of arrays, format array('name' => 'paramname', 'prompt' => * 'text to display', 'type' => 'string'[, default => 'default value']) * @return array */ function confirmDialog($params) { $answers = $prompts = $types = array(); foreach ($params as $param) { $prompts[$param['name']] = $param['prompt']; $types[$param['name']] = $param['type']; $answers[$param['name']] = isset($param['default']) ? $param['default'] : ''; } $tried = false; do { if ($tried) { $i = 1; foreach ($answers as $var => $value) { if (!strlen($value)) { echo $this->bold("* Enter an answer for #" . $i . ": ({$prompts[$var]})\n"); } $i++; } } $answers = $this->userDialog('', $prompts, $types, $answers); $tried = true; } while (is_array($answers) && count(array_filter($answers)) != count($prompts)); return $answers; } function userDialog($command, $prompts, $types = array(), $defaults = array(), $screensize = 20) { if (!is_array($prompts)) { return array(); } $testprompts = array_keys($prompts); $result = $defaults; reset($prompts); if (count($prompts) === 1) { foreach ($prompts as $key => $prompt) { $type = $types[$key]; $default = @$defaults[$key]; print "$prompt "; if ($default) { print "[$default] "; } print ": "; $line = fgets(STDIN, 2048); $result[$key] = ($default && trim($line) == '') ? $default : trim($line); } return $result; } $first_run = true; while (true) { $descLength = max(array_map('strlen', $prompts)); $descFormat = "%-{$descLength}s"; $last = count($prompts); $i = 0; foreach ($prompts as $n => $var) { $res = isset($result[$n]) ? $result[$n] : null; printf("%2d. $descFormat : %s\n", ++$i, $prompts[$n], $res); } print "\n1-$last, 'all', 'abort', or Enter to continue: "; $tmp = trim(fgets(STDIN, 1024)); if (empty($tmp)) { break; } if ($tmp == 'abort') { return false; } if (isset($testprompts[(int)$tmp - 1])) { $var = $testprompts[(int)$tmp - 1]; $desc = $prompts[$var]; $current = @$result[$var]; print "$desc [$current] : "; $tmp = trim(fgets(STDIN, 1024)); if ($tmp !== '') { $result[$var] = $tmp; } } elseif ($tmp == 'all') { foreach ($prompts as $var => $desc) { $current = $result[$var]; print "$desc [$current] : "; $tmp = trim(fgets(STDIN, 1024)); if (trim($tmp) !== '') { $result[$var] = trim($tmp); } } } $first_run = false; } return $result; } function userConfirm($prompt, $default = 'yes') { if (PHP_VERSION_ID < 70000) { trigger_error("PEAR_Frontend_CLI::userConfirm not yet converted", E_USER_ERROR); } else { throw new Error('PEAR_Frontend_CLI::userConfirm not yet converted'); } static $positives = array('y', 'yes', 'on', '1'); static $negatives = array('n', 'no', 'off', '0'); print "$this->lp$prompt [$default] : "; $fp = fopen("php://stdin", "r"); $line = fgets($fp, 2048); fclose($fp); $answer = strtolower(trim($line)); if (empty($answer)) { $answer = $default; } if (in_array($answer, $positives)) { return true; } if (in_array($answer, $negatives)) { return false; } if (in_array($default, $positives)) { return true; } return false; } function outputData($data, $command = '_default') { switch ($command) { case 'channel-info': foreach ($data as $type => $section) { if ($type == 'main') { $section['data'] = array_values($section['data']); } $this->outputData($section); } break; case 'install': case 'upgrade': case 'upgrade-all': if (is_array($data) && isset($data['release_warnings'])) { $this->_displayLine(''); $this->_startTable(array( 'border' => false, 'caption' => 'Release Warnings' )); $this->_tableRow(array($data['release_warnings']), null, array(1 => array('wrap' => 55))); $this->_endTable(); $this->_displayLine(''); } $this->_displayLine(is_array($data) ? $data['data'] : $data); break; case 'search': $this->_startTable($data); if (isset($data['headline']) && is_array($data['headline'])) { $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55))); } $packages = array(); foreach($data['data'] as $category) { foreach($category as $name => $pkg) { $packages[$pkg[0]] = $pkg; } } $p = array_keys($packages); natcasesort($p); foreach ($p as $name) { $this->_tableRow($packages[$name], null, array(1 => array('wrap' => 55))); } $this->_endTable(); break; case 'list-all': if (!isset($data['data'])) { $this->_displayLine('No packages in channel'); break; } $this->_startTable($data); if (isset($data['headline']) && is_array($data['headline'])) { $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55))); } $packages = array(); foreach($data['data'] as $category) { foreach($category as $name => $pkg) { $packages[$pkg[0]] = $pkg; } } $p = array_keys($packages); natcasesort($p); foreach ($p as $name) { $pkg = $packages[$name]; unset($pkg[4], $pkg[5]); $this->_tableRow($pkg, null, array(1 => array('wrap' => 55))); } $this->_endTable(); break; case 'config-show': $data['border'] = false; $opts = array( 0 => array('wrap' => 30), 1 => array('wrap' => 20), 2 => array('wrap' => 35) ); $this->_startTable($data); if (isset($data['headline']) && is_array($data['headline'])) { $this->_tableRow($data['headline'], array('bold' => true), $opts); } foreach ($data['data'] as $group) { foreach ($group as $value) { if ($value[2] == '') { $value[2] = ""; } $this->_tableRow($value, null, $opts); } } $this->_endTable(); break; case 'remote-info': $d = $data; $data = array( 'caption' => 'Package details:', 'border' => false, 'data' => array( array("Latest", $data['stable']), array("Installed", $data['installed']), array("Package", $data['name']), array("License", $data['license']), array("Category", $data['category']), array("Summary", $data['summary']), array("Description", $data['description']), ), ); if (isset($d['deprecated']) && $d['deprecated']) { $conf = &PEAR_Config::singleton(); $reg = $conf->getRegistry(); $name = $reg->parsedPackageNameToString($d['deprecated'], true); $data['data'][] = array('Deprecated! use', $name); } default: { if (is_array($data)) { $this->_startTable($data); $count = count($data['data'][0]); if ($count == 2) { $opts = array(0 => array('wrap' => 25), 1 => array('wrap' => 48) ); } elseif ($count == 3) { $opts = array(0 => array('wrap' => 30), 1 => array('wrap' => 20), 2 => array('wrap' => 35) ); } else { $opts = null; } if (isset($data['headline']) && is_array($data['headline'])) { $this->_tableRow($data['headline'], array('bold' => true), $opts); } if (is_array($data['data'])) { foreach($data['data'] as $row) { $this->_tableRow($row, null, $opts); } } else { $this->_tableRow(array($data['data']), null, $opts); } $this->_endTable(); } else { $this->_displayLine($data); } } } } function log($text, $append_crlf = true) { if ($append_crlf) { return $this->_displayLine($text); } return $this->_display($text); } function bold($text) { if (empty($this->term['bold'])) { return strtoupper($text); } return $this->term['bold'] . $text . $this->term['normal']; } function _displayHeading($title) { print $this->lp.$this->bold($title)."\n"; print $this->lp.str_repeat("=", strlen($title))."\n"; } function _startTable($params = array()) { $params['table_data'] = array(); $params['widest'] = array(); // indexed by column $params['highest'] = array(); // indexed by row $params['ncols'] = 0; $this->params = $params; } function _tableRow($columns, $rowparams = array(), $colparams = array()) { $highest = 1; for ($i = 0; $i < count($columns); $i++) { $col = &$columns[$i]; if (isset($colparams[$i]) && !empty($colparams[$i]['wrap'])) { $col = wordwrap($col, $colparams[$i]['wrap']); } if (strpos($col, "\n") !== false) { $multiline = explode("\n", $col); $w = 0; foreach ($multiline as $n => $line) { $len = strlen($line); if ($len > $w) { $w = $len; } } $lines = count($multiline); } else { $w = strlen($col); } if (isset($this->params['widest'][$i])) { if ($w > $this->params['widest'][$i]) { $this->params['widest'][$i] = $w; } } else { $this->params['widest'][$i] = $w; } $tmp = count_chars($columns[$i], 1); // handle unix, mac and windows formats $lines = (isset($tmp[10]) ? $tmp[10] : (isset($tmp[13]) ? $tmp[13] : 0)) + 1; if ($lines > $highest) { $highest = $lines; } } if (count($columns) > $this->params['ncols']) { $this->params['ncols'] = count($columns); } $new_row = array( 'data' => $columns, 'height' => $highest, 'rowparams' => $rowparams, 'colparams' => $colparams, ); $this->params['table_data'][] = $new_row; } function _endTable() { extract($this->params); if (!empty($caption)) { $this->_displayHeading($caption); } if (count($table_data) === 0) { return; } if (!isset($width)) { $width = $widest; } else { for ($i = 0; $i < $ncols; $i++) { if (!isset($width[$i])) { $width[$i] = $widest[$i]; } } } $border = false; if (empty($border)) { $cellstart = ''; $cellend = ' '; $rowend = ''; $padrowend = false; $borderline = ''; } else { $cellstart = '| '; $cellend = ' '; $rowend = '|'; $padrowend = true; $borderline = '+'; foreach ($width as $w) { $borderline .= str_repeat('-', $w + strlen($cellstart) + strlen($cellend) - 1); $borderline .= '+'; } } if ($borderline) { $this->_displayLine($borderline); } for ($i = 0; $i < count($table_data); $i++) { extract($table_data[$i]); if (!is_array($rowparams)) { $rowparams = array(); } if (!is_array($colparams)) { $colparams = array(); } $rowlines = array(); if ($height > 1) { for ($c = 0; $c < count($data); $c++) { $rowlines[$c] = preg_split('/(\r?\n|\r)/', $data[$c]); if (count($rowlines[$c]) < $height) { $rowlines[$c] = array_pad($rowlines[$c], $height, ''); } } } else { for ($c = 0; $c < count($data); $c++) { $rowlines[$c] = array($data[$c]); } } for ($r = 0; $r < $height; $r++) { $rowtext = ''; for ($c = 0; $c < count($data); $c++) { if (isset($colparams[$c])) { $attribs = array_merge($rowparams, $colparams); } else { $attribs = $rowparams; } $w = isset($width[$c]) ? $width[$c] : 0; //$cell = $data[$c]; $cell = $rowlines[$c][$r]; $l = strlen($cell); if ($l > $w) { $cell = substr($cell, 0, $w); } if (isset($attribs['bold'])) { $cell = $this->bold($cell); } if ($l < $w) { // not using str_pad here because we may // add bold escape characters to $cell $cell .= str_repeat(' ', $w - $l); } $rowtext .= $cellstart . $cell . $cellend; } if (!$border) { $rowtext = rtrim($rowtext); } $rowtext .= $rowend; $this->_displayLine($rowtext); } } if ($borderline) { $this->_displayLine($borderline); } } function _displayLine($text) { print "$this->lp$text\n"; } function _display($text) { print $text; } } PK=1]'pear/PEAR/Task/Postinstallscript/rw.phpnu[ - read/write version * * PHP versions 4 and 5 * * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a10 */ /** * Base class */ require_once 'PEAR/Task/Postinstallscript.php'; /** * Abstracts the postinstallscript file task xml. * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a10 */ class PEAR_Task_Postinstallscript_rw extends PEAR_Task_Postinstallscript { /** * parent package file object * * @var PEAR_PackageFile_v2_rw */ public $_pkg; /** * Enter description here... * * @param PEAR_PackageFile_v2_rw $pkg Package * @param PEAR_Config $config Config * @param PEAR_Frontend $logger Logger * @param array $fileXml XML * * @return PEAR_Task_Postinstallscript_rw */ function __construct(&$pkg, &$config, &$logger, $fileXml) { parent::__construct($config, $logger, PEAR_TASK_PACKAGE); $this->_contents = $fileXml; $this->_pkg = &$pkg; $this->_params = array(); } public function validate() { return $this->validateXml($this->_pkg, $this->_params, $this->config, $this->_contents); } public function getName() { return 'postinstallscript'; } /** * add a simple to the post-install script * * Order is significant, so call this method in the same * sequence the users should see the paramgroups. The $params * parameter should either be the result of a call to {@link getParam()} * or an array of calls to getParam(). * * Use {@link addConditionTypeGroup()} to add a containing * a tag * * @param string $id id as seen by the script * @param array|false $params array of getParam() calls, or false for no params * @param string|false $instructions */ public function addParamGroup($id, $params = false, $instructions = false) { if ($params && isset($params[0]) && !isset($params[1])) { $params = $params[0]; } $stuff = array( $this->_pkg->getTasksNs().':id' => $id, ); if ($instructions) { $stuff[$this->_pkg->getTasksNs().':instructions'] = $instructions; } if ($params) { $stuff[$this->_pkg->getTasksNs().':param'] = $params; } $this->_params[$this->_pkg->getTasksNs().':paramgroup'][] = $stuff; } /** * Add a complex to the post-install script with conditions * * This inserts a with * * Order is significant, so call this method in the same * sequence the users should see the paramgroups. The $params * parameter should either be the result of a call to {@link getParam()} * or an array of calls to getParam(). * * Use {@link addParamGroup()} to add a simple * * @param string $id id as seen by the script * @param string $oldgroup id of the section referenced by * * @param string $param name of the from the older section referenced * by * @param string $value value to match of the parameter * @param string $conditiontype one of '=', '!=', 'preg_match' * @param array|false $params array of getParam() calls, or false for no params * @param string|false $instructions */ public function addConditionTypeGroup($id, $oldgroup, $param, $value, $conditiontype = '=', $params = false, $instructions = false ) { if ($params && isset($params[0]) && !isset($params[1])) { $params = $params[0]; } $stuff = array( $this->_pkg->getTasksNs().':id' => $id, ); if ($instructions) { $stuff[$this->_pkg->getTasksNs().':instructions'] = $instructions; } $stuff[$this->_pkg->getTasksNs().':name'] = $oldgroup.'::'.$param; $stuff[$this->_pkg->getTasksNs().':conditiontype'] = $conditiontype; $stuff[$this->_pkg->getTasksNs().':value'] = $value; if ($params) { $stuff[$this->_pkg->getTasksNs().':param'] = $params; } $this->_params[$this->_pkg->getTasksNs().':paramgroup'][] = $stuff; } public function getXml() { return $this->_params; } /** * Use to set up a param tag for use in creating a paramgroup * * @param mixed $name Name of parameter * @param mixed $prompt Prompt * @param string $type Type, defaults to 'string' * @param mixed $default Default value * * @return array */ public function getParam( $name, $prompt, $type = 'string', $default = null ) { if ($default !== null) { return array( $this->_pkg->getTasksNs().':name' => $name, $this->_pkg->getTasksNs().':prompt' => $prompt, $this->_pkg->getTasksNs().':type' => $type, $this->_pkg->getTasksNs().':default' => $default, ); } return array( $this->_pkg->getTasksNs().':name' => $name, $this->_pkg->getTasksNs().':prompt' => $prompt, $this->_pkg->getTasksNs().':type' => $type, ); } } PK=1]]6pear/PEAR/Task/Replace/rw.phpnu[ - read/write version * * PHP versions 4 and 5 * * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a10 */ /** * Base class */ require_once 'PEAR/Task/Replace.php'; /** * Abstracts the replace task xml. * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a10 */ class PEAR_Task_Replace_rw extends PEAR_Task_Replace { public function __construct(&$pkg, &$config, &$logger, $fileXml) { parent::__construct($config, $logger, PEAR_TASK_PACKAGE); $this->_contents = $fileXml; $this->_pkg = &$pkg; $this->_params = array(); } public function validate() { return $this->validateXml($this->_pkg, $this->_params, $this->config, $this->_contents); } public function setInfo($from, $to, $type) { $this->_params = array('attribs' => array('from' => $from, 'to' => $to, 'type' => $type)); } public function getName() { return 'replace'; } public function getXml() { return $this->_params; } } PK=1]6pear/PEAR/Task/Replace.phpnu[ * * PHP versions 4 and 5 * * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * Base class */ require_once 'PEAR/Task/Common.php'; /** * Implements the replace file task. * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_Task_Replace extends PEAR_Task_Common { public $type = 'simple'; public $phase = PEAR_TASK_PACKAGEANDINSTALL; public $_replacements; /** * Validate the raw xml at parsing-time. * * @param PEAR_PackageFile_v2 * @param array raw, parsed xml * @param PEAR_Config */ public static function validateXml($pkg, $xml, $config, $fileXml) { if (!isset($xml['attribs'])) { return array(PEAR_TASK_ERROR_NOATTRIBS); } if (!isset($xml['attribs']['type'])) { return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'type'); } if (!isset($xml['attribs']['to'])) { return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'to'); } if (!isset($xml['attribs']['from'])) { return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'from'); } if ($xml['attribs']['type'] == 'pear-config') { if (!in_array($xml['attribs']['to'], $config->getKeys())) { return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'], $config->getKeys(), ); } } elseif ($xml['attribs']['type'] == 'php-const') { if (defined($xml['attribs']['to'])) { return true; } else { return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'], array('valid PHP constant'), ); } } elseif ($xml['attribs']['type'] == 'package-info') { if (in_array( $xml['attribs']['to'], array('name', 'summary', 'channel', 'notes', 'extends', 'description', 'release_notes', 'license', 'release-license', 'license-uri', 'version', 'api-version', 'state', 'api-state', 'release_date', 'date', 'time', ) )) { return true; } else { return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'], array('name', 'summary', 'channel', 'notes', 'extends', 'description', 'release_notes', 'license', 'release-license', 'license-uri', 'version', 'api-version', 'state', 'api-state', 'release_date', 'date', 'time', ), ); } } else { return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'type', $xml['attribs']['type'], array('pear-config', 'package-info', 'php-const'), ); } return true; } /** * Initialize a task instance with the parameters * @param array raw, parsed xml * @param unused * @param unused */ public function init($xml, $attribs, $lastVersion = null) { $this->_replacements = isset($xml['attribs']) ? array($xml) : $xml; } /** * Do a package.xml 1.0 replacement, with additional package-info fields available * * See validateXml() source for the complete list of allowed fields * * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 * @param string file contents * @param string the eventual final file location (informational only) * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail * (use $this->throwError), otherwise return the new contents */ public function startSession($pkg, $contents, $dest) { $subst_from = $subst_to = array(); foreach ($this->_replacements as $a) { $a = $a['attribs']; $to = ''; if ($a['type'] == 'pear-config') { if ($this->installphase == PEAR_TASK_PACKAGE) { return false; } if ($a['to'] == 'master_server') { $chan = $this->registry->getChannel($pkg->getChannel()); if (!PEAR::isError($chan)) { $to = $chan->getServer(); } else { $this->logger->log(0, "$dest: invalid pear-config replacement: $a[to]"); return false; } } else { if ($this->config->isDefinedLayer('ftp')) { // try the remote config file first $to = $this->config->get($a['to'], 'ftp', $pkg->getChannel()); if (is_null($to)) { // then default to local $to = $this->config->get($a['to'], null, $pkg->getChannel()); } } else { $to = $this->config->get($a['to'], null, $pkg->getChannel()); } } if (is_null($to)) { $this->logger->log(0, "$dest: invalid pear-config replacement: $a[to]"); return false; } } elseif ($a['type'] == 'php-const') { if ($this->installphase == PEAR_TASK_PACKAGE) { return false; } if (defined($a['to'])) { $to = constant($a['to']); } else { $this->logger->log(0, "$dest: invalid php-const replacement: $a[to]"); return false; } } else { if ($t = $pkg->packageInfo($a['to'])) { $to = $t; } else { $this->logger->log(0, "$dest: invalid package-info replacement: $a[to]"); return false; } } if (!is_null($to)) { $subst_from[] = $a['from']; $subst_to[] = $to; } } $this->logger->log( 3, "doing ".sizeof($subst_from). " substitution(s) for $dest" ); if (sizeof($subst_from)) { $contents = str_replace($subst_from, $subst_to, $contents); } return $contents; } } PK=1]`x9x9$pear/PEAR/Task/Postinstallscript.phpnu[ * * PHP versions 4 and 5 * * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * Base class */ require_once 'PEAR/Task/Common.php'; /** * Implements the postinstallscript file task. * * Note that post-install scripts are handled separately from installation, by the * "pear run-scripts" command * * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_Task_Postinstallscript extends PEAR_Task_Common { public $type = 'script'; public $_class; public $_params; public $_obj; /** * * @var PEAR_PackageFile_v2 */ public $_pkg; public $_contents; public $phase = PEAR_TASK_INSTALL; /** * Validate the raw xml at parsing-time. * * This also attempts to validate the script to make sure it meets the criteria * for a post-install script * * @param PEAR_PackageFile_v2 * @param array The XML contents of the tag * @param PEAR_Config * @param array the entire parsed tag */ public static function validateXml($pkg, $xml, $config, $fileXml) { if ($fileXml['role'] != 'php') { return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'. $fileXml['name'].'" must be role="php"', ); } PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $file = $pkg->getFileContents($fileXml['name']); if (PEAR::isError($file)) { PEAR::popErrorHandling(); return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'. $fileXml['name'].'" is not valid: '. $file->getMessage(), ); } elseif ($file === null) { return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'. $fileXml['name'].'" could not be retrieved for processing!', ); } else { $analysis = $pkg->analyzeSourceCode($file, true); if (!$analysis) { PEAR::popErrorHandling(); $warnings = ''; foreach ($pkg->getValidationWarnings() as $warn) { $warnings .= $warn['message']."\n"; } return array(PEAR_TASK_ERROR_INVALID, 'Analysis of post-install script "'. $fileXml['name'].'" failed: '.$warnings, ); } if (count($analysis['declared_classes']) != 1) { PEAR::popErrorHandling(); return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'. $fileXml['name'].'" must declare exactly 1 class', ); } $class = $analysis['declared_classes'][0]; if ($class != str_replace( array('/', '.php'), array('_', ''), $fileXml['name'] ).'_postinstall') { PEAR::popErrorHandling(); return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'. $fileXml['name'].'" class "'.$class.'" must be named "'. str_replace( array('/', '.php'), array('_', ''), $fileXml['name'] ).'_postinstall"', ); } if (!isset($analysis['declared_methods'][$class])) { PEAR::popErrorHandling(); return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'. $fileXml['name'].'" must declare methods init() and run()', ); } $methods = array('init' => 0, 'run' => 1); foreach ($analysis['declared_methods'][$class] as $method) { if (isset($methods[$method])) { unset($methods[$method]); } } if (count($methods)) { PEAR::popErrorHandling(); return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'. $fileXml['name'].'" must declare methods init() and run()', ); } } PEAR::popErrorHandling(); $definedparams = array(); $tasksNamespace = $pkg->getTasksNs().':'; if (!isset($xml[$tasksNamespace.'paramgroup']) && isset($xml['paramgroup'])) { // in order to support the older betas, which did not expect internal tags // to also use the namespace $tasksNamespace = ''; } if (isset($xml[$tasksNamespace.'paramgroup'])) { $params = $xml[$tasksNamespace.'paramgroup']; if (!is_array($params) || !isset($params[0])) { $params = array($params); } foreach ($params as $param) { if (!isset($param[$tasksNamespace.'id'])) { return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'. $fileXml['name'].'" must have '. 'an '.$tasksNamespace.'id> tag', ); } if (isset($param[$tasksNamespace.'name'])) { if (!in_array($param[$tasksNamespace.'name'], $definedparams)) { return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'. $fileXml['name'].'" '.$tasksNamespace. 'paramgroup> id "'.$param[$tasksNamespace.'id']. '" parameter "'.$param[$tasksNamespace.'name']. '" has not been previously defined', ); } if (!isset($param[$tasksNamespace.'conditiontype'])) { return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'. $fileXml['name'].'" '.$tasksNamespace. 'paramgroup> id "'.$param[$tasksNamespace.'id']. '" must have a '.$tasksNamespace. 'conditiontype> tag containing either "=", '. '"!=", or "preg_match"', ); } if (!in_array( $param[$tasksNamespace.'conditiontype'], array('=', '!=', 'preg_match') )) { return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'. $fileXml['name'].'" '.$tasksNamespace. 'paramgroup> id "'.$param[$tasksNamespace.'id']. '" must have a '.$tasksNamespace. 'conditiontype> tag containing either "=", '. '"!=", or "preg_match"', ); } if (!isset($param[$tasksNamespace.'value'])) { return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'. $fileXml['name'].'" '.$tasksNamespace. 'paramgroup> id "'.$param[$tasksNamespace.'id']. '" must have a '.$tasksNamespace. 'value> tag containing expected parameter value', ); } } if (isset($param[$tasksNamespace.'instructions'])) { if (!is_string($param[$tasksNamespace.'instructions'])) { return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'. $fileXml['name'].'" '.$tasksNamespace. 'paramgroup> id "'.$param[$tasksNamespace.'id']. '" '.$tasksNamespace.'instructions> must be simple text', ); } } if (!isset($param[$tasksNamespace.'param'])) { continue; // is no longer required } $subparams = $param[$tasksNamespace.'param']; if (!is_array($subparams) || !isset($subparams[0])) { $subparams = array($subparams); } foreach ($subparams as $subparam) { if (!isset($subparam[$tasksNamespace.'name'])) { return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'. $fileXml['name'].'" parameter for '. $tasksNamespace.'paramgroup> id "'. $param[$tasksNamespace.'id'].'" must have '. 'a '.$tasksNamespace.'name> tag', ); } if (!preg_match( '/[a-zA-Z0-9]+/', $subparam[$tasksNamespace.'name'] )) { return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'. $fileXml['name'].'" parameter "'. $subparam[$tasksNamespace.'name']. '" for '.$tasksNamespace.'paramgroup> id "'. $param[$tasksNamespace.'id']. '" is not a valid name. Must contain only alphanumeric characters', ); } if (!isset($subparam[$tasksNamespace.'prompt'])) { return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'. $fileXml['name'].'" parameter "'. $subparam[$tasksNamespace.'name']. '" for '.$tasksNamespace.'paramgroup> id "'. $param[$tasksNamespace.'id']. '" must have a '.$tasksNamespace.'prompt> tag', ); } if (!isset($subparam[$tasksNamespace.'type'])) { return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'. $fileXml['name'].'" parameter "'. $subparam[$tasksNamespace.'name']. '" for '.$tasksNamespace.'paramgroup> id "'. $param[$tasksNamespace.'id']. '" must have a '.$tasksNamespace.'type> tag', ); } $definedparams[] = $param[$tasksNamespace.'id'].'::'. $subparam[$tasksNamespace.'name']; } } } return true; } /** * Initialize a task instance with the parameters * @param array $xml raw, parsed xml * @param array $fileattribs attributes from the tag containing * this task * @param string|null $lastversion last installed version of this package, * if any (useful for upgrades) */ public function init($xml, $fileattribs, $lastversion) { $this->_class = str_replace('/', '_', $fileattribs['name']); $this->_filename = $fileattribs['name']; $this->_class = str_replace('.php', '', $this->_class).'_postinstall'; $this->_params = $xml; $this->_lastversion = $lastversion; } /** * Strip the tasks: namespace from internal params * * @access private */ public function _stripNamespace($params = null) { if ($params === null) { $params = array(); if (!is_array($this->_params)) { return; } foreach ($this->_params as $i => $param) { if (is_array($param)) { $param = $this->_stripNamespace($param); } $params[str_replace($this->_pkg->getTasksNs().':', '', $i)] = $param; } $this->_params = $params; } else { $newparams = array(); foreach ($params as $i => $param) { if (is_array($param)) { $param = $this->_stripNamespace($param); } $newparams[str_replace($this->_pkg->getTasksNs().':', '', $i)] = $param; } return $newparams; } } /** * Unlike other tasks, the installed file name is passed in instead of the * file contents, because this task is handled post-installation * * @param mixed $pkg PEAR_PackageFile_v1|PEAR_PackageFile_v2 * @param string $contents file name * @param string $dest the eventual final file location (informational only) * * @return bool|PEAR_Error false to skip this file, PEAR_Error to fail * (use $this->throwError) */ public function startSession($pkg, $contents, $dest) { if ($this->installphase != PEAR_TASK_INSTALL) { return false; } // remove the tasks: namespace if present $this->_pkg = $pkg; $this->_stripNamespace(); $this->logger->log( 0, 'Including external post-installation script "'. $contents.'" - any errors are in this script' ); include_once $contents; if (class_exists($this->_class)) { $this->logger->log(0, 'Inclusion succeeded'); } else { return $this->throwError( 'init of post-install script class "'.$this->_class .'" failed' ); } $this->_obj = new $this->_class(); $this->logger->log(1, 'running post-install script "'.$this->_class.'->init()"'); PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $res = $this->_obj->init($this->config, $pkg, $this->_lastversion); PEAR::popErrorHandling(); if ($res) { $this->logger->log(0, 'init succeeded'); } else { return $this->throwError( 'init of post-install script "'.$this->_class. '->init()" failed' ); } $this->_contents = $contents; return true; } /** * No longer used * * @see PEAR_PackageFile_v2::runPostinstallScripts() * @param array an array of tasks * @param string install or upgrade * @access protected */ public static function run($tasks) { } } PK=1]Ņepear/PEAR/Task/Unixeol/rw.phpnu[ - read/write version * * PHP versions 4 and 5 * * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a10 */ /** * Base class */ require_once 'PEAR/Task/Unixeol.php'; /** * Abstracts the unixeol task xml. * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a10 */ class PEAR_Task_Unixeol_rw extends PEAR_Task_Unixeol { function __construct(&$pkg, &$config, &$logger, $fileXml) { parent::__construct($config, $logger, PEAR_TASK_PACKAGE); $this->_contents = $fileXml; $this->_pkg = &$pkg; $this->_params = array(); } public function validate() { return true; } public function getName() { return 'unixeol'; } public function getXml() { return ''; } } ?> PK=1]J€pear/PEAR/Task/Unixeol.phpnu[ * * PHP versions 4 and 5 * * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * Base class */ require_once 'PEAR/Task/Common.php'; /** * Implements the unix line endings file task. * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_Task_Unixeol extends PEAR_Task_Common { public $type = 'simple'; public $phase = PEAR_TASK_PACKAGE; public $_replacements; /** * Validate the raw xml at parsing-time. * * @param PEAR_PackageFile_v2 * @param array raw, parsed xml * @param PEAR_Config */ public static function validateXml($pkg, $xml, $config, $fileXml) { if ($xml != '') { return array(PEAR_TASK_ERROR_INVALID, 'no attributes allowed'); } return true; } /** * Initialize a task instance with the parameters * @param array raw, parsed xml * @param unused * @param unused */ public function init($xml, $attribs, $lastVersion = null) { } /** * Replace all line endings with line endings customized for the current OS * * See validateXml() source for the complete list of allowed fields * * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 * @param string file contents * @param string the eventual final file location (informational only) * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail * (use $this->throwError), otherwise return the new contents */ public function startSession($pkg, $contents, $dest) { $this->logger->log(3, "replacing all line endings with \\n in $dest"); return preg_replace("/\r\n|\n\r|\r|\n/", "\n", $contents); } } PK=1]?pear/PEAR/Task/Windowseol.phpnu[ * * PHP versions 4 and 5 * * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * Base class */ require_once 'PEAR/Task/Common.php'; /** * Implements the windows line endsings file task. * * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_Task_Windowseol extends PEAR_Task_Common { public $type = 'simple'; public $phase = PEAR_TASK_PACKAGE; public $_replacements; /** * Validate the raw xml at parsing-time. * * @param PEAR_PackageFile_v2 * @param array raw, parsed xml * @param PEAR_Config */ public static function validateXml($pkg, $xml, $config, $fileXml) { if ($xml != '') { return array(PEAR_TASK_ERROR_INVALID, 'no attributes allowed'); } return true; } /** * Initialize a task instance with the parameters * @param array raw, parsed xml * @param unused * @param unused */ public function init($xml, $attribs, $lastVersion = null) { } /** * Replace all line endings with windows line endings * * See validateXml() source for the complete list of allowed fields * * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 * @param string file contents * @param string the eventual final file location (informational only) * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail * (use $this->throwError), otherwise return the new contents */ public function startSession($pkg, $contents, $dest) { $this->logger->log(3, "replacing all line endings with \\r\\n in $dest"); return preg_replace("/\r\n|\n\r|\r|\n/", "\r\n", $contents); } } PK=1]d~_kpear/PEAR/Task/Common.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /**#@+ * Error codes for task validation routines */ define('PEAR_TASK_ERROR_NOATTRIBS', 1); define('PEAR_TASK_ERROR_MISSING_ATTRIB', 2); define('PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE', 3); define('PEAR_TASK_ERROR_INVALID', 4); /**#@-*/ define('PEAR_TASK_PACKAGE', 1); define('PEAR_TASK_INSTALL', 2); define('PEAR_TASK_PACKAGEANDINSTALL', 3); /** * A task is an operation that manipulates the contents of a file. * * Simple tasks operate on 1 file. Multiple tasks are executed after all files have been * processed and installed, and are designed to operate on all files containing the task. * The Post-install script task simply takes advantage of the fact that it will be run * after installation, replace is a simple task. * * Combining tasks is possible, but ordering is significant. * * * * * * * This will first replace any instance of @data-dir@ in the test.php file * with the path to the current data directory. Then, it will include the * test.php file and run the script it contains to configure the package post-installation. * * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 * @abstract */ class PEAR_Task_Common { /** * Valid types for this version are 'simple' and 'multiple' * * - simple tasks operate on the contents of a file and write out changes to disk * - multiple tasks operate on the contents of many files and write out the * changes directly to disk * * Child task classes must override this property. * * @access protected */ protected $type = 'simple'; /** * Determines which install phase this task is executed under */ public $phase = PEAR_TASK_INSTALL; /** * @access protected */ protected $config; /** * @access protected */ protected $registry; /** * @access protected */ public $logger; /** * @access protected */ protected $installphase; /** * @param PEAR_Config * @param PEAR_Common */ function __construct(&$config, &$logger, $phase) { $this->config = &$config; $this->registry = &$config->getRegistry(); $this->logger = &$logger; $this->installphase = $phase; if ($this->type == 'multiple') { $GLOBALS['_PEAR_TASK_POSTINSTANCES'][get_class($this)][] = &$this; } } /** * Validate the basic contents of a task tag. * * @param PEAR_PackageFile_v2 * @param array * @param PEAR_Config * @param array the entire parsed tag * * @return true|array On error, return an array in format: * array(PEAR_TASK_ERROR_???[, param1][, param2][, ...]) * * For PEAR_TASK_ERROR_MISSING_ATTRIB, pass the attribute name in * For PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, pass the attribute name and * an array of legal values in * * @abstract */ public static function validateXml($pkg, $xml, $config, $fileXml) { } /** * Initialize a task instance with the parameters * * @param array raw, parsed xml * @param array attributes from the tag containing this task * @param string|null last installed version of this package * @abstract */ public function init($xml, $fileAttributes, $lastVersion) { } /** * Begin a task processing session. All multiple tasks will be processed * after each file has been successfully installed, all simple tasks should * perform their task here and return any errors using the custom * throwError() method to allow forward compatibility * * This method MUST NOT write out any changes to disk * * @param PEAR_PackageFile_v2 * @param string file contents * @param string the eventual final file location (informational only) * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail * (use $this->throwError), otherwise return the new contents * @abstract */ public function startSession($pkg, $contents, $dest) { } /** * This method is used to process each of the tasks for a particular * multiple class type. Simple tasks need not implement this method. * * @param array an array of tasks * @access protected */ public static function run($tasks) { } /** * @final */ public static function hasPostinstallTasks() { return isset($GLOBALS['_PEAR_TASK_POSTINSTANCES']); } /** * @final */ public static function runPostinstallTasks() { foreach ($GLOBALS['_PEAR_TASK_POSTINSTANCES'] as $class => $tasks) { $err = call_user_func( array($class, 'run'), $GLOBALS['_PEAR_TASK_POSTINSTANCES'][$class] ); if ($err) { return PEAR_Task_Common::throwError($err); } } unset($GLOBALS['_PEAR_TASK_POSTINSTANCES']); } /** * Determines whether a role is a script * @return bool */ public function isScript() { return $this->type == 'script'; } public function throwError($msg, $code = -1) { include_once 'PEAR.php'; return PEAR::raiseError($msg, $code); } } PK=1]62M")) pear/PEAR/Task/Windowseol/rw.phpnu[ - read/write version * * PHP versions 4 and 5 * * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a10 */ /** * Base class */ require_once 'PEAR/Task/Windowseol.php'; /** * Abstracts the windowseol task xml. * * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a10 */ class PEAR_Task_Windowseol_rw extends PEAR_Task_Windowseol { function __construct(&$pkg, &$config, &$logger, $fileXml) { parent::__construct($config, $logger, PEAR_TASK_PACKAGE); $this->_contents = $fileXml; $this->_pkg = &$pkg; $this->_params = array(); } public function validate() { return true; } public function getName() { return 'windowseol'; } public function getXml() { return ''; } } ?> PK=1]k  pear/PEAR/Downloader.phpnu[ * @author Stig Bakken * @author Tomas V. V. Cox * @author Martin Jansen * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.3.0 */ /** * Needed for constants, extending */ require_once 'PEAR/Common.php'; require_once 'PEAR/Proxy.php'; define('PEAR_INSTALLER_OK', 1); define('PEAR_INSTALLER_FAILED', 0); define('PEAR_INSTALLER_SKIPPED', -1); define('PEAR_INSTALLER_ERROR_NO_PREF_STATE', 2); /** * Administration class used to download anything from the internet (PEAR Packages, * static URLs, xml files) * * @category pear * @package PEAR * @author Greg Beaver * @author Stig Bakken * @author Tomas V. V. Cox * @author Martin Jansen * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.3.0 */ class PEAR_Downloader extends PEAR_Common { /** * @var PEAR_Registry * @access private */ var $_registry; /** * Preferred Installation State (snapshot, devel, alpha, beta, stable) * @var string|null * @access private */ var $_preferredState; /** * Options from command-line passed to Install. * * Recognized options:
* - onlyreqdeps : install all required dependencies as well * - alldeps : install all dependencies, including optional * - installroot : base relative path to install files in * - force : force a download even if warnings would prevent it * - nocompress : download uncompressed tarballs * - configureoptions : additional configure options * @see PEAR_Command_Install * @access private * @var array */ var $_options; /** * Downloaded Packages after a call to download(). * * Format of each entry: * * * array('pkg' => 'package_name', 'file' => '/path/to/local/file', * 'info' => array() // parsed package.xml * ); * * @access private * @var array */ var $_downloadedPackages = array(); /** * Packages slated for download. * * This is used to prevent downloading a package more than once should it be a dependency * for two packages to be installed. * Format of each entry: * *
     * array('package_name1' => parsed package.xml, 'package_name2' => parsed package.xml,
     * );
     * 
* @access private * @var array */ var $_toDownload = array(); /** * Array of every package installed, with names lower-cased. * * Format: * * array('package1' => 0, 'package2' => 1, ); * * @var array */ var $_installed = array(); /** * @var array * @access private */ var $_errorStack = array(); /** * @var boolean * @access private */ var $_internalDownload = false; /** * Temporary variable used in sorting packages by dependency in {@link sortPkgDeps()} * @var array * @access private */ var $_packageSortTree; /** * Temporary directory, or configuration value where downloads will occur * @var string */ var $_downloadDir; /** * List of methods that can be called both statically and non-statically. * @var array */ protected static $bivalentMethods = array( 'setErrorHandling' => true, 'raiseError' => true, 'throwError' => true, 'pushErrorHandling' => true, 'popErrorHandling' => true, 'downloadHttp' => true, ); /** * @param PEAR_Frontend_* * @param array * @param PEAR_Config */ function __construct($ui = null, $options = array(), $config = null) { parent::__construct(); $this->_options = $options; if ($config !== null) { $this->config = &$config; $this->_preferredState = $this->config->get('preferred_state'); } $this->ui = &$ui; if (!$this->_preferredState) { // don't inadvertently use a non-set preferred_state $this->_preferredState = null; } if ($config !== null) { if (isset($this->_options['installroot'])) { $this->config->setInstallRoot($this->_options['installroot']); } $this->_registry = &$config->getRegistry(); } if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) { $this->_installed = $this->_registry->listAllPackages(); foreach ($this->_installed as $key => $unused) { if (!count($unused)) { continue; } $strtolower = function($a) { return strtolower($a); }; array_walk($this->_installed[$key], $strtolower); } } } /** * Attempt to discover a channel's remote capabilities from * its server name * @param string * @return boolean */ function discover($channel) { $this->log(1, 'Attempting to discover channel "' . $channel . '"...'); PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $callback = $this->ui ? array(&$this, '_downloadCallback') : null; if (!class_exists('System')) { require_once 'System.php'; } $tmpdir = $this->config->get('temp_dir'); $tmp = System::mktemp('-d -t "' . $tmpdir . '"'); $a = $this->downloadHttp('http://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false); PEAR::popErrorHandling(); if (PEAR::isError($a)) { // Attempt to fallback to https automatically. PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $this->log(1, 'Attempting fallback to https instead of http on channel "' . $channel . '"...'); $a = $this->downloadHttp('https://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false); PEAR::popErrorHandling(); if (PEAR::isError($a)) { return false; } } list($a, $lastmodified) = $a; if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } $b = new PEAR_ChannelFile; if ($b->fromXmlFile($a)) { unlink($a); if ($this->config->get('auto_discover')) { $this->_registry->addChannel($b, $lastmodified); $alias = $b->getName(); if ($b->getName() == $this->_registry->channelName($b->getAlias())) { $alias = $b->getAlias(); } $this->log(1, 'Auto-discovered channel "' . $channel . '", alias "' . $alias . '", adding to registry'); } return true; } unlink($a); return false; } /** * For simpler unit-testing * @param PEAR_Downloader * @return PEAR_Downloader_Package */ function newDownloaderPackage(&$t) { if (!class_exists('PEAR_Downloader_Package')) { require_once 'PEAR/Downloader/Package.php'; } $a = new PEAR_Downloader_Package($t); return $a; } /** * For simpler unit-testing * @param PEAR_Config * @param array * @param array * @param int */ function &getDependency2Object(&$c, $i, $p, $s) { if (!class_exists('PEAR_Dependency2')) { require_once 'PEAR/Dependency2.php'; } $z = new PEAR_Dependency2($c, $i, $p, $s); return $z; } function &download($params) { if (!count($params)) { $a = array(); return $a; } if (!isset($this->_registry)) { $this->_registry = &$this->config->getRegistry(); } $channelschecked = array(); // convert all parameters into PEAR_Downloader_Package objects foreach ($params as $i => $param) { $params[$i] = $this->newDownloaderPackage($this); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $err = $params[$i]->initialize($param); PEAR::staticPopErrorHandling(); if (!$err) { // skip parameters that were missed by preferred_state continue; } if (PEAR::isError($err)) { if (!isset($this->_options['soft']) && $err->getMessage() !== '') { $this->log(0, $err->getMessage()); } $params[$i] = false; if (is_object($param)) { $param = $param->getChannel() . '/' . $param->getPackage(); } if (!isset($this->_options['soft'])) { $this->log(2, 'Package "' . $param . '" is not valid'); } // Message logged above in a specific verbose mode, passing null to not show up on CLI $this->pushError(null, PEAR_INSTALLER_SKIPPED); } else { do { if ($params[$i] && $params[$i]->getType() == 'local') { // bug #7090 skip channel.xml check for local packages break; } if ($params[$i] && !isset($channelschecked[$params[$i]->getChannel()]) && !isset($this->_options['offline']) ) { $channelschecked[$params[$i]->getChannel()] = true; PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); if (!class_exists('System')) { require_once 'System.php'; } $curchannel = $this->_registry->getChannel($params[$i]->getChannel()); if (PEAR::isError($curchannel)) { PEAR::staticPopErrorHandling(); return $this->raiseError($curchannel); } if (PEAR::isError($dir = $this->getDownloadDir())) { PEAR::staticPopErrorHandling(); break; } $mirror = $this->config->get('preferred_mirror', null, $params[$i]->getChannel()); $url = 'http://' . $mirror . '/channel.xml'; $a = $this->downloadHttp($url, $this->ui, $dir, null, $curchannel->lastModified()); PEAR::staticPopErrorHandling(); if ($a === false) { //channel.xml not modified break; } else if (PEAR::isError($a)) { // Attempt fallback to https automatically PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $a = $this->downloadHttp('https://' . $mirror . '/channel.xml', $this->ui, $dir, null, $curchannel->lastModified()); PEAR::staticPopErrorHandling(); if (PEAR::isError($a) || !$a) { break; } } $this->log(0, 'WARNING: channel "' . $params[$i]->getChannel() . '" has ' . 'updated its protocols, use "' . PEAR_RUNTYPE . ' channel-update ' . $params[$i]->getChannel() . '" to update'); } } while (false); if ($params[$i] && !isset($this->_options['downloadonly'])) { if (isset($this->_options['packagingroot'])) { $checkdir = $this->_prependPath( $this->config->get('php_dir', null, $params[$i]->getChannel()), $this->_options['packagingroot']); } else { $checkdir = $this->config->get('php_dir', null, $params[$i]->getChannel()); } while ($checkdir && $checkdir != '/' && !file_exists($checkdir)) { $checkdir = dirname($checkdir); } if ($checkdir == '.') { $checkdir = '/'; } if (!is_writeable($checkdir)) { return PEAR::raiseError('Cannot install, php_dir for channel "' . $params[$i]->getChannel() . '" is not writeable by the current user'); } } } } unset($channelschecked); PEAR_Downloader_Package::removeDuplicates($params); if (!count($params)) { $a = array(); return $a; } if (!isset($this->_options['nodeps']) && !isset($this->_options['offline'])) { $reverify = true; while ($reverify) { $reverify = false; foreach ($params as $i => $param) { //PHP Bug 40768 / PEAR Bug #10944 //Nested foreaches fail in PHP 5.2.1 key($params); $ret = $params[$i]->detectDependencies($params); if (PEAR::isError($ret)) { $reverify = true; $params[$i] = false; PEAR_Downloader_Package::removeDuplicates($params); if (!isset($this->_options['soft'])) { $this->log(0, $ret->getMessage()); } continue 2; } } } } if (isset($this->_options['offline'])) { $this->log(3, 'Skipping dependency download check, --offline specified'); } if (!count($params)) { $a = array(); return $a; } while (PEAR_Downloader_Package::mergeDependencies($params)); PEAR_Downloader_Package::removeDuplicates($params, true); $errorparams = array(); if (PEAR_Downloader_Package::detectStupidDuplicates($params, $errorparams)) { if (count($errorparams)) { foreach ($errorparams as $param) { $name = $this->_registry->parsedPackageNameToString($param->getParsedPackage()); $this->pushError('Duplicate package ' . $name . ' found', PEAR_INSTALLER_FAILED); } $a = array(); return $a; } } PEAR_Downloader_Package::removeInstalled($params); if (!count($params)) { $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED); $a = array(); return $a; } PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $err = $this->analyzeDependencies($params); PEAR::popErrorHandling(); if (!count($params)) { $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED); $a = array(); return $a; } $ret = array(); $newparams = array(); if (isset($this->_options['pretend'])) { return $params; } $somefailed = false; foreach ($params as $i => $package) { PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $pf = &$params[$i]->download(); PEAR::staticPopErrorHandling(); if (PEAR::isError($pf)) { if (!isset($this->_options['soft'])) { $this->log(1, $pf->getMessage()); $this->log(0, 'Error: cannot download "' . $this->_registry->parsedPackageNameToString($package->getParsedPackage(), true) . '"'); } $somefailed = true; continue; } $newparams[] = &$params[$i]; $ret[] = array( 'file' => $pf->getArchiveFile(), 'info' => &$pf, 'pkg' => $pf->getPackage() ); } if ($somefailed) { // remove params that did not download successfully PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $err = $this->analyzeDependencies($newparams, true); PEAR::popErrorHandling(); if (!count($newparams)) { $this->pushError('Download failed', PEAR_INSTALLER_FAILED); $a = array(); return $a; } } $this->_downloadedPackages = $ret; return $newparams; } /** * @param array all packages to be installed */ function analyzeDependencies(&$params, $force = false) { if (isset($this->_options['downloadonly'])) { return; } PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $redo = true; $reset = $hasfailed = $failed = false; while ($redo) { $redo = false; foreach ($params as $i => $param) { $deps = $param->getDeps(); if (!$deps) { $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(), $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING); $send = $param->getPackageFile(); $installcheck = $depchecker->validatePackage($send, $this, $params); if (PEAR::isError($installcheck)) { if (!isset($this->_options['soft'])) { $this->log(0, $installcheck->getMessage()); } $hasfailed = true; $params[$i] = false; $reset = true; $redo = true; $failed = false; PEAR_Downloader_Package::removeDuplicates($params); continue 2; } continue; } if (!$reset && $param->alreadyValidated() && !$force) { continue; } if (count($deps)) { $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(), $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING); $send = $param->getPackageFile(); if ($send === null) { $send = $param->getDownloadURL(); } $installcheck = $depchecker->validatePackage($send, $this, $params); if (PEAR::isError($installcheck)) { if (!isset($this->_options['soft'])) { $this->log(0, $installcheck->getMessage()); } $hasfailed = true; $params[$i] = false; $reset = true; $redo = true; $failed = false; PEAR_Downloader_Package::removeDuplicates($params); continue 2; } $failed = false; if (isset($deps['required']) && is_array($deps['required'])) { foreach ($deps['required'] as $type => $dep) { // note: Dependency2 will never return a PEAR_Error if ignore-errors // is specified, so soft is needed to turn off logging if (!isset($dep[0])) { if (PEAR::isError($e = $depchecker->{"validate{$type}Dependency"}($dep, true, $params))) { $failed = true; if (!isset($this->_options['soft'])) { $this->log(0, $e->getMessage()); } } elseif (is_array($e) && !$param->alreadyValidated()) { if (!isset($this->_options['soft'])) { $this->log(0, $e[0]); } } } else { foreach ($dep as $d) { if (PEAR::isError($e = $depchecker->{"validate{$type}Dependency"}($d, true, $params))) { $failed = true; if (!isset($this->_options['soft'])) { $this->log(0, $e->getMessage()); } } elseif (is_array($e) && !$param->alreadyValidated()) { if (!isset($this->_options['soft'])) { $this->log(0, $e[0]); } } } } } if (isset($deps['optional']) && is_array($deps['optional'])) { foreach ($deps['optional'] as $type => $dep) { if (!isset($dep[0])) { if (PEAR::isError($e = $depchecker->{"validate{$type}Dependency"}($dep, false, $params))) { $failed = true; if (!isset($this->_options['soft'])) { $this->log(0, $e->getMessage()); } } elseif (is_array($e) && !$param->alreadyValidated()) { if (!isset($this->_options['soft'])) { $this->log(0, $e[0]); } } } else { foreach ($dep as $d) { if (PEAR::isError($e = $depchecker->{"validate{$type}Dependency"}($d, false, $params))) { $failed = true; if (!isset($this->_options['soft'])) { $this->log(0, $e->getMessage()); } } elseif (is_array($e) && !$param->alreadyValidated()) { if (!isset($this->_options['soft'])) { $this->log(0, $e[0]); } } } } } } $groupname = $param->getGroup(); if (isset($deps['group']) && $groupname) { if (!isset($deps['group'][0])) { $deps['group'] = array($deps['group']); } $found = false; foreach ($deps['group'] as $group) { if ($group['attribs']['name'] == $groupname) { $found = true; break; } } if ($found) { unset($group['attribs']); foreach ($group as $type => $dep) { if (!isset($dep[0])) { if (PEAR::isError($e = $depchecker->{"validate{$type}Dependency"}($dep, false, $params))) { $failed = true; if (!isset($this->_options['soft'])) { $this->log(0, $e->getMessage()); } } elseif (is_array($e) && !$param->alreadyValidated()) { if (!isset($this->_options['soft'])) { $this->log(0, $e[0]); } } } else { foreach ($dep as $d) { if (PEAR::isError($e = $depchecker->{"validate{$type}Dependency"}($d, false, $params))) { $failed = true; if (!isset($this->_options['soft'])) { $this->log(0, $e->getMessage()); } } elseif (is_array($e) && !$param->alreadyValidated()) { if (!isset($this->_options['soft'])) { $this->log(0, $e[0]); } } } } } } } } else { foreach ($deps as $dep) { if (PEAR::isError($e = $depchecker->validateDependency1($dep, $params))) { $failed = true; if (!isset($this->_options['soft'])) { $this->log(0, $e->getMessage()); } } elseif (is_array($e) && !$param->alreadyValidated()) { if (!isset($this->_options['soft'])) { $this->log(0, $e[0]); } } } } $params[$i]->setValidated(); } if ($failed) { $hasfailed = true; $params[$i] = false; $reset = true; $redo = true; $failed = false; PEAR_Downloader_Package::removeDuplicates($params); continue 2; } } } PEAR::staticPopErrorHandling(); if ($hasfailed && (isset($this->_options['ignore-errors']) || isset($this->_options['nodeps']))) { // this is probably not needed, but just in case if (!isset($this->_options['soft'])) { $this->log(0, 'WARNING: dependencies failed'); } } } /** * Retrieve the directory that downloads will happen in * @access private * @return string */ function getDownloadDir() { if (isset($this->_downloadDir)) { return $this->_downloadDir; } $downloaddir = $this->config->get('download_dir'); if (empty($downloaddir) || (is_dir($downloaddir) && !is_writable($downloaddir))) { if (is_dir($downloaddir) && !is_writable($downloaddir)) { $this->log(0, 'WARNING: configuration download directory "' . $downloaddir . '" is not writeable. Change download_dir config variable to ' . 'a writeable dir to avoid this warning'); } if (!class_exists('System')) { require_once 'System.php'; } if (PEAR::isError($downloaddir = System::mktemp('-d'))) { return $downloaddir; } $this->log(3, '+ tmp dir created at ' . $downloaddir); } if (!is_writable($downloaddir)) { if (PEAR::isError(System::mkdir(array('-p', $downloaddir))) || !is_writable($downloaddir)) { return PEAR::raiseError('download directory "' . $downloaddir . '" is not writeable. Change download_dir config variable to ' . 'a writeable dir'); } } return $this->_downloadDir = $downloaddir; } function setDownloadDir($dir) { if (!@is_writable($dir)) { if (PEAR::isError(System::mkdir(array('-p', $dir)))) { return PEAR::raiseError('download directory "' . $dir . '" is not writeable. Change download_dir config variable to ' . 'a writeable dir'); } } $this->_downloadDir = $dir; } function configSet($key, $value, $layer = 'user', $channel = false) { $this->config->set($key, $value, $layer, $channel); $this->_preferredState = $this->config->get('preferred_state', null, $channel); if (!$this->_preferredState) { // don't inadvertently use a non-set preferred_state $this->_preferredState = null; } } function setOptions($options) { $this->_options = $options; } function getOptions() { return $this->_options; } /** * @param array output of {@link parsePackageName()} * @access private */ function _getPackageDownloadUrl($parr) { $curchannel = $this->config->get('default_channel'); $this->configSet('default_channel', $parr['channel']); // getDownloadURL returns an array. On error, it only contains information // on the latest release as array(version, info). On success it contains // array(version, info, download url string) $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state'); if (!$this->_registry->channelExists($parr['channel'])) { do { if ($this->config->get('auto_discover') && $this->discover($parr['channel'])) { break; } $this->configSet('default_channel', $curchannel); return PEAR::raiseError('Unknown remote channel: ' . $parr['channel']); } while (false); } $chan = $this->_registry->getChannel($parr['channel']); if (PEAR::isError($chan)) { return $chan; } PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $version = $this->_registry->packageInfo($parr['package'], 'version', $parr['channel']); $stability = $this->_registry->packageInfo($parr['package'], 'stability', $parr['channel']); // package is installed - use the installed release stability level if (!isset($parr['state']) && $stability !== null) { $state = $stability['release']; } PEAR::staticPopErrorHandling(); $base2 = false; $preferred_mirror = $this->config->get('preferred_mirror'); if (!$chan->supportsREST($preferred_mirror) || ( !($base2 = $chan->getBaseURL('REST1.3', $preferred_mirror)) && !($base = $chan->getBaseURL('REST1.0', $preferred_mirror)) ) ) { return $this->raiseError($parr['channel'] . ' is using an unsupported protocol - This should never happen. Use --force to continue'); } if ($base2) { $rest = &$this->config->getREST('1.3', $this->_options); $base = $base2; } else { $rest = &$this->config->getREST('1.0', $this->_options); } $downloadVersion = false; if (!isset($parr['version']) && !isset($parr['state']) && $version && !PEAR::isError($version) && !isset($this->_options['downloadonly']) ) { $downloadVersion = $version; } $url = $rest->getDownloadURL($base, $parr, $state, $downloadVersion, $chan->getName()); if (PEAR::isError($url)) { $this->configSet('default_channel', $curchannel); return $url; } if ($parr['channel'] != $curchannel) { $this->configSet('default_channel', $curchannel); } if (!is_array($url)) { return $url; } $url['raw'] = false; // no checking is necessary for REST if (!is_array($url['info'])) { return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' . 'this should never happen'); } if (!isset($this->_options['force']) && !isset($this->_options['downloadonly']) && $version && !PEAR::isError($version) && !isset($parr['group']) ) { if (version_compare($version, $url['version'], '=')) { return PEAR::raiseError($this->_registry->parsedPackageNameToString( $parr, true) . ' is already installed and is the same as the ' . 'released version ' . $url['version'], -976); } if (version_compare($version, $url['version'], '>')) { return PEAR::raiseError($this->_registry->parsedPackageNameToString( $parr, true) . ' is already installed and is newer than detected ' . 'released version ' . $url['version'], -976); } } if (isset($url['info']['required']) || $url['compatible']) { require_once 'PEAR/PackageFile/v2.php'; $pf = new PEAR_PackageFile_v2; $pf->setRawChannel($parr['channel']); if ($url['compatible']) { $pf->setRawCompatible($url['compatible']); } } else { require_once 'PEAR/PackageFile/v1.php'; $pf = new PEAR_PackageFile_v1; } $pf->setRawPackage($url['package']); $pf->setDeps($url['info']); if ($url['compatible']) { $pf->setCompatible($url['compatible']); } $pf->setRawState($url['stability']); $url['info'] = &$pf; if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) { $ext = '.tar'; } else { $ext = '.tgz'; } if (is_array($url) && isset($url['url'])) { $url['url'] .= $ext; } return $url; } /** * @param array dependency array * @access private */ function _getDepPackageDownloadUrl($dep, $parr) { $xsdversion = isset($dep['rel']) ? '1.0' : '2.0'; $curchannel = $this->config->get('default_channel'); if (isset($dep['uri'])) { $xsdversion = '2.0'; $chan = $this->_registry->getChannel('__uri'); if (PEAR::isError($chan)) { return $chan; } $version = $this->_registry->packageInfo($dep['name'], 'version', '__uri'); $this->configSet('default_channel', '__uri'); } else { if (isset($dep['channel'])) { $remotechannel = $dep['channel']; } else { $remotechannel = 'pear.php.net'; } if (!$this->_registry->channelExists($remotechannel)) { do { if ($this->config->get('auto_discover')) { if ($this->discover($remotechannel)) { break; } } return PEAR::raiseError('Unknown remote channel: ' . $remotechannel); } while (false); } $chan = $this->_registry->getChannel($remotechannel); if (PEAR::isError($chan)) { return $chan; } $version = $this->_registry->packageInfo($dep['name'], 'version', $remotechannel); $this->configSet('default_channel', $remotechannel); } $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state'); if (isset($parr['state']) && isset($parr['version'])) { unset($parr['state']); } if (isset($dep['uri'])) { $info = $this->newDownloaderPackage($this); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $err = $info->initialize($dep); PEAR::staticPopErrorHandling(); if (!$err) { // skip parameters that were missed by preferred_state return PEAR::raiseError('Cannot initialize dependency'); } if (PEAR::isError($err)) { if (!isset($this->_options['soft'])) { $this->log(0, $err->getMessage()); } if (is_object($info)) { $param = $info->getChannel() . '/' . $info->getPackage(); } return PEAR::raiseError('Package "' . $param . '" is not valid'); } return $info; } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) && ( ($base2 = $chan->getBaseURL('REST1.3', $this->config->get('preferred_mirror'))) || ($base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) ) ) { if ($base2) { $base = $base2; $rest = &$this->config->getREST('1.3', $this->_options); } else { $rest = &$this->config->getREST('1.0', $this->_options); } $url = $rest->getDepDownloadURL($base, $xsdversion, $dep, $parr, $state, $version, $chan->getName()); if (PEAR::isError($url)) { return $url; } if ($parr['channel'] != $curchannel) { $this->configSet('default_channel', $curchannel); } if (!is_array($url)) { return $url; } $url['raw'] = false; // no checking is necessary for REST if (!is_array($url['info'])) { return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' . 'this should never happen'); } if (isset($url['info']['required'])) { if (!class_exists('PEAR_PackageFile_v2')) { require_once 'PEAR/PackageFile/v2.php'; } $pf = new PEAR_PackageFile_v2; $pf->setRawChannel($remotechannel); } else { if (!class_exists('PEAR_PackageFile_v1')) { require_once 'PEAR/PackageFile/v1.php'; } $pf = new PEAR_PackageFile_v1; } $pf->setRawPackage($url['package']); $pf->setDeps($url['info']); if ($url['compatible']) { $pf->setCompatible($url['compatible']); } $pf->setRawState($url['stability']); $url['info'] = &$pf; if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) { $ext = '.tar'; } else { $ext = '.tgz'; } if (is_array($url) && isset($url['url'])) { $url['url'] .= $ext; } return $url; } return $this->raiseError($parr['channel'] . ' is using a unsupported protocol - This should never happen.'); } /** * @deprecated in favor of _getPackageDownloadUrl */ function getPackageDownloadUrl($package, $version = null, $channel = false) { if ($version) { $package .= "-$version"; } if ($this === null || $this->_registry === null) { $package = "http://pear.php.net/get/$package"; } else { $chan = $this->_registry->getChannel($channel); if (PEAR::isError($chan)) { return ''; } $package = "http://" . $chan->getServer() . "/get/$package"; } if (!extension_loaded("zlib")) { $package .= '?uncompress=yes'; } return $package; } /** * Retrieve a list of downloaded packages after a call to {@link download()}. * * Also resets the list of downloaded packages. * @return array */ function getDownloadedPackages() { $ret = $this->_downloadedPackages; $this->_downloadedPackages = array(); $this->_toDownload = array(); return $ret; } function _downloadCallback($msg, $params = null) { switch ($msg) { case 'saveas': $this->log(1, "downloading $params ..."); break; case 'done': $this->log(1, '...done: ' . number_format($params, 0, '', ',') . ' bytes'); break; case 'bytesread': static $bytes; if (empty($bytes)) { $bytes = 0; } if (!($bytes % 10240)) { $this->log(1, '.', false); } $bytes += $params; break; case 'start': if($params[1] == -1) { $length = "Unknown size"; } else { $length = number_format($params[1], 0, '', ',')." bytes"; } $this->log(1, "Starting to download {$params[0]} ($length)"); break; } if (method_exists($this->ui, '_downloadCallback')) $this->ui->_downloadCallback($msg, $params); } function _prependPath($path, $prepend) { if (strlen($prepend) > 0) { if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) { if (preg_match('/^[a-z]:/i', $prepend)) { $prepend = substr($prepend, 2); } elseif ($prepend[0] != '\\') { $prepend = "\\$prepend"; } $path = substr($path, 0, 2) . $prepend . substr($path, 2); } else { $path = $prepend . $path; } } return $path; } /** * @param string * @param integer */ function pushError($errmsg, $code = -1) { array_push($this->_errorStack, array($errmsg, $code)); } function getErrorMsgs() { $msgs = array(); $errs = $this->_errorStack; foreach ($errs as $err) { $msgs[] = $err[0]; } $this->_errorStack = array(); return $msgs; } /** * for BC * * @deprecated */ function sortPkgDeps(&$packages, $uninstall = false) { $uninstall ? $this->sortPackagesForUninstall($packages) : $this->sortPackagesForInstall($packages); } /** * Sort a list of arrays of array(downloaded packagefilename) by dependency. * * This uses the topological sort method from graph theory, and the * Structures_Graph package to properly sort dependencies for installation. * @param array an array of downloaded PEAR_Downloader_Packages * @return array array of array(packagefilename, package.xml contents) */ function sortPackagesForInstall(&$packages) { require_once 'Structures/Graph.php'; require_once 'Structures/Graph/Node.php'; require_once 'Structures/Graph/Manipulator/TopologicalSorter.php'; $depgraph = new Structures_Graph(true); $nodes = array(); $reg = &$this->config->getRegistry(); foreach ($packages as $i => $package) { $pname = $reg->parsedPackageNameToString( array( 'channel' => $package->getChannel(), 'package' => strtolower($package->getPackage()), )); $nodes[$pname] = new Structures_Graph_Node; $nodes[$pname]->setData($packages[$i]); $depgraph->addNode($nodes[$pname]); } $deplinks = array(); foreach ($nodes as $package => $node) { $pf = &$node->getData(); $pdeps = $pf->getDeps(true); if (!$pdeps) { continue; } if ($pf->getPackagexmlVersion() == '1.0') { foreach ($pdeps as $dep) { if ($dep['type'] != 'pkg' || (isset($dep['optional']) && $dep['optional'] == 'yes')) { continue; } $dname = $reg->parsedPackageNameToString( array( 'channel' => 'pear.php.net', 'package' => strtolower($dep['name']), )); if (isset($nodes[$dname])) { if (!isset($deplinks[$dname])) { $deplinks[$dname] = array(); } $deplinks[$dname][$package] = 1; // dependency is in installed packages continue; } $dname = $reg->parsedPackageNameToString( array( 'channel' => 'pecl.php.net', 'package' => strtolower($dep['name']), )); if (isset($nodes[$dname])) { if (!isset($deplinks[$dname])) { $deplinks[$dname] = array(); } $deplinks[$dname][$package] = 1; // dependency is in installed packages continue; } } } else { // the only ordering we care about is: // 1) subpackages must be installed before packages that depend on them // 2) required deps must be installed before packages that depend on them if (isset($pdeps['required']['subpackage'])) { $t = $pdeps['required']['subpackage']; if (!isset($t[0])) { $t = array($t); } $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); } if (isset($pdeps['group'])) { if (!isset($pdeps['group'][0])) { $pdeps['group'] = array($pdeps['group']); } foreach ($pdeps['group'] as $group) { if (isset($group['subpackage'])) { $t = $group['subpackage']; if (!isset($t[0])) { $t = array($t); } $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); } } } if (isset($pdeps['optional']['subpackage'])) { $t = $pdeps['optional']['subpackage']; if (!isset($t[0])) { $t = array($t); } $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); } if (isset($pdeps['required']['package'])) { $t = $pdeps['required']['package']; if (!isset($t[0])) { $t = array($t); } $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); } if (isset($pdeps['group'])) { if (!isset($pdeps['group'][0])) { $pdeps['group'] = array($pdeps['group']); } foreach ($pdeps['group'] as $group) { if (isset($group['package'])) { $t = $group['package']; if (!isset($t[0])) { $t = array($t); } $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); } } } } } $this->_detectDepCycle($deplinks); foreach ($deplinks as $dependent => $parents) { foreach ($parents as $parent => $unused) { $nodes[$dependent]->connectTo($nodes[$parent]); } } $installOrder = Structures_Graph_Manipulator_TopologicalSorter::sort($depgraph); $ret = array(); for ($i = 0, $count = count($installOrder); $i < $count; $i++) { foreach ($installOrder[$i] as $index => $sortedpackage) { $data = &$installOrder[$i][$index]->getData(); $ret[] = &$nodes[$reg->parsedPackageNameToString( array( 'channel' => $data->getChannel(), 'package' => strtolower($data->getPackage()), ))]->getData(); } } $packages = $ret; return; } /** * Detect recursive links between dependencies and break the cycles * * @param array * @access private */ function _detectDepCycle(&$deplinks) { do { $keepgoing = false; foreach ($deplinks as $dep => $parents) { foreach ($parents as $parent => $unused) { // reset the parent cycle detector $this->_testCycle(null, null, null); if ($this->_testCycle($dep, $deplinks, $parent)) { $keepgoing = true; unset($deplinks[$dep][$parent]); if (count($deplinks[$dep]) == 0) { unset($deplinks[$dep]); } continue 3; } } } } while ($keepgoing); } function _testCycle($test, $deplinks, $dep) { static $visited = array(); if ($test === null) { $visited = array(); return; } // this happens when a parent has a dep cycle on another dependency // but the child is not part of the cycle if (isset($visited[$dep])) { return false; } $visited[$dep] = 1; if ($test == $dep) { return true; } if (isset($deplinks[$dep])) { if (in_array($test, array_keys($deplinks[$dep]), true)) { return true; } foreach ($deplinks[$dep] as $parent => $unused) { if ($this->_testCycle($test, $deplinks, $parent)) { return true; } } } return false; } /** * Set up the dependency for installation parsing * * @param array $t dependency information * @param PEAR_Registry $reg * @param array $deplinks list of dependency links already established * @param array $nodes all existing package nodes * @param string $package parent package name * @access private */ function _setupGraph($t, $reg, &$deplinks, &$nodes, $package) { foreach ($t as $dep) { $depchannel = !isset($dep['channel']) ? '__uri': $dep['channel']; $dname = $reg->parsedPackageNameToString( array( 'channel' => $depchannel, 'package' => strtolower($dep['name']), )); if (isset($nodes[$dname])) { if (!isset($deplinks[$dname])) { $deplinks[$dname] = array(); } $deplinks[$dname][$package] = 1; } } } function _dependsOn($a, $b) { return $this->_checkDepTree(strtolower($a->getChannel()), strtolower($a->getPackage()), $b); } function _checkDepTree($channel, $package, $b, $checked = array()) { $checked[$channel][$package] = true; if (!isset($this->_depTree[$channel][$package])) { return false; } if (isset($this->_depTree[$channel][$package][strtolower($b->getChannel())] [strtolower($b->getPackage())])) { return true; } foreach ($this->_depTree[$channel][$package] as $ch => $packages) { foreach ($packages as $pa => $true) { if ($this->_checkDepTree($ch, $pa, $b, $checked)) { return true; } } } return false; } function _sortInstall($a, $b) { if (!$a->getDeps() && !$b->getDeps()) { return 0; // neither package has dependencies, order is insignificant } if ($a->getDeps() && !$b->getDeps()) { return 1; // $a must be installed after $b because $a has dependencies } if (!$a->getDeps() && $b->getDeps()) { return -1; // $b must be installed after $a because $b has dependencies } // both packages have dependencies if ($this->_dependsOn($a, $b)) { return 1; } if ($this->_dependsOn($b, $a)) { return -1; } return 0; } /** * Download a file through HTTP. Considers suggested file name in * Content-disposition: header and can run a callback function for * different events. The callback will be called with two * parameters: the callback type, and parameters. The implemented * callback types are: * * 'setup' called at the very beginning, parameter is a UI object * that should be used for all output * 'message' the parameter is a string with an informational message * 'saveas' may be used to save with a different file name, the * parameter is the filename that is about to be used. * If a 'saveas' callback returns a non-empty string, * that file name will be used as the filename instead. * Note that $save_dir will not be affected by this, only * the basename of the file. * 'start' download is starting, parameter is number of bytes * that are expected, or -1 if unknown * 'bytesread' parameter is the number of bytes read so far * 'done' download is complete, parameter is the total number * of bytes read * 'connfailed' if the TCP/SSL connection fails, this callback is called * with array(host,port,errno,errmsg) * 'writefailed' if writing to disk fails, this callback is called * with array(destfile,errmsg) * * If an HTTP proxy has been configured (http_proxy PEAR_Config * setting), the proxy will be used. * * @param string $url the URL to download * @param object $ui PEAR_Frontend_* instance * @param object $config PEAR_Config instance * @param string $save_dir directory to save file in * @param mixed $callback function/method to call for status * updates * @param false|string|array $lastmodified header values to check against for caching * use false to return the header values from this download * @param false|array $accept Accept headers to send * @param false|string $channel Channel to use for retrieving authentication * @return mixed Returns the full path of the downloaded file or a PEAR * error on failure. If the error is caused by * socket-related errors, the error object will * have the fsockopen error code available through * getCode(). If caching is requested, then return the header * values. * If $lastmodified was given and the there are no changes, * boolean false is returned. * * @access public */ public static function _downloadHttp( $object, $url, &$ui, $save_dir = '.', $callback = null, $lastmodified = null, $accept = false, $channel = false ) { static $redirect = 0; // always reset , so we are clean case of error $wasredirect = $redirect; $redirect = 0; if ($callback) { call_user_func($callback, 'setup', array(&$ui)); } $info = parse_url($url); if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) { return PEAR::raiseError('Cannot download non-http URL "' . $url . '"'); } if (!isset($info['host'])) { return PEAR::raiseError('Cannot download from non-URL "' . $url . '"'); } $host = isset($info['host']) ? $info['host'] : null; $port = isset($info['port']) ? $info['port'] : null; $path = isset($info['path']) ? $info['path'] : null; if ($object !== null) { $config = $object->config; } else { $config = &PEAR_Config::singleton(); } $proxy = new PEAR_Proxy($config); if ($proxy->isProxyConfigured() && $callback) { call_user_func($callback, 'message', "Using HTTP proxy $host:$port"); } if (empty($port)) { $port = (isset($info['scheme']) && $info['scheme'] == 'https') ? 443 : 80; } $scheme = (isset($info['scheme']) && $info['scheme'] == 'https') ? 'https' : 'http'; $secure = ($scheme == 'https'); $fp = $proxy->openSocket($host, $port, $secure); if (PEAR::isError($fp)) { if ($callback) { $errno = $fp->getCode(); $errstr = $fp->getMessage(); call_user_func($callback, 'connfailed', array($host, $port, $errno, $errstr)); } return $fp; } $requestPath = $path; if ($proxy->isProxyConfigured()) { $requestPath = $url; } if ($lastmodified === false || $lastmodified) { $request = "GET $requestPath HTTP/1.1\r\n"; } else { $request = "GET $requestPath HTTP/1.0\r\n"; } $request .= "Host: $host\r\n"; $ifmodifiedsince = ''; if (is_array($lastmodified)) { if (isset($lastmodified['Last-Modified'])) { $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n"; } if (isset($lastmodified['ETag'])) { $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n"; } } else { $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : ''); } $request .= $ifmodifiedsince . "User-Agent: PEAR/1.10.18/PHP/" . PHP_VERSION . "\r\n"; if ($object !== null) { // only pass in authentication for non-static calls $username = $config->get('username', null, $channel); $password = $config->get('password', null, $channel); if ($username && $password) { $tmp = base64_encode("$username:$password"); $request .= "Authorization: Basic $tmp\r\n"; } } $proxyAuth = $proxy->getProxyAuth(); if ($proxyAuth) { $request .= 'Proxy-Authorization: Basic ' . $proxyAuth . "\r\n"; } if ($accept) { $request .= 'Accept: ' . implode(', ', $accept) . "\r\n"; } $request .= "Connection: close\r\n"; $request .= "\r\n"; fwrite($fp, $request); $headers = array(); $reply = 0; while (trim($line = fgets($fp, 1024))) { if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) { $headers[strtolower($matches[1])] = trim($matches[2]); } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) { $reply = (int)$matches[1]; if ($reply == 304 && ($lastmodified || ($lastmodified === false))) { return false; } if (!in_array($reply, array(200, 301, 302, 303, 305, 307))) { return PEAR::raiseError("File $scheme://$host:$port$path not valid (received: $line)"); } } } if ($reply != 200) { if (!isset($headers['location'])) { return PEAR::raiseError("File $scheme://$host:$port$path not valid (redirected but no location)"); } if ($wasredirect > 4) { return PEAR::raiseError("File $scheme://$host:$port$path not valid (redirection looped more than 5 times)"); } $redirect = $wasredirect + 1; return static::_downloadHttp($object, $headers['location'], $ui, $save_dir, $callback, $lastmodified, $accept); } if (isset($headers['content-disposition']) && preg_match('/\sfilename=\"([^;]*\S)\"\s*(;|\\z)/', $headers['content-disposition'], $matches)) { $save_as = basename($matches[1]); } else { $save_as = basename($url); } if ($callback) { $tmp = call_user_func($callback, 'saveas', $save_as); if ($tmp) { $save_as = $tmp; } } $dest_file = $save_dir . DIRECTORY_SEPARATOR . $save_as; if (is_link($dest_file)) { return PEAR::raiseError('SECURITY ERROR: Will not write to ' . $dest_file . ' as it is symlinked to ' . readlink($dest_file) . ' - Possible symlink attack'); } if (!$wp = @fopen($dest_file, 'wb')) { fclose($fp); if ($callback) { call_user_func($callback, 'writefailed', array($dest_file, error_get_last()["message"])); } return PEAR::raiseError("could not open $dest_file for writing"); } $length = isset($headers['content-length']) ? $headers['content-length'] : -1; $bytes = 0; if ($callback) { call_user_func($callback, 'start', array(basename($dest_file), $length)); } if (isset($headers['transfer-encoding']) && strtolower($headers['transfer-encoding']) === 'chunked') { while (!feof($fp)) { $chunk_size = hexdec(fgets($fp)); if ($chunk_size === 0) { break; } $data = fread($fp, $chunk_size); $bytes += strlen($data); if ($callback) { call_user_func($callback, 'bytesread', $bytes); } if (!@fwrite($wp, $data)) { fclose($fp); if ($callback) { call_user_func($callback, 'writefailed', array($dest_file, error_get_last()["message"])); } return PEAR::raiseError( "$dest_file: write failed (" . error_get_last()["message"] . ")"); } fgets($fp); // Skip the trailing CRLF } } else { while ($data = fread($fp, 1024)) { $bytes += strlen($data); if ($callback) { call_user_func($callback, 'bytesread', $bytes); } if (!@fwrite($wp, $data)) { fclose($fp); if ($callback) { call_user_func($callback, 'writefailed', array($dest_file, error_get_last()["message"])); } return PEAR::raiseError( "$dest_file: write failed (" . error_get_last()["message"] . ")"); } } } fclose($fp); fclose($wp); if ($callback) { call_user_func($callback, 'done', $bytes); } if ($lastmodified === false || $lastmodified) { if (isset($headers['etag'])) { $lastmodified = array('ETag' => $headers['etag']); } if (isset($headers['last-modified'])) { if (is_array($lastmodified)) { $lastmodified['Last-Modified'] = $headers['last-modified']; } else { $lastmodified = $headers['last-modified']; } } return array($dest_file, $lastmodified, $headers); } return $dest_file; } } PK=1]Z3)) pear/PEAR/Downloader/Package.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * Error code when parameter initialization fails because no releases * exist within preferred_state, but releases do exist */ define('PEAR_DOWNLOADER_PACKAGE_STATE', -1003); /** * Error code when parameter initialization fails because no releases * exist that will work with the existing PHP version */ define('PEAR_DOWNLOADER_PACKAGE_PHPVERSION', -1004); /** * Coordinates download parameters and manages their dependencies * prior to downloading them. * * Input can come from three sources: * * - local files (archives or package.xml) * - remote files (downloadable urls) * - abstract package names * * The first two elements are handled cleanly by PEAR_PackageFile, but the third requires * accessing pearweb's xml-rpc interface to determine necessary dependencies, and the * format returned of dependencies is slightly different from that used in package.xml. * * This class hides the differences between these elements, and makes automatic * dependency resolution a piece of cake. It also manages conflicts when * two classes depend on incompatible dependencies, or differing versions of the same * package dependency. In addition, download will not be attempted if the php version is * not supported, PEAR installer version is not supported, or non-PECL extensions are not * installed. * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_Downloader_Package { /** * @var PEAR_Downloader */ var $_downloader; /** * @var PEAR_Config */ var $_config; /** * @var PEAR_Registry */ var $_registry; /** * Used to implement packagingroot properly * @var PEAR_Registry */ var $_installRegistry; /** * @var PEAR_PackageFile_v1|PEAR_PackageFile|v2 */ var $_packagefile; /** * @var array */ var $_parsedname; /** * @var array */ var $_downloadURL; /** * @var array */ var $_downloadDeps = array(); /** * @var boolean */ var $_valid = false; /** * @var boolean */ var $_analyzed = false; /** * if this or a parent package was invoked with Package-state, this is set to the * state variable. * * This allows temporary reassignment of preferred_state for a parent package and all of * its dependencies. * @var string|false */ var $_explicitState = false; /** * If this package is invoked with Package#group, this variable will be true */ var $_explicitGroup = false; /** * Package type local|url * @var string */ var $_type; /** * Contents of package.xml, if downloaded from a remote channel * @var string|false * @access private */ var $_rawpackagefile; /** * @var boolean * @access private */ var $_validated = false; /** * @param PEAR_Downloader */ function __construct(&$downloader) { $this->_downloader = &$downloader; $this->_config = &$this->_downloader->config; $this->_registry = &$this->_config->getRegistry(); $options = $downloader->getOptions(); if (isset($options['packagingroot'])) { $this->_config->setInstallRoot($options['packagingroot']); $this->_installRegistry = &$this->_config->getRegistry(); $this->_config->setInstallRoot(false); } else { $this->_installRegistry = &$this->_registry; } $this->_valid = $this->_analyzed = false; } /** * Parse the input and determine whether this is a local file, a remote uri, or an * abstract package name. * * This is the heart of the PEAR_Downloader_Package(), and is used in * {@link PEAR_Downloader::download()} * @param string * @return bool|PEAR_Error */ function initialize($param) { $origErr = $this->_fromFile($param); if ($this->_valid) { return true; } $options = $this->_downloader->getOptions(); if (isset($options['offline'])) { if (PEAR::isError($origErr) && !isset($options['soft'])) { foreach ($origErr->getUserInfo() as $userInfo) { if (isset($userInfo['message'])) { $this->_downloader->log(0, $userInfo['message']); } } $this->_downloader->log(0, $origErr->getMessage()); } return PEAR::raiseError('Cannot download non-local package "' . $param . '"'); } $err = $this->_fromUrl($param); if (PEAR::isError($err) || !$this->_valid) { if ($this->_type == 'url') { if (PEAR::isError($err) && !isset($options['soft'])) { $this->_downloader->log(0, $err->getMessage()); } return PEAR::raiseError("Invalid or missing remote package file"); } $err = $this->_fromString($param); if (PEAR::isError($err) || !$this->_valid) { if (PEAR::isError($err) && $err->getCode() == PEAR_DOWNLOADER_PACKAGE_STATE) { return false; // instruct the downloader to silently skip } if (isset($this->_type) && $this->_type == 'local' && PEAR::isError($origErr)) { if (is_array($origErr->getUserInfo())) { foreach ($origErr->getUserInfo() as $err) { if (is_array($err)) { $err = $err['message']; } if (!isset($options['soft'])) { $this->_downloader->log(0, $err); } } } if (!isset($options['soft'])) { $this->_downloader->log(0, $origErr->getMessage()); } if (is_array($param)) { $param = $this->_registry->parsedPackageNameToString($param, true); } if (!isset($options['soft'])) { $this->_downloader->log(2, "Cannot initialize '$param', invalid or missing package file"); } // Passing no message back - already logged above return PEAR::raiseError(); } if (PEAR::isError($err) && !isset($options['soft'])) { $this->_downloader->log(0, $err->getMessage()); } if (is_array($param)) { $param = $this->_registry->parsedPackageNameToString($param, true); } if (!isset($options['soft'])) { $this->_downloader->log(2, "Cannot initialize '$param', invalid or missing package file"); } // Passing no message back - already logged above return PEAR::raiseError(); } } return true; } /** * Retrieve any non-local packages * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|PEAR_Error */ function &download() { if (isset($this->_packagefile)) { return $this->_packagefile; } if (isset($this->_downloadURL['url'])) { $this->_isvalid = false; $info = $this->getParsedPackage(); foreach ($info as $i => $p) { $info[$i] = strtolower($p); } $err = $this->_fromUrl($this->_downloadURL['url'], $this->_registry->parsedPackageNameToString($this->_parsedname, true)); $newinfo = $this->getParsedPackage(); foreach ($newinfo as $i => $p) { $newinfo[$i] = strtolower($p); } if ($info != $newinfo) { do { if ($info['channel'] == 'pecl.php.net' && $newinfo['channel'] == 'pear.php.net') { $info['channel'] = 'pear.php.net'; if ($info == $newinfo) { // skip the channel check if a pecl package says it's a PEAR package break; } } if ($info['channel'] == 'pear.php.net' && $newinfo['channel'] == 'pecl.php.net') { $info['channel'] = 'pecl.php.net'; if ($info == $newinfo) { // skip the channel check if a pecl package says it's a PEAR package break; } } return PEAR::raiseError('CRITICAL ERROR: We are ' . $this->_registry->parsedPackageNameToString($info) . ', but the file ' . 'downloaded claims to be ' . $this->_registry->parsedPackageNameToString($this->getParsedPackage())); } while (false); } if (PEAR::isError($err) || !$this->_valid) { return $err; } } $this->_type = 'local'; return $this->_packagefile; } function &getPackageFile() { return $this->_packagefile; } function &getDownloader() { return $this->_downloader; } function getType() { return $this->_type; } /** * Like {@link initialize()}, but operates on a dependency */ function fromDepURL($dep) { $this->_downloadURL = $dep; if (isset($dep['uri'])) { $options = $this->_downloader->getOptions(); if (!extension_loaded("zlib") || isset($options['nocompress'])) { $ext = '.tar'; } else { $ext = '.tgz'; } PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $err = $this->_fromUrl($dep['uri'] . $ext); PEAR::popErrorHandling(); if (PEAR::isError($err)) { if (!isset($options['soft'])) { $this->_downloader->log(0, $err->getMessage()); } return PEAR::raiseError('Invalid uri dependency "' . $dep['uri'] . $ext . '", ' . 'cannot download'); } } else { $this->_parsedname = array( 'package' => $dep['info']->getPackage(), 'channel' => $dep['info']->getChannel(), 'version' => $dep['version'] ); if (!isset($dep['nodefault'])) { $this->_parsedname['group'] = 'default'; // download the default dependency group $this->_explicitGroup = false; } $this->_rawpackagefile = $dep['raw']; } } function detectDependencies($params) { $options = $this->_downloader->getOptions(); if (isset($options['downloadonly'])) { return; } if (isset($options['offline'])) { $this->_downloader->log(3, 'Skipping dependency download check, --offline specified'); return; } $pname = $this->getParsedPackage(); if (!$pname) { return; } $deps = $this->getDeps(); if (!$deps) { return; } if (isset($deps['required'])) { // package.xml 2.0 return $this->_detect2($deps, $pname, $options, $params); } return $this->_detect1($deps, $pname, $options, $params); } function setValidated() { $this->_validated = true; } function alreadyValidated() { return $this->_validated; } /** * Remove packages to be downloaded that are already installed * @param array of PEAR_Downloader_Package objects */ public static function removeInstalled(&$params) { if (!isset($params[0])) { return; } $options = $params[0]->_downloader->getOptions(); if (!isset($options['downloadonly'])) { foreach ($params as $i => $param) { $package = $param->getPackage(); $channel = $param->getChannel(); // remove self if already installed with this version // this does not need any pecl magic - we only remove exact matches if ($param->_installRegistry->packageExists($package, $channel)) { $packageVersion = $param->_installRegistry->packageInfo($package, 'version', $channel); if (version_compare($packageVersion, $param->getVersion(), '==')) { if (!isset($options['force']) && !isset($options['packagingroot'])) { $info = $param->getParsedPackage(); unset($info['version']); unset($info['state']); if (!isset($options['soft'])) { $param->_downloader->log(1, 'Skipping package "' . $param->getShortName() . '", already installed as version ' . $packageVersion); } $params[$i] = false; } } elseif (!isset($options['force']) && !isset($options['upgrade']) && !isset($options['soft']) && !isset($options['packagingroot'])) { $info = $param->getParsedPackage(); $param->_downloader->log(1, 'Skipping package "' . $param->getShortName() . '", already installed as version ' . $packageVersion); $params[$i] = false; } } } } PEAR_Downloader_Package::removeDuplicates($params); } function _detect2($deps, $pname, $options, $params) { $this->_downloadDeps = array(); $groupnotfound = false; foreach (array('package', 'subpackage') as $packagetype) { // get required dependency group if (isset($deps['required'][$packagetype])) { if (isset($deps['required'][$packagetype][0])) { foreach ($deps['required'][$packagetype] as $dep) { if (isset($dep['conflicts'])) { // skip any package that this package conflicts with continue; } $ret = $this->_detect2Dep($dep, $pname, 'required', $params); if (is_array($ret)) { $this->_downloadDeps[] = $ret; } elseif (PEAR::isError($ret) && !isset($options['soft'])) { $this->_downloader->log(0, $ret->getMessage()); } } } else { $dep = $deps['required'][$packagetype]; if (!isset($dep['conflicts'])) { // skip any package that this package conflicts with $ret = $this->_detect2Dep($dep, $pname, 'required', $params); if (is_array($ret)) { $this->_downloadDeps[] = $ret; } elseif (PEAR::isError($ret) && !isset($options['soft'])) { $this->_downloader->log(0, $ret->getMessage()); } } } } // get optional dependency group, if any if (isset($deps['optional'][$packagetype])) { $skipnames = array(); if (!isset($deps['optional'][$packagetype][0])) { $deps['optional'][$packagetype] = array($deps['optional'][$packagetype]); } foreach ($deps['optional'][$packagetype] as $dep) { $skip = false; if (!isset($options['alldeps'])) { $dep['package'] = $dep['name']; if (!isset($options['soft'])) { $this->_downloader->log(3, 'Notice: package "' . $this->_registry->parsedPackageNameToString($this->getParsedPackage(), true) . '" optional dependency "' . $this->_registry->parsedPackageNameToString(array('package' => $dep['name'], 'channel' => 'pear.php.net'), true) . '" will not be automatically downloaded'); } $skipnames[] = $this->_registry->parsedPackageNameToString($dep, true); $skip = true; unset($dep['package']); } $ret = $this->_detect2Dep($dep, $pname, 'optional', $params); if (PEAR::isError($ret) && !isset($options['soft'])) { $this->_downloader->log(0, $ret->getMessage()); } if (!$ret) { $dep['package'] = $dep['name']; $skip = count($skipnames) ? $skipnames[count($skipnames) - 1] : ''; if ($skip == $this->_registry->parsedPackageNameToString($dep, true)) { array_pop($skipnames); } } if (!$skip && is_array($ret)) { $this->_downloadDeps[] = $ret; } } if (count($skipnames)) { if (!isset($options['soft'])) { $this->_downloader->log(1, 'Did not download optional dependencies: ' . implode(', ', $skipnames) . ', use --alldeps to download automatically'); } } } // get requested dependency group, if any $groupname = $this->getGroup(); $explicit = $this->_explicitGroup; if (!$groupname) { if (!$this->canDefault()) { continue; } $groupname = 'default'; // try the default dependency group } if ($groupnotfound) { continue; } if (isset($deps['group'])) { if (isset($deps['group']['attribs'])) { if (strtolower($deps['group']['attribs']['name']) == strtolower($groupname)) { $group = $deps['group']; } elseif ($explicit) { if (!isset($options['soft'])) { $this->_downloader->log(0, 'Warning: package "' . $this->_registry->parsedPackageNameToString($pname, true) . '" has no dependency ' . 'group named "' . $groupname . '"'); } $groupnotfound = true; continue; } } else { $found = false; foreach ($deps['group'] as $group) { if (strtolower($group['attribs']['name']) == strtolower($groupname)) { $found = true; break; } } if (!$found) { if ($explicit) { if (!isset($options['soft'])) { $this->_downloader->log(0, 'Warning: package "' . $this->_registry->parsedPackageNameToString($pname, true) . '" has no dependency ' . 'group named "' . $groupname . '"'); } } $groupnotfound = true; continue; } } } if (isset($group) && isset($group[$packagetype])) { if (isset($group[$packagetype][0])) { foreach ($group[$packagetype] as $dep) { $ret = $this->_detect2Dep($dep, $pname, 'dependency group "' . $group['attribs']['name'] . '"', $params); if (is_array($ret)) { $this->_downloadDeps[] = $ret; } elseif (PEAR::isError($ret) && !isset($options['soft'])) { $this->_downloader->log(0, $ret->getMessage()); } } } else { $ret = $this->_detect2Dep($group[$packagetype], $pname, 'dependency group "' . $group['attribs']['name'] . '"', $params); if (is_array($ret)) { $this->_downloadDeps[] = $ret; } elseif (PEAR::isError($ret) && !isset($options['soft'])) { $this->_downloader->log(0, $ret->getMessage()); } } } } } function _detect2Dep($dep, $pname, $group, $params) { if (isset($dep['conflicts'])) { return true; } $options = $this->_downloader->getOptions(); if (isset($dep['uri'])) { return array('uri' => $dep['uri'], 'dep' => $dep);; } $testdep = $dep; $testdep['package'] = $dep['name']; if (PEAR_Downloader_Package::willDownload($testdep, $params)) { $dep['package'] = $dep['name']; if (!isset($options['soft'])) { $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group . ' dependency "' . $this->_registry->parsedPackageNameToString($dep, true) . '", will be installed'); } return false; } $options = $this->_downloader->getOptions(); PEAR::pushErrorHandling(PEAR_ERROR_RETURN); if ($this->_explicitState) { $pname['state'] = $this->_explicitState; } $url = $this->_downloader->_getDepPackageDownloadUrl($dep, $pname); if (PEAR::isError($url)) { PEAR::popErrorHandling(); return $url; } $dep['package'] = $dep['name']; $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params, $group == 'optional' && !isset($options['alldeps']), true); PEAR::popErrorHandling(); if (PEAR::isError($ret)) { if (!isset($options['soft'])) { $this->_downloader->log(0, $ret->getMessage()); } return false; } // check to see if a dep is already installed and is the same or newer if (!isset($dep['min']) && !isset($dep['max']) && !isset($dep['recommended'])) { $oper = 'has'; } else { $oper = 'gt'; } // do not try to move this before getDepPackageDownloadURL // we can't determine whether upgrade is necessary until we know what // version would be downloaded if (!isset($options['force']) && $this->isInstalled($ret, $oper)) { $version = $this->_installRegistry->packageInfo($dep['name'], 'version', $dep['channel']); $dep['package'] = $dep['name']; if (!isset($options['soft'])) { $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group . ' dependency "' . $this->_registry->parsedPackageNameToString($dep, true) . '" version ' . $url['version'] . ', already installed as version ' . $version); } return false; } if (isset($dep['nodefault'])) { $ret['nodefault'] = true; } return $ret; } function _detect1($deps, $pname, $options, $params) { $this->_downloadDeps = array(); $skipnames = array(); foreach ($deps as $dep) { $nodownload = false; if (isset ($dep['type']) && $dep['type'] === 'pkg') { $dep['channel'] = 'pear.php.net'; $dep['package'] = $dep['name']; switch ($dep['rel']) { case 'not' : continue 2; case 'ge' : case 'eq' : case 'gt' : case 'has' : $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ? 'required' : 'optional'; if (PEAR_Downloader_Package::willDownload($dep, $params)) { $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group . ' dependency "' . $this->_registry->parsedPackageNameToString($dep, true) . '", will be installed'); continue 2; } $fakedp = new PEAR_PackageFile_v1; $fakedp->setPackage($dep['name']); // skip internet check if we are not upgrading (bug #5810) if (!isset($options['upgrade']) && $this->isInstalled( $fakedp, $dep['rel'])) { $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group . ' dependency "' . $this->_registry->parsedPackageNameToString($dep, true) . '", is already installed'); continue 2; } } PEAR::pushErrorHandling(PEAR_ERROR_RETURN); if ($this->_explicitState) { $pname['state'] = $this->_explicitState; } $url = $this->_downloader->_getDepPackageDownloadUrl($dep, $pname); $chan = 'pear.php.net'; if (PEAR::isError($url)) { // check to see if this is a pecl package that has jumped // from pear.php.net to pecl.php.net channel if (!class_exists('PEAR_Dependency2')) { require_once 'PEAR/Dependency2.php'; } $newdep = PEAR_Dependency2::normalizeDep($dep); $newdep = $newdep[0]; $newdep['channel'] = 'pecl.php.net'; $chan = 'pecl.php.net'; $url = $this->_downloader->_getDepPackageDownloadUrl($newdep, $pname); $obj = &$this->_installRegistry->getPackage($dep['name']); if (PEAR::isError($url)) { PEAR::popErrorHandling(); if ($obj !== null && $this->isInstalled($obj, $dep['rel'])) { $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ? 'required' : 'optional'; $dep['package'] = $dep['name']; if (!isset($options['soft'])) { $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group . ' dependency "' . $this->_registry->parsedPackageNameToString($dep, true) . '", already installed as version ' . $obj->getVersion()); } $skip = count($skipnames) ? $skipnames[count($skipnames) - 1] : ''; if ($skip == $this->_registry->parsedPackageNameToString($dep, true)) { array_pop($skipnames); } continue; } else { if (isset($dep['optional']) && $dep['optional'] == 'yes') { $this->_downloader->log(2, $this->getShortName() . ': Skipping optional dependency "' . $this->_registry->parsedPackageNameToString($dep, true) . '", no releases exist'); continue; } else { return $url; } } } } PEAR::popErrorHandling(); if (!isset($options['alldeps'])) { if (isset($dep['optional']) && $dep['optional'] == 'yes') { if (!isset($options['soft'])) { $this->_downloader->log(3, 'Notice: package "' . $this->getShortName() . '" optional dependency "' . $this->_registry->parsedPackageNameToString( array('channel' => $chan, 'package' => $dep['name']), true) . '" will not be automatically downloaded'); } $skipnames[] = $this->_registry->parsedPackageNameToString( array('channel' => $chan, 'package' => $dep['name']), true); $nodownload = true; } } if (!isset($options['alldeps']) && !isset($options['onlyreqdeps'])) { if (!isset($dep['optional']) || $dep['optional'] == 'no') { if (!isset($options['soft'])) { $this->_downloader->log(3, 'Notice: package "' . $this->getShortName() . '" required dependency "' . $this->_registry->parsedPackageNameToString( array('channel' => $chan, 'package' => $dep['name']), true) . '" will not be automatically downloaded'); } $skipnames[] = $this->_registry->parsedPackageNameToString( array('channel' => $chan, 'package' => $dep['name']), true); $nodownload = true; } } // check to see if a dep is already installed // do not try to move this before getDepPackageDownloadURL // we can't determine whether upgrade is necessary until we know what // version would be downloaded if (!isset($options['force']) && $this->isInstalled( $url, $dep['rel'])) { $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ? 'required' : 'optional'; $dep['package'] = $dep['name']; if (isset($newdep)) { $version = $this->_installRegistry->packageInfo($newdep['name'], 'version', $newdep['channel']); } else { $version = $this->_installRegistry->packageInfo($dep['name'], 'version'); } $dep['version'] = $url['version']; if (!isset($options['soft'])) { $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group . ' dependency "' . $this->_registry->parsedPackageNameToString($dep, true) . '", already installed as version ' . $version); } $skip = count($skipnames) ? $skipnames[count($skipnames) - 1] : ''; if ($skip == $this->_registry->parsedPackageNameToString($dep, true)) { array_pop($skipnames); } continue; } if ($nodownload) { continue; } PEAR::pushErrorHandling(PEAR_ERROR_RETURN); if (isset($newdep)) { $dep = $newdep; } $dep['package'] = $dep['name']; $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params, isset($dep['optional']) && $dep['optional'] == 'yes' && !isset($options['alldeps']), true); PEAR::popErrorHandling(); if (PEAR::isError($ret)) { if (!isset($options['soft'])) { $this->_downloader->log(0, $ret->getMessage()); } continue; } $this->_downloadDeps[] = $ret; } } if (count($skipnames)) { if (!isset($options['soft'])) { $this->_downloader->log(1, 'Did not download dependencies: ' . implode(', ', $skipnames) . ', use --alldeps or --onlyreqdeps to download automatically'); } } } function setDownloadURL($pkg) { $this->_downloadURL = $pkg; } /** * Set the package.xml object for this downloaded package * * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 $pkg */ function setPackageFile(&$pkg) { $this->_packagefile = &$pkg; } function getShortName() { return $this->_registry->parsedPackageNameToString(array('channel' => $this->getChannel(), 'package' => $this->getPackage()), true); } function getParsedPackage() { if (isset($this->_packagefile) || isset($this->_parsedname)) { return array('channel' => $this->getChannel(), 'package' => $this->getPackage(), 'version' => $this->getVersion()); } return false; } function getDownloadURL() { return $this->_downloadURL; } function canDefault() { if (isset($this->_downloadURL) && isset($this->_downloadURL['nodefault'])) { return false; } return true; } function getPackage() { if (isset($this->_packagefile)) { return $this->_packagefile->getPackage(); } elseif (isset($this->_downloadURL['info'])) { return $this->_downloadURL['info']->getPackage(); } return false; } /** * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 */ function isSubpackage(&$pf) { if (isset($this->_packagefile)) { return $this->_packagefile->isSubpackage($pf); } elseif (isset($this->_downloadURL['info'])) { return $this->_downloadURL['info']->isSubpackage($pf); } return false; } function getPackageType() { if (isset($this->_packagefile)) { return $this->_packagefile->getPackageType(); } elseif (isset($this->_downloadURL['info'])) { return $this->_downloadURL['info']->getPackageType(); } return false; } function isBundle() { if (isset($this->_packagefile)) { return $this->_packagefile->getPackageType() == 'bundle'; } return false; } function getPackageXmlVersion() { if (isset($this->_packagefile)) { return $this->_packagefile->getPackagexmlVersion(); } elseif (isset($this->_downloadURL['info'])) { return $this->_downloadURL['info']->getPackagexmlVersion(); } return '1.0'; } function getChannel() { if (isset($this->_packagefile)) { return $this->_packagefile->getChannel(); } elseif (isset($this->_downloadURL['info'])) { return $this->_downloadURL['info']->getChannel(); } return false; } function getURI() { if (isset($this->_packagefile)) { return $this->_packagefile->getURI(); } elseif (isset($this->_downloadURL['info'])) { return $this->_downloadURL['info']->getURI(); } return false; } function getVersion() { if (isset($this->_packagefile)) { return $this->_packagefile->getVersion(); } elseif (isset($this->_downloadURL['version'])) { return $this->_downloadURL['version']; } return false; } function isCompatible($pf) { if (isset($this->_packagefile)) { return $this->_packagefile->isCompatible($pf); } elseif (isset($this->_downloadURL['info'])) { return $this->_downloadURL['info']->isCompatible($pf); } return true; } function setGroup($group) { $this->_parsedname['group'] = $group; } function getGroup() { if (isset($this->_parsedname['group'])) { return $this->_parsedname['group']; } return ''; } function isExtension($name) { if (isset($this->_packagefile)) { return $this->_packagefile->isExtension($name); } elseif (isset($this->_downloadURL['info'])) { if ($this->_downloadURL['info']->getPackagexmlVersion() == '2.0') { return $this->_downloadURL['info']->getProvidesExtension() == $name; } return false; } return false; } function getDeps() { if (isset($this->_packagefile)) { $ver = $this->_packagefile->getPackagexmlVersion(); if (version_compare($ver, '2.0', '>=')) { return $this->_packagefile->getDeps(true); } return $this->_packagefile->getDeps(); } elseif (isset($this->_downloadURL['info'])) { $ver = $this->_downloadURL['info']->getPackagexmlVersion(); if (version_compare($ver, '2.0', '>=')) { return $this->_downloadURL['info']->getDeps(true); } return $this->_downloadURL['info']->getDeps(); } return array(); } /** * @param array Parsed array from {@link PEAR_Registry::parsePackageName()} or a dependency * returned from getDepDownloadURL() */ function isEqual($param) { if (is_object($param)) { $channel = $param->getChannel(); $package = $param->getPackage(); if ($param->getURI()) { $param = array( 'channel' => $param->getChannel(), 'package' => $param->getPackage(), 'version' => $param->getVersion(), 'uri' => $param->getURI(), ); } else { $param = array( 'channel' => $param->getChannel(), 'package' => $param->getPackage(), 'version' => $param->getVersion(), ); } } else { if (isset($param['uri'])) { if ($this->getChannel() != '__uri') { return false; } return $param['uri'] == $this->getURI(); } $package = isset($param['package']) ? $param['package'] : $param['info']->getPackage(); $channel = isset($param['channel']) ? $param['channel'] : $param['info']->getChannel(); if (isset($param['rel'])) { if (!class_exists('PEAR_Dependency2')) { require_once 'PEAR/Dependency2.php'; } $newdep = PEAR_Dependency2::normalizeDep($param); $newdep = $newdep[0]; } elseif (isset($param['min'])) { $newdep = $param; } } if (isset($newdep)) { if (!isset($newdep['min'])) { $newdep['min'] = '0'; } if (!isset($newdep['max'])) { $newdep['max'] = '100000000000000000000'; } // use magic to support pecl packages suddenly jumping to the pecl channel // we need to support both dependency possibilities if ($channel == 'pear.php.net' && $this->getChannel() == 'pecl.php.net') { if ($package == $this->getPackage()) { $channel = 'pecl.php.net'; } } if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') { if ($package == $this->getPackage()) { $channel = 'pear.php.net'; } } return (strtolower($package) == strtolower($this->getPackage()) && $channel == $this->getChannel() && version_compare($newdep['min'], $this->getVersion(), '<=') && version_compare($newdep['max'], $this->getVersion(), '>=')); } // use magic to support pecl packages suddenly jumping to the pecl channel if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') { if (strtolower($package) == strtolower($this->getPackage())) { $channel = 'pear.php.net'; } } if (isset($param['version'])) { return (strtolower($package) == strtolower($this->getPackage()) && $channel == $this->getChannel() && $param['version'] == $this->getVersion()); } return strtolower($package) == strtolower($this->getPackage()) && $channel == $this->getChannel(); } function isInstalled($dep, $oper = '==') { if (!$dep) { return false; } if ($oper != 'ge' && $oper != 'gt' && $oper != 'has' && $oper != '==') { return false; } if (is_object($dep)) { $package = $dep->getPackage(); $channel = $dep->getChannel(); if ($dep->getURI()) { $dep = array( 'uri' => $dep->getURI(), 'version' => $dep->getVersion(), ); } else { $dep = array( 'version' => $dep->getVersion(), ); } } else { if (isset($dep['uri'])) { $channel = '__uri'; $package = $dep['dep']['name']; } else { $channel = $dep['info']->getChannel(); $package = $dep['info']->getPackage(); } } $options = $this->_downloader->getOptions(); $test = $this->_installRegistry->packageExists($package, $channel); if (!$test && $channel == 'pecl.php.net') { // do magic to allow upgrading from old pecl packages to new ones $test = $this->_installRegistry->packageExists($package, 'pear.php.net'); $channel = 'pear.php.net'; } if ($test) { if (isset($dep['uri'])) { if ($this->_installRegistry->packageInfo($package, 'uri', '__uri') == $dep['uri']) { return true; } } if (isset($options['upgrade'])) { $packageVersion = $this->_installRegistry->packageInfo($package, 'version', $channel); if (version_compare($packageVersion, $dep['version'], '>=')) { return true; } return false; } return true; } return false; } /** * Detect duplicate package names with differing versions * * If a user requests to install Date 1.4.6 and Date 1.4.7, * for instance, this is a logic error. This method * detects this situation. * * @param array $params array of PEAR_Downloader_Package objects * @param array $errorparams empty array * @return array array of stupid duplicated packages in PEAR_Downloader_Package obejcts */ public static function detectStupidDuplicates($params, &$errorparams) { $existing = array(); foreach ($params as $i => $param) { $package = $param->getPackage(); $channel = $param->getChannel(); $group = $param->getGroup(); if (!isset($existing[$channel . '/' . $package])) { $existing[$channel . '/' . $package] = array(); } if (!isset($existing[$channel . '/' . $package][$group])) { $existing[$channel . '/' . $package][$group] = array(); } $existing[$channel . '/' . $package][$group][] = $i; } $indices = array(); foreach ($existing as $package => $groups) { foreach ($groups as $group => $dupes) { if (count($dupes) > 1) { $indices = $indices + $dupes; } } } $indices = array_unique($indices); foreach ($indices as $index) { $errorparams[] = $params[$index]; } return count($errorparams); } /** * @param array * @param bool ignore install groups - for final removal of dupe packages */ public static function removeDuplicates(&$params, $ignoreGroups = false) { $pnames = array(); foreach ($params as $i => $param) { if (!$param) { continue; } if ($param->getPackage()) { $group = $ignoreGroups ? '' : $param->getGroup(); $pnames[$i] = $param->getChannel() . '/' . $param->getPackage() . '-' . $param->getVersion() . '#' . $group; } } $pnames = array_unique($pnames); $unset = array_diff(array_keys($params), array_keys($pnames)); $testp = array_flip($pnames); foreach ($params as $i => $param) { if (!$param) { $unset[] = $i; continue; } if (!is_a($param, 'PEAR_Downloader_Package')) { $unset[] = $i; continue; } $group = $ignoreGroups ? '' : $param->getGroup(); if (!isset($testp[$param->getChannel() . '/' . $param->getPackage() . '-' . $param->getVersion() . '#' . $group])) { $unset[] = $i; } } foreach ($unset as $i) { unset($params[$i]); } $ret = array(); foreach ($params as $i => $param) { $ret[] = &$params[$i]; } $params = array(); foreach ($ret as $i => $param) { $params[] = &$ret[$i]; } } function explicitState() { return $this->_explicitState; } function setExplicitState($s) { $this->_explicitState = $s; } /** */ public static function mergeDependencies(&$params) { $bundles = $newparams = array(); foreach ($params as $i => $param) { if (!$param->isBundle()) { continue; } $bundles[] = $i; $pf = &$param->getPackageFile(); $newdeps = array(); $contents = $pf->getBundledPackages(); if (!is_array($contents)) { $contents = array($contents); } foreach ($contents as $file) { $filecontents = $pf->getFileContents($file); $dl = &$param->getDownloader(); $options = $dl->getOptions(); if (PEAR::isError($dir = $dl->getDownloadDir())) { return $dir; } $fp = @fopen($dir . DIRECTORY_SEPARATOR . $file, 'wb'); if (!$fp) { continue; } // FIXME do symlink check fwrite($fp, $filecontents, strlen($filecontents)); fclose($fp); if ($s = $params[$i]->explicitState()) { $obj->setExplicitState($s); } $obj = new PEAR_Downloader_Package($params[$i]->getDownloader()); PEAR::pushErrorHandling(PEAR_ERROR_RETURN); if (PEAR::isError($dir = $dl->getDownloadDir())) { PEAR::popErrorHandling(); return $dir; } $a = $dir . DIRECTORY_SEPARATOR . $file; $e = $obj->_fromFile($a); PEAR::popErrorHandling(); if (PEAR::isError($e)) { if (!isset($options['soft'])) { $dl->log(0, $e->getMessage()); } continue; } if (!PEAR_Downloader_Package::willDownload($obj, array_merge($params, $newparams)) && !$param->isInstalled($obj)) { $newparams[] = $obj; } } } foreach ($bundles as $i) { unset($params[$i]); // remove bundles - only their contents matter for installation } PEAR_Downloader_Package::removeDuplicates($params); // strip any unset indices if (count($newparams)) { // add in bundled packages for install foreach ($newparams as $i => $unused) { $params[] = &$newparams[$i]; } $newparams = array(); } foreach ($params as $i => $param) { $newdeps = array(); foreach ($param->_downloadDeps as $dep) { $merge = array_merge($params, $newparams); if (!PEAR_Downloader_Package::willDownload($dep, $merge) && !$param->isInstalled($dep) ) { $newdeps[] = $dep; } else { //var_dump($dep); // detect versioning conflicts here } } // convert the dependencies into PEAR_Downloader_Package objects for the next time around $params[$i]->_downloadDeps = array(); foreach ($newdeps as $dep) { $obj = new PEAR_Downloader_Package($params[$i]->getDownloader()); if ($s = $params[$i]->explicitState()) { $obj->setExplicitState($s); } PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $e = $obj->fromDepURL($dep); PEAR::popErrorHandling(); if (PEAR::isError($e)) { if (!isset($options['soft'])) { $obj->_downloader->log(0, $e->getMessage()); } continue; } $e = $obj->detectDependencies($params); if (PEAR::isError($e)) { if (!isset($options['soft'])) { $obj->_downloader->log(0, $e->getMessage()); } } $newparams[] = $obj; } } if (count($newparams)) { foreach ($newparams as $i => $unused) { $params[] = &$newparams[$i]; } return true; } return false; } /** */ public static function willDownload($param, $params) { if (!is_array($params)) { return false; } foreach ($params as $obj) { if ($obj->isEqual($param)) { return true; } } return false; } /** * For simpler unit-testing * @param PEAR_Config * @param int * @param string */ function &getPackagefileObject(&$c, $d) { $a = new PEAR_PackageFile($c, $d); return $a; } /** * This will retrieve from a local file if possible, and parse out * a group name as well. The original parameter will be modified to reflect this. * @param string|array can be a parsed package name as well * @access private */ function _fromFile(&$param) { $saveparam = $param; if (is_string($param) && substr($param, 0, 10) !== 'channel://') { if (!@file_exists($param)) { $test = explode('#', $param); $group = array_pop($test); if (@file_exists(implode('#', $test))) { $this->setGroup($group); $param = implode('#', $test); $this->_explicitGroup = true; } } if (@is_file($param)) { $this->_type = 'local'; $options = $this->_downloader->getOptions(); $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->_debug); PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $pf = &$pkg->fromAnyFile($param, PEAR_VALIDATE_INSTALLING); PEAR::popErrorHandling(); if (PEAR::isError($pf)) { $this->_valid = false; $param = $saveparam; return $pf; } $this->_packagefile = &$pf; if (!$this->getGroup()) { $this->setGroup('default'); // install the default dependency group } return $this->_valid = true; } } $param = $saveparam; return $this->_valid = false; } function _fromUrl($param, $saveparam = '') { if (!is_array($param) && (preg_match('#^(http|https|ftp)://#', $param))) { $options = $this->_downloader->getOptions(); $this->_type = 'url'; $callback = $this->_downloader->ui ? array(&$this->_downloader, '_downloadCallback') : null; $this->_downloader->pushErrorHandling(PEAR_ERROR_RETURN); if (PEAR::isError($dir = $this->_downloader->getDownloadDir())) { $this->_downloader->popErrorHandling(); return $dir; } $this->_downloader->log(3, 'Downloading "' . $param . '"'); $file = $this->_downloader->downloadHttp($param, $this->_downloader->ui, $dir, $callback, null, false, $this->getChannel()); $this->_downloader->popErrorHandling(); if (PEAR::isError($file)) { if (!empty($saveparam)) { $saveparam = ", cannot download \"$saveparam\""; } $err = PEAR::raiseError('Could not download from "' . $param . '"' . $saveparam . ' (' . $file->getMessage() . ')'); return $err; } if ($this->_rawpackagefile) { require_once 'Archive/Tar.php'; $tar = new Archive_Tar($file); $packagexml = $tar->extractInString('package2.xml'); if (!$packagexml) { $packagexml = $tar->extractInString('package.xml'); } if (str_replace(array("\n", "\r"), array('',''), $packagexml) != str_replace(array("\n", "\r"), array('',''), $this->_rawpackagefile)) { if ($this->getChannel() != 'pear.php.net') { return PEAR::raiseError('CRITICAL ERROR: package.xml downloaded does ' . 'not match value returned from xml-rpc'); } // be more lax for the existing PEAR packages that have not-ok // characters in their package.xml $this->_downloader->log(0, 'CRITICAL WARNING: The "' . $this->getPackage() . '" package has invalid characters in its ' . 'package.xml. The next version of PEAR may not be able to install ' . 'this package for security reasons. Please open a bug report at ' . 'http://pear.php.net/package/' . $this->getPackage() . '/bugs'); } } // whew, download worked! $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->debug); PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $pf = &$pkg->fromAnyFile($file, PEAR_VALIDATE_INSTALLING); PEAR::popErrorHandling(); if (PEAR::isError($pf)) { if (is_array($pf->getUserInfo())) { foreach ($pf->getUserInfo() as $err) { if (is_array($err)) { $err = $err['message']; } if (!isset($options['soft'])) { $this->_downloader->log(0, "Validation Error: $err"); } } } if (!isset($options['soft'])) { $this->_downloader->log(0, $pf->getMessage()); } ///FIXME need to pass back some error code that we can use to match with to cancel all further operations /// At least stop all deps of this package from being installed $out = $saveparam ? $saveparam : $param; $err = PEAR::raiseError('Download of "' . $out . '" succeeded, but it is not a valid package archive'); $this->_valid = false; return $err; } $this->_packagefile = &$pf; $this->setGroup('default'); // install the default dependency group return $this->_valid = true; } return $this->_valid = false; } /** * * @param string|array pass in an array of format * array( * 'package' => 'pname', * ['channel' => 'channame',] * ['version' => 'version',] * ['state' => 'state',]) * or a string of format [channame/]pname[-version|-state] */ function _fromString($param) { $options = $this->_downloader->getOptions(); $channel = $this->_config->get('default_channel'); PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $pname = $this->_registry->parsePackageName($param, $channel); PEAR::popErrorHandling(); if (PEAR::isError($pname)) { if ($pname->getCode() == 'invalid') { $this->_valid = false; return false; } if ($pname->getCode() == 'channel') { $parsed = $pname->getUserInfo(); if ($this->_downloader->discover($parsed['channel'])) { if ($this->_config->get('auto_discover')) { PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $pname = $this->_registry->parsePackageName($param, $channel); PEAR::popErrorHandling(); } else { if (!isset($options['soft'])) { $this->_downloader->log(0, 'Channel "' . $parsed['channel'] . '" is not initialized, use ' . '"pear channel-discover ' . $parsed['channel'] . '" to initialize' . 'or pear config-set auto_discover 1'); } } } if (PEAR::isError($pname)) { if (!isset($options['soft'])) { $this->_downloader->log(0, $pname->getMessage()); } if (is_array($param)) { $param = $this->_registry->parsedPackageNameToString($param); } $err = PEAR::raiseError('invalid package name/package file "' . $param . '"'); $this->_valid = false; return $err; } } else { if (!isset($options['soft'])) { $this->_downloader->log(0, $pname->getMessage()); } $err = PEAR::raiseError('invalid package name/package file "' . $param . '"'); $this->_valid = false; return $err; } } if (!isset($this->_type)) { $this->_type = 'rest'; } $this->_parsedname = $pname; $this->_explicitState = isset($pname['state']) ? $pname['state'] : false; $this->_explicitGroup = isset($pname['group']) ? true : false; $info = $this->_downloader->_getPackageDownloadUrl($pname); if (PEAR::isError($info)) { if ($info->getCode() != -976 && $pname['channel'] == 'pear.php.net') { // try pecl $pname['channel'] = 'pecl.php.net'; if ($test = $this->_downloader->_getPackageDownloadUrl($pname)) { if (!PEAR::isError($test)) { $info = PEAR::raiseError($info->getMessage() . ' - package ' . $this->_registry->parsedPackageNameToString($pname, true) . ' can be installed with "pecl install ' . $pname['package'] . '"'); } else { $pname['channel'] = 'pear.php.net'; } } else { $pname['channel'] = 'pear.php.net'; } } return $info; } $this->_rawpackagefile = $info['raw']; $ret = $this->_analyzeDownloadURL($info, $param, $pname); if (PEAR::isError($ret)) { return $ret; } if ($ret) { $this->_downloadURL = $ret; return $this->_valid = (bool) $ret; } } /** * @param array output of package.getDownloadURL * @param string|array|object information for detecting packages to be downloaded, and * for errors * @param array name information of the package * @param array|null packages to be downloaded * @param bool is this an optional dependency? * @param bool is this any kind of dependency? * @access private */ function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = false, $isdependency = false) { if (!is_string($param) && PEAR_Downloader_Package::willDownload($param, $params)) { return false; } if ($info === false) { $saveparam = !is_string($param) ? ", cannot download \"$param\"" : ''; // no releases exist return PEAR::raiseError('No releases for package "' . $this->_registry->parsedPackageNameToString($pname, true) . '" exist' . $saveparam); } if (strtolower($info['info']->getChannel()) != strtolower($pname['channel'])) { $err = false; if ($pname['channel'] == 'pecl.php.net') { if ($info['info']->getChannel() != 'pear.php.net') { $err = true; } } elseif ($info['info']->getChannel() == 'pecl.php.net') { if ($pname['channel'] != 'pear.php.net') { $err = true; } } else { $err = true; } if ($err) { return PEAR::raiseError('SECURITY ERROR: package in channel "' . $pname['channel'] . '" retrieved another channel\'s name for download! ("' . $info['info']->getChannel() . '")'); } } $preferred_state = $this->_config->get('preferred_state'); if (!isset($info['url'])) { $package_version = $this->_registry->packageInfo($info['info']->getPackage(), 'version', $info['info']->getChannel()); if ($this->isInstalled($info)) { if ($isdependency && version_compare($info['version'], $package_version, '<=')) { // ignore bogus errors of "failed to download dependency" // if it is already installed and the one that would be // downloaded is older or the same version (Bug #7219) return false; } } if ($info['version'] === $package_version) { if (!isset($options['soft'])) { $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] . '/' . $pname['package'] . '-' . $package_version. ', additionally the suggested version' . ' (' . $package_version . ') is the same as the locally installed one.'); } return false; } if (version_compare($info['version'], $package_version, '<=')) { if (!isset($options['soft'])) { $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] . '/' . $pname['package'] . '-' . $package_version . ', additionally the suggested version' . ' (' . $info['version'] . ') is a lower version than the locally installed one (' . $package_version . ').'); } return false; } $instead = ', will instead download version ' . $info['version'] . ', stability "' . $info['info']->getState() . '"'; // releases exist, but we failed to get any if (isset($this->_downloader->_options['force'])) { if (isset($pname['version'])) { $vs = ', version "' . $pname['version'] . '"'; } elseif (isset($pname['state'])) { $vs = ', stability "' . $pname['state'] . '"'; } elseif ($param == 'dependency') { if (!class_exists('PEAR_Common')) { require_once 'PEAR/Common.php'; } if (!in_array($info['info']->getState(), PEAR_Common::betterStates($preferred_state, true))) { if ($optional) { // don't spit out confusing error message return $this->_downloader->_getPackageDownloadUrl( array('package' => $pname['package'], 'channel' => $pname['channel'], 'version' => $info['version'])); } $vs = ' within preferred state "' . $preferred_state . '"'; } else { if (!class_exists('PEAR_Dependency2')) { require_once 'PEAR/Dependency2.php'; } if ($optional) { // don't spit out confusing error message return $this->_downloader->_getPackageDownloadUrl( array('package' => $pname['package'], 'channel' => $pname['channel'], 'version' => $info['version'])); } $vs = PEAR_Dependency2::_getExtraString($pname); $instead = ''; } } else { $vs = ' within preferred state "' . $preferred_state . '"'; } if (!isset($options['soft'])) { $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] . '/' . $pname['package'] . $vs . $instead); } // download the latest release return $this->_downloader->_getPackageDownloadUrl( array('package' => $pname['package'], 'channel' => $pname['channel'], 'version' => $info['version'])); } else { if (isset($info['php']) && $info['php']) { $err = PEAR::raiseError('Failed to download ' . $this->_registry->parsedPackageNameToString( array('channel' => $pname['channel'], 'package' => $pname['package']), true) . ', latest release is version ' . $info['php']['v'] . ', but it requires PHP version "' . $info['php']['m'] . '", use "' . $this->_registry->parsedPackageNameToString( array('channel' => $pname['channel'], 'package' => $pname['package'], 'version' => $info['php']['v'])) . '" to install', PEAR_DOWNLOADER_PACKAGE_PHPVERSION); return $err; } // construct helpful error message if (isset($pname['version'])) { $vs = ', version "' . $pname['version'] . '"'; } elseif (isset($pname['state'])) { $vs = ', stability "' . $pname['state'] . '"'; } elseif ($param == 'dependency') { if (!class_exists('PEAR_Common')) { require_once 'PEAR/Common.php'; } if (!in_array($info['info']->getState(), PEAR_Common::betterStates($preferred_state, true))) { if ($optional) { // don't spit out confusing error message, and don't die on // optional dep failure! return $this->_downloader->_getPackageDownloadUrl( array('package' => $pname['package'], 'channel' => $pname['channel'], 'version' => $info['version'])); } $vs = ' within preferred state "' . $preferred_state . '"'; } else { if (!class_exists('PEAR_Dependency2')) { require_once 'PEAR/Dependency2.php'; } if ($optional) { // don't spit out confusing error message, and don't die on // optional dep failure! return $this->_downloader->_getPackageDownloadUrl( array('package' => $pname['package'], 'channel' => $pname['channel'], 'version' => $info['version'])); } $vs = PEAR_Dependency2::_getExtraString($pname); } } else { $vs = ' within preferred state "' . $this->_downloader->config->get('preferred_state') . '"'; } $options = $this->_downloader->getOptions(); // this is only set by the "download-all" command if (isset($options['ignorepreferred_state'])) { $err = PEAR::raiseError( 'Failed to download ' . $this->_registry->parsedPackageNameToString( array('channel' => $pname['channel'], 'package' => $pname['package']), true) . $vs . ', latest release is version ' . $info['version'] . ', stability "' . $info['info']->getState() . '", use "' . $this->_registry->parsedPackageNameToString( array('channel' => $pname['channel'], 'package' => $pname['package'], 'version' => $info['version'])) . '" to install', PEAR_DOWNLOADER_PACKAGE_STATE); return $err; } // Checks if the user has a package installed already and checks the release against // the state against the installed package, this allows upgrades for packages // with lower stability than the preferred_state $stability = $this->_registry->packageInfo($pname['package'], 'stability', $pname['channel']); if (!$this->isInstalled($info) || !in_array($info['info']->getState(), PEAR_Common::betterStates($stability['release'], true)) ) { $err = PEAR::raiseError( 'Failed to download ' . $this->_registry->parsedPackageNameToString( array('channel' => $pname['channel'], 'package' => $pname['package']), true) . $vs . ', latest release is version ' . $info['version'] . ', stability "' . $info['info']->getState() . '", use "' . $this->_registry->parsedPackageNameToString( array('channel' => $pname['channel'], 'package' => $pname['package'], 'version' => $info['version'])) . '" to install'); return $err; } } } if (isset($info['deprecated']) && $info['deprecated']) { $this->_downloader->log(0, 'WARNING: "' . $this->_registry->parsedPackageNameToString( array('channel' => $info['info']->getChannel(), 'package' => $info['info']->getPackage()), true) . '" is deprecated in favor of "' . $this->_registry->parsedPackageNameToString($info['deprecated'], true) . '"'); } return $info; } } PK=1]xbgbgpear/PEAR/Common.phpnu[ * @author Tomas V. V. Cox * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1.0 * @deprecated File deprecated since Release 1.4.0a1 */ /** * Include error handling */ require_once 'PEAR.php'; /** * PEAR_Common error when an invalid PHP file is passed to PEAR_Common::analyzeSourceCode() */ define('PEAR_COMMON_ERROR_INVALIDPHP', 1); define('_PEAR_COMMON_PACKAGE_NAME_PREG', '[A-Za-z][a-zA-Z0-9_]+'); define('PEAR_COMMON_PACKAGE_NAME_PREG', '/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '\\z/'); // this should allow: 1, 1.0, 1.0RC1, 1.0dev, 1.0dev123234234234, 1.0a1, 1.0b1, 1.0pl1 define('_PEAR_COMMON_PACKAGE_VERSION_PREG', '\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?'); define('PEAR_COMMON_PACKAGE_VERSION_PREG', '/^' . _PEAR_COMMON_PACKAGE_VERSION_PREG . '\\z/i'); // XXX far from perfect :-) define('_PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '(' . _PEAR_COMMON_PACKAGE_NAME_PREG . ')(-([.0-9a-zA-Z]+))?'); define('PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_PACKAGE_DOWNLOAD_PREG . '\\z/'); define('_PEAR_CHANNELS_NAME_PREG', '[A-Za-z][a-zA-Z0-9\.]+'); define('PEAR_CHANNELS_NAME_PREG', '/^' . _PEAR_CHANNELS_NAME_PREG . '\\z/'); // this should allow any dns or IP address, plus a path - NO UNDERSCORES ALLOWED define('_PEAR_CHANNELS_SERVER_PREG', '[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*(\/[a-zA-Z0-9\-]+)*'); define('PEAR_CHANNELS_SERVER_PREG', '/^' . _PEAR_CHANNELS_SERVER_PREG . '\\z/i'); define('_PEAR_CHANNELS_PACKAGE_PREG', '(' ._PEAR_CHANNELS_SERVER_PREG . ')\/(' . _PEAR_COMMON_PACKAGE_NAME_PREG . ')'); define('PEAR_CHANNELS_PACKAGE_PREG', '/^' . _PEAR_CHANNELS_PACKAGE_PREG . '\\z/i'); define('_PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '(' . _PEAR_CHANNELS_NAME_PREG . ')::(' . _PEAR_COMMON_PACKAGE_NAME_PREG . ')(-([.0-9a-zA-Z]+))?'); define('PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_CHANNEL_DOWNLOAD_PREG . '\\z/'); /** * List of temporary files and directories registered by * PEAR_Common::addTempFile(). * @var array */ $GLOBALS['_PEAR_Common_tempfiles'] = array(); /** * Valid maintainer roles * @var array */ $GLOBALS['_PEAR_Common_maintainer_roles'] = array('lead','developer','contributor','helper'); /** * Valid release states * @var array */ $GLOBALS['_PEAR_Common_release_states'] = array('alpha','beta','stable','snapshot','devel'); /** * Valid dependency types * @var array */ $GLOBALS['_PEAR_Common_dependency_types'] = array('pkg','ext','php','prog','ldlib','rtlib','os','websrv','sapi'); /** * Valid dependency relations * @var array */ $GLOBALS['_PEAR_Common_dependency_relations'] = array('has','eq','lt','le','gt','ge','not', 'ne'); /** * Valid file roles * @var array */ $GLOBALS['_PEAR_Common_file_roles'] = array('php','ext','test','doc','data','src','script'); /** * Valid replacement types * @var array */ $GLOBALS['_PEAR_Common_replacement_types'] = array('php-const', 'pear-config', 'package-info'); /** * Valid "provide" types * @var array */ $GLOBALS['_PEAR_Common_provide_types'] = array('ext', 'prog', 'class', 'function', 'feature', 'api'); /** * Valid "provide" types * @var array */ $GLOBALS['_PEAR_Common_script_phases'] = array('pre-install', 'post-install', 'pre-uninstall', 'post-uninstall', 'pre-build', 'post-build', 'pre-configure', 'post-configure', 'pre-setup', 'post-setup'); /** * Class providing common functionality for PEAR administration classes. * @category pear * @package PEAR * @author Stig Bakken * @author Tomas V. V. Cox * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 * @deprecated This class will disappear, and its components will be spread * into smaller classes, like the AT&T breakup, as of Release 1.4.0a1 */ class PEAR_Common extends PEAR { /** * User Interface object (PEAR_Frontend_* class). If null, * the log() method uses print. * @var object */ var $ui = null; /** * Configuration object (PEAR_Config). * @var PEAR_Config */ var $config = null; /** stack of elements, gives some sort of XML context */ var $element_stack = array(); /** name of currently parsed XML element */ var $current_element; /** array of attributes of the currently parsed XML element */ var $current_attributes = array(); /** assoc with information about a package */ var $pkginfo = array(); var $current_path = null; /** * Flag variable used to mark a valid package file * @var boolean * @access private */ var $_validPackageFile; /** * PEAR_Common constructor * * @access public */ function __construct() { parent::__construct(); $this->config = &PEAR_Config::singleton(); $this->debug = $this->config->get('verbose'); } /** * PEAR_Common destructor * * @access private */ function _PEAR_Common() { // doesn't work due to bug #14744 //$tempfiles = $this->_tempfiles; $tempfiles =& $GLOBALS['_PEAR_Common_tempfiles']; while ($file = array_shift($tempfiles)) { if (@is_dir($file)) { if (!class_exists('System')) { require_once 'System.php'; } System::rm(array('-rf', $file)); } elseif (file_exists($file)) { unlink($file); } } } /** * Register a temporary file or directory. When the destructor is * executed, all registered temporary files and directories are * removed. * * @param string $file name of file or directory * * @return void * * @access public */ static function addTempFile($file) { if (!class_exists('PEAR_Frontend')) { require_once 'PEAR/Frontend.php'; } PEAR_Frontend::addTempFile($file); } /** * Wrapper to System::mkDir(), creates a directory as well as * any necessary parent directories. * * @param string $dir directory name * * @return bool TRUE on success, or a PEAR error * * @access public */ function mkDirHier($dir) { // Only used in Installer - move it there ? $this->log(2, "+ create dir $dir"); if (!class_exists('System')) { require_once 'System.php'; } return System::mkDir(array('-p', $dir)); } /** * Logging method. * * @param int $level log level (0 is quiet, higher is noisier) * @param string $msg message to write to the log * * @return void */ public function log($level, $msg, $append_crlf = true) { if ($this->debug >= $level) { if (!class_exists('PEAR_Frontend')) { require_once 'PEAR/Frontend.php'; } $ui = &PEAR_Frontend::singleton(); if (is_a($ui, 'PEAR_Frontend')) { $ui->log($msg, $append_crlf); } else { print "$msg\n"; } } } /** * Create and register a temporary directory. * * @param string $tmpdir (optional) Directory to use as tmpdir. * Will use system defaults (for example * /tmp or c:\windows\temp) if not specified * * @return string name of created directory * * @access public */ function mkTempDir($tmpdir = '') { $topt = $tmpdir ? array('-t', $tmpdir) : array(); $topt = array_merge($topt, array('-d', 'pear')); if (!class_exists('System')) { require_once 'System.php'; } if (!$tmpdir = System::mktemp($topt)) { return false; } self::addTempFile($tmpdir); return $tmpdir; } /** * Set object that represents the frontend to be used. * * @param object Reference of the frontend object * @return void * @access public */ function setFrontendObject(&$ui) { $this->ui = &$ui; } /** * Return an array containing all of the states that are more stable than * or equal to the passed in state * * @param string Release state * @param boolean Determines whether to include $state in the list * @return false|array False if $state is not a valid release state */ static function betterStates($state, $include = false) { static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable'); $i = array_search($state, $states); if ($i === false) { return false; } if ($include) { $i--; } return array_slice($states, $i + 1); } /** * Get the valid roles for a PEAR package maintainer * * @return array */ public static function getUserRoles() { return $GLOBALS['_PEAR_Common_maintainer_roles']; } /** * Get the valid package release states of packages * * @return array */ public static function getReleaseStates() { return $GLOBALS['_PEAR_Common_release_states']; } /** * Get the implemented dependency types (php, ext, pkg etc.) * * @return array */ public static function getDependencyTypes() { return $GLOBALS['_PEAR_Common_dependency_types']; } /** * Get the implemented dependency relations (has, lt, ge etc.) * * @return array */ public static function getDependencyRelations() { return $GLOBALS['_PEAR_Common_dependency_relations']; } /** * Get the implemented file roles * * @return array */ public static function getFileRoles() { return $GLOBALS['_PEAR_Common_file_roles']; } /** * Get the implemented file replacement types in * * @return array */ public static function getReplacementTypes() { return $GLOBALS['_PEAR_Common_replacement_types']; } /** * Get the implemented file replacement types in * * @return array */ public static function getProvideTypes() { return $GLOBALS['_PEAR_Common_provide_types']; } /** * Get the implemented file replacement types in * * @return array */ public static function getScriptPhases() { return $GLOBALS['_PEAR_Common_script_phases']; } /** * Test whether a string contains a valid package name. * * @param string $name the package name to test * * @return bool * * @access public */ function validPackageName($name) { return (bool)preg_match(PEAR_COMMON_PACKAGE_NAME_PREG, $name); } /** * Test whether a string contains a valid package version. * * @param string $ver the package version to test * * @return bool * * @access public */ function validPackageVersion($ver) { return (bool)preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver); } /** * @param string $path relative or absolute include path * @return boolean */ public static function isIncludeable($path) { if (file_exists($path) && is_readable($path)) { return true; } $ipath = explode(PATH_SEPARATOR, ini_get('include_path')); foreach ($ipath as $include) { $test = realpath($include . DIRECTORY_SEPARATOR . $path); if (file_exists($test) && is_readable($test)) { return true; } } return false; } function _postProcessChecks($pf) { if (!PEAR::isError($pf)) { return $this->_postProcessValidPackagexml($pf); } $errs = $pf->getUserinfo(); if (is_array($errs)) { foreach ($errs as $error) { $e = $this->raiseError($error['message'], $error['code'], null, null, $error); } } return $pf; } /** * Returns information about a package file. Expects the name of * a gzipped tar file as input. * * @param string $file name of .tgz file * * @return array array with package information * * @access public * @deprecated use PEAR_PackageFile->fromTgzFile() instead * */ function infoFromTgzFile($file) { $packagefile = new PEAR_PackageFile($this->config); $pf = &$packagefile->fromTgzFile($file, PEAR_VALIDATE_NORMAL); return $this->_postProcessChecks($pf); } /** * Returns information about a package file. Expects the name of * a package xml file as input. * * @param string $descfile name of package xml file * * @return array array with package information * * @access public * @deprecated use PEAR_PackageFile->fromPackageFile() instead * */ function infoFromDescriptionFile($descfile) { $packagefile = new PEAR_PackageFile($this->config); $pf = &$packagefile->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL); return $this->_postProcessChecks($pf); } /** * Returns information about a package file. Expects the contents * of a package xml file as input. * * @param string $data contents of package.xml file * * @return array array with package information * * @access public * @deprecated use PEAR_PackageFile->fromXmlstring() instead * */ function infoFromString($data) { $packagefile = new PEAR_PackageFile($this->config); $pf = &$packagefile->fromXmlString($data, PEAR_VALIDATE_NORMAL, false); return $this->_postProcessChecks($pf); } /** * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 * @return array */ function _postProcessValidPackagexml(&$pf) { if (!is_a($pf, 'PEAR_PackageFile_v2')) { $this->pkginfo = $pf->toArray(); return $this->pkginfo; } // sort of make this into a package.xml 1.0-style array // changelog is not converted to old format. $arr = $pf->toArray(true); $arr = array_merge($arr, $arr['old']); unset($arr['old'], $arr['xsdversion'], $arr['contents'], $arr['compatible'], $arr['channel'], $arr['uri'], $arr['dependencies'], $arr['phprelease'], $arr['extsrcrelease'], $arr['zendextsrcrelease'], $arr['extbinrelease'], $arr['zendextbinrelease'], $arr['bundle'], $arr['lead'], $arr['developer'], $arr['helper'], $arr['contributor']); $arr['filelist'] = $pf->getFilelist(); $this->pkginfo = $arr; return $arr; } /** * Returns package information from different sources * * This method is able to extract information about a package * from a .tgz archive or from a XML package definition file. * * @access public * @param string Filename of the source ('package.xml', '.tgz') * @return string * @deprecated use PEAR_PackageFile->fromAnyFile() instead */ function infoFromAny($info) { if (is_string($info) && file_exists($info)) { $packagefile = new PEAR_PackageFile($this->config); $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL); if (PEAR::isError($pf)) { $errs = $pf->getUserinfo(); if (is_array($errs)) { foreach ($errs as $error) { $e = $this->raiseError($error['message'], $error['code'], null, null, $error); } } return $pf; } return $this->_postProcessValidPackagexml($pf); } return $info; } /** * Return an XML document based on the package info (as returned * by the PEAR_Common::infoFrom* methods). * * @param array $pkginfo package info * * @return string XML data * * @access public * @deprecated use a PEAR_PackageFile_v* object's generator instead */ function xmlFromInfo($pkginfo) { $config = &PEAR_Config::singleton(); $packagefile = new PEAR_PackageFile($config); $pf = &$packagefile->fromArray($pkginfo); $gen = &$pf->getDefaultGenerator(); return $gen->toXml(PEAR_VALIDATE_PACKAGING); } /** * Validate XML package definition file. * * @param string $info Filename of the package archive or of the * package definition file * @param array $errors Array that will contain the errors * @param array $warnings Array that will contain the warnings * @param string $dir_prefix (optional) directory where source files * may be found, or empty if they are not available * @access public * @return boolean * @deprecated use the validation of PEAR_PackageFile objects */ function validatePackageInfo($info, &$errors, &$warnings, $dir_prefix = '') { $config = &PEAR_Config::singleton(); $packagefile = new PEAR_PackageFile($config); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); if (strpos($info, 'fromXmlString($info, PEAR_VALIDATE_NORMAL, ''); } else { $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL); } PEAR::staticPopErrorHandling(); if (PEAR::isError($pf)) { $errs = $pf->getUserinfo(); if (is_array($errs)) { foreach ($errs as $error) { if ($error['level'] == 'error') { $errors[] = $error['message']; } else { $warnings[] = $error['message']; } } } return false; } return true; } /** * Build a "provides" array from data returned by * analyzeSourceCode(). The format of the built array is like * this: * * array( * 'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'), * ... * ) * * * @param array $srcinfo array with information about a source file * as returned by the analyzeSourceCode() method. * * @return void * * @access public * */ function buildProvidesArray($srcinfo) { $file = basename($srcinfo['source_file']); $pn = ''; if (isset($this->_packageName)) { $pn = $this->_packageName; } $pnl = strlen($pn); foreach ($srcinfo['declared_classes'] as $class) { $key = "class;$class"; if (isset($this->pkginfo['provides'][$key])) { continue; } $this->pkginfo['provides'][$key] = array('file'=> $file, 'type' => 'class', 'name' => $class); if (isset($srcinfo['inheritance'][$class])) { $this->pkginfo['provides'][$key]['extends'] = $srcinfo['inheritance'][$class]; } } foreach ($srcinfo['declared_methods'] as $class => $methods) { foreach ($methods as $method) { $function = "$class::$method"; $key = "function;$function"; if ($method[0] == '_' || !strcasecmp($method, $class) || isset($this->pkginfo['provides'][$key])) { continue; } $this->pkginfo['provides'][$key] = array('file'=> $file, 'type' => 'function', 'name' => $function); } } foreach ($srcinfo['declared_functions'] as $function) { $key = "function;$function"; if ($function[0] == '_' || isset($this->pkginfo['provides'][$key])) { continue; } if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) { $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\""; } $this->pkginfo['provides'][$key] = array('file'=> $file, 'type' => 'function', 'name' => $function); } } /** * Analyze the source code of the given PHP file * * @param string Filename of the PHP file * @return mixed * @access public */ static function analyzeSourceCode($file) { if (!class_exists('PEAR_PackageFile_v2_Validator')) { require_once 'PEAR/PackageFile/v2/Validator.php'; } $a = new PEAR_PackageFile_v2_Validator; return $a->analyzeSourceCode($file); } function detectDependencies($any, $status_callback = null) { if (!function_exists("token_get_all")) { return false; } if (PEAR::isError($info = $this->infoFromAny($any))) { return $this->raiseError($info); } if (!is_array($info)) { return false; } $deps = array(); $used_c = $decl_c = $decl_f = $decl_m = array(); foreach ($info['filelist'] as $file => $fa) { $tmp = $this->analyzeSourceCode($file); $used_c = @array_merge($used_c, $tmp['used_classes']); $decl_c = @array_merge($decl_c, $tmp['declared_classes']); $decl_f = @array_merge($decl_f, $tmp['declared_functions']); $decl_m = @array_merge($decl_m, $tmp['declared_methods']); $inheri = @array_merge($inheri, $tmp['inheritance']); } $used_c = array_unique($used_c); $decl_c = array_unique($decl_c); $undecl_c = array_diff($used_c, $decl_c); return array('used_classes' => $used_c, 'declared_classes' => $decl_c, 'declared_methods' => $decl_m, 'declared_functions' => $decl_f, 'undeclared_classes' => $undecl_c, 'inheritance' => $inheri, ); } /** * Download a file through HTTP. Considers suggested file name in * Content-disposition: header and can run a callback function for * different events. The callback will be called with two * parameters: the callback type, and parameters. The implemented * callback types are: * * 'setup' called at the very beginning, parameter is a UI object * that should be used for all output * 'message' the parameter is a string with an informational message * 'saveas' may be used to save with a different file name, the * parameter is the filename that is about to be used. * If a 'saveas' callback returns a non-empty string, * that file name will be used as the filename instead. * Note that $save_dir will not be affected by this, only * the basename of the file. * 'start' download is starting, parameter is number of bytes * that are expected, or -1 if unknown * 'bytesread' parameter is the number of bytes read so far * 'done' download is complete, parameter is the total number * of bytes read * 'connfailed' if the TCP connection fails, this callback is called * with array(host,port,errno,errmsg) * 'writefailed' if writing to disk fails, this callback is called * with array(destfile,errmsg) * * If an HTTP proxy has been configured (http_proxy PEAR_Config * setting), the proxy will be used. * * @param string $url the URL to download * @param object $ui PEAR_Frontend_* instance * @param object $config PEAR_Config instance * @param string $save_dir (optional) directory to save file in * @param mixed $callback (optional) function/method to call for status * updates * @param false|string|array $lastmodified header values to check against * for caching * use false to return the header * values from this download * @param false|array $accept Accept headers to send * @param false|string $channel Channel to use for retrieving * authentication * * @return mixed Returns the full path of the downloaded file or a PEAR * error on failure. If the error is caused by * socket-related errors, the error object will * have the fsockopen error code available through * getCode(). If caching is requested, then return the header * values. * If $lastmodified was given and the there are no changes, * boolean false is returned. * * @access public */ function downloadHttp( $url, &$ui, $save_dir = '.', $callback = null, $lastmodified = null, $accept = false, $channel = false ) { if (!class_exists('PEAR_Downloader')) { require_once 'PEAR/Downloader.php'; } return PEAR_Downloader::_downloadHttp( $this, $url, $ui, $save_dir, $callback, $lastmodified, $accept, $channel ); } } require_once 'PEAR/Config.php'; require_once 'PEAR/PackageFile.php'; PK=1].a00pear/PEAR/Command.phpnu[ * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ /** * Needed for error handling */ require_once 'PEAR.php'; require_once 'PEAR/Frontend.php'; require_once 'PEAR/XMLParser.php'; /** * List of commands and what classes they are implemented in. * @var array command => implementing class */ $GLOBALS['_PEAR_Command_commandlist'] = array(); /** * List of commands and their descriptions * @var array command => description */ $GLOBALS['_PEAR_Command_commanddesc'] = array(); /** * List of shortcuts to common commands. * @var array shortcut => command */ $GLOBALS['_PEAR_Command_shortcuts'] = array(); /** * Array of command objects * @var array class => object */ $GLOBALS['_PEAR_Command_objects'] = array(); /** * PEAR command class, a simple factory class for administrative * commands. * * How to implement command classes: * * - The class must be called PEAR_Command_Nnn, installed in the * "PEAR/Common" subdir, with a method called getCommands() that * returns an array of the commands implemented by the class (see * PEAR/Command/Install.php for an example). * * - The class must implement a run() function that is called with three * params: * * (string) command name * (array) assoc array with options, freely defined by each * command, for example: * array('force' => true) * (array) list of the other parameters * * The run() function returns a PEAR_CommandResponse object. Use * these methods to get information: * * int getStatus() Returns PEAR_COMMAND_(SUCCESS|FAILURE|PARTIAL) * *_PARTIAL means that you need to issue at least * one more command to complete the operation * (used for example for validation steps). * * string getMessage() Returns a message for the user. Remember, * no HTML or other interface-specific markup. * * If something unexpected happens, run() returns a PEAR error. * * - DON'T OUTPUT ANYTHING! Return text for output instead. * * - DON'T USE HTML! The text you return will be used from both Gtk, * web and command-line interfaces, so for now, keep everything to * plain text. * * - DON'T USE EXIT OR DIE! Always use pear errors. From static * classes do PEAR::raiseError(), from other classes do * $this->raiseError(). * @category pear * @package PEAR * @author Stig Bakken * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ class PEAR_Command { // {{{ factory() /** * Get the right object for executing a command. * * @param string $command The name of the command * @param object $config Instance of PEAR_Config object * * @return object the command object or a PEAR error */ public static function &factory($command, &$config) { if (empty($GLOBALS['_PEAR_Command_commandlist'])) { PEAR_Command::registerCommands(); } if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) { $command = $GLOBALS['_PEAR_Command_shortcuts'][$command]; } if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) { $a = PEAR::raiseError("unknown command `$command'"); return $a; } $class = $GLOBALS['_PEAR_Command_commandlist'][$command]; if (!class_exists($class)) { require_once $GLOBALS['_PEAR_Command_objects'][$class]; } if (!class_exists($class)) { $a = PEAR::raiseError("unknown command `$command'"); return $a; } $ui =& PEAR_Command::getFrontendObject(); $obj = new $class($ui, $config); return $obj; } // }}} // {{{ & getObject() public static function &getObject($command) { $class = $GLOBALS['_PEAR_Command_commandlist'][$command]; if (!class_exists($class)) { require_once $GLOBALS['_PEAR_Command_objects'][$class]; } if (!class_exists($class)) { return PEAR::raiseError("unknown command `$command'"); } $ui =& PEAR_Command::getFrontendObject(); $config = &PEAR_Config::singleton(); $obj = new $class($ui, $config); return $obj; } // }}} // {{{ & getFrontendObject() /** * Get instance of frontend object. * * @return object|PEAR_Error */ public static function &getFrontendObject() { $a = &PEAR_Frontend::singleton(); return $a; } // }}} // {{{ & setFrontendClass() /** * Load current frontend class. * * @param string $uiclass Name of class implementing the frontend * * @return object the frontend object, or a PEAR error */ public static function &setFrontendClass($uiclass) { $a = &PEAR_Frontend::setFrontendClass($uiclass); return $a; } // }}} // {{{ setFrontendType() /** * Set current frontend. * * @param string $uitype Name of the frontend type (for example "CLI") * * @return object the frontend object, or a PEAR error */ public static function setFrontendType($uitype) { $uiclass = 'PEAR_Frontend_' . $uitype; return PEAR_Command::setFrontendClass($uiclass); } // }}} // {{{ registerCommands() /** * Scan through the Command directory looking for classes * and see what commands they implement. * * @param bool (optional) if FALSE (default), the new list of * commands should replace the current one. If TRUE, * new entries will be merged with old. * * @param string (optional) where (what directory) to look for * classes, defaults to the Command subdirectory of * the directory from where this file (__FILE__) is * included. * * @return bool TRUE on success, a PEAR error on failure */ public static function registerCommands($merge = false, $dir = null) { $parser = new PEAR_XMLParser; if ($dir === null) { $dir = dirname(__FILE__) . '/Command'; } if (!is_dir($dir)) { return PEAR::raiseError("registerCommands: opendir($dir) '$dir' does not exist or is not a directory"); } $dp = @opendir($dir); if (empty($dp)) { return PEAR::raiseError("registerCommands: opendir($dir) failed"); } if (!$merge) { $GLOBALS['_PEAR_Command_commandlist'] = array(); } while ($file = readdir($dp)) { if ($file[0] == '.' || substr($file, -4) != '.xml') { continue; } $f = substr($file, 0, -4); $class = "PEAR_Command_" . $f; // List of commands if (empty($GLOBALS['_PEAR_Command_objects'][$class])) { $GLOBALS['_PEAR_Command_objects'][$class] = "$dir/" . $f . '.php'; } $parser->parse(file_get_contents("$dir/$file")); $implements = $parser->getData(); foreach ($implements as $command => $desc) { if ($command == 'attribs') { continue; } if (isset($GLOBALS['_PEAR_Command_commandlist'][$command])) { return PEAR::raiseError('Command "' . $command . '" already registered in ' . 'class "' . $GLOBALS['_PEAR_Command_commandlist'][$command] . '"'); } $GLOBALS['_PEAR_Command_commandlist'][$command] = $class; $GLOBALS['_PEAR_Command_commanddesc'][$command] = $desc['summary']; if (isset($desc['shortcut'])) { $shortcut = $desc['shortcut']; if (isset($GLOBALS['_PEAR_Command_shortcuts'][$shortcut])) { return PEAR::raiseError('Command shortcut "' . $shortcut . '" already ' . 'registered to command "' . $command . '" in class "' . $GLOBALS['_PEAR_Command_commandlist'][$command] . '"'); } $GLOBALS['_PEAR_Command_shortcuts'][$shortcut] = $command; } if (isset($desc['options']) && $desc['options']) { foreach ($desc['options'] as $oname => $option) { if (isset($option['shortopt']) && strlen($option['shortopt']) > 1) { return PEAR::raiseError('Option "' . $oname . '" short option "' . $option['shortopt'] . '" must be ' . 'only 1 character in Command "' . $command . '" in class "' . $class . '"'); } } } } } ksort($GLOBALS['_PEAR_Command_shortcuts']); ksort($GLOBALS['_PEAR_Command_commandlist']); @closedir($dp); return true; } // }}} // {{{ getCommands() /** * Get the list of currently supported commands, and what * classes implement them. * * @return array command => implementing class */ public static function getCommands() { if (empty($GLOBALS['_PEAR_Command_commandlist'])) { PEAR_Command::registerCommands(); } return $GLOBALS['_PEAR_Command_commandlist']; } // }}} // {{{ getShortcuts() /** * Get the list of command shortcuts. * * @return array shortcut => command */ public static function getShortcuts() { if (empty($GLOBALS['_PEAR_Command_shortcuts'])) { PEAR_Command::registerCommands(); } return $GLOBALS['_PEAR_Command_shortcuts']; } // }}} // {{{ getGetoptArgs() /** * Compiles arguments for getopt. * * @param string $command command to get optstring for * @param string $short_args (reference) short getopt format * @param array $long_args (reference) long getopt format * * @return void */ public static function getGetoptArgs($command, &$short_args, &$long_args) { if (empty($GLOBALS['_PEAR_Command_commandlist'])) { PEAR_Command::registerCommands(); } if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) { $command = $GLOBALS['_PEAR_Command_shortcuts'][$command]; } if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) { return null; } $obj = &PEAR_Command::getObject($command); return $obj->getGetoptArgs($command, $short_args, $long_args); } // }}} // {{{ getDescription() /** * Get description for a command. * * @param string $command Name of the command * * @return string command description */ public static function getDescription($command) { if (!isset($GLOBALS['_PEAR_Command_commanddesc'][$command])) { return null; } return $GLOBALS['_PEAR_Command_commanddesc'][$command]; } // }}} // {{{ getHelp() /** * Get help for command. * * @param string $command Name of the command to return help for */ public static function getHelp($command) { $cmds = PEAR_Command::getCommands(); if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) { $command = $GLOBALS['_PEAR_Command_shortcuts'][$command]; } if (isset($cmds[$command])) { $obj = &PEAR_Command::getObject($command); return $obj->getHelp($command); } return false; } // }}} }PK=1] pear/PEAR/ErrorStack.phpnu[ * @copyright 2004-2008 Greg Beaver * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR_ErrorStack */ /** * Singleton storage * * Format: *
 * array(
 *  'package1' => PEAR_ErrorStack object,
 *  'package2' => PEAR_ErrorStack object,
 *  ...
 * )
 * 
* @access private * @global array $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] */ $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] = array(); /** * Global error callback (default) * * This is only used if set to non-false. * is the default callback for * all packages, whereas specific packages may set a default callback * for all instances, regardless of whether they are a singleton or not. * * To exclude non-singletons, only set the local callback for the singleton * @see PEAR_ErrorStack::setDefaultCallback() * @access private * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] */ $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] = array( '*' => false, ); /** * Global Log object (default) * * This is only used if set to non-false. Use to set a default log object for * all stacks, regardless of instantiation order or location * @see PEAR_ErrorStack::setDefaultLogger() * @access private * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] */ $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = false; /** * Global Overriding Callback * * This callback will override any error callbacks that specific loggers have set. * Use with EXTREME caution * @see PEAR_ErrorStack::staticPushCallback() * @access private * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] */ $GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array(); /**#@+ * One of four possible return values from the error Callback * @see PEAR_ErrorStack::_errorCallback() */ /** * If this is returned, then the error will be both pushed onto the stack * and logged. */ define('PEAR_ERRORSTACK_PUSHANDLOG', 1); /** * If this is returned, then the error will only be pushed onto the stack, * and not logged. */ define('PEAR_ERRORSTACK_PUSH', 2); /** * If this is returned, then the error will only be logged, but not pushed * onto the error stack. */ define('PEAR_ERRORSTACK_LOG', 3); /** * If this is returned, then the error is completely ignored. */ define('PEAR_ERRORSTACK_IGNORE', 4); /** * If this is returned, then the error is logged and die() is called. */ define('PEAR_ERRORSTACK_DIE', 5); /**#@-*/ /** * Error code for an attempt to instantiate a non-class as a PEAR_ErrorStack in * the singleton method. */ define('PEAR_ERRORSTACK_ERR_NONCLASS', 1); /** * Error code for an attempt to pass an object into {@link PEAR_ErrorStack::getMessage()} * that has no __toString() method */ define('PEAR_ERRORSTACK_ERR_OBJTOSTRING', 2); /** * Error Stack Implementation * * Usage: * * // global error stack * $global_stack = &PEAR_ErrorStack::singleton('MyPackage'); * // local error stack * $local_stack = new PEAR_ErrorStack('MyPackage'); * * @author Greg Beaver * @version 1.10.18 * @package PEAR_ErrorStack * @category Debugging * @copyright 2004-2008 Greg Beaver * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR_ErrorStack */ class PEAR_ErrorStack { /** * Errors are stored in the order that they are pushed on the stack. * @since 0.4alpha Errors are no longer organized by error level. * This renders pop() nearly unusable, and levels could be more easily * handled in a callback anyway * @var array * @access private */ var $_errors = array(); /** * Storage of errors by level. * * Allows easy retrieval and deletion of only errors from a particular level * @since PEAR 1.4.0dev * @var array * @access private */ var $_errorsByLevel = array(); /** * Package name this error stack represents * @var string * @access protected */ var $_package; /** * Determines whether a PEAR_Error is thrown upon every error addition * @var boolean * @access private */ var $_compat = false; /** * If set to a valid callback, this will be used to generate the error * message from the error code, otherwise the message passed in will be * used * @var false|string|array * @access private */ var $_msgCallback = false; /** * If set to a valid callback, this will be used to generate the error * context for an error. For PHP-related errors, this will be a file * and line number as retrieved from debug_backtrace(), but can be * customized for other purposes. The error might actually be in a separate * configuration file, or in a database query. * @var false|string|array * @access protected */ var $_contextCallback = false; /** * If set to a valid callback, this will be called every time an error * is pushed onto the stack. The return value will be used to determine * whether to allow an error to be pushed or logged. * * The return value must be one an PEAR_ERRORSTACK_* constant * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG * @var false|string|array * @access protected */ var $_errorCallback = array(); /** * PEAR::Log object for logging errors * @var false|Log * @access protected */ var $_logger = false; /** * Error messages - designed to be overridden * @var array * @abstract */ var $_errorMsgs = array(); /** * Set up a new error stack * * @param string $package name of the package this error stack represents * @param callback $msgCallback callback used for error message generation * @param callback $contextCallback callback used for context generation, * defaults to {@link getFileLine()} * @param boolean $throwPEAR_Error */ function __construct($package, $msgCallback = false, $contextCallback = false, $throwPEAR_Error = false) { $this->_package = $package; $this->setMessageCallback($msgCallback); $this->setContextCallback($contextCallback); $this->_compat = $throwPEAR_Error; } /** * Return a single error stack for this package. * * Note that all parameters are ignored if the stack for package $package * has already been instantiated * @param string $package name of the package this error stack represents * @param callback $msgCallback callback used for error message generation * @param callback $contextCallback callback used for context generation, * defaults to {@link getFileLine()} * @param boolean $throwPEAR_Error * @param string $stackClass class to instantiate * * @return PEAR_ErrorStack */ public static function &singleton( $package, $msgCallback = false, $contextCallback = false, $throwPEAR_Error = false, $stackClass = 'PEAR_ErrorStack' ) { if (isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) { return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]; } if (!class_exists($stackClass)) { if (function_exists('debug_backtrace')) { $trace = debug_backtrace(); } PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_NONCLASS, 'exception', array('stackclass' => $stackClass), 'stack class "%stackclass%" is not a valid class name (should be like PEAR_ErrorStack)', false, $trace); } $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package] = new $stackClass($package, $msgCallback, $contextCallback, $throwPEAR_Error); return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]; } /** * Internal error handler for PEAR_ErrorStack class * * Dies if the error is an exception (and would have died anyway) * @access private */ function _handleError($err) { if ($err['level'] == 'exception') { $message = $err['message']; if (isset($_SERVER['REQUEST_URI'])) { echo '
'; } else { echo "\n"; } var_dump($err['context']); die($message); } } /** * Set up a PEAR::Log object for all error stacks that don't have one * @param Log $log */ public static function setDefaultLogger(&$log) { if (is_object($log) && method_exists($log, 'log') ) { $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log; } elseif (is_callable($log)) { $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log; } } /** * Set up a PEAR::Log object for this error stack * @param Log $log */ function setLogger(&$log) { if (is_object($log) && method_exists($log, 'log') ) { $this->_logger = &$log; } elseif (is_callable($log)) { $this->_logger = &$log; } } /** * Set an error code => error message mapping callback * * This method sets the callback that can be used to generate error * messages for any instance * @param array|string Callback function/method */ function setMessageCallback($msgCallback) { if (!$msgCallback) { $this->_msgCallback = array(&$this, 'getErrorMessage'); } else { if (is_callable($msgCallback)) { $this->_msgCallback = $msgCallback; } } } /** * Get an error code => error message mapping callback * * This method returns the current callback that can be used to generate error * messages * @return array|string|false Callback function/method or false if none */ function getMessageCallback() { return $this->_msgCallback; } /** * Sets a default callback to be used by all error stacks * * This method sets the callback that can be used to generate error * messages for a singleton * @param array|string Callback function/method * @param string Package name, or false for all packages */ public static function setDefaultCallback($callback = false, $package = false) { if (!is_callable($callback)) { $callback = false; } $package = $package ? $package : '*'; $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$package] = $callback; } /** * Set a callback that generates context information (location of error) for an error stack * * This method sets the callback that can be used to generate context * information for an error. Passing in NULL will disable context generation * and remove the expensive call to debug_backtrace() * @param array|string|null Callback function/method */ function setContextCallback($contextCallback) { if ($contextCallback === null) { return $this->_contextCallback = false; } if (!$contextCallback) { $this->_contextCallback = array(&$this, 'getFileLine'); } else { if (is_callable($contextCallback)) { $this->_contextCallback = $contextCallback; } } } /** * Set an error Callback * If set to a valid callback, this will be called every time an error * is pushed onto the stack. The return value will be used to determine * whether to allow an error to be pushed or logged. * * The return value must be one of the ERRORSTACK_* constants. * * This functionality can be used to emulate PEAR's pushErrorHandling, and * the PEAR_ERROR_CALLBACK mode, without affecting the integrity of * the error stack or logging * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG * @see popCallback() * @param string|array $cb */ function pushCallback($cb) { array_push($this->_errorCallback, $cb); } /** * Remove a callback from the error callback stack * @see pushCallback() * @return array|string|false */ function popCallback() { if (!count($this->_errorCallback)) { return false; } return array_pop($this->_errorCallback); } /** * Set a temporary overriding error callback for every package error stack * * Use this to temporarily disable all existing callbacks (can be used * to emulate the @ operator, for instance) * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG * @see staticPopCallback(), pushCallback() * @param string|array $cb */ public static function staticPushCallback($cb) { array_push($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'], $cb); } /** * Remove a temporary overriding error callback * @see staticPushCallback() * @return array|string|false */ public static function staticPopCallback() { $ret = array_pop($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK']); if (!is_array($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'])) { $GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array(); } return $ret; } /** * Add an error to the stack * * If the message generator exists, it is called with 2 parameters. * - the current Error Stack object * - an array that is in the same format as an error. Available indices * are 'code', 'package', 'time', 'params', 'level', and 'context' * * Next, if the error should contain context information, this is * handled by the context grabbing method. * Finally, the error is pushed onto the proper error stack * @param int $code Package-specific error code * @param string $level Error level. This is NOT spell-checked * @param array $params associative array of error parameters * @param string $msg Error message, or a portion of it if the message * is to be generated * @param array $repackage If this error re-packages an error pushed by * another package, place the array returned from * {@link pop()} in this parameter * @param array $backtrace Protected parameter: use this to pass in the * {@link debug_backtrace()} that should be used * to find error context * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also * thrown. If a PEAR_Error is returned, the userinfo * property is set to the following array: * * * array( * 'code' => $code, * 'params' => $params, * 'package' => $this->_package, * 'level' => $level, * 'time' => time(), * 'context' => $context, * 'message' => $msg, * //['repackage' => $err] repackaged error array/Exception class * ); * * * Normally, the previous array is returned. */ function push($code, $level = 'error', $params = array(), $msg = false, $repackage = false, $backtrace = false) { $context = false; // grab error context if ($this->_contextCallback) { if (!$backtrace) { $backtrace = debug_backtrace(); } $context = call_user_func($this->_contextCallback, $code, $params, $backtrace); } // save error $time = explode(' ', microtime()); $time = $time[1] + $time[0]; $err = array( 'code' => $code, 'params' => $params, 'package' => $this->_package, 'level' => $level, 'time' => $time, 'context' => $context, 'message' => $msg, ); if ($repackage) { $err['repackage'] = $repackage; } // set up the error message, if necessary if ($this->_msgCallback) { $msg = call_user_func_array($this->_msgCallback, array(&$this, $err)); $err['message'] = $msg; } $push = $log = true; $die = false; // try the overriding callback first $callback = $this->staticPopCallback(); if ($callback) { $this->staticPushCallback($callback); } if (!is_callable($callback)) { // try the local callback next $callback = $this->popCallback(); if (is_callable($callback)) { $this->pushCallback($callback); } else { // try the default callback $callback = isset($GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package]) ? $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package] : $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']['*']; } } if (is_callable($callback)) { switch(call_user_func($callback, $err)){ case PEAR_ERRORSTACK_IGNORE: return $err; break; case PEAR_ERRORSTACK_PUSH: $log = false; break; case PEAR_ERRORSTACK_LOG: $push = false; break; case PEAR_ERRORSTACK_DIE: $die = true; break; // anything else returned has the same effect as pushandlog } } if ($push) { array_unshift($this->_errors, $err); if (!isset($this->_errorsByLevel[$err['level']])) { $this->_errorsByLevel[$err['level']] = array(); } $this->_errorsByLevel[$err['level']][] = &$this->_errors[0]; } if ($log) { if ($this->_logger || $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']) { $this->_log($err); } } if ($die) { die(); } if ($this->_compat && $push) { return $this->raiseError($msg, $code, null, null, $err); } return $err; } /** * Static version of {@link push()} * * @param string $package Package name this error belongs to * @param int $code Package-specific error code * @param string $level Error level. This is NOT spell-checked * @param array $params associative array of error parameters * @param string $msg Error message, or a portion of it if the message * is to be generated * @param array $repackage If this error re-packages an error pushed by * another package, place the array returned from * {@link pop()} in this parameter * @param array $backtrace Protected parameter: use this to pass in the * {@link debug_backtrace()} that should be used * to find error context * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also * thrown. see docs for {@link push()} */ public static function staticPush( $package, $code, $level = 'error', $params = array(), $msg = false, $repackage = false, $backtrace = false ) { $s = &PEAR_ErrorStack::singleton($package); if ($s->_contextCallback) { if (!$backtrace) { if (function_exists('debug_backtrace')) { $backtrace = debug_backtrace(); } } } return $s->push($code, $level, $params, $msg, $repackage, $backtrace); } /** * Log an error using PEAR::Log * @param array $err Error array * @param array $levels Error level => Log constant map * @access protected */ function _log($err) { if ($this->_logger) { $logger = &$this->_logger; } else { $logger = &$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']; } if (is_a($logger, 'Log')) { $levels = array( 'exception' => PEAR_LOG_CRIT, 'alert' => PEAR_LOG_ALERT, 'critical' => PEAR_LOG_CRIT, 'error' => PEAR_LOG_ERR, 'warning' => PEAR_LOG_WARNING, 'notice' => PEAR_LOG_NOTICE, 'info' => PEAR_LOG_INFO, 'debug' => PEAR_LOG_DEBUG); if (isset($levels[$err['level']])) { $level = $levels[$err['level']]; } else { $level = PEAR_LOG_INFO; } $logger->log($err['message'], $level, $err); } else { // support non-standard logs call_user_func($logger, $err); } } /** * Pop an error off of the error stack * * @return false|array * @since 0.4alpha it is no longer possible to specify a specific error * level to return - the last error pushed will be returned, instead */ function pop() { $err = @array_shift($this->_errors); if (!is_null($err)) { @array_pop($this->_errorsByLevel[$err['level']]); if (!count($this->_errorsByLevel[$err['level']])) { unset($this->_errorsByLevel[$err['level']]); } } return $err; } /** * Pop an error off of the error stack, static method * * @param string package name * @return boolean * @since PEAR1.5.0a1 */ static function staticPop($package) { if ($package) { if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) { return false; } return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->pop(); } } /** * Determine whether there are any errors on the stack * @param string|array Level name. Use to determine if any errors * of level (string), or levels (array) have been pushed * @return boolean */ function hasErrors($level = false) { if ($level) { return isset($this->_errorsByLevel[$level]); } return count($this->_errors); } /** * Retrieve all errors since last purge * * @param boolean set in order to empty the error stack * @param string level name, to return only errors of a particular severity * @return array */ function getErrors($purge = false, $level = false) { if (!$purge) { if ($level) { if (!isset($this->_errorsByLevel[$level])) { return array(); } else { return $this->_errorsByLevel[$level]; } } else { return $this->_errors; } } if ($level) { $ret = $this->_errorsByLevel[$level]; foreach ($this->_errorsByLevel[$level] as $i => $unused) { // entries are references to the $_errors array $this->_errorsByLevel[$level][$i] = false; } // array_filter removes all entries === false $this->_errors = array_filter($this->_errors); unset($this->_errorsByLevel[$level]); return $ret; } $ret = $this->_errors; $this->_errors = array(); $this->_errorsByLevel = array(); return $ret; } /** * Determine whether there are any errors on a single error stack, or on any error stack * * The optional parameter can be used to test the existence of any errors without the need of * singleton instantiation * @param string|false Package name to check for errors * @param string Level name to check for a particular severity * @return boolean */ public static function staticHasErrors($package = false, $level = false) { if ($package) { if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) { return false; } return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->hasErrors($level); } foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) { if ($obj->hasErrors($level)) { return true; } } return false; } /** * Get a list of all errors since last purge, organized by package * @since PEAR 1.4.0dev BC break! $level is now in the place $merge used to be * @param boolean $purge Set to purge the error stack of existing errors * @param string $level Set to a level name in order to retrieve only errors of a particular level * @param boolean $merge Set to return a flat array, not organized by package * @param array $sortfunc Function used to sort a merged array - default * sorts by time, and should be good for most cases * * @return array */ public static function staticGetErrors( $purge = false, $level = false, $merge = false, $sortfunc = array('PEAR_ErrorStack', '_sortErrors') ) { $ret = array(); if (!is_callable($sortfunc)) { $sortfunc = array('PEAR_ErrorStack', '_sortErrors'); } foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) { $test = $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->getErrors($purge, $level); if ($test) { if ($merge) { $ret = array_merge($ret, $test); } else { $ret[$package] = $test; } } } if ($merge) { usort($ret, $sortfunc); } return $ret; } /** * Error sorting function, sorts by time * @access private */ public static function _sortErrors($a, $b) { if ($a['time'] == $b['time']) { return 0; } if ($a['time'] < $b['time']) { return 1; } return -1; } /** * Standard file/line number/function/class context callback * * This function uses a backtrace generated from {@link debug_backtrace()} * and so will not work at all in PHP < 4.3.0. The frame should * reference the frame that contains the source of the error. * @return array|false either array('file' => file, 'line' => line, * 'function' => function name, 'class' => class name) or * if this doesn't work, then false * @param unused * @param integer backtrace frame. * @param array Results of debug_backtrace() */ public static function getFileLine($code, $params, $backtrace = null) { if ($backtrace === null) { return false; } $frame = 0; $functionframe = 1; if (!isset($backtrace[1])) { $functionframe = 0; } else { while (isset($backtrace[$functionframe]['function']) && $backtrace[$functionframe]['function'] == 'eval' && isset($backtrace[$functionframe + 1])) { $functionframe++; } } if (isset($backtrace[$frame])) { if (!isset($backtrace[$frame]['file'])) { $frame++; } $funcbacktrace = $backtrace[$functionframe]; $filebacktrace = $backtrace[$frame]; $ret = array('file' => $filebacktrace['file'], 'line' => $filebacktrace['line']); // rearrange for eval'd code or create function errors if (strpos($filebacktrace['file'], '(') && preg_match(';^(.*?)\((\d+)\) : (.*?)\\z;', $filebacktrace['file'], $matches)) { $ret['file'] = $matches[1]; $ret['line'] = $matches[2] + 0; } if (isset($funcbacktrace['function']) && isset($backtrace[1])) { if ($funcbacktrace['function'] != 'eval') { if ($funcbacktrace['function'] == '__lambda_func') { $ret['function'] = 'create_function() code'; } else { $ret['function'] = $funcbacktrace['function']; } } } if (isset($funcbacktrace['class']) && isset($backtrace[1])) { $ret['class'] = $funcbacktrace['class']; } return $ret; } return false; } /** * Standard error message generation callback * * This method may also be called by a custom error message generator * to fill in template values from the params array, simply * set the third parameter to the error message template string to use * * The special variable %__msg% is reserved: use it only to specify * where a message passed in by the user should be placed in the template, * like so: * * Error message: %msg% - internal error * * If the message passed like so: * * * $stack->push(ERROR_CODE, 'error', array(), 'server error 500'); * * * The returned error message will be "Error message: server error 500 - * internal error" * @param PEAR_ErrorStack * @param array * @param string|false Pre-generated error message template * * @return string */ public static function getErrorMessage(&$stack, $err, $template = false) { if ($template) { $mainmsg = $template; } else { $mainmsg = $stack->getErrorMessageTemplate($err['code']); } $mainmsg = str_replace('%__msg%', $err['message'], $mainmsg); if (is_array($err['params']) && count($err['params'])) { foreach ($err['params'] as $name => $val) { if (is_array($val)) { // @ is needed in case $val is a multi-dimensional array $val = @implode(', ', $val); } if (is_object($val)) { if (method_exists($val, '__toString')) { $val = $val->__toString(); } else { PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_OBJTOSTRING, 'warning', array('obj' => get_class($val)), 'object %obj% passed into getErrorMessage, but has no __toString() method'); $val = 'Object'; } } $mainmsg = str_replace('%' . $name . '%', $val, $mainmsg); } } return $mainmsg; } /** * Standard Error Message Template generator from code * @return string */ function getErrorMessageTemplate($code) { if (!isset($this->_errorMsgs[$code])) { return '%__msg%'; } return $this->_errorMsgs[$code]; } /** * Set the Error Message Template array * * The array format must be: *
     * array(error code => 'message template',...)
     * 
* * Error message parameters passed into {@link push()} will be used as input * for the error message. If the template is 'message %foo% was %bar%', and the * parameters are array('foo' => 'one', 'bar' => 'six'), the error message returned will * be 'message one was six' * @return string */ function setErrorMessageTemplate($template) { $this->_errorMsgs = $template; } /** * emulate PEAR::raiseError() * * @return PEAR_Error */ function raiseError() { require_once 'PEAR.php'; $args = func_get_args(); return call_user_func_array(array('PEAR', 'raiseError'), $args); } } $stack = &PEAR_ErrorStack::singleton('PEAR_ErrorStack'); $stack->pushCallback(array('PEAR_ErrorStack', '_handleError')); ?> PK=1]Xpear/PEAR/Installer/Role.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * base class for installer roles */ require_once 'PEAR/Installer/Role/Common.php'; require_once 'PEAR/XMLParser.php'; /** * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_Installer_Role { /** * Set up any additional configuration variables that file roles require * * Never call this directly, it is called by the PEAR_Config constructor * @param PEAR_Config */ public static function initializeConfig(&$config) { if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) { PEAR_Installer_Role::registerRoles(); } foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $class => $info) { if (!$info['config_vars']) { continue; } $config->_addConfigVars($class, $info['config_vars']); } } /** * @param PEAR_PackageFile_v2 * @param string role name * @param PEAR_Config * @return PEAR_Installer_Role_Common */ public static function &factory($pkg, $role, &$config) { if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) { PEAR_Installer_Role::registerRoles(); } if (!in_array($role, PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) { $a = false; return $a; } $a = 'PEAR_Installer_Role_' . ucfirst($role); if (!class_exists($a)) { require_once str_replace('_', '/', $a) . '.php'; } $b = new $a($config); return $b; } /** * Get a list of file roles that are valid for the particular release type. * * For instance, src files serve no purpose in regular php releases. * @param string * @param bool clear cache * @return array */ public static function getValidRoles($release, $clear = false) { if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) { PEAR_Installer_Role::registerRoles(); } static $ret = array(); if ($clear) { $ret = array(); } if (isset($ret[$release])) { return $ret[$release]; } $ret[$release] = array(); foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) { if (in_array($release, $okreleases['releasetypes'])) { $ret[$release][] = strtolower(str_replace('PEAR_Installer_Role_', '', $role)); } } return $ret[$release]; } /** * Get a list of roles that require their files to be installed * * Most roles must be installed, but src and package roles, for instance * are pseudo-roles. src files are compiled into a new extension. Package * roles are actually fully bundled releases of a package * @param bool clear cache * @return array */ public static function getInstallableRoles($clear = false) { if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) { PEAR_Installer_Role::registerRoles(); } static $ret; if ($clear) { unset($ret); } if (isset($ret)) { return $ret; } $ret = array(); foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) { if ($okreleases['installable']) { $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role)); } } return $ret; } /** * Return an array of roles that are affected by the baseinstalldir attribute * * Most roles ignore this attribute, and instead install directly into: * PackageName/filepath * so a tests file tests/file.phpt is installed into PackageName/tests/filepath.php * @param bool clear cache * @return array */ public static function getBaseinstallRoles($clear = false) { if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) { PEAR_Installer_Role::registerRoles(); } static $ret; if ($clear) { unset($ret); } if (isset($ret)) { return $ret; } $ret = array(); foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) { if ($okreleases['honorsbaseinstall']) { $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role)); } } return $ret; } /** * Return an array of file roles that should be analyzed for PHP content at package time, * like the "php" role. * @param bool clear cache * @return array */ public static function getPhpRoles($clear = false) { if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) { PEAR_Installer_Role::registerRoles(); } static $ret; if ($clear) { unset($ret); } if (isset($ret)) { return $ret; } $ret = array(); foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) { if ($okreleases['phpfile']) { $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role)); } } return $ret; } /** * Scan through the Command directory looking for classes * and see what commands they implement. * @param string which directory to look for classes, defaults to * the Installer/Roles subdirectory of * the directory from where this file (__FILE__) is * included. * * @return bool TRUE on success, a PEAR error on failure */ public static function registerRoles($dir = null) { $GLOBALS['_PEAR_INSTALLER_ROLES'] = array(); $parser = new PEAR_XMLParser; if ($dir === null) { $dir = dirname(__FILE__) . '/Role'; } if (!file_exists($dir) || !is_dir($dir)) { return PEAR::raiseError("registerRoles: opendir($dir) failed: does not exist/is not directory"); } $dp = @opendir($dir); if (empty($dp)) { return PEAR::raiseError("registerRoles: opendir($dir) failed: $php_errmsg"); } while ($entry = readdir($dp)) { if ($entry[0] == '.' || substr($entry, -4) != '.xml') { continue; } $class = "PEAR_Installer_Role_".substr($entry, 0, -4); // List of roles if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'][$class])) { $file = "$dir/$entry"; $parser->parse(file_get_contents($file)); $data = $parser->getData(); if (!is_array($data['releasetypes'])) { $data['releasetypes'] = array($data['releasetypes']); } $GLOBALS['_PEAR_INSTALLER_ROLES'][$class] = $data; } } closedir($dp); ksort($GLOBALS['_PEAR_INSTALLER_ROLES']); PEAR_Installer_Role::getBaseinstallRoles(true); PEAR_Installer_Role::getInstallableRoles(true); PEAR_Installer_Role::getPhpRoles(true); PEAR_Installer_Role::getValidRoles('****', true); return true; } }PK=1]BB pear/PEAR/Installer/Role/Ext.xmlnu[ extbin zendextbin 1 ext_dir 1 1 PK=1]ç   pear/PEAR/Installer/Role/Doc.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_Installer_Role_Doc extends PEAR_Installer_Role_Common {} ?>PK=1]>1H pear/PEAR/Installer/Role/Www.xmlnu[ php extsrc extbin zendextsrc zendextbin 1 www_dir 1 PK=1]̊К   pear/PEAR/Installer/Role/Ext.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_Installer_Role_Ext extends PEAR_Installer_Role_Common {} ?>PK=1]0c pear/PEAR/Installer/Role/Man.xmlnu[ php extsrc extbin zendextsrc zendextbin 1 man_dir 1 PK=1]B] !pear/PEAR/Installer/Role/Test.xmlnu[ php extsrc extbin zendextsrc zendextbin 1 test_dir PK=1]3]   pear/PEAR/Installer/Role/Www.phpnu[ * @copyright 2007-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.7.0 */ /** * @category pear * @package PEAR * @author Greg Beaver * @copyright 2007-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.7.0 */ class PEAR_Installer_Role_Www extends PEAR_Installer_Role_Common {} ?>PK=1]iL!pear/PEAR/Installer/Role/Test.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_Installer_Role_Test extends PEAR_Installer_Role_Common {} ?>PK=1]h&P* pear/PEAR/Installer/Role/Doc.xmlnu[ php extsrc extbin zendextsrc zendextbin 1 doc_dir PK=1](GG#pear/PEAR/Installer/Role/Common.phpnu[ * @copyright 1997-2006 The PHP Group * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * Base class for all installation roles. * * This class allows extensibility of file roles. Packages with complex * customization can now provide custom file roles along with the possibility of * adding configuration values to match. * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2006 The PHP Group * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_Installer_Role_Common { /** * @var PEAR_Config * @access protected */ var $config; /** * @param PEAR_Config */ function __construct(&$config) { $this->config = $config; } /** * Retrieve configuration information about a file role from its XML info * * @param string $role Role Classname, as in "PEAR_Installer_Role_Data" * @return array */ function getInfo($role) { if (empty($GLOBALS['_PEAR_INSTALLER_ROLES'][$role])) { return PEAR::raiseError('Unknown Role class: "' . $role . '"'); } return $GLOBALS['_PEAR_INSTALLER_ROLES'][$role]; } /** * This is called for each file to set up the directories and files * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 * @param array attributes from the tag * @param string file name * @return array an array consisting of: * * 1 the original, pre-baseinstalldir installation directory * 2 the final installation directory * 3 the full path to the final location of the file * 4 the location of the pre-installation file */ function processInstallation($pkg, $atts, $file, $tmp_path, $layer = null) { $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this))))); if (PEAR::isError($roleInfo)) { return $roleInfo; } if (!$roleInfo['locationconfig']) { return false; } if ($roleInfo['honorsbaseinstall']) { $dest_dir = $save_destdir = $this->config->get($roleInfo['locationconfig'], $layer, $pkg->getChannel()); if (!empty($atts['baseinstalldir'])) { $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir']; } } elseif ($roleInfo['unusualbaseinstall']) { $dest_dir = $save_destdir = $this->config->get($roleInfo['locationconfig'], $layer, $pkg->getChannel()) . DIRECTORY_SEPARATOR . $pkg->getPackage(); if (!empty($atts['baseinstalldir'])) { $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir']; } } else { $dest_dir = $save_destdir = $this->config->get($roleInfo['locationconfig'], $layer, $pkg->getChannel()) . DIRECTORY_SEPARATOR . $pkg->getPackage(); } if (dirname($file) != '.' && empty($atts['install-as'])) { $dest_dir .= DIRECTORY_SEPARATOR . dirname($file); } if (empty($atts['install-as'])) { $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file); } else { $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as']; } $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file; // Clean up the DIRECTORY_SEPARATOR mess $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR; list($dest_dir, $dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"), array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), array($dest_dir, $dest_file, $orig_file)); return array($save_destdir, $dest_dir, $dest_file, $orig_file); } /** * Get the name of the configuration variable that specifies the location of this file * @return string|false */ function getLocationConfig() { $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this))))); if (PEAR::isError($roleInfo)) { return $roleInfo; } return $roleInfo['locationconfig']; } /** * Do any unusual setup here * @param PEAR_Installer * @param PEAR_PackageFile_v2 * @param array file attributes * @param string file name */ function setup(&$installer, $pkg, $atts, $file) { } function isExecutable() { $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this))))); if (PEAR::isError($roleInfo)) { return $roleInfo; } return $roleInfo['executable']; } function isInstallable() { $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this))))); if (PEAR::isError($roleInfo)) { return $roleInfo; } return $roleInfo['installable']; } function isExtension() { $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this))))); if (PEAR::isError($roleInfo)) { return $roleInfo; } return $roleInfo['phpextension']; } } ?> PK=1]fsz!pear/PEAR/Installer/Role/Data.xmlnu[ php extsrc extbin zendextsrc zendextbin 1 data_dir PK=1]<( pear/PEAR/Installer/Role/Cfg.phpnu[ * @copyright 2007-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.7.0 */ /** * @category pear * @package PEAR * @author Greg Beaver * @copyright 2007-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.7.0 */ class PEAR_Installer_Role_Cfg extends PEAR_Installer_Role_Common { /** * @var PEAR_Installer */ var $installer; /** * the md5 of the original file * * @var unknown_type */ var $md5 = null; /** * Do any unusual setup here * @param PEAR_Installer * @param PEAR_PackageFile_v2 * @param array file attributes * @param string file name */ function setup(&$installer, $pkg, $atts, $file) { $this->installer = &$installer; $reg = &$this->installer->config->getRegistry(); $package = $reg->getPackage($pkg->getPackage(), $pkg->getChannel()); if ($package) { $filelist = $package->getFilelist(); if (isset($filelist[$file]) && isset($filelist[$file]['md5sum'])) { $this->md5 = $filelist[$file]['md5sum']; } } } function processInstallation($pkg, $atts, $file, $tmp_path, $layer = null) { $test = parent::processInstallation($pkg, $atts, $file, $tmp_path, $layer); if (@file_exists($test[2]) && @file_exists($test[3])) { $md5 = md5_file($test[2]); // configuration has already been installed, check for mods if ($md5 !== $this->md5 && $md5 !== md5_file($test[3])) { // configuration has been modified, so save our version as // configfile-version $old = $test[2]; $test[2] .= '.new-' . $pkg->getVersion(); // backup original and re-install it PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $tmpcfg = $this->config->get('temp_dir'); $newloc = System::mkdir(array('-p', $tmpcfg)); if (!$newloc) { // try temp_dir $newloc = System::mktemp(array('-d')); if (!$newloc || PEAR::isError($newloc)) { PEAR::popErrorHandling(); return PEAR::raiseError('Could not save existing configuration file '. $old . ', unable to install. Please set temp_dir ' . 'configuration variable to a writeable location and try again'); } } else { $newloc = $tmpcfg; } $temp_file = $newloc . DIRECTORY_SEPARATOR . uniqid('savefile'); if (!@copy($old, $temp_file)) { PEAR::popErrorHandling(); return PEAR::raiseError('Could not save existing configuration file '. $old . ', unable to install. Please set temp_dir ' . 'configuration variable to a writeable location and try again'); } PEAR::popErrorHandling(); $this->installer->log(0, "WARNING: configuration file $old is being installed as $test[2], you should manually merge in changes to the existing configuration file"); $this->installer->addFileOperation('rename', array($temp_file, $old, false)); $this->installer->addFileOperation('delete', array($temp_file)); } } return $test; } }PK=1]X}%% pear/PEAR/Installer/Role/Man.phpnu[ * @copyright 2011 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version SVN: $Id: $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.10.0 */ /** * @category pear * @package PEAR * @author Hannes Magnusson * @copyright 2011 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.10.0 */ class PEAR_Installer_Role_Man extends PEAR_Installer_Role_Common {} ?> PK=1]P'#pear/PEAR/Installer/Role/Script.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_Installer_Role_Script extends PEAR_Installer_Role_Common {} ?>PK=1]zq pear/PEAR/Installer/Role/Php.xmlnu[ php extsrc extbin zendextsrc zendextbin 1 php_dir 1 1 PK=1]/ pear/PEAR/Installer/Role/Cfg.xmlnu[ php extsrc extbin zendextsrc zendextbin 1 cfg_dir 1 PK=1]r+k!pear/PEAR/Installer/Role/Data.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_Installer_Role_Data extends PEAR_Installer_Role_Common {} ?>PK=1]ؗ?rr pear/PEAR/Installer/Role/Src.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_Installer_Role_Src extends PEAR_Installer_Role_Common { function setup(&$installer, $pkg, $atts, $file) { $installer->source_files++; } } ?>PK=1] >l   pear/PEAR/Installer/Role/Php.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_Installer_Role_Php extends PEAR_Installer_Role_Common {} ?>PK=1]@vа#pear/PEAR/Installer/Role/Script.xmlnu[ php extsrc extbin zendextsrc zendextbin 1 bin_dir 1 1 PK=1]p"" pear/PEAR/Installer/Role/Src.xmlnu[ extsrc zendextsrc 1 temp_dir PK=1]v]m6m6pear/PEAR/Exception.phpnu[ * @author Hans Lellelid * @author Bertrand Mansion * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.3.3 */ /** * Base PEAR_Exception Class * * 1) Features: * * - Nestable exceptions (throw new PEAR_Exception($msg, $prev_exception)) * - Definable triggers, shot when exceptions occur * - Pretty and informative error messages * - Added more context info available (like class, method or cause) * - cause can be a PEAR_Exception or an array of mixed * PEAR_Exceptions/PEAR_ErrorStack warnings * - callbacks for specific exception classes and their children * * 2) Ideas: * * - Maybe a way to define a 'template' for the output * * 3) Inherited properties from PHP Exception Class: * * protected $message * protected $code * protected $line * protected $file * private $trace * * 4) Inherited methods from PHP Exception Class: * * __clone * __construct * getMessage * getCode * getFile * getLine * getTraceSafe * getTraceSafeAsString * __toString * * 5) Usage example * * * require_once 'PEAR/Exception.php'; * * class Test { * function foo() { * throw new PEAR_Exception('Error Message', ERROR_CODE); * } * } * * function myLogger($pear_exception) { * echo $pear_exception->getMessage(); * } * // each time a exception is thrown the 'myLogger' will be called * // (its use is completely optional) * PEAR_Exception::addObserver('myLogger'); * $test = new Test; * try { * $test->foo(); * } catch (PEAR_Exception $e) { * print $e; * } * * * @category pear * @package PEAR * @author Tomas V.V.Cox * @author Hans Lellelid * @author Bertrand Mansion * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.3.3 * */ class PEAR_Exception extends Exception { const OBSERVER_PRINT = -2; const OBSERVER_TRIGGER = -4; const OBSERVER_DIE = -8; protected $cause; private static $_observers = array(); private static $_uniqueid = 0; private $_trace; /** * Supported signatures: * - PEAR_Exception(string $message); * - PEAR_Exception(string $message, int $code); * - PEAR_Exception(string $message, Exception $cause); * - PEAR_Exception(string $message, Exception $cause, int $code); * - PEAR_Exception(string $message, PEAR_Error $cause); * - PEAR_Exception(string $message, PEAR_Error $cause, int $code); * - PEAR_Exception(string $message, array $causes); * - PEAR_Exception(string $message, array $causes, int $code); * @param string exception message * @param int|Exception|PEAR_Error|array|null exception cause * @param int|null exception code or null */ public function __construct($message, $p2 = null, $p3 = null) { if (is_int($p2)) { $code = $p2; $this->cause = null; } elseif (is_object($p2) || is_array($p2)) { // using is_object allows both Exception and PEAR_Error if (is_object($p2) && !($p2 instanceof Exception)) { if (!class_exists('PEAR_Error') || !($p2 instanceof PEAR_Error)) { throw new PEAR_Exception('exception cause must be Exception, ' . 'array, or PEAR_Error'); } } $code = $p3; if (is_array($p2) && isset($p2['message'])) { // fix potential problem of passing in a single warning $p2 = array($p2); } $this->cause = $p2; } else { $code = null; $this->cause = null; } parent::__construct($message, $code); $this->signal(); } /** * @param mixed $callback - A valid php callback, see php func is_callable() * - A PEAR_Exception::OBSERVER_* constant * - An array(const PEAR_Exception::OBSERVER_*, * mixed $options) * @param string $label The name of the observer. Use this if you want * to remove it later with removeObserver() */ public static function addObserver($callback, $label = 'default') { self::$_observers[$label] = $callback; } public static function removeObserver($label = 'default') { unset(self::$_observers[$label]); } /** * @return int unique identifier for an observer */ public static function getUniqueId() { return self::$_uniqueid++; } private function signal() { foreach (self::$_observers as $func) { if (is_callable($func)) { call_user_func($func, $this); continue; } settype($func, 'array'); switch ($func[0]) { case self::OBSERVER_PRINT : $f = (isset($func[1])) ? $func[1] : '%s'; printf($f, $this->getMessage()); break; case self::OBSERVER_TRIGGER : $f = (isset($func[1])) ? $func[1] : E_USER_NOTICE; trigger_error($this->getMessage(), $f); break; case self::OBSERVER_DIE : $f = (isset($func[1])) ? $func[1] : '%s'; die(printf($f, $this->getMessage())); break; default: trigger_error('invalid observer type', E_USER_WARNING); } } } /** * Return specific error information that can be used for more detailed * error messages or translation. * * This method may be overridden in child exception classes in order * to add functionality not present in PEAR_Exception and is a placeholder * to define API * * The returned array must be an associative array of parameter => value like so: *
     * array('name' => $name, 'context' => array(...))
     * 
* @return array */ public function getErrorData() { return array(); } /** * Returns the exception that caused this exception to be thrown * @access public * @return Exception|array The context of the exception */ public function getCause() { return $this->cause; } /** * Function must be public to call on caused exceptions * @param array */ public function getCauseMessage(&$causes) { $trace = $this->getTraceSafe(); $cause = array('class' => get_class($this), 'message' => $this->message, 'file' => 'unknown', 'line' => 'unknown'); if (isset($trace[0])) { if (isset($trace[0]['file'])) { $cause['file'] = $trace[0]['file']; $cause['line'] = $trace[0]['line']; } } $causes[] = $cause; if ($this->cause instanceof PEAR_Exception) { $this->cause->getCauseMessage($causes); } elseif ($this->cause instanceof Exception) { $causes[] = array('class' => get_class($this->cause), 'message' => $this->cause->getMessage(), 'file' => $this->cause->getFile(), 'line' => $this->cause->getLine()); } elseif (class_exists('PEAR_Error') && $this->cause instanceof PEAR_Error) { $causes[] = array('class' => get_class($this->cause), 'message' => $this->cause->getMessage(), 'file' => 'unknown', 'line' => 'unknown'); } elseif (is_array($this->cause)) { foreach ($this->cause as $cause) { if ($cause instanceof PEAR_Exception) { $cause->getCauseMessage($causes); } elseif ($cause instanceof Exception) { $causes[] = array('class' => get_class($cause), 'message' => $cause->getMessage(), 'file' => $cause->getFile(), 'line' => $cause->getLine()); } elseif (class_exists('PEAR_Error') && $cause instanceof PEAR_Error) { $causes[] = array('class' => get_class($cause), 'message' => $cause->getMessage(), 'file' => 'unknown', 'line' => 'unknown'); } elseif (is_array($cause) && isset($cause['message'])) { // PEAR_ErrorStack warning $causes[] = array( 'class' => $cause['package'], 'message' => $cause['message'], 'file' => isset($cause['context']['file']) ? $cause['context']['file'] : 'unknown', 'line' => isset($cause['context']['line']) ? $cause['context']['line'] : 'unknown', ); } } } } public function getTraceSafe() { if (!isset($this->_trace)) { $this->_trace = $this->getTrace(); if (empty($this->_trace)) { $backtrace = debug_backtrace(); $this->_trace = array($backtrace[count($backtrace)-1]); } } return $this->_trace; } public function getErrorClass() { $trace = $this->getTraceSafe(); return $trace[0]['class']; } public function getErrorMethod() { $trace = $this->getTraceSafe(); return $trace[0]['function']; } public function __toString() { if (isset($_SERVER['REQUEST_URI'])) { return $this->toHtml(); } return $this->toText(); } public function toHtml() { $trace = $this->getTraceSafe(); $causes = array(); $this->getCauseMessage($causes); $html = '' . "\n"; foreach ($causes as $i => $cause) { $html .= '\n"; } $html .= '' . "\n" . '' . '' . '' . "\n"; foreach ($trace as $k => $v) { $html .= '' . '' . '' . "\n"; } $html .= '' . '' . '' . "\n" . '
' . str_repeat('-', $i) . ' ' . $cause['class'] . ': ' . htmlspecialchars($cause['message']) . ' in ' . $cause['file'] . ' ' . 'on line ' . $cause['line'] . '' . "
Exception trace
#FunctionLocation
' . $k . ''; if (!empty($v['class'])) { $html .= $v['class'] . $v['type']; } $html .= $v['function']; $args = array(); if (!empty($v['args'])) { foreach ($v['args'] as $arg) { if (is_null($arg)) $args[] = 'null'; elseif (is_array($arg)) $args[] = 'Array'; elseif (is_object($arg)) $args[] = 'Object('.get_class($arg).')'; elseif (is_bool($arg)) $args[] = $arg ? 'true' : 'false'; elseif (is_int($arg) || is_double($arg)) $args[] = $arg; else { $arg = (string)$arg; $str = htmlspecialchars(substr($arg, 0, 16)); if (strlen($arg) > 16) $str .= '…'; $args[] = "'" . $str . "'"; } } } $html .= '(' . implode(', ',$args) . ')' . '' . (isset($v['file']) ? $v['file'] : 'unknown') . ':' . (isset($v['line']) ? $v['line'] : 'unknown') . '
' . ($k+1) . '{main} 
'; return $html; } public function toText() { $causes = array(); $this->getCauseMessage($causes); $causeMsg = ''; foreach ($causes as $i => $cause) { $causeMsg .= str_repeat(' ', $i) . $cause['class'] . ': ' . $cause['message'] . ' in ' . $cause['file'] . ' on line ' . $cause['line'] . "\n"; } return $causeMsg . $this->getTraceAsString(); } }PK=1]oABBpear/PEAR/REST.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * For downloading xml files */ require_once 'PEAR.php'; require_once 'PEAR/XMLParser.php'; require_once 'PEAR/Proxy.php'; /** * Intelligently retrieve data, following hyperlinks if necessary, and re-directing * as well * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_REST { var $config; var $_options; function __construct(&$config, $options = array()) { $this->config = &$config; $this->_options = $options; } /** * Retrieve REST data, but always retrieve the local cache if it is available. * * This is useful for elements that should never change, such as information on a particular * release * @param string full URL to this resource * @param array|false contents of the accept-encoding header * @param boolean if true, xml will be returned as a string, otherwise, xml will be * parsed using PEAR_XMLParser * @return string|array */ function retrieveCacheFirst($url, $accept = false, $forcestring = false, $channel = false) { $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR . md5($url) . 'rest.cachefile'; if (file_exists($cachefile)) { return unserialize(implode('', file($cachefile))); } return $this->retrieveData($url, $accept, $forcestring, $channel); } /** * Retrieve a remote REST resource * @param string full URL to this resource * @param array|false contents of the accept-encoding header * @param boolean if true, xml will be returned as a string, otherwise, xml will be * parsed using PEAR_XMLParser * @return string|array */ function retrieveData($url, $accept = false, $forcestring = false, $channel = false) { $cacheId = $this->getCacheId($url); if ($ret = $this->useLocalCache($url, $cacheId)) { return $ret; } $file = $trieddownload = false; if (!isset($this->_options['offline'])) { $trieddownload = true; $file = $this->downloadHttp($url, $cacheId ? $cacheId['lastChange'] : false, $accept, $channel); } if (PEAR::isError($file)) { if ($file->getCode() !== -9276) { return $file; } $trieddownload = false; $file = false; // use local copy if available on socket connect error } if (!$file) { $ret = $this->getCache($url); if (!PEAR::isError($ret) && $trieddownload) { // reset the age of the cache if the server says it was unmodified $result = $this->saveCache($url, $ret, null, true, $cacheId); if (PEAR::isError($result)) { return PEAR::raiseError($result->getMessage()); } } return $ret; } if (is_array($file)) { $headers = $file[2]; $lastmodified = $file[1]; $content = $file[0]; } else { $headers = array(); $lastmodified = false; $content = $file; } if ($forcestring) { $result = $this->saveCache($url, $content, $lastmodified, false, $cacheId); if (PEAR::isError($result)) { return PEAR::raiseError($result->getMessage()); } return $content; } if (isset($headers['content-type'])) { $content_type = explode(";", $headers['content-type']); $content_type = $content_type[0]; switch ($content_type) { case 'text/xml' : case 'application/xml' : case 'text/plain' : if ($content_type === 'text/plain') { $check = substr($content, 0, 5); if ($check !== 'parse($content); PEAR::popErrorHandling(); if (PEAR::isError($err)) { return PEAR::raiseError('Invalid xml downloaded from "' . $url . '": ' . $err->getMessage()); } $content = $parser->getData(); case 'text/html' : default : // use it as a string } } else { // assume XML $parser = new PEAR_XMLParser; $parser->parse($content); $content = $parser->getData(); } $result = $this->saveCache($url, $content, $lastmodified, false, $cacheId); if (PEAR::isError($result)) { return PEAR::raiseError($result->getMessage()); } return $content; } function useLocalCache($url, $cacheid = null) { if (!is_array($cacheid)) { $cacheid = $this->getCacheId($url); } $cachettl = $this->config->get('cache_ttl'); // If cache is newer than $cachettl seconds, we use the cache! if (is_array($cacheid) && time() - $cacheid['age'] < $cachettl) { return $this->getCache($url); } return false; } /** * @param string $url * * @return bool|mixed */ function getCacheId($url) { $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR . md5($url) . 'rest.cacheid'; if (!file_exists($cacheidfile)) { return false; } $ret = unserialize(implode('', file($cacheidfile))); return $ret; } function getCache($url) { $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR . md5($url) . 'rest.cachefile'; if (!file_exists($cachefile)) { return PEAR::raiseError('No cached content available for "' . $url . '"'); } return unserialize(implode('', file($cachefile))); } /** * @param string full URL to REST resource * @param string original contents of the REST resource * @param array HTTP Last-Modified and ETag headers * @param bool if true, then the cache id file should be regenerated to * trigger a new time-to-live value */ function saveCache($url, $contents, $lastmodified, $nochange = false, $cacheid = null) { $cache_dir = $this->config->get('cache_dir'); $d = $cache_dir . DIRECTORY_SEPARATOR . md5($url); $cacheidfile = $d . 'rest.cacheid'; $cachefile = $d . 'rest.cachefile'; if (!is_dir($cache_dir)) { if (System::mkdir(array('-p', $cache_dir)) === false) { return PEAR::raiseError("The value of config option cache_dir ($cache_dir) is not a directory and attempts to create the directory failed."); } } if (!is_writeable($cache_dir)) { // If writing to the cache dir is not going to work, silently do nothing. // An ugly hack, but retains compat with PEAR 1.9.1 where many commands // work fine as non-root user (w/out write access to default cache dir). return true; } if ($cacheid === null && $nochange) { $cacheid = unserialize(implode('', file($cacheidfile))); } $idData = serialize(array( 'age' => time(), 'lastChange' => ($nochange ? $cacheid['lastChange'] : $lastmodified), )); $result = $this->saveCacheFile($cacheidfile, $idData); if (PEAR::isError($result)) { return $result; } elseif ($nochange) { return true; } $result = $this->saveCacheFile($cachefile, serialize($contents)); if (PEAR::isError($result)) { if (file_exists($cacheidfile)) { @unlink($cacheidfile); } return $result; } return true; } function saveCacheFile($file, $contents) { $len = strlen($contents); $cachefile_fp = @fopen($file, 'xb'); // x is the O_CREAT|O_EXCL mode if ($cachefile_fp !== false) { // create file if (fwrite($cachefile_fp, $contents, $len) < $len) { fclose($cachefile_fp); return PEAR::raiseError("Could not write $file."); } } else { // update file $cachefile_fp = @fopen($file, 'r+b'); // do not truncate file if (!$cachefile_fp) { return PEAR::raiseError("Could not open $file for writing."); } if (OS_WINDOWS) { $not_symlink = !is_link($file); // see bug #18834 } else { $cachefile_lstat = lstat($file); $cachefile_fstat = fstat($cachefile_fp); $not_symlink = $cachefile_lstat['mode'] == $cachefile_fstat['mode'] && $cachefile_lstat['ino'] == $cachefile_fstat['ino'] && $cachefile_lstat['dev'] == $cachefile_fstat['dev'] && $cachefile_fstat['nlink'] === 1; } if ($not_symlink) { ftruncate($cachefile_fp, 0); // NOW truncate if (fwrite($cachefile_fp, $contents, $len) < $len) { fclose($cachefile_fp); return PEAR::raiseError("Could not write $file."); } } else { fclose($cachefile_fp); $link = function_exists('readlink') ? readlink($file) : $file; return PEAR::raiseError('SECURITY ERROR: Will not write to ' . $file . ' as it is symlinked to ' . $link . ' - Possible symlink attack'); } } fclose($cachefile_fp); return true; } /** * Efficiently Download a file through HTTP. Returns downloaded file as a string in-memory * This is best used for small files * * If an HTTP proxy has been configured (http_proxy PEAR_Config * setting), the proxy will be used. * * @param string $url the URL to download * @param string $save_dir directory to save file in * @param false|string|array $lastmodified header values to check against for caching * use false to return the header values from this download * @param false|array $accept Accept headers to send * @return string|array Returns the contents of the downloaded file or a PEAR * error on failure. If the error is caused by * socket-related errors, the error object will * have the fsockopen error code available through * getCode(). If caching is requested, then return the header * values. * * @access public */ function downloadHttp($url, $lastmodified = null, $accept = false, $channel = false) { static $redirect = 0; // always reset , so we are clean case of error $wasredirect = $redirect; $redirect = 0; $info = parse_url($url); if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) { return PEAR::raiseError('Cannot download non-http URL "' . $url . '"'); } if (!isset($info['host'])) { return PEAR::raiseError('Cannot download from non-URL "' . $url . '"'); } $host = isset($info['host']) ? $info['host'] : null; $port = isset($info['port']) ? $info['port'] : null; $path = isset($info['path']) ? $info['path'] : null; $schema = (isset($info['scheme']) && $info['scheme'] == 'https') ? 'https' : 'http'; $proxy = new PEAR_Proxy($this->config); if (empty($port)) { $port = (isset($info['scheme']) && $info['scheme'] == 'https') ? 443 : 80; } if ($proxy->isProxyConfigured() && $schema === 'http') { $request = "GET $url HTTP/1.1\r\n"; } else { $request = "GET $path HTTP/1.1\r\n"; } $request .= "Host: $host\r\n"; $ifmodifiedsince = ''; if (is_array($lastmodified)) { if (isset($lastmodified['Last-Modified'])) { $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n"; } if (isset($lastmodified['ETag'])) { $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n"; } } else { $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : ''); } $request .= $ifmodifiedsince . "User-Agent: PEAR/1.10.18/PHP/" . PHP_VERSION . "\r\n"; $username = $this->config->get('username', null, $channel); $password = $this->config->get('password', null, $channel); if ($username && $password) { $tmp = base64_encode("$username:$password"); $request .= "Authorization: Basic $tmp\r\n"; } $proxyAuth = $proxy->getProxyAuth(); if ($proxyAuth) { $request .= 'Proxy-Authorization: Basic ' . $proxyAuth . "\r\n"; } if ($accept) { $request .= 'Accept: ' . implode(', ', $accept) . "\r\n"; } $request .= "Accept-Encoding:\r\n"; $request .= "Connection: close\r\n"; $request .= "\r\n"; $secure = ($schema == 'https'); $fp = $proxy->openSocket($host, $port, $secure); if (PEAR::isError($fp)) { return $fp; } fwrite($fp, $request); $headers = array(); $reply = 0; while ($line = trim(fgets($fp, 1024))) { if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) { $headers[strtolower($matches[1])] = trim($matches[2]); } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) { $reply = (int)$matches[1]; if ($reply == 304 && ($lastmodified || ($lastmodified === false))) { return false; } if (!in_array($reply, array(200, 301, 302, 303, 305, 307))) { return PEAR::raiseError("File $schema://$host:$port$path not valid (received: $line)"); } } } if ($reply != 200) { if (!isset($headers['location'])) { return PEAR::raiseError("File $schema://$host:$port$path not valid (redirected but no location)"); } if ($wasredirect > 4) { return PEAR::raiseError("File $schema://$host:$port$path not valid (redirection looped more than 5 times)"); } $redirect = $wasredirect + 1; return $this->downloadHttp($headers['location'], $lastmodified, $accept, $channel); } $length = isset($headers['content-length']) ? $headers['content-length'] : -1; $data = ''; if (isset($headers['transfer-encoding']) && strtolower($headers['transfer-encoding']) === 'chunked') { while (!feof($fp)) { $chunk_size = hexdec(fgets($fp)); if ($chunk_size === 0) { break; } $data .= fread($fp, $chunk_size); fgets($fp); // Skip the trailing CRLF } } else { while ($chunk = @fread($fp, 8192)) { $data .= $chunk; } } fclose($fp); if ($lastmodified === false || $lastmodified) { if (isset($headers['etag'])) { $lastmodified = array('ETag' => $headers['etag']); } if (isset($headers['last-modified'])) { if (is_array($lastmodified)) { $lastmodified['Last-Modified'] = $headers['last-modified']; } else { $lastmodified = $headers['last-modified']; } } return array($data, $lastmodified, $headers); } return $data; } } PK=1]K'opear/PEAR/Frontend.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * Include error handling */ //require_once 'PEAR.php'; /** * Which user interface class is being used. * @var string class name */ $GLOBALS['_PEAR_FRONTEND_CLASS'] = 'PEAR_Frontend_CLI'; /** * Instance of $_PEAR_Command_uiclass. * @var object */ $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = null; /** * Singleton-based frontend for PEAR user input/output * * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_Frontend extends PEAR { /** * Retrieve the frontend object * @return PEAR_Frontend_CLI|PEAR_Frontend_Web|PEAR_Frontend_Gtk */ public static function &singleton($type = null) { if ($type === null) { if (!isset($GLOBALS['_PEAR_FRONTEND_SINGLETON'])) { $a = false; return $a; } return $GLOBALS['_PEAR_FRONTEND_SINGLETON']; } $a = PEAR_Frontend::setFrontendClass($type); return $a; } /** * Set the frontend class that will be used by calls to {@link singleton()} * * Frontends are expected to conform to the PEAR naming standard of * _ => DIRECTORY_SEPARATOR (PEAR_Frontend_CLI is in PEAR/Frontend/CLI.php) * @param string $uiclass full class name * @return PEAR_Frontend */ public static function &setFrontendClass($uiclass) { if (is_object($GLOBALS['_PEAR_FRONTEND_SINGLETON']) && is_a($GLOBALS['_PEAR_FRONTEND_SINGLETON'], $uiclass)) { return $GLOBALS['_PEAR_FRONTEND_SINGLETON']; } if (!class_exists($uiclass)) { $file = str_replace('_', '/', $uiclass) . '.php'; if (PEAR_Frontend::isIncludeable($file)) { include_once $file; } } if (class_exists($uiclass)) { $obj = new $uiclass; // quick test to see if this class implements a few of the most // important frontend methods if (is_a($obj, 'PEAR_Frontend')) { $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = &$obj; $GLOBALS['_PEAR_FRONTEND_CLASS'] = $uiclass; return $obj; } $err = PEAR::raiseError("not a frontend class: $uiclass"); return $err; } $err = PEAR::raiseError("no such class: $uiclass"); return $err; } /** * Set the frontend class that will be used by calls to {@link singleton()} * * Frontends are expected to be a descendant of PEAR_Frontend * @param PEAR_Frontend * @return PEAR_Frontend */ public static function &setFrontendObject($uiobject) { if (is_object($GLOBALS['_PEAR_FRONTEND_SINGLETON']) && is_a($GLOBALS['_PEAR_FRONTEND_SINGLETON'], get_class($uiobject))) { return $GLOBALS['_PEAR_FRONTEND_SINGLETON']; } if (!is_a($uiobject, 'PEAR_Frontend')) { $err = PEAR::raiseError('not a valid frontend class: (' . get_class($uiobject) . ')'); return $err; } $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = &$uiobject; $GLOBALS['_PEAR_FRONTEND_CLASS'] = get_class($uiobject); return $uiobject; } /** * @param string $path relative or absolute include path * @return boolean */ public static function isIncludeable($path) { if (file_exists($path) && is_readable($path)) { return true; } $fp = @fopen($path, 'r', true); if ($fp) { fclose($fp); return true; } return false; } /** * @param PEAR_Config */ function setConfig(&$config) { } /** * This can be overridden to allow session-based temporary file management * * By default, all files are deleted at the end of a session. The web installer * needs to be able to sustain a list over many sessions in order to support * user interaction with install scripts */ static function addTempFile($file) { $GLOBALS['_PEAR_Common_tempfiles'][] = $file; } /** * Log an action * * @param string $msg the message to log * @param boolean $append_crlf * @return boolean true * @abstract */ function log($msg, $append_crlf = true) { } /** * Run a post-installation script * * @param array $scripts array of post-install scripts * @abstract */ function runPostinstallScripts(&$scripts) { } /** * Display human-friendly output formatted depending on the * $command parameter. * * This should be able to handle basic output data with no command * @param mixed $data data structure containing the information to display * @param string $command command from which this method was called * @abstract */ function outputData($data, $command = '_default') { } /** * Display a modal form dialog and return the given input * * A frontend that requires multiple requests to retrieve and process * data must take these needs into account, and implement the request * handling code. * @param string $command command from which this method was called * @param array $prompts associative array. keys are the input field names * and values are the description * @param array $types array of input field types (text, password, * etc.) keys have to be the same like in $prompts * @param array $defaults array of default values. again keys have * to be the same like in $prompts. Do not depend * on a default value being set. * @return array input sent by the user * @abstract */ function userDialog($command, $prompts, $types = array(), $defaults = array()) { } } PK=1]ٷjpear/PEAR/Config.phpnu[ * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ /** * Required for error handling */ require_once 'PEAR.php'; require_once 'PEAR/Registry.php'; require_once 'PEAR/Installer/Role.php'; require_once 'System.php'; /** * Last created PEAR_Config instance. * @var object */ $GLOBALS['_PEAR_Config_instance'] = null; if (!defined('PEAR_INSTALL_DIR') || !PEAR_INSTALL_DIR) { $PEAR_INSTALL_DIR = PHP_LIBDIR . DIRECTORY_SEPARATOR . 'pear'; } else { $PEAR_INSTALL_DIR = PEAR_INSTALL_DIR; } // Below we define constants with default values for all configuration // parameters except username/password. All of them can have their // defaults set through environment variables. The reason we use the // PHP_ prefix is for some security, PHP protects environment // variables starting with PHP_*. // default channel and preferred mirror is based on whether we are invoked through // the "pear" or the "pecl" command if (!defined('PEAR_RUNTYPE')) { define('PEAR_RUNTYPE', 'pear'); } if (PEAR_RUNTYPE == 'pear') { define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pear.php.net'); } else { define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pecl.php.net'); } if (getenv('PHP_PEAR_SYSCONF_DIR')) { define('PEAR_CONFIG_SYSCONFDIR', getenv('PHP_PEAR_SYSCONF_DIR')); } elseif (getenv('SystemRoot')) { define('PEAR_CONFIG_SYSCONFDIR', getenv('SystemRoot')); } else { define('PEAR_CONFIG_SYSCONFDIR', PHP_SYSCONFDIR); } // Default for master_server if (getenv('PHP_PEAR_MASTER_SERVER')) { define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', getenv('PHP_PEAR_MASTER_SERVER')); } else { define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', 'pear.php.net'); } // Default for http_proxy if (getenv('PHP_PEAR_HTTP_PROXY')) { define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('PHP_PEAR_HTTP_PROXY')); } elseif (getenv('http_proxy')) { define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('http_proxy')); } else { define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', ''); } // Default for php_dir if (getenv('PHP_PEAR_INSTALL_DIR')) { define('PEAR_CONFIG_DEFAULT_PHP_DIR', getenv('PHP_PEAR_INSTALL_DIR')); } else { define('PEAR_CONFIG_DEFAULT_PHP_DIR', $PEAR_INSTALL_DIR); } // Default for metadata_dir if (getenv('PHP_PEAR_METADATA_DIR')) { define('PEAR_CONFIG_DEFAULT_METADATA_DIR', getenv('PHP_PEAR_METADATA_DIR')); } else { define('PEAR_CONFIG_DEFAULT_METADATA_DIR', ''); } // Default for ext_dir if (getenv('PHP_PEAR_EXTENSION_DIR')) { define('PEAR_CONFIG_DEFAULT_EXT_DIR', getenv('PHP_PEAR_EXTENSION_DIR')); } else { if (ini_get('extension_dir')) { define('PEAR_CONFIG_DEFAULT_EXT_DIR', ini_get('extension_dir')); } elseif (defined('PEAR_EXTENSION_DIR') && file_exists(PEAR_EXTENSION_DIR) && is_dir(PEAR_EXTENSION_DIR)) { define('PEAR_CONFIG_DEFAULT_EXT_DIR', PEAR_EXTENSION_DIR); } elseif (defined('PHP_EXTENSION_DIR')) { define('PEAR_CONFIG_DEFAULT_EXT_DIR', PHP_EXTENSION_DIR); } else { define('PEAR_CONFIG_DEFAULT_EXT_DIR', '.'); } } // Default for doc_dir if (getenv('PHP_PEAR_DOC_DIR')) { define('PEAR_CONFIG_DEFAULT_DOC_DIR', getenv('PHP_PEAR_DOC_DIR')); } else { define('PEAR_CONFIG_DEFAULT_DOC_DIR', $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'docs'); } // Default for bin_dir if (getenv('PHP_PEAR_BIN_DIR')) { define('PEAR_CONFIG_DEFAULT_BIN_DIR', getenv('PHP_PEAR_BIN_DIR')); } else { define('PEAR_CONFIG_DEFAULT_BIN_DIR', PHP_BINDIR); } // Default for data_dir if (getenv('PHP_PEAR_DATA_DIR')) { define('PEAR_CONFIG_DEFAULT_DATA_DIR', getenv('PHP_PEAR_DATA_DIR')); } else { define('PEAR_CONFIG_DEFAULT_DATA_DIR', $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'data'); } // Default for cfg_dir if (getenv('PHP_PEAR_CFG_DIR')) { define('PEAR_CONFIG_DEFAULT_CFG_DIR', getenv('PHP_PEAR_CFG_DIR')); } else { define('PEAR_CONFIG_DEFAULT_CFG_DIR', $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'cfg'); } // Default for www_dir if (getenv('PHP_PEAR_WWW_DIR')) { define('PEAR_CONFIG_DEFAULT_WWW_DIR', getenv('PHP_PEAR_WWW_DIR')); } else { define('PEAR_CONFIG_DEFAULT_WWW_DIR', $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'www'); } // Default for man_dir if (getenv('PHP_PEAR_MAN_DIR')) { define('PEAR_CONFIG_DEFAULT_MAN_DIR', getenv('PHP_PEAR_MAN_DIR')); } else { if (defined('PHP_MANDIR')) { // Added in PHP5.3.7 define('PEAR_CONFIG_DEFAULT_MAN_DIR', PHP_MANDIR); } else { define('PEAR_CONFIG_DEFAULT_MAN_DIR', PHP_PREFIX . DIRECTORY_SEPARATOR . 'local' . DIRECTORY_SEPARATOR .'man'); } } // Default for test_dir if (getenv('PHP_PEAR_TEST_DIR')) { define('PEAR_CONFIG_DEFAULT_TEST_DIR', getenv('PHP_PEAR_TEST_DIR')); } else { define('PEAR_CONFIG_DEFAULT_TEST_DIR', $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'tests'); } // Default for temp_dir if (getenv('PHP_PEAR_TEMP_DIR')) { define('PEAR_CONFIG_DEFAULT_TEMP_DIR', getenv('PHP_PEAR_TEMP_DIR')); } else { define('PEAR_CONFIG_DEFAULT_TEMP_DIR', System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' . DIRECTORY_SEPARATOR . 'temp'); } // Default for cache_dir if (getenv('PHP_PEAR_CACHE_DIR')) { define('PEAR_CONFIG_DEFAULT_CACHE_DIR', getenv('PHP_PEAR_CACHE_DIR')); } else { define('PEAR_CONFIG_DEFAULT_CACHE_DIR', System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' . DIRECTORY_SEPARATOR . 'cache'); } // Default for download_dir if (getenv('PHP_PEAR_DOWNLOAD_DIR')) { define('PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR', getenv('PHP_PEAR_DOWNLOAD_DIR')); } else { define('PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR', System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' . DIRECTORY_SEPARATOR . 'download'); } // Default for php_bin if (getenv('PHP_PEAR_PHP_BIN')) { define('PEAR_CONFIG_DEFAULT_PHP_BIN', getenv('PHP_PEAR_PHP_BIN')); } else { define('PEAR_CONFIG_DEFAULT_PHP_BIN', PEAR_CONFIG_DEFAULT_BIN_DIR. DIRECTORY_SEPARATOR.'php'.(OS_WINDOWS ? '.exe' : '')); } // Default for verbose if (getenv('PHP_PEAR_VERBOSE')) { define('PEAR_CONFIG_DEFAULT_VERBOSE', getenv('PHP_PEAR_VERBOSE')); } else { define('PEAR_CONFIG_DEFAULT_VERBOSE', 1); } // Default for preferred_state if (getenv('PHP_PEAR_PREFERRED_STATE')) { define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', getenv('PHP_PEAR_PREFERRED_STATE')); } else { define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', 'stable'); } // Default for umask if (getenv('PHP_PEAR_UMASK')) { define('PEAR_CONFIG_DEFAULT_UMASK', getenv('PHP_PEAR_UMASK')); } else { define('PEAR_CONFIG_DEFAULT_UMASK', decoct(umask())); } // Default for cache_ttl if (getenv('PHP_PEAR_CACHE_TTL')) { define('PEAR_CONFIG_DEFAULT_CACHE_TTL', getenv('PHP_PEAR_CACHE_TTL')); } else { define('PEAR_CONFIG_DEFAULT_CACHE_TTL', 3600); } // Default for sig_type if (getenv('PHP_PEAR_SIG_TYPE')) { define('PEAR_CONFIG_DEFAULT_SIG_TYPE', getenv('PHP_PEAR_SIG_TYPE')); } else { define('PEAR_CONFIG_DEFAULT_SIG_TYPE', 'gpg'); } // Default for sig_bin if (getenv('PHP_PEAR_SIG_BIN')) { define('PEAR_CONFIG_DEFAULT_SIG_BIN', getenv('PHP_PEAR_SIG_BIN')); } else { define('PEAR_CONFIG_DEFAULT_SIG_BIN', System::which( 'gpg', OS_WINDOWS ? 'c:\gnupg\gpg.exe' : '/usr/local/bin/gpg')); } // Default for sig_keydir if (getenv('PHP_PEAR_SIG_KEYDIR')) { define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR', getenv('PHP_PEAR_SIG_KEYDIR')); } else { define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR', PEAR_CONFIG_SYSCONFDIR . DIRECTORY_SEPARATOR . 'pearkeys'); } /** * This is a class for storing configuration data, keeping track of * which are system-defined, user-defined or defaulted. * @category pear * @package PEAR * @author Stig Bakken * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ class PEAR_Config extends PEAR { /** * Array of config files used. * * @var array layer => config file */ var $files = array( 'system' => '', 'user' => '', ); var $layers = array(); /** * Configuration data, two-dimensional array where the first * dimension is the config layer ('user', 'system' and 'default'), * and the second dimension is keyname => value. * * The order in the first dimension is important! Earlier * layers will shadow later ones when a config value is * requested (if a 'user' value exists, it will be returned first, * then 'system' and finally 'default'). * * @var array layer => array(keyname => value, ...) */ var $configuration = array( 'user' => array(), 'system' => array(), 'default' => array(), ); /** * Configuration values that can be set for a channel * * All other configuration values can only have a global value * @var array * @access private */ var $_channelConfigInfo = array( 'php_dir', 'ext_dir', 'doc_dir', 'bin_dir', 'data_dir', 'cfg_dir', 'test_dir', 'www_dir', 'php_bin', 'php_prefix', 'php_suffix', 'username', 'password', 'verbose', 'preferred_state', 'umask', 'preferred_mirror', 'php_ini' ); /** * Channels that can be accessed * @see setChannels() * @var array * @access private */ var $_channels = array('pear.php.net', 'pecl.php.net', '__uri'); /** * This variable is used to control the directory values returned * @see setInstallRoot(); * @var string|false * @access private */ var $_installRoot = false; /** * If requested, this will always refer to the registry * contained in php_dir * @var PEAR_Registry */ var $_registry = array(); /** * @var array * @access private */ var $_regInitialized = array(); /** * @var bool * @access private */ var $_noRegistry = false; /** * amount of errors found while parsing config * @var integer * @access private */ var $_errorsFound = 0; var $_lastError = null; /** * Information about the configuration data. Stores the type, * default value and a documentation string for each configuration * value. * * @var array layer => array(infotype => value, ...) */ var $configuration_info = array( // Channels/Internet Access 'default_channel' => array( 'type' => 'string', 'default' => PEAR_CONFIG_DEFAULT_CHANNEL, 'doc' => 'the default channel to use for all non explicit commands', 'prompt' => 'Default Channel', 'group' => 'Internet Access', ), 'preferred_mirror' => array( 'type' => 'string', 'default' => PEAR_CONFIG_DEFAULT_CHANNEL, 'doc' => 'the default server or mirror to use for channel actions', 'prompt' => 'Default Channel Mirror', 'group' => 'Internet Access', ), 'remote_config' => array( 'type' => 'password', 'default' => '', 'doc' => 'ftp url of remote configuration file to use for synchronized install', 'prompt' => 'Remote Configuration File', 'group' => 'Internet Access', ), 'auto_discover' => array( 'type' => 'integer', 'default' => 0, 'doc' => 'whether to automatically discover new channels', 'prompt' => 'Auto-discover new Channels', 'group' => 'Internet Access', ), // Internet Access 'master_server' => array( 'type' => 'string', 'default' => 'pear.php.net', 'doc' => 'name of the main PEAR server [NOT USED IN THIS VERSION]', 'prompt' => 'PEAR server [DEPRECATED]', 'group' => 'Internet Access', ), 'http_proxy' => array( 'type' => 'string', 'default' => PEAR_CONFIG_DEFAULT_HTTP_PROXY, 'doc' => 'HTTP proxy (host:port) to use when downloading packages', 'prompt' => 'HTTP Proxy Server Address', 'group' => 'Internet Access', ), // File Locations 'php_dir' => array( 'type' => 'directory', 'default' => PEAR_CONFIG_DEFAULT_PHP_DIR, 'doc' => 'directory where .php files are installed', 'prompt' => 'PEAR directory', 'group' => 'File Locations', ), 'ext_dir' => array( 'type' => 'directory', 'default' => PEAR_CONFIG_DEFAULT_EXT_DIR, 'doc' => 'directory where loadable extensions are installed', 'prompt' => 'PHP extension directory', 'group' => 'File Locations', ), 'doc_dir' => array( 'type' => 'directory', 'default' => PEAR_CONFIG_DEFAULT_DOC_DIR, 'doc' => 'directory where documentation is installed', 'prompt' => 'PEAR documentation directory', 'group' => 'File Locations', ), 'bin_dir' => array( 'type' => 'directory', 'default' => PEAR_CONFIG_DEFAULT_BIN_DIR, 'doc' => 'directory where executables are installed', 'prompt' => 'PEAR executables directory', 'group' => 'File Locations', ), 'data_dir' => array( 'type' => 'directory', 'default' => PEAR_CONFIG_DEFAULT_DATA_DIR, 'doc' => 'directory where data files are installed', 'prompt' => 'PEAR data directory', 'group' => 'File Locations (Advanced)', ), 'cfg_dir' => array( 'type' => 'directory', 'default' => PEAR_CONFIG_DEFAULT_CFG_DIR, 'doc' => 'directory where modifiable configuration files are installed', 'prompt' => 'PEAR configuration file directory', 'group' => 'File Locations (Advanced)', ), 'www_dir' => array( 'type' => 'directory', 'default' => PEAR_CONFIG_DEFAULT_WWW_DIR, 'doc' => 'directory where www frontend files (html/js) are installed', 'prompt' => 'PEAR www files directory', 'group' => 'File Locations (Advanced)', ), 'man_dir' => array( 'type' => 'directory', 'default' => PEAR_CONFIG_DEFAULT_MAN_DIR, 'doc' => 'directory where unix manual pages are installed', 'prompt' => 'Systems manpage files directory', 'group' => 'File Locations (Advanced)', ), 'test_dir' => array( 'type' => 'directory', 'default' => PEAR_CONFIG_DEFAULT_TEST_DIR, 'doc' => 'directory where regression tests are installed', 'prompt' => 'PEAR test directory', 'group' => 'File Locations (Advanced)', ), 'cache_dir' => array( 'type' => 'directory', 'default' => PEAR_CONFIG_DEFAULT_CACHE_DIR, 'doc' => 'directory which is used for web service cache', 'prompt' => 'PEAR Installer cache directory', 'group' => 'File Locations (Advanced)', ), 'temp_dir' => array( 'type' => 'directory', 'default' => PEAR_CONFIG_DEFAULT_TEMP_DIR, 'doc' => 'directory which is used for all temp files', 'prompt' => 'PEAR Installer temp directory', 'group' => 'File Locations (Advanced)', ), 'download_dir' => array( 'type' => 'directory', 'default' => PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR, 'doc' => 'directory which is used for all downloaded files', 'prompt' => 'PEAR Installer download directory', 'group' => 'File Locations (Advanced)', ), 'php_bin' => array( 'type' => 'file', 'default' => PEAR_CONFIG_DEFAULT_PHP_BIN, 'doc' => 'PHP CLI/CGI binary for executing scripts', 'prompt' => 'PHP CLI/CGI binary', 'group' => 'File Locations (Advanced)', ), 'php_prefix' => array( 'type' => 'string', 'default' => '', 'doc' => '--program-prefix for php_bin\'s ./configure, used for pecl installs', 'prompt' => '--program-prefix passed to PHP\'s ./configure', 'group' => 'File Locations (Advanced)', ), 'php_suffix' => array( 'type' => 'string', 'default' => '', 'doc' => '--program-suffix for php_bin\'s ./configure, used for pecl installs', 'prompt' => '--program-suffix passed to PHP\'s ./configure', 'group' => 'File Locations (Advanced)', ), 'php_ini' => array( 'type' => 'file', 'default' => '', 'doc' => 'location of php.ini in which to enable PECL extensions on install', 'prompt' => 'php.ini location', 'group' => 'File Locations (Advanced)', ), 'metadata_dir' => array( 'type' => 'directory', 'default' => PEAR_CONFIG_DEFAULT_METADATA_DIR, 'doc' => 'directory where metadata files are installed (registry, filemap, channels, ...)', 'prompt' => 'PEAR metadata directory', 'group' => 'File Locations (Advanced)', ), // Maintainers 'username' => array( 'type' => 'string', 'default' => '', 'doc' => '(maintainers) your PEAR account name', 'prompt' => 'PEAR username (for maintainers)', 'group' => 'Maintainers', ), 'password' => array( 'type' => 'password', 'default' => '', 'doc' => '(maintainers) your PEAR account password', 'prompt' => 'PEAR password (for maintainers)', 'group' => 'Maintainers', ), // Advanced 'verbose' => array( 'type' => 'integer', 'default' => PEAR_CONFIG_DEFAULT_VERBOSE, 'doc' => 'verbosity level 0: really quiet 1: somewhat quiet 2: verbose 3: debug', 'prompt' => 'Debug Log Level', 'group' => 'Advanced', ), 'preferred_state' => array( 'type' => 'set', 'default' => PEAR_CONFIG_DEFAULT_PREFERRED_STATE, 'doc' => 'the installer will prefer releases with this state when installing packages without a version or state specified', 'valid_set' => array( 'stable', 'beta', 'alpha', 'devel', 'snapshot'), 'prompt' => 'Preferred Package State', 'group' => 'Advanced', ), 'umask' => array( 'type' => 'mask', 'default' => PEAR_CONFIG_DEFAULT_UMASK, 'doc' => 'umask used when creating files (Unix-like systems only)', 'prompt' => 'Unix file mask', 'group' => 'Advanced', ), 'cache_ttl' => array( 'type' => 'integer', 'default' => PEAR_CONFIG_DEFAULT_CACHE_TTL, 'doc' => 'amount of secs where the local cache is used and not updated', 'prompt' => 'Cache TimeToLive', 'group' => 'Advanced', ), 'sig_type' => array( 'type' => 'set', 'default' => PEAR_CONFIG_DEFAULT_SIG_TYPE, 'doc' => 'which package signature mechanism to use', 'valid_set' => array('gpg'), 'prompt' => 'Package Signature Type', 'group' => 'Maintainers', ), 'sig_bin' => array( 'type' => 'string', 'default' => PEAR_CONFIG_DEFAULT_SIG_BIN, 'doc' => 'which package signature mechanism to use', 'prompt' => 'Signature Handling Program', 'group' => 'Maintainers', ), 'sig_keyid' => array( 'type' => 'string', 'default' => '', 'doc' => 'which key to use for signing with', 'prompt' => 'Signature Key Id', 'group' => 'Maintainers', ), 'sig_keydir' => array( 'type' => 'directory', 'default' => PEAR_CONFIG_DEFAULT_SIG_KEYDIR, 'doc' => 'directory where signature keys are located', 'prompt' => 'Signature Key Directory', 'group' => 'Maintainers', ), // __channels is reserved - used for channel-specific configuration ); /** * Constructor. * * @param string file to read user-defined options from * @param string file to read system-wide defaults from * @param bool determines whether a registry object "follows" * the value of php_dir (is automatically created * and moved when php_dir is changed) * @param bool if true, fails if configuration files cannot be loaded * * @access public * * @see PEAR_Config::singleton */ function __construct($user_file = '', $system_file = '', $ftp_file = false, $strict = true) { parent::__construct(); PEAR_Installer_Role::initializeConfig($this); $sl = DIRECTORY_SEPARATOR; if (empty($user_file)) { if (OS_WINDOWS) { $user_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini'; } else { $user_file = getenv('HOME') . $sl . '.pearrc'; } } if (empty($system_file)) { $system_file = PEAR_CONFIG_SYSCONFDIR . $sl; if (OS_WINDOWS) { $system_file .= 'pearsys.ini'; } else { $system_file .= 'pear.conf'; } } $this->layers = array_keys($this->configuration); $this->files['user'] = $user_file; $this->files['system'] = $system_file; if ($user_file && file_exists($user_file)) { $this->pushErrorHandling(PEAR_ERROR_RETURN); $this->readConfigFile($user_file, 'user', $strict); $this->popErrorHandling(); if ($this->_errorsFound > 0) { return; } } if ($system_file && @file_exists($system_file)) { $this->mergeConfigFile($system_file, false, 'system', $strict); if ($this->_errorsFound > 0) { return; } } if (!$ftp_file) { $ftp_file = $this->get('remote_config'); } if ($ftp_file && defined('PEAR_REMOTEINSTALL_OK')) { $this->readFTPConfigFile($ftp_file); } foreach ($this->configuration_info as $key => $info) { $this->configuration['default'][$key] = $info['default']; } $this->_registry['default'] = new PEAR_Registry( $this->configuration['default']['php_dir'], false, false, $this->configuration['default']['metadata_dir']); $this->_registry['default']->setConfig($this, false); $this->_regInitialized['default'] = false; //$GLOBALS['_PEAR_Config_instance'] = &$this; } /** * Return the default locations of user and system configuration files */ public static function getDefaultConfigFiles() { $sl = DIRECTORY_SEPARATOR; if (OS_WINDOWS) { return array( 'user' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini', 'system' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pearsys.ini' ); } return array( 'user' => getenv('HOME') . $sl . '.pearrc', 'system' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.conf' ); } /** * Static singleton method. If you want to keep only one instance * of this class in use, this method will give you a reference to * the last created PEAR_Config object if one exists, or create a * new object. * * @param string (optional) file to read user-defined options from * @param string (optional) file to read system-wide defaults from * * @return object an existing or new PEAR_Config instance * * @see PEAR_Config::PEAR_Config */ public static function &singleton($user_file = '', $system_file = '', $strict = true) { if (is_object($GLOBALS['_PEAR_Config_instance'])) { return $GLOBALS['_PEAR_Config_instance']; } $t_conf = new PEAR_Config($user_file, $system_file, false, $strict); if ($t_conf->_errorsFound > 0) { return $t_conf->_lastError; } $GLOBALS['_PEAR_Config_instance'] = &$t_conf; return $GLOBALS['_PEAR_Config_instance']; } /** * Determine whether any configuration files have been detected, and whether a * registry object can be retrieved from this configuration. * @return bool * @since PEAR 1.4.0a1 */ function validConfiguration() { if ($this->isDefinedLayer('user') || $this->isDefinedLayer('system')) { return true; } return false; } /** * Reads configuration data from a file. All existing values in * the config layer are discarded and replaced with data from the * file. * @param string file to read from, if NULL or not specified, the * last-used file for the same layer (second param) is used * @param string config layer to insert data into ('user' or 'system') * @return bool TRUE on success or a PEAR error on failure */ function readConfigFile($file = null, $layer = 'user', $strict = true) { if (empty($this->files[$layer])) { return $this->raiseError("unknown config layer `$layer'"); } if ($file === null) { $file = $this->files[$layer]; } $data = $this->_readConfigDataFrom($file); if (PEAR::isError($data)) { if (!$strict) { return true; } $this->_errorsFound++; $this->_lastError = $data; return $data; } $this->files[$layer] = $file; $this->_decodeInput($data); $this->configuration[$layer] = $data; $this->_setupChannels(); if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) { $this->_registry[$layer] = new PEAR_Registry( $phpdir, false, false, $this->get('metadata_dir', $layer, 'pear.php.net')); $this->_registry[$layer]->setConfig($this, false); $this->_regInitialized[$layer] = false; } else { unset($this->_registry[$layer]); } return true; } /** * @param string url to the remote config file, like ftp://www.example.com/pear/config.ini * @return true|PEAR_Error */ function readFTPConfigFile($path) { do { // poor man's try if (!class_exists('PEAR_FTP')) { if (!class_exists('PEAR_Common')) { require_once 'PEAR/Common.php'; } if (PEAR_Common::isIncludeable('PEAR/FTP.php')) { require_once 'PEAR/FTP.php'; } } if (!class_exists('PEAR_FTP')) { return PEAR::raiseError('PEAR_RemoteInstaller must be installed to use remote config'); } $this->_ftp = new PEAR_FTP; $this->_ftp->pushErrorHandling(PEAR_ERROR_RETURN); $e = $this->_ftp->init($path); if (PEAR::isError($e)) { $this->_ftp->popErrorHandling(); return $e; } $tmp = System::mktemp('-d'); PEAR_Common::addTempFile($tmp); $e = $this->_ftp->get(basename($path), $tmp . DIRECTORY_SEPARATOR . 'pear.ini', false, FTP_BINARY); if (PEAR::isError($e)) { $this->_ftp->popErrorHandling(); return $e; } PEAR_Common::addTempFile($tmp . DIRECTORY_SEPARATOR . 'pear.ini'); $this->_ftp->disconnect(); $this->_ftp->popErrorHandling(); $this->files['ftp'] = $tmp . DIRECTORY_SEPARATOR . 'pear.ini'; $e = $this->readConfigFile(null, 'ftp'); if (PEAR::isError($e)) { return $e; } $fail = array(); foreach ($this->configuration_info as $key => $val) { if (in_array($this->getGroup($key), array('File Locations', 'File Locations (Advanced)')) && $this->getType($key) == 'directory') { // any directory configs must be set for this to work if (!isset($this->configuration['ftp'][$key])) { $fail[] = $key; } } } if (!count($fail)) { return true; } $fail = '"' . implode('", "', $fail) . '"'; unset($this->files['ftp']); unset($this->configuration['ftp']); return PEAR::raiseError('ERROR: Ftp configuration file must set all ' . 'directory configuration variables. These variables were not set: ' . $fail); } while (false); // poor man's catch unset($this->files['ftp']); return PEAR::raiseError('no remote host specified'); } /** * Reads the existing configurations and creates the _channels array from it */ function _setupChannels() { $set = array_flip(array_values($this->_channels)); foreach ($this->configuration as $layer => $data) { $i = 1000; if (isset($data['__channels']) && is_array($data['__channels'])) { foreach ($data['__channels'] as $channel => $info) { $set[$channel] = $i++; } } } $this->_channels = array_values(array_flip($set)); $this->setChannels($this->_channels); } function deleteChannel($channel) { $ch = strtolower($channel); foreach ($this->configuration as $layer => $data) { if (isset($data['__channels']) && isset($data['__channels'][$ch])) { unset($this->configuration[$layer]['__channels'][$ch]); } } $this->_channels = array_flip($this->_channels); unset($this->_channels[$ch]); $this->_channels = array_flip($this->_channels); } /** * Merges data into a config layer from a file. Does the same * thing as readConfigFile, except it does not replace all * existing values in the config layer. * @param string file to read from * @param bool whether to overwrite existing data (default TRUE) * @param string config layer to insert data into ('user' or 'system') * @param string if true, errors are returned if file opening fails * @return bool TRUE on success or a PEAR error on failure */ function mergeConfigFile($file, $override = true, $layer = 'user', $strict = true) { if (empty($this->files[$layer])) { return $this->raiseError("unknown config layer `$layer'"); } if ($file === null) { $file = $this->files[$layer]; } $data = $this->_readConfigDataFrom($file); if (PEAR::isError($data)) { if (!$strict) { return true; } $this->_errorsFound++; $this->_lastError = $data; return $data; } $this->_decodeInput($data); if ($override) { $this->configuration[$layer] = PEAR_Config::arrayMergeRecursive($this->configuration[$layer], $data); } else { $this->configuration[$layer] = PEAR_Config::arrayMergeRecursive($data, $this->configuration[$layer]); } $this->_setupChannels(); if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) { $this->_registry[$layer] = new PEAR_Registry( $phpdir, false, false, $this->get('metadata_dir', $layer, 'pear.php.net')); $this->_registry[$layer]->setConfig($this, false); $this->_regInitialized[$layer] = false; } else { unset($this->_registry[$layer]); } return true; } /** * @param array * @param array * @return array */ public static function arrayMergeRecursive($arr2, $arr1) { $ret = array(); foreach ($arr2 as $key => $data) { if (!isset($arr1[$key])) { $ret[$key] = $data; unset($arr1[$key]); continue; } if (is_array($data)) { if (!is_array($arr1[$key])) { $ret[$key] = $arr1[$key]; unset($arr1[$key]); continue; } $ret[$key] = PEAR_Config::arrayMergeRecursive($arr1[$key], $arr2[$key]); unset($arr1[$key]); } } return array_merge($ret, $arr1); } /** * Writes data into a config layer from a file. * * @param string|null file to read from, or null for default * @param string config layer to insert data into ('user' or * 'system') * @param string|null data to write to config file or null for internal data [DEPRECATED] * @return bool TRUE on success or a PEAR error on failure */ function writeConfigFile($file = null, $layer = 'user', $data = null) { $this->_lazyChannelSetup($layer); if ($layer == 'both' || $layer == 'all') { foreach ($this->files as $type => $file) { $err = $this->writeConfigFile($file, $type, $data); if (PEAR::isError($err)) { return $err; } } return true; } if (empty($this->files[$layer])) { return $this->raiseError("unknown config file type `$layer'"); } if ($file === null) { $file = $this->files[$layer]; } $data = ($data === null) ? $this->configuration[$layer] : $data; $this->_encodeOutput($data); $opt = array('-p', dirname($file)); if (!@System::mkDir($opt)) { return $this->raiseError("could not create directory: " . dirname($file)); } if (file_exists($file) && is_file($file) && !is_writeable($file)) { return $this->raiseError("no write access to $file!"); } $fp = @fopen($file, "w"); if (!$fp) { return $this->raiseError("PEAR_Config::writeConfigFile fopen('$file','w') failed ($php_errormsg)"); } $contents = "#PEAR_Config 0.9\n" . serialize($data); if (!@fwrite($fp, $contents)) { return $this->raiseError("PEAR_Config::writeConfigFile: fwrite failed ($php_errormsg)"); } return true; } /** * Reads configuration data from a file and returns the parsed data * in an array. * * @param string file to read from * @return array configuration data or a PEAR error on failure * @access private */ function _readConfigDataFrom($file) { $fp = false; if (file_exists($file)) { $fp = @fopen($file, "r"); } if (!$fp) { return $this->raiseError("PEAR_Config::readConfigFile fopen('$file','r') failed"); } $size = filesize($file); fclose($fp); $contents = file_get_contents($file); if (empty($contents)) { return $this->raiseError('Configuration file "' . $file . '" is empty'); } $version = false; if (preg_match('/^#PEAR_Config\s+(\S+)\s+/si', $contents, $matches)) { $version = $matches[1]; $contents = substr($contents, strlen($matches[0])); } else { // Museum config file if (substr($contents,0,2) == 'a:') { $version = '0.1'; } } if ($version && version_compare("$version", '1', '<')) { $data = @unserialize($contents); if (!is_array($data) && !$data) { if ($contents == serialize(false)) { $data = array(); } else { $err = $this->raiseError("PEAR_Config: bad data in $file"); return $err; } } if (!is_array($data)) { if (strlen(trim($contents)) > 0) { $error = "PEAR_Config: bad data in $file"; $err = $this->raiseError($error); return $err; } $data = array(); } // add parsing of newer formats here... } else { $err = $this->raiseError("$file: unknown version `$version'"); return $err; } return $data; } /** * Gets the file used for storing the config for a layer * * @param string $layer 'user' or 'system' */ function getConfFile($layer) { return $this->files[$layer]; } /** * @param string Configuration class name, used for detecting duplicate calls * @param array information on a role as parsed from its xml file * @return true|PEAR_Error * @access private */ function _addConfigVars($class, $vars) { static $called = array(); if (isset($called[$class])) { return; } $called[$class] = 1; if (count($vars) > 3) { return $this->raiseError('Roles can only define 3 new config variables or less'); } foreach ($vars as $name => $var) { if (!is_array($var)) { return $this->raiseError('Configuration information must be an array'); } if (!isset($var['type'])) { return $this->raiseError('Configuration information must contain a type'); } elseif (!in_array($var['type'], array('string', 'mask', 'password', 'directory', 'file', 'set'))) { return $this->raiseError( 'Configuration type must be one of directory, file, string, ' . 'mask, set, or password'); } if (!isset($var['default'])) { return $this->raiseError( 'Configuration information must contain a default value ("default" index)'); } if (is_array($var['default'])) { $real_default = ''; foreach ($var['default'] as $config_var => $val) { if (strpos($config_var, 'text') === 0) { $real_default .= $val; } elseif (strpos($config_var, 'constant') === 0) { if (!defined($val)) { return $this->raiseError( 'Unknown constant "' . $val . '" requested in ' . 'default value for configuration variable "' . $name . '"'); } $real_default .= constant($val); } elseif (isset($this->configuration_info[$config_var])) { $real_default .= $this->configuration_info[$config_var]['default']; } else { return $this->raiseError( 'Unknown request for "' . $config_var . '" value in ' . 'default value for configuration variable "' . $name . '"'); } } $var['default'] = $real_default; } if ($var['type'] == 'integer') { $var['default'] = (integer) $var['default']; } if (!isset($var['doc'])) { return $this->raiseError( 'Configuration information must contain a summary ("doc" index)'); } if (!isset($var['prompt'])) { return $this->raiseError( 'Configuration information must contain a simple prompt ("prompt" index)'); } if (!isset($var['group'])) { return $this->raiseError( 'Configuration information must contain a simple group ("group" index)'); } if (isset($this->configuration_info[$name])) { return $this->raiseError('Configuration variable "' . $name . '" already exists'); } $this->configuration_info[$name] = $var; // fix bug #7351: setting custom config variable in a channel fails $this->_channelConfigInfo[] = $name; } return true; } /** * Encodes/scrambles configuration data before writing to files. * Currently, 'password' values will be base64-encoded as to avoid * that people spot cleartext passwords by accident. * * @param array (reference) array to encode values in * @return bool TRUE on success * @access private */ function _encodeOutput(&$data) { foreach ($data as $key => $value) { if ($key == '__channels') { foreach ($data['__channels'] as $channel => $blah) { $this->_encodeOutput($data['__channels'][$channel]); } } if (!isset($this->configuration_info[$key])) { continue; } $type = $this->configuration_info[$key]['type']; switch ($type) { // we base64-encode passwords so they are at least // not shown in plain by accident case 'password': { $data[$key] = base64_encode($data[$key]); break; } case 'mask': { $data[$key] = octdec($data[$key]); break; } } } return true; } /** * Decodes/unscrambles configuration data after reading from files. * * @param array (reference) array to encode values in * @return bool TRUE on success * @access private * * @see PEAR_Config::_encodeOutput */ function _decodeInput(&$data) { if (!is_array($data)) { return true; } foreach ($data as $key => $value) { if ($key == '__channels') { foreach ($data['__channels'] as $channel => $blah) { $this->_decodeInput($data['__channels'][$channel]); } } if (!isset($this->configuration_info[$key])) { continue; } $type = $this->configuration_info[$key]['type']; switch ($type) { case 'password': { $data[$key] = base64_decode($data[$key]); break; } case 'mask': { $data[$key] = decoct($data[$key]); break; } } } return true; } /** * Retrieve the default channel. * * On startup, channels are not initialized, so if the default channel is not * pear.php.net, then initialize the config. * @param string registry layer * @return string|false */ function getDefaultChannel($layer = null) { $ret = false; if ($layer === null) { foreach ($this->layers as $layer) { if (isset($this->configuration[$layer]['default_channel'])) { $ret = $this->configuration[$layer]['default_channel']; break; } } } elseif (isset($this->configuration[$layer]['default_channel'])) { $ret = $this->configuration[$layer]['default_channel']; } if ($ret == 'pear.php.net' && defined('PEAR_RUNTYPE') && PEAR_RUNTYPE == 'pecl') { $ret = 'pecl.php.net'; } if ($ret) { if ($ret != 'pear.php.net') { $this->_lazyChannelSetup(); } return $ret; } return PEAR_CONFIG_DEFAULT_CHANNEL; } /** * Returns a configuration value, prioritizing layers as per the * layers property. * * @param string config key * @return mixed the config value, or NULL if not found * @access public */ function get($key, $layer = null, $channel = false) { if (!isset($this->configuration_info[$key])) { return null; } if ($key == '__channels') { return null; } if ($key == 'default_channel') { return $this->getDefaultChannel($layer); } if (!$channel) { $channel = $this->getDefaultChannel(); } elseif ($channel != 'pear.php.net') { $this->_lazyChannelSetup(); } $channel = strtolower($channel); $test = (in_array($key, $this->_channelConfigInfo)) ? $this->_getChannelValue($key, $layer, $channel) : null; if ($test !== null) { if ($this->_installRoot) { if (in_array($this->getGroup($key), array('File Locations', 'File Locations (Advanced)')) && $this->getType($key) == 'directory') { return $this->_prependPath($test, $this->_installRoot); } } return $test; } if ($layer === null) { foreach ($this->layers as $layer) { if (isset($this->configuration[$layer][$key])) { $test = $this->configuration[$layer][$key]; if ($this->_installRoot) { if (in_array($this->getGroup($key), array('File Locations', 'File Locations (Advanced)')) && $this->getType($key) == 'directory') { return $this->_prependPath($test, $this->_installRoot); } } if ($key == 'preferred_mirror') { $reg = &$this->getRegistry(); if (is_object($reg)) { $chan = $reg->getChannel($channel); if (PEAR::isError($chan)) { return $channel; } if (!$chan->getMirror($test) && $chan->getName() != $test) { return $channel; // mirror does not exist } } } return $test; } } } elseif (isset($this->configuration[$layer][$key])) { $test = $this->configuration[$layer][$key]; if ($this->_installRoot) { if (in_array($this->getGroup($key), array('File Locations', 'File Locations (Advanced)')) && $this->getType($key) == 'directory') { return $this->_prependPath($test, $this->_installRoot); } } if ($key == 'preferred_mirror') { $reg = &$this->getRegistry(); if (is_object($reg)) { $chan = $reg->getChannel($channel); if (PEAR::isError($chan)) { return $channel; } if (!$chan->getMirror($test) && $chan->getName() != $test) { return $channel; // mirror does not exist } } } return $test; } return null; } /** * Returns a channel-specific configuration value, prioritizing layers as per the * layers property. * * @param string config key * @return mixed the config value, or NULL if not found * @access private */ function _getChannelValue($key, $layer, $channel) { if ($key == '__channels' || $channel == 'pear.php.net') { return null; } $ret = null; if ($layer === null) { foreach ($this->layers as $ilayer) { if (isset($this->configuration[$ilayer]['__channels'][$channel][$key])) { $ret = $this->configuration[$ilayer]['__channels'][$channel][$key]; break; } } } elseif (isset($this->configuration[$layer]['__channels'][$channel][$key])) { $ret = $this->configuration[$layer]['__channels'][$channel][$key]; } if ($key != 'preferred_mirror') { return $ret; } if ($ret !== null) { $reg = &$this->getRegistry($layer); if (is_object($reg)) { $chan = $reg->getChannel($channel); if (PEAR::isError($chan)) { return $channel; } if (!$chan->getMirror($ret) && $chan->getName() != $ret) { return $channel; // mirror does not exist } } return $ret; } if ($channel != $this->getDefaultChannel($layer)) { return $channel; // we must use the channel name as the preferred mirror // if the user has not chosen an alternate } return $this->getDefaultChannel($layer); } /** * Set a config value in a specific layer (defaults to 'user'). * Enforces the types defined in the configuration_info array. An * integer config variable will be cast to int, and a set config * variable will be validated against its legal values. * * @param string config key * @param string config value * @param string (optional) config layer * @param string channel to set this value for, or null for global value * @return bool TRUE on success, FALSE on failure */ function set($key, $value, $layer = 'user', $channel = false) { if ($key == '__channels') { return false; } if (!isset($this->configuration[$layer])) { return false; } if ($key == 'default_channel') { // can only set this value globally $channel = 'pear.php.net'; if ($value != 'pear.php.net') { $this->_lazyChannelSetup($layer); } } if ($key == 'preferred_mirror') { if ($channel == '__uri') { return false; // can't set the __uri pseudo-channel's mirror } $reg = &$this->getRegistry($layer); if (is_object($reg)) { $chan = $reg->getChannel($channel ? $channel : 'pear.php.net'); if (PEAR::isError($chan)) { return false; } if (!$chan->getMirror($value) && $chan->getName() != $value) { return false; // mirror does not exist } } } if (!isset($this->configuration_info[$key])) { return false; } extract($this->configuration_info[$key]); switch ($type) { case 'integer': $value = (int)$value; break; case 'set': { // If a valid_set is specified, require the value to // be in the set. If there is no valid_set, accept // any value. if ($valid_set) { reset($valid_set); if ((key($valid_set) === 0 && !in_array($value, $valid_set)) || (key($valid_set) !== 0 && empty($valid_set[$value]))) { return false; } } break; } } if (!$channel) { $channel = $this->get('default_channel', null, 'pear.php.net'); } if (!in_array($channel, $this->_channels)) { $this->_lazyChannelSetup($layer); $reg = &$this->getRegistry($layer); if ($reg) { $channel = $reg->channelName($channel); } if (!in_array($channel, $this->_channels)) { return false; } } if ($channel != 'pear.php.net') { if (in_array($key, $this->_channelConfigInfo)) { $this->configuration[$layer]['__channels'][$channel][$key] = $value; return true; } return false; } if ($key == 'default_channel') { if (!isset($reg)) { $reg = &$this->getRegistry($layer); if (!$reg) { $reg = &$this->getRegistry(); } } if ($reg) { $value = $reg->channelName($value); } if (!$value) { return false; } } $this->configuration[$layer][$key] = $value; if ($key == 'php_dir' && !$this->_noRegistry) { if (!isset($this->_registry[$layer]) || $value != $this->_registry[$layer]->install_dir) { $this->_registry[$layer] = new PEAR_Registry($value); $this->_regInitialized[$layer] = false; $this->_registry[$layer]->setConfig($this, false); } } return true; } function _lazyChannelSetup($uselayer = false) { if ($this->_noRegistry) { return; } $merge = false; foreach ($this->_registry as $layer => $p) { if ($uselayer && $uselayer != $layer) { continue; } if (!$this->_regInitialized[$layer]) { if ($layer == 'default' && isset($this->_registry['user']) || isset($this->_registry['system'])) { // only use the default registry if there are no alternatives continue; } if (!is_object($this->_registry[$layer])) { if ($phpdir = $this->get('php_dir', $layer, 'pear.php.net')) { $this->_registry[$layer] = new PEAR_Registry( $phpdir, false, false, $this->get('metadata_dir', $layer, 'pear.php.net')); $this->_registry[$layer]->setConfig($this, false); $this->_regInitialized[$layer] = false; } else { unset($this->_registry[$layer]); return; } } $this->setChannels($this->_registry[$layer]->listChannels(), $merge); $this->_regInitialized[$layer] = true; $merge = true; } } } /** * Set the list of channels. * * This should be set via a call to {@link PEAR_Registry::listChannels()} * @param array * @param bool * @return bool success of operation */ function setChannels($channels, $merge = false) { if (!is_array($channels)) { return false; } if ($merge) { $this->_channels = array_merge($this->_channels, $channels); } else { $this->_channels = $channels; } foreach ($channels as $channel) { $channel = strtolower($channel); if ($channel == 'pear.php.net') { continue; } foreach ($this->layers as $layer) { if (!isset($this->configuration[$layer]['__channels'])) { $this->configuration[$layer]['__channels'] = array(); } if (!isset($this->configuration[$layer]['__channels'][$channel]) || !is_array($this->configuration[$layer]['__channels'][$channel])) { $this->configuration[$layer]['__channels'][$channel] = array(); } } } return true; } /** * Get the type of a config value. * * @param string config key * * @return string type, one of "string", "integer", "file", * "directory", "set" or "password". * * @access public * */ function getType($key) { if (isset($this->configuration_info[$key])) { return $this->configuration_info[$key]['type']; } return false; } /** * Get the documentation for a config value. * * @param string config key * @return string documentation string * * @access public * */ function getDocs($key) { if (isset($this->configuration_info[$key])) { return $this->configuration_info[$key]['doc']; } return false; } /** * Get the short documentation for a config value. * * @param string config key * @return string short documentation string * * @access public * */ function getPrompt($key) { if (isset($this->configuration_info[$key])) { return $this->configuration_info[$key]['prompt']; } return false; } /** * Get the parameter group for a config key. * * @param string config key * @return string parameter group * * @access public * */ function getGroup($key) { if (isset($this->configuration_info[$key])) { return $this->configuration_info[$key]['group']; } return false; } /** * Get the list of parameter groups. * * @return array list of parameter groups * * @access public * */ function getGroups() { $tmp = array(); foreach ($this->configuration_info as $key => $info) { $tmp[$info['group']] = 1; } return array_keys($tmp); } /** * Get the list of the parameters in a group. * * @param string $group parameter group * @return array list of parameters in $group * * @access public * */ function getGroupKeys($group) { $keys = array(); foreach ($this->configuration_info as $key => $info) { if ($info['group'] == $group) { $keys[] = $key; } } return $keys; } /** * Get the list of allowed set values for a config value. Returns * NULL for config values that are not sets. * * @param string config key * @return array enumerated array of set values, or NULL if the * config key is unknown or not a set * * @access public * */ function getSetValues($key) { if (isset($this->configuration_info[$key]) && isset($this->configuration_info[$key]['type']) && $this->configuration_info[$key]['type'] == 'set') { $valid_set = $this->configuration_info[$key]['valid_set']; reset($valid_set); if (key($valid_set) === 0) { return $valid_set; } return array_keys($valid_set); } return null; } /** * Get all the current config keys. * * @return array simple array of config keys * * @access public */ function getKeys() { $keys = array(); foreach ($this->layers as $layer) { $test = $this->configuration[$layer]; if (isset($test['__channels'])) { foreach ($test['__channels'] as $channel => $configs) { $keys = array_merge($keys, $configs); } } unset($test['__channels']); $keys = array_merge($keys, $test); } return array_keys($keys); } /** * Remove the a config key from a specific config layer. * * @param string config key * @param string (optional) config layer * @param string (optional) channel (defaults to default channel) * @return bool TRUE on success, FALSE on failure * * @access public */ function remove($key, $layer = 'user', $channel = null) { if ($channel === null) { $channel = $this->getDefaultChannel(); } if ($channel !== 'pear.php.net') { if (isset($this->configuration[$layer]['__channels'][$channel][$key])) { unset($this->configuration[$layer]['__channels'][$channel][$key]); return true; } } if (isset($this->configuration[$layer][$key])) { unset($this->configuration[$layer][$key]); return true; } return false; } /** * Temporarily remove an entire config layer. USE WITH CARE! * * @param string config key * @param string (optional) config layer * @return bool TRUE on success, FALSE on failure * * @access public */ function removeLayer($layer) { if (isset($this->configuration[$layer])) { $this->configuration[$layer] = array(); return true; } return false; } /** * Stores configuration data in a layer. * * @param string config layer to store * @return bool TRUE on success, or PEAR error on failure * * @access public */ function store($layer = 'user', $data = null) { return $this->writeConfigFile(null, $layer, $data); } /** * Tells what config layer that gets to define a key. * * @param string config key * @param boolean return the defining channel * * @return string|array the config layer, or an empty string if not found. * * if $returnchannel, the return is an array array('layer' => layername, * 'channel' => channelname), or an empty string if not found * * @access public */ function definedBy($key, $returnchannel = false) { foreach ($this->layers as $layer) { $channel = $this->getDefaultChannel(); if ($channel !== 'pear.php.net') { if (isset($this->configuration[$layer]['__channels'][$channel][$key])) { if ($returnchannel) { return array('layer' => $layer, 'channel' => $channel); } return $layer; } } if (isset($this->configuration[$layer][$key])) { if ($returnchannel) { return array('layer' => $layer, 'channel' => 'pear.php.net'); } return $layer; } } return ''; } /** * Tells whether a given key exists as a config value. * * @param string config key * @return bool whether exists in this object * * @access public */ function isDefined($key) { foreach ($this->layers as $layer) { if (isset($this->configuration[$layer][$key])) { return true; } } return false; } /** * Tells whether a given config layer exists. * * @param string config layer * @return bool whether exists in this object * * @access public */ function isDefinedLayer($layer) { return isset($this->configuration[$layer]); } /** * Returns the layers defined (except the 'default' one) * * @return array of the defined layers */ function getLayers() { $cf = $this->configuration; unset($cf['default']); return array_keys($cf); } function apiVersion() { return '1.1'; } /** * @return PEAR_Registry */ function &getRegistry($use = null) { $layer = $use === null ? 'user' : $use; if (isset($this->_registry[$layer])) { return $this->_registry[$layer]; } elseif ($use === null && isset($this->_registry['system'])) { return $this->_registry['system']; } elseif ($use === null && isset($this->_registry['default'])) { return $this->_registry['default']; } elseif ($use) { $a = false; return $a; } // only go here if null was passed in echo "CRITICAL ERROR: Registry could not be initialized from any value"; exit(1); } /** * This is to allow customization like the use of installroot * @param PEAR_Registry * @return bool */ function setRegistry(&$reg, $layer = 'user') { if ($this->_noRegistry) { return false; } if (!in_array($layer, array('user', 'system'))) { return false; } $this->_registry[$layer] = &$reg; if (is_object($reg)) { $this->_registry[$layer]->setConfig($this, false); } return true; } function noRegistry() { $this->_noRegistry = true; } /** * @return PEAR_REST */ function &getREST($version, $options = array()) { $version = str_replace('.', '', $version); if (!class_exists($class = 'PEAR_REST_' . $version)) { require_once 'PEAR/REST/' . $version . '.php'; } $remote = new $class($this, $options); return $remote; } /** * The ftp server is set in {@link readFTPConfigFile()}. It exists only if a * remote configuration file has been specified * @return PEAR_FTP|false */ function &getFTP() { if (isset($this->_ftp)) { return $this->_ftp; } $a = false; return $a; } static function _prependPath($path, $prepend) { if (strlen($prepend) > 0) { if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) { if (preg_match('/^[a-z]:/i', $prepend)) { $prepend = substr($prepend, 2); } elseif ($prepend[0] != '\\') { $prepend = "\\$prepend"; } $path = substr($path, 0, 2) . $prepend . substr($path, 2); } else { $path = $prepend . $path; } } return $path; } /** * @param string|false installation directory to prepend to all _dir variables, or false to * disable */ function setInstallRoot($root) { if (substr($root, -1) == DIRECTORY_SEPARATOR) { $root = substr($root, 0, -1); } $old = $this->_installRoot; $this->_installRoot = $root; if (($old != $root) && !$this->_noRegistry) { foreach (array_keys($this->_registry) as $layer) { if ($layer == 'ftp' || !isset($this->_registry[$layer])) { continue; } $this->_registry[$layer] = new PEAR_Registry( $this->get('php_dir', $layer, 'pear.php.net'), false, false, $this->get('metadata_dir', $layer, 'pear.php.net')); $this->_registry[$layer]->setConfig($this, false); $this->_regInitialized[$layer] = false; } } } } PK=1]Gapear/PEAR/ChannelFile.phpnu[ * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * Needed for error handling */ require_once 'PEAR/ErrorStack.php'; require_once 'PEAR/XMLParser.php'; require_once 'PEAR/Common.php'; /** * Error code if the channel.xml tag does not contain a valid version */ define('PEAR_CHANNELFILE_ERROR_NO_VERSION', 1); /** * Error code if the channel.xml tag version is not supported (version 1.0 is the only supported version, * currently */ define('PEAR_CHANNELFILE_ERROR_INVALID_VERSION', 2); /** * Error code if parsing is attempted with no xml extension */ define('PEAR_CHANNELFILE_ERROR_NO_XML_EXT', 3); /** * Error code if creating the xml parser resource fails */ define('PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER', 4); /** * Error code used for all sax xml parsing errors */ define('PEAR_CHANNELFILE_ERROR_PARSER_ERROR', 5); /**#@+ * Validation errors */ /** * Error code when channel name is missing */ define('PEAR_CHANNELFILE_ERROR_NO_NAME', 6); /** * Error code when channel name is invalid */ define('PEAR_CHANNELFILE_ERROR_INVALID_NAME', 7); /** * Error code when channel summary is missing */ define('PEAR_CHANNELFILE_ERROR_NO_SUMMARY', 8); /** * Error code when channel summary is multi-line */ define('PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY', 9); /** * Error code when channel server is missing for protocol */ define('PEAR_CHANNELFILE_ERROR_NO_HOST', 10); /** * Error code when channel server is invalid for protocol */ define('PEAR_CHANNELFILE_ERROR_INVALID_HOST', 11); /** * Error code when a mirror name is invalid */ define('PEAR_CHANNELFILE_ERROR_INVALID_MIRROR', 21); /** * Error code when a mirror type is invalid */ define('PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE', 22); /** * Error code when an attempt is made to generate xml, but the parsed content is invalid */ define('PEAR_CHANNELFILE_ERROR_INVALID', 23); /** * Error code when an empty package name validate regex is passed in */ define('PEAR_CHANNELFILE_ERROR_EMPTY_REGEX', 24); /** * Error code when a tag has no version */ define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION', 25); /** * Error code when a tag has no name */ define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME', 26); /** * Error code when a tag has no name */ define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME', 27); /** * Error code when a tag has no version attribute */ define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION', 28); /** * Error code when a mirror does not exist but is called for in one of the set* * methods. */ define('PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND', 32); /** * Error code when a server port is not numeric */ define('PEAR_CHANNELFILE_ERROR_INVALID_PORT', 33); /** * Error code when contains no version attribute */ define('PEAR_CHANNELFILE_ERROR_NO_STATICVERSION', 34); /** * Error code when contains no type attribute in a protocol definition */ define('PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE', 35); /** * Error code when a mirror is defined and the channel.xml represents the __uri pseudo-channel */ define('PEAR_CHANNELFILE_URI_CANT_MIRROR', 36); /** * Error code when ssl attribute is present and is not "yes" */ define('PEAR_CHANNELFILE_ERROR_INVALID_SSL', 37); /**#@-*/ /** * Mirror types allowed. Currently only internet servers are recognized. */ $GLOBALS['_PEAR_CHANNELS_MIRROR_TYPES'] = array('server'); /** * The Channel handling class * * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2009 The Authors * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 1.10.18 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_ChannelFile { /** * @access private * @var PEAR_ErrorStack * @access private */ var $_stack; /** * Supported channel.xml versions, for parsing * @var array * @access private */ var $_supportedVersions = array('1.0'); /** * Parsed channel information * @var array * @access private */ var $_channelInfo; /** * index into the subchannels array, used for parsing xml * @var int * @access private */ var $_subchannelIndex; /** * index into the mirrors array, used for parsing xml * @var int * @access private */ var $_mirrorIndex; /** * Flag used to determine the validity of parsed content * @var boolean * @access private */ var $_isValid = false; function __construct() { $this->_stack = new PEAR_ErrorStack('PEAR_ChannelFile'); $this->_stack->setErrorMessageTemplate($this->_getErrorMessage()); $this->_isValid = false; } /** * @return array * @access protected */ function _getErrorMessage() { return array( PEAR_CHANNELFILE_ERROR_INVALID_VERSION => 'While parsing channel.xml, an invalid version number "%version% was passed in, expecting one of %versions%', PEAR_CHANNELFILE_ERROR_NO_VERSION => 'No version number found in tag', PEAR_CHANNELFILE_ERROR_NO_XML_EXT => '%error%', PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER => 'Unable to create XML parser', PEAR_CHANNELFILE_ERROR_PARSER_ERROR => '%error%', PEAR_CHANNELFILE_ERROR_NO_NAME => 'Missing channel name', PEAR_CHANNELFILE_ERROR_INVALID_NAME => 'Invalid channel %tag% "%name%"', PEAR_CHANNELFILE_ERROR_NO_SUMMARY => 'Missing channel summary', PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY => 'Channel summary should be on one line, but is multi-line', PEAR_CHANNELFILE_ERROR_NO_HOST => 'Missing channel server for %type% server', PEAR_CHANNELFILE_ERROR_INVALID_HOST => 'Server name "%server%" is invalid for %type% server', PEAR_CHANNELFILE_ERROR_INVALID_MIRROR => 'Invalid mirror name "%name%", mirror type %type%', PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE => 'Invalid mirror type "%type%"', PEAR_CHANNELFILE_ERROR_INVALID => 'Cannot generate xml, contents are invalid', PEAR_CHANNELFILE_ERROR_EMPTY_REGEX => 'packagenameregex cannot be empty', PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION => '%parent% %protocol% function has no version', PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME => '%parent% %protocol% function has no name', PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE => '%parent% rest baseurl has no type', PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME => 'Validation package has no name in tag', PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION => 'Validation package "%package%" has no version', PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND => 'Mirror "%mirror%" does not exist', PEAR_CHANNELFILE_ERROR_INVALID_PORT => 'Port "%port%" must be numeric', PEAR_CHANNELFILE_ERROR_NO_STATICVERSION => ' tag must contain version attribute', PEAR_CHANNELFILE_URI_CANT_MIRROR => 'The __uri pseudo-channel cannot have mirrors', PEAR_CHANNELFILE_ERROR_INVALID_SSL => '%server% has invalid ssl attribute "%ssl%" can only be yes or not present', ); } /** * @param string contents of package.xml file * @return bool success of parsing */ function fromXmlString($data) { if (preg_match('/_supportedVersions)) { $this->_stack->push(PEAR_CHANNELFILE_ERROR_INVALID_VERSION, 'error', array('version' => $channelversion[1])); return false; } $parser = new PEAR_XMLParser; $result = $parser->parse($data); if ($result !== true) { if ($result->getCode() == 1) { $this->_stack->push(PEAR_CHANNELFILE_ERROR_NO_XML_EXT, 'error', array('error' => $result->getMessage())); } else { $this->_stack->push(PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER, 'error'); } return false; } $this->_channelInfo = $parser->getData(); return true; } else { $this->_stack->push(PEAR_CHANNELFILE_ERROR_NO_VERSION, 'error', array('xml' => $data)); return false; } } /** * @return array */ function toArray() { if (!$this->_isValid && !$this->validate()) { return false; } return $this->_channelInfo; } /** * @param array * * @return PEAR_ChannelFile|false false if invalid */ public static function &fromArray( $data, $compatibility = false, $stackClass = 'PEAR_ErrorStack' ) { $a = new PEAR_ChannelFile($compatibility, $stackClass); $a->_fromArray($data); if (!$a->validate()) { $a = false; return $a; } return $a; } /** * Unlike {@link fromArray()} this does not do any validation * * @param array * * @return PEAR_ChannelFile */ public static function &fromArrayWithErrors( $data, $compatibility = false, $stackClass = 'PEAR_ErrorStack' ) { $a = new PEAR_ChannelFile($compatibility, $stackClass); $a->_fromArray($data); return $a; } /** * @param array * @access private */ function _fromArray($data) { $this->_channelInfo = $data; } /** * Wrapper to {@link PEAR_ErrorStack::getErrors()} * @param boolean determines whether to purge the error stack after retrieving * @return array */ function getErrors($purge = false) { return $this->_stack->getErrors($purge); } /** * Unindent given string (?) * * @param string $str The string that has to be unindented. * @return string * @access private */ function _unIndent($str) { // remove leading newlines $str = preg_replace('/^[\r\n]+/', '', $str); // find whitespace at the beginning of the first line $indent_len = strspn($str, " \t"); $indent = substr($str, 0, $indent_len); $data = ''; // remove the same amount of whitespace from following lines foreach (explode("\n", $str) as $line) { if (substr($line, 0, $indent_len) == $indent) { $data .= substr($line, $indent_len) . "\n"; } } return $data; } /** * Parse a channel.xml file. Expects the name of * a channel xml file as input. * * @param string $descfile name of channel xml file * @return bool success of parsing */ function fromXmlFile($descfile) { if (!file_exists($descfile) || !is_file($descfile) || !is_readable($descfile) || (!$fp = fopen($descfile, 'r'))) { require_once 'PEAR.php'; return PEAR::raiseError("Unable to open $descfile"); } // read the whole thing so we only get one cdata callback // for each block of cdata fclose($fp); $data = file_get_contents($descfile); return $this->fromXmlString($data); } /** * Parse channel information from different sources * * This method is able to extract information about a channel * from an .xml file or a string * * @access public * @param string Filename of the source or the source itself * @return bool */ function fromAny($info) { if (is_string($info) && file_exists($info) && strlen($info) < 255) { $tmp = substr($info, -4); if ($tmp == '.xml') { $info = $this->fromXmlFile($info); } else { $fp = fopen($info, "r"); $test = fread($fp, 5); fclose($fp); if ($test == "fromXmlFile($info); } } if (PEAR::isError($info)) { require_once 'PEAR.php'; return PEAR::raiseError($info); } } if (is_string($info)) { $info = $this->fromXmlString($info); } return $info; } /** * Return an XML document based on previous parsing and modifications * * @return string XML data * * @access public */ function toXml() { if (!$this->_isValid && !$this->validate()) { $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID); return false; } if (!isset($this->_channelInfo['attribs']['version'])) { $this->_channelInfo['attribs']['version'] = '1.0'; } $channelInfo = $this->_channelInfo; $ret = "\n"; $ret .= " $channelInfo[name] " . htmlspecialchars($channelInfo['summary'])." "; if (isset($channelInfo['suggestedalias'])) { $ret .= ' ' . $channelInfo['suggestedalias'] . "\n"; } if (isset($channelInfo['validatepackage'])) { $ret .= ' ' . htmlspecialchars($channelInfo['validatepackage']['_content']) . "\n"; } $ret .= " \n"; $ret .= ' _makeRestXml($channelInfo['servers']['primary']['rest'], ' '); } $ret .= " \n"; if (isset($channelInfo['servers']['mirror'])) { $ret .= $this->_makeMirrorsXml($channelInfo); } $ret .= " \n"; $ret .= ""; return str_replace("\r", "\n", str_replace("\r\n", "\n", $ret)); } /** * Generate the tag * @access private */ function _makeRestXml($info, $indent) { $ret = $indent . "\n"; if (isset($info['baseurl']) && !isset($info['baseurl'][0])) { $info['baseurl'] = array($info['baseurl']); } if (isset($info['baseurl'])) { foreach ($info['baseurl'] as $url) { $ret .= "$indent \n"; } } $ret .= $indent . "\n"; return $ret; } /** * Generate the tag * @access private */ function _makeMirrorsXml($channelInfo) { $ret = ""; if (!isset($channelInfo['servers']['mirror'][0])) { $channelInfo['servers']['mirror'] = array($channelInfo['servers']['mirror']); } foreach ($channelInfo['servers']['mirror'] as $mirror) { $ret .= ' _makeRestXml($mirror['rest'], ' '); } $ret .= " \n"; } else { $ret .= "/>\n"; } } return $ret; } /** * Generate the tag * @access private */ function _makeFunctionsXml($functions, $indent, $rest = false) { $ret = ''; if (!isset($functions[0])) { $functions = array($functions); } foreach ($functions as $function) { $ret .= "$indent\n"; } return $ret; } /** * Validation error. Also marks the object contents as invalid * @param error code * @param array error information * @access private */ function _validateError($code, $params = array()) { $this->_stack->push($code, 'error', $params); $this->_isValid = false; } /** * Validation warning. Does not mark the object contents invalid. * @param error code * @param array error information * @access private */ function _validateWarning($code, $params = array()) { $this->_stack->push($code, 'warning', $params); } /** * Validate parsed file. * * @access public * @return boolean */ function validate() { $this->_isValid = true; $info = $this->_channelInfo; if (empty($info['name'])) { $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_NAME); } elseif (!$this->validChannelServer($info['name'])) { if ($info['name'] != '__uri') { $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, array('tag' => 'name', 'name' => $info['name'])); } } if (empty($info['summary'])) { $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY); } elseif (strpos(trim($info['summary']), "\n") !== false) { $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY, array('summary' => $info['summary'])); } if (isset($info['suggestedalias'])) { if (!$this->validChannelServer($info['suggestedalias'])) { $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, array('tag' => 'suggestedalias', 'name' =>$info['suggestedalias'])); } } if (isset($info['localalias'])) { if (!$this->validChannelServer($info['localalias'])) { $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, array('tag' => 'localalias', 'name' =>$info['localalias'])); } } if (isset($info['validatepackage'])) { if (!isset($info['validatepackage']['_content'])) { $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME); } if (!isset($info['validatepackage']['attribs']['version'])) { $content = isset($info['validatepackage']['_content']) ? $info['validatepackage']['_content'] : null; $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION, array('package' => $content)); } } if (isset($info['servers']['primary']['attribs'], $info['servers']['primary']['attribs']['port']) && !is_numeric($info['servers']['primary']['attribs']['port'])) { $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_PORT, array('port' => $info['servers']['primary']['attribs']['port'])); } if (isset($info['servers']['primary']['attribs'], $info['servers']['primary']['attribs']['ssl']) && $info['servers']['primary']['attribs']['ssl'] != 'yes') { $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL, array('ssl' => $info['servers']['primary']['attribs']['ssl'], 'server' => $info['name'])); } if (isset($info['servers']['primary']['rest']) && isset($info['servers']['primary']['rest']['baseurl'])) { $this->_validateFunctions('rest', $info['servers']['primary']['rest']['baseurl']); } if (isset($info['servers']['mirror'])) { if ($this->_channelInfo['name'] == '__uri') { $this->_validateError(PEAR_CHANNELFILE_URI_CANT_MIRROR); } if (!isset($info['servers']['mirror'][0])) { $info['servers']['mirror'] = array($info['servers']['mirror']); } foreach ($info['servers']['mirror'] as $mirror) { if (!isset($mirror['attribs']['host'])) { $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_HOST, array('type' => 'mirror')); } elseif (!$this->validChannelServer($mirror['attribs']['host'])) { $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_HOST, array('server' => $mirror['attribs']['host'], 'type' => 'mirror')); } if (isset($mirror['attribs']['ssl']) && $mirror['attribs']['ssl'] != 'yes') { $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL, array('ssl' => $info['ssl'], 'server' => $mirror['attribs']['host'])); } if (isset($mirror['rest'])) { $this->_validateFunctions('rest', $mirror['rest']['baseurl'], $mirror['attribs']['host']); } } } return $this->_isValid; } /** * @param string rest - protocol name this function applies to * @param array the functions * @param string the name of the parent element (mirror name, for instance) */ function _validateFunctions($protocol, $functions, $parent = '') { if (!isset($functions[0])) { $functions = array($functions); } foreach ($functions as $function) { if (!isset($function['_content']) || empty($function['_content'])) { $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME, array('parent' => $parent, 'protocol' => $protocol)); } if ($protocol == 'rest') { if (!isset($function['attribs']['type']) || empty($function['attribs']['type'])) { $this->_validateError(PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE, array('parent' => $parent, 'protocol' => $protocol)); } } else { if (!isset($function['attribs']['version']) || empty($function['attribs']['version'])) { $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION, array('parent' => $parent, 'protocol' => $protocol)); } } } } /** * Test whether a string contains a valid channel server. * @param string $ver the package version to test * @return bool */ function validChannelServer($server) { if ($server == '__uri') { return true; } return (bool) preg_match(PEAR_CHANNELS_SERVER_PREG, $server); } /** * @return string|false */ function getName() { if (isset($this->_channelInfo['name'])) { return $this->_channelInfo['name']; } return false; } /** * @return string|false */ function getServer() { if (isset($this->_channelInfo['name'])) { return $this->_channelInfo['name']; } return false; } /** * @return int|80 port number to connect to */ function getPort($mirror = false) { if ($mirror) { if ($mir = $this->getMirror($mirror)) { if (isset($mir['attribs']['port'])) { return $mir['attribs']['port']; } if ($this->getSSL($mirror)) { return 443; } return 80; } return false; } if (isset($this->_channelInfo['servers']['primary']['attribs']['port'])) { return $this->_channelInfo['servers']['primary']['attribs']['port']; } if ($this->getSSL()) { return 443; } return 80; } /** * @return bool Determines whether secure sockets layer (SSL) is used to connect to this channel */ function getSSL($mirror = false) { if ($mirror) { if ($mir = $this->getMirror($mirror)) { if (isset($mir['attribs']['ssl'])) { return true; } return false; } return false; } if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) { return true; } return false; } /** * @return string|false */ function getSummary() { if (isset($this->_channelInfo['summary'])) { return $this->_channelInfo['summary']; } return false; } /** * @param string protocol type * @param string Mirror name * @return array|false */ function getFunctions($protocol, $mirror = false) { if ($this->getName() == '__uri') { return false; } $function = $protocol == 'rest' ? 'baseurl' : 'function'; if ($mirror) { if ($mir = $this->getMirror($mirror)) { if (isset($mir[$protocol][$function])) { return $mir[$protocol][$function]; } } return false; } if (isset($this->_channelInfo['servers']['primary'][$protocol][$function])) { return $this->_channelInfo['servers']['primary'][$protocol][$function]; } return false; } /** * @param string Protocol type * @param string Function name (null to return the * first protocol of the type requested) * @param string Mirror name, if any * @return array */ function getFunction($type, $name = null, $mirror = false) { $protocols = $this->getFunctions($type, $mirror); if (!$protocols) { return false; } foreach ($protocols as $protocol) { if ($name === null) { return $protocol; } if ($protocol['_content'] != $name) { continue; } return $protocol; } return false; } /** * @param string protocol type * @param string protocol name * @param string version * @param string mirror name * @return boolean */ function supports($type, $name = null, $mirror = false, $version = '1.0') { $protocols = $this->getFunctions($type, $mirror); if (!$protocols) { return false; } foreach ($protocols as $protocol) { if ($protocol['attribs']['version'] != $version) { continue; } if ($name === null) { return true; } if ($protocol['_content'] != $name) { continue; } return true; } return false; } /** * Determines whether a channel supports Representational State Transfer (REST) protocols * for retrieving channel information * @param string * @return bool */ function supportsREST($mirror = false) { if ($mirror == $this->_channelInfo['name']) { $mirror = false; } if ($mirror) { if ($mir = $this->getMirror($mirror)) { return isset($mir['rest']); } return false; } return isset($this->_channelInfo['servers']['primary']['rest']); } /** * Get the URL to access a base resource. * * Hyperlinks in the returned xml will be used to retrieve the proper information * needed. This allows extreme extensibility and flexibility in implementation * @param string Resource Type to retrieve */ function getBaseURL($resourceType, $mirror = false) { if ($mirror == $this->_channelInfo['name']) { $mirror = false; } if ($mirror) { $mir = $this->getMirror($mirror); if (!$mir) { return false; } $rest = $mir['rest']; } else { $rest = $this->_channelInfo['servers']['primary']['rest']; } if (!isset($rest['baseurl'][0])) { $rest['baseurl'] = array($rest['baseurl']); } foreach ($rest['baseurl'] as $baseurl) { if (strtolower($baseurl['attribs']['type']) == strtolower($resourceType)) { return $baseurl['_content']; } } return false; } /** * Since REST does not implement RPC, provide this as a logical wrapper around * resetFunctions for REST * @param string|false mirror name, if any */ function resetREST($mirror = false) { return $this->resetFunctions('rest', $mirror); } /** * Empty all protocol definitions * @param string protocol type * @param string|false mirror name, if any */ function resetFunctions($type, $mirror = false) { if ($mirror) { if (isset($this->_channelInfo['servers']['mirror'])) { $mirrors = $this->_channelInfo['servers']['mirror']; if (!isset($mirrors[0])) { $mirrors = array($mirrors); } foreach ($mirrors as $i => $mir) { if ($mir['attribs']['host'] == $mirror) { if (isset($this->_channelInfo['servers']['mirror'][$i][$type])) { unset($this->_channelInfo['servers']['mirror'][$i][$type]); } return true; } } return false; } return false; } if (isset($this->_channelInfo['servers']['primary'][$type])) { unset($this->_channelInfo['servers']['primary'][$type]); } return true; } /** * Set a channel's protocols to the protocols supported by pearweb */ function setDefaultPEARProtocols($version = '1.0', $mirror = false) { switch ($version) { case '1.0' : $this->resetREST($mirror); if (!isset($this->_channelInfo['servers'])) { $this->_channelInfo['servers'] = array('primary' => array('rest' => array())); } elseif (!isset($this->_channelInfo['servers']['primary'])) { $this->_channelInfo['servers']['primary'] = array('rest' => array()); } return true; break; default : return false; break; } } /** * @return array */ function getMirrors() { if (isset($this->_channelInfo['servers']['mirror'])) { $mirrors = $this->_channelInfo['servers']['mirror']; if (!isset($mirrors[0])) { $mirrors = array($mirrors); } return $mirrors; } return array(); } /** * Get the unserialized XML representing a mirror * @return array|false */ function getMirror($server) { foreach ($this->getMirrors() as $mirror) { if ($mirror['attribs']['host'] == $server) { return $mirror; } } return false; } /** * @param string * @return string|false * @error PEAR_CHANNELFILE_ERROR_NO_NAME * @error PEAR_CHANNELFILE_ERROR_INVALID_NAME */ function setName($name) { return $this->setServer($name); } /** * Set the socket number (port) that is used to connect to this channel * @param integer * @param string|false name of the mirror server, or false for the primary */ function setPort($port, $mirror = false) { if ($mirror) { if (!isset($this->_channelInfo['servers']['mirror'])) { $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND, array('mirror' => $mirror)); return false; } if (isset($this->_channelInfo['servers']['mirror'][0])) { foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) { if ($mirror == $mir['attribs']['host']) { $this->_channelInfo['servers']['mirror'][$i]['attribs']['port'] = $port; return true; } } return false; } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) { $this->_channelInfo['servers']['mirror']['attribs']['port'] = $port; $this->_isValid = false; return true; } } $this->_channelInfo['servers']['primary']['attribs']['port'] = $port; $this->_isValid = false; return true; } /** * Set the socket number (port) that is used to connect to this channel * @param bool Determines whether to turn on SSL support or turn it off * @param string|false name of the mirror server, or false for the primary */ function setSSL($ssl = true, $mirror = false) { if ($mirror) { if (!isset($this->_channelInfo['servers']['mirror'])) { $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND, array('mirror' => $mirror)); return false; } if (isset($this->_channelInfo['servers']['mirror'][0])) { foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) { if ($mirror == $mir['attribs']['host']) { if (!$ssl) { if (isset($this->_channelInfo['servers']['mirror'][$i] ['attribs']['ssl'])) { unset($this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl']); } } else { $this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl'] = 'yes'; } return true; } } return false; } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) { if (!$ssl) { if (isset($this->_channelInfo['servers']['mirror']['attribs']['ssl'])) { unset($this->_channelInfo['servers']['mirror']['attribs']['ssl']); } } else { $this->_channelInfo['servers']['mirror']['attribs']['ssl'] = 'yes'; } $this->_isValid = false; return true; } } if ($ssl) { $this->_channelInfo['servers']['primary']['attribs']['ssl'] = 'yes'; } else { if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) { unset($this->_channelInfo['servers']['primary']['attribs']['ssl']); } } $this->_isValid = false; return true; } /** * @param string * @return string|false * @error PEAR_CHANNELFILE_ERROR_NO_SERVER * @error PEAR_CHANNELFILE_ERROR_INVALID_SERVER */ function setServer($server, $mirror = false) { if (empty($server)) { $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SERVER); return false; } elseif (!$this->validChannelServer($server)) { $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, array('tag' => 'name', 'name' => $server)); return false; } if ($mirror) { $found = false; foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) { if ($mirror == $mir['attribs']['host']) { $found = true; break; } } if (!$found) { $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND, array('mirror' => $mirror)); return false; } $this->_channelInfo['mirror'][$i]['attribs']['host'] = $server; return true; } $this->_channelInfo['name'] = $server; return true; } /** * @param string * @return boolean success * @error PEAR_CHANNELFILE_ERROR_NO_SUMMARY * @warning PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY */ function setSummary($summary) { if (empty($summary)) { $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY); return false; } elseif (strpos(trim($summary), "\n") !== false) { $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY, array('summary' => $summary)); } $this->_channelInfo['summary'] = $summary; return true; } /** * @param string * @param boolean determines whether the alias is in channel.xml or local * @return boolean success */ function setAlias($alias, $local = false) { if (!$this->validChannelServer($alias)) { $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, array('tag' => 'suggestedalias', 'name' => $alias)); return false; } if ($local) { $this->_channelInfo['localalias'] = $alias; } else { $this->_channelInfo['suggestedalias'] = $alias; } return true; } /** * @return string */ function getAlias() { if (isset($this->_channelInfo['localalias'])) { return $this->_channelInfo['localalias']; } if (isset($this->_channelInfo['suggestedalias'])) { return $this->_channelInfo['suggestedalias']; } if (isset($this->_channelInfo['name'])) { return $this->_channelInfo['name']; } return ''; } /** * Set the package validation object if it differs from PEAR's default * The class must be includeable via changing _ in the classname to path separator, * but no checking of this is made. * @param string|false pass in false to reset to the default packagename regex * @return boolean success */ function setValidationPackage($validateclass, $version) { if (empty($validateclass)) { unset($this->_channelInfo['validatepackage']); } $this->_channelInfo['validatepackage'] = array('_content' => $validateclass); $this->_channelInfo['validatepackage']['attribs'] = array('version' => $version); } /** * Add a protocol to the provides section * @param string protocol type * @param string protocol version * @param string protocol name, if any * @param string mirror name, if this is a mirror's protocol * @return bool */ function addFunction($type, $version, $name = '', $mirror = false) { if ($mirror) { return $this->addMirrorFunction($mirror, $type, $version, $name); } $set = array('attribs' => array('version' => $version), '_content' => $name); if (!isset($this->_channelInfo['servers']['primary'][$type]['function'])) { if (!isset($this->_channelInfo['servers'])) { $this->_channelInfo['servers'] = array('primary' => array($type => array())); } elseif (!isset($this->_channelInfo['servers']['primary'])) { $this->_channelInfo['servers']['primary'] = array($type => array()); } $this->_channelInfo['servers']['primary'][$type]['function'] = $set; $this->_isValid = false; return true; } elseif (!isset($this->_channelInfo['servers']['primary'][$type]['function'][0])) { $this->_channelInfo['servers']['primary'][$type]['function'] = array( $this->_channelInfo['servers']['primary'][$type]['function']); } $this->_channelInfo['servers']['primary'][$type]['function'][] = $set; return true; } /** * Add a protocol to a mirror's provides section * @param string mirror name (server) * @param string protocol type * @param string protocol version * @param string protocol name, if any */ function addMirrorFunction($mirror, $type, $version, $name = '') { if (!isset($this->_channelInfo['servers']['mirror'])) { $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND, array('mirror' => $mirror)); return false; } $setmirror = false; if (isset($this->_channelInfo['servers']['mirror'][0])) { foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) { if ($mirror == $mir['attribs']['host']) { $setmirror = &$this->_channelInfo['servers']['mirror'][$i]; break; } } } else { if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) { $setmirror = &$this->_channelInfo['servers']['mirror']; } } if (!$setmirror) { $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND, array('mirror' => $mirror)); return false; } $set = array('attribs' => array('version' => $version), '_content' => $name); if (!isset($setmirror[$type]['function'])) { $setmirror[$type]['function'] = $set; $this->_isValid = false; return true; } elseif (!isset($setmirror[$type]['function'][0])) { $setmirror[$type]['function'] = array($setmirror[$type]['function']); } $setmirror[$type]['function'][] = $set; $this->_isValid = false; return true; } /** * @param string Resource Type this url links to * @param string URL * @param string|false mirror name, if this is not a primary server REST base URL */ function setBaseURL($resourceType, $url, $mirror = false) { if ($mirror) { if (!isset($this->_channelInfo['servers']['mirror'])) { $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND, array('mirror' => $mirror)); return false; } $setmirror = false; if (isset($this->_channelInfo['servers']['mirror'][0])) { foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) { if ($mirror == $mir['attribs']['host']) { $setmirror = &$this->_channelInfo['servers']['mirror'][$i]; break; } } } else { if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) { $setmirror = &$this->_channelInfo['servers']['mirror']; } } } else { $setmirror = &$this->_channelInfo['servers']['primary']; } $set = array('attribs' => array('type' => $resourceType), '_content' => $url); if (!isset($setmirror['rest'])) { $setmirror['rest'] = array(); } if (!isset($setmirror['rest']['baseurl'])) { $setmirror['rest']['baseurl'] = $set; $this->_isValid = false; return true; } elseif (!isset($setmirror['rest']['baseurl'][0])) { $setmirror['rest']['baseurl'] = array($setmirror['rest']['baseurl']); } foreach ($setmirror['rest']['baseurl'] as $i => $url) { if ($url['attribs']['type'] == $resourceType) { $this->_isValid = false; $setmirror['rest']['baseurl'][$i] = $set; return true; } } $setmirror['rest']['baseurl'][] = $set; $this->_isValid = false; return true; } /** * @param string mirror server * @param int mirror http port * @return boolean */ function addMirror($server, $port = null) { if ($this->_channelInfo['name'] == '__uri') { return false; // the __uri channel cannot have mirrors by definition } $set = array('attribs' => array('host' => $server)); if (is_numeric($port)) { $set['attribs']['port'] = $port; } if (!isset($this->_channelInfo['servers']['mirror'])) { $this->_channelInfo['servers']['mirror'] = $set; return true; } if (!isset($this->_channelInfo['servers']['mirror'][0])) { $this->_channelInfo['servers']['mirror'] = array($this->_channelInfo['servers']['mirror']); } $this->_channelInfo['servers']['mirror'][] = $set; return true; } /** * Retrieve the name of the validation package for this channel * @return string|false */ function getValidationPackage() { if (!$this->_isValid && !$this->validate()) { return false; } if (!isset($this->_channelInfo['validatepackage'])) { return array('attribs' => array('version' => 'default'), '_content' => 'PEAR_Validate'); } return $this->_channelInfo['validatepackage']; } /** * Retrieve the object that can be used for custom validation * @param string|false the name of the package to validate. If the package is * the channel validation package, PEAR_Validate is returned * @return PEAR_Validate|false false is returned if the validation package * cannot be located */ function &getValidationObject($package = false) { if (!class_exists('PEAR_Validate')) { require_once 'PEAR/Validate.php'; } if (!$this->_isValid) { if (!$this->validate()) { $a = false; return $a; } } if (isset($this->_channelInfo['validatepackage'])) { if ($package == $this->_channelInfo['validatepackage']) { // channel validation packages are always validated by PEAR_Validate $val = new PEAR_Validate; return $val; } if (!class_exists(str_replace('.', '_', $this->_channelInfo['validatepackage']['_content']))) { if ($this->isIncludeable(str_replace('_', '/', $this->_channelInfo['validatepackage']['_content']) . '.php')) { include_once str_replace('_', '/', $this->_channelInfo['validatepackage']['_content']) . '.php'; $vclass = str_replace('.', '_', $this->_channelInfo['validatepackage']['_content']); $val = new $vclass; } else { $a = false; return $a; } } else { $vclass = str_replace('.', '_', $this->_channelInfo['validatepackage']['_content']); $val = new $vclass; } } else { $val = new PEAR_Validate; } return $val; } function isIncludeable($path) { $possibilities = explode(PATH_SEPARATOR, ini_get('include_path')); foreach ($possibilities as $dir) { if (file_exists($dir . DIRECTORY_SEPARATOR . $path) && is_readable($dir . DIRECTORY_SEPARATOR . $path)) { return true; } } return false; } /** * This function is used by the channel updater and retrieves a value set by * the registry, or the current time if it has not been set * @return string */ function lastModified() { if (isset($this->_channelInfo['_lastmodified'])) { return $this->_channelInfo['_lastmodified']; } return time(); } } PK=1]tfLgg.licenses/ea-php83-php-bcmath/libbcmath_LICENSEnu[ GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! PK=1]u#licenses/ea-php83-pear/LICENSE-PEARnu[Copyright (c) 1997-2009, Stig Bakken , Gregory Beaver , Helgi Þormar Þorbjörnsson , Tomas V.V.Cox , Martin Jansen . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PK=1]DGe/licenses/ea-php83-pear/LICENSE-Structures_Graphnu[ GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. PK=1]%licenses/ea-php83-php-fpm/fpm_LICENSEnu[Copyright (c) 2007-2009, Andrei Nigmatulin All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PK=1] _>>man/man1/php-config.1.gznu[Tێ0}W 6R>tJ59\;e{Nҽ R̙3g&CS7qd+Żw/b[3H5?5o,,{X]Y1T{j@3l@be' G&`V$S}~Ύ|~hțE:6^^s 7nj&@^2K9ɥTV2Bu L?(K_}7ލJHnfU5ӝXa,"8'BTx̗\̐%7T րHGARSQVqtZjL6 YIo0e(XI ~5ӝ&BFG3.>[j(v*T/w1!g m!i:a DQ^&-wZ3mpwZi/8 c6[V1MM§7A7ӷbA]o\= ?+?P&cɏ,ͺ9o~4ͫM &0鼛1P_/>.^:وؚZ9Y cQxge xXQXc1)UumŸۖeB~9-$ LڶPH2;Nf?/}OMK8(ZT;5G<2dGUn=\AVE=+1 Z)j. Y?_)]PK=1]P?==man/man1/phpize.1.gznu[}Ro0~_q⩓ @٤O4+(D Ti2AۊRlBjSs0c]?y n }E-j cEӇuL<ɡB+ }0BD U'9n;w]Vi,KuZ KB)X;Q\9]0ĢF^|3 \ H3~Q#wXNHO KkR) Y_ka|~8^$iP_^8"_?Cxg9M*{v|%ʶha%f_XA|eX|K/H*@[]rЊ-n RZ\5T{3mQ/B7'?G=KHvp X'CfN2٨sTqKwrEݢ{.+Z+Z!]FX :cy\EXPK=1]n4$$man/man1/phar.phar.1.gznu[+WM3/H,3z\u-PK=1]4l man/man1/phar.1.gznu[Zmo8_~H KCXfbPm"ZR{f7;6f{C?4(rf8 )_6Tch _;[x~㵓v6SY7D4<#Oۖ7?(mb⩙g2 ej2- kS&0|w~1 EtOռl^(Dt:'sA^x &rlL2wvn ?Ya\ؔĊ9HadT 4 7H~pZL/DGL ,ez ޚb* )"Ik],m FVޖeIo BvT\B% ʝ^&:XY/4?Bk d?&*dzW8\;] Cŝd}jYl~&Կ<& s!t5{/7NHP:6cT5oº$&l)r6R&2I C\X4'c3ҐE5q@ &,FȤӓ2P-kL4M=$о%a fC05bWI*}X=EjRQ8Ntef'nIU^dba3c-5pF /G!UJ(avղݓX TuJ;cyҗ/G#R?s9vvDU·+2^~R."oL7kuF\fkĆrq,+Og/G17"qwCSnJTA QtkƀY*zUEBb^ţ*>VO`kUϫa8F?:*? O~WxRP&~n%zʴś& ɶQG:z5:"YuNSEr~-)ҹ2j: TBq4ٲ/,eIՒHSP-Mp˚JUZoq7dIrcdk]Q ܏8e~Ef%X O0tÆ䋄b^ wAMV%v!Yc3Y [nL_`m^:"*1̪F|aIEYo)APPpluaw=4׬HFͬhcp Y`-ݠ+֭XPU$Nw9%uGfyAfzQX3tR{H%)$zb/}RMSФt J'b$Z}NHoFhDEPW10W>y+'VzJS%U&'j݂ Ꝑu8sځ}te+`KT&rpTy/ǃf- mvYL츍hHe"tGuCTf5jt4?Le+>r 'ϱ&#l2p ˑ l?8%J$þ=mŜ [hjS7l׳\CrμgQ]lnR*$qR"MRL)=dŒxS'uBRaujEN9xQG&!)@ Y&Y%'Z(g N1#@DW78xB#ÙgzC&j&N9 ^+DFPtB./7WJlLs`>:ѱ65 7FPUkdyZ}!X׽%Q̜ь0\g)UnJmsLi%_{mlP?|&yY4*Ng9zp ߠ~l=#}HrXXƳQx9nPI -\}3b686oej:uUv|wt+ms1M_nt6~58n.hnwxQ|4oN}xg k3C*ii̒Ջ=1< + /^ ¢D>ubpw|bxw>Odtx!~aQㄜ^6O8|7r=p8pEFL;`xύ^0lMf:wĘ*r`=)QI .1ޗt @ˡXp%~!"ˊ:ۖL䁺e#_oKg%֍1-GxZb*z.Sl|K_i0Q|?3zסDt> KG9ZBf={|~[a9Mc:}yZ'tt@g(6>bj{ƈ(+5kҌT $4i?ʛ iIyj HFAm-BκK'"TvLDR\=xb!T\\a^*;gW(dM.R-wpW$st}s;d>cr@_V;1ՙ>|3vaywb?z\`)? uzEGm&n=ק@⫧<4Dž% YWI1e- |tv7\/ yW;!R*[S\YEe pMf!PE9%UPY%NOޫHգLPy)(züB-&*MOobq?%- !tb#UA.S" D4ǚ0Sx^am,ɐjE۸uzQ`Mz}RRI/&wo

]ÙA+K*Lʁx}BȂIԨX.4*+!8F L=m35HDf *|a[IJI*̈+m΄ѧ&"C5a"A{]9 *~fA [,~&UQ]ب{9IVMb-P`QUj!Lt!7܀mA}Zx^2:/EU9gBgAiJ_FUΑ뢠`\:p]*ۢ25H`N#Wpˑ6%]HL#X! 'y87:4 Nt ;8aB _OT-%&+.NFh'=jt9L.Y Ju|ux"9l6Ҫ| /D(2(RILo}o񭒮usA8#(*$|#&>)ʹ-$Olp#}}5Dp<ơZulpVMrZJR`lj:m=8}M!b*zJ8s?Yt >PBjRfI)?ݵu|sCVo=l~2 D&^+վJ֓ֈ8?ۓ0̡̘}eĐEBRdBuFOA HT*td+iZ>T ~DBH*UΡSJݖ|"_[BG ^S=,Iȭ '7s=gW3vэ/t"vFx e@&.eaDu3q\Ȅ- HF?pwŊ%ruزI?΂ێ덲CψU@,z}Z|8VщU^0ˆK6'#Sm]I?{spl8K L[[k lM83j(i ]fLF[2}*-$ϱI&xGOQ_ӡ$/s R4;ήWћf Uu @{ëv&%)sqzomPe\ PQx )ncC4nMw'ook鿉htG"+vU27bRe8.87u?u* {rsVN:vG7K?S;ܰ$ ҊY'`bQ֭&ݱc*|HP]^У> WB32_E I}=KAӤ~R=fշXv_h.~[_mI\nլqcRq{Gq#۹Z|p5+.F6t@;PN)AYf to&s2ӕ 6*b^.ZcwGCn:?QtZpM>5qT`=!:#G8L%es\83fݱIh.Q6XTh.xUlp5bcSVH^ 0 ^@ǝ9{ ]wPkU"q(-[hLAсiCh?tJ!F:OT4Co>I:7CN>zei=ϰ gbh^?I! ^f&5uT6ť|unz*RhT?6r&"/IA_q j"%fJC)(;JkV#$_R `o|OC}>C~^}ԷA|t~.NF7@O.A<^w+0cq- ,%=ƹ*lO߽i2CVdbUxiZ5)qWAP,C&ڹ / 9ywy+~1n}g8yn@J=~8S-T%tsNEĕSlEU}rH[1JJ=w ~ ^D2Qy#c$M5!U^dQٗs 2z!9oM ةQvFo0B<ql_7GF ޿剥7P n?0uv8I0 %zE '\,̔ߩ$zhCKj|x~7\n)X ̿//=۟o.3S,^bD/lM7~,]5 aEMe~[| j:*T4䁤Ljxv~=:?`ߢuRgaf&IeYujGql=ڌTWE!$ j@S^T/!3rBF*·z?3S9pKыbSnE&PK=1]NCg man/man7/ea-php83.7.gznu[Xmo8_un]&iE4$;"%f# )}f(N=$3 ;R\\|0/'? 1B:1SV.\n!KG\ί>ǎweW<)٫}=14YV#*)qSo hN]'IyJΔȌfVt|2<\o~2׳򠓨V(\EI1*;r>TǥϺۉM֧EHu]?73Zqc0D ·_Z}W[b4WĀܬi'Lsime.u9,gVeMRU*2ϗyMEMPS@Rbq*]-\=i"*+guNUf,=(d*Km4EP JPhN?.X4VO=9膾~({؋bxҍ97?\y7-Ls?SP}T/wYn_A.^y*әt5]y*\-kj+B!}je $^r'la̬qqܛ_ Cڠ*3˕cP2G/bcgjxwgl}0 L$b}Yr|bQ(S0տu' z J3F¸E82:g8̀;LHգL*.)n )7z#" ^9*6 (^ZHHL'Y![M(Y 3k%f,e}5]֥w*L) ]P6ux0(!}mSfۨ3v(M@FI i4˪r=},]ր>l]ԗM:-? lriثɑɹgxc'Hlg}n܉ d5d&S05>6{\s𻹮\Yha7`\j&arٙ%& $hW9@#6ުu9ޔڣ8fCNu TFptnφxJp]{XS$Zk/BGQ6uKS# iC@b+˗k+h $? !T@l2MpRH,+ӾEL顀|.I)6*-)'CfXH?ύ4XšCa©XٚU56syH"m! jd&pc܀$\TT* $8M42Z̠^zV~n-DC7|%]nNl*כL2<'P&;ic|>Xm:ye[ . ܓrV`vڴy؎voA72s?ŢktH?jN:VC*KF%dK穙8UK ծOz\ Ձ֖n}C*BS5 +[:1 W@ Wv8%]MFb1[M@]I,aMl5,nw^]M47v1Z ;Ʃ~d㧝n}) r!J 0٨.>-pmB>|OBtr{ Ϊm.Z,. ȅe۟Cީ+{Hu\{۬\>7 nit}8`<5 kX&/3is\%zQ 3VziӄZbMfA~;zhpq?ή,8_Ҧn-u>W+9榯~i4[5۸vJ%bnusySn m4{WCc8-ۢ [d#N^  ? PK=1]uman/man8/php-fpm.8.gznu[X[~6/E%jmQ@j r' 9"KrعVA|3CJ\mps6w#{߲B[vkwjXY׹x ]F|)jfj*z+nMJ1`]|&$X+ EAͯQ|;Tl2O )q(73"cb8(#.[0H%/ 3NZp~K'QeXkLTKeUf~wyIגA^Z%Ӯ^cB&'JVJcEM(Ӵƚ=88Ty=FikQoOGZ);F+c<ދw[+z%s9VdءʅM7fg8s]i]t[yK k컓N JlgD6iK# Aj[=w1y nC3reD&MU,HO٤bx](V+"! $;(!(^ W/l4ᑔqK>Ч_!Uѧzې =# 8hVRdW{V;^SF5Z2HւY) ~ \}W.[@-PH&kO,jijڨ/X:!d~e|xӖt3ȈJ_L,|-RCr˪Pk0W Mlr+n[OV;V7(P}M\ڠ25lxB=6aG>CvqF C:|L'7gPVߐ9lZbNŪ7EGj92Ku]) -U;9Sɼ&[Tc+CPvcԓ!W+046Zh0= # =  |jDl>r@#ֈ2| 8gј(1 U L,XnrWgb=Z'O7eq{@܈C˟wS!Ljt6Q.dZ6:gq#vJ.x:ƪQ['ϒȸ UlnO';›G f-[.#SlA듬AW!xoM}c)殆 uh3j'-CڿކQ}q,ZJ?b5`'krGK@"Ri7DiD!&ؾ򠢣Q+yo)7dPZqB|*!t:sRyryƷ $G$hJ;I` O%U!-X%Yޡ0K9a^qŽPˎ1i R/CRvrp]2߰ݢaW#z!QG^<Sg Z ~bldcrCSj5(`5(94{GNit dy~j袕FXki#ud2JYGs>$. TC]9~-}ƼU;v4s? wO9=vlM PKD1]kى!licenses/alt-openssl-libs/LICENSEnu[ LICENSE ISSUES ============== The OpenSSL toolkit stays under a double license, i.e. both the conditions of the OpenSSL License and the original SSLeay license apply to the toolkit. See below for the actual license texts. Actually both licenses are BSD-style Open Source licenses. In case of any license issues related to OpenSSL please contact openssl-core@openssl.org. OpenSSL License --------------- /* ==================================================================== * Copyright (c) 1998-2018 The OpenSSL Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" * * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please contact * openssl-core@openssl.org. * * 5. Products derived from this software may not be called "OpenSSL" * nor may "OpenSSL" appear in their names without prior written * permission of the OpenSSL Project. * * 6. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit (http://www.openssl.org/)" * * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This product includes cryptographic software written by Eric Young * (eay@cryptsoft.com). This product includes software written by Tim * Hudson (tjh@cryptsoft.com). * */ Original SSLeay License ----------------------- /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * * This package is an SSL implementation written * by Eric Young (eay@cryptsoft.com). * The implementation was written so as to conform with Netscapes SSL. * * This library is free for commercial and non-commercial use as long as * the following conditions are aheared to. The following conditions * apply to all code found in this distribution, be it the RC4, RSA, * lhash, DES, etc., code; not just the SSL code. The SSL documentation * included with this distribution is covered by the same copyright terms * except that the holder is Tim Hudson (tjh@cryptsoft.com). * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. * If this package is used in a product, Eric Young should be given attribution * as the author of the parts of the library used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * "This product includes cryptographic software written by * Eric Young (eay@cryptsoft.com)" * The word 'cryptographic' can be left out if the rouines from the library * being used are not cryptographic related :-). * 4. If you include any Windows specific code (or a derivative thereof) from * the apps directory (application code) you must include an acknowledgement: * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] */ PKCl1]tjBEElicenses/ea-libargon2/LICENSEnu[Argon2 reference source code package - reference C implementations Copyright 2015 Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves You may use this work under the terms of a Creative Commons CC0 1.0 License/Waiver or the Apache Public License 2.0, at your option. The terms of these licenses can be found at: - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 The terms of the licenses are reproduced below. -------------------------------------------------------------------------------- Creative Commons Legal Code CC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; ii. moral rights retained by the original author(s) and/or performer(s); iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; v. rights protecting the extraction, dissemination, use and reuse of data in a Work; vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. -------------------------------------------------------------------------------- Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. PK1]{ydoc/alt-libwebp/ChangeLognu[370aa581 webp_js/README.md: add some more code formatting (``) f83c6b32 CMakeLists: add warning for incorrect emscripten config 6a3e656b update ChangeLog (tag: v1.6.0-rc1) bf0bf1e7 api.md: add WebPValidateDecoderConfig to pseudocode e8ae210d update NEWS ce53efd7 bump version to 1.6.0 1c333170 update AUTHORS 85e098e5 webpmux: fix heap overflow w/-get/-set 418340d8 Merge "Make histogram allocation and access more readable and type-safe." into main 23ce76fa Merge "VP8BitReaderSetBuffer: move NULL check to call site" into main bbf3cbb1 VP8BitReaderSetBuffer: move NULL check to call site f6b87e03 Fix const style guide 8852f89a Have lossless return the same results with/without -mt e015dcc0 Make histogram allocation and access more readable and type-safe. 753ed11e enc_neon.c: fix aarch64 compilation w/gcc < 8.5.0 0cd0b7a7 enc_fuzzer.cc: remove duplicate include 2209ffba swig,cosmetics: normalize includes 15e2e1ee analysis_enc.c: remove unused include 98c27801 IWYU: Include all headers for symbols used in files. eb3ff781 Only use valid histograms in VP8LHistogramSet 57e324e2 Refactor VP8LHistogram histogram_enc.cc 7191a602 Merge "Generalize trivial histograms" into main 19696e0a Merge "alpha_processing_sse2: quiet signed conv warning" into main 89b01ecc Merge "cwebp: add `-resize_mode`" into main 52a430a7 Generalize trivial histograms e53e2130 Cache all costs in the histograms f8b360c4 alpha_processing_sse2: quiet signed conv warning eb4f8137 cwebp: add `-resize_mode` ad52d5fc dec/dsp/enc/utils,cosmetics: rm struct member '_' suffix ed7cd6a7 utils.c,cosmetics: rm struct member '_' suffix 3a23b0f0 random_utils.[hc],cosmetics: rm struct member '_' suffix a99d0e6f quant_levels_dec_utils.c,cosmetics: rm struct member '_' suffix 1ed4654d huffman_encode_utils.[hc],cosmetics: rm struct member '_' suffix f0689e48 config_enc.c,cosmetics: rm struct member '_' suffix 24262266 mux,cosmetics: rm struct member '_' suffix 3f54b1aa demux,cosmetics: rm struct member '_' suffix 295804e4 examples,cosmetics: rm struct member '_' suffix 5225592f Refactor VP8LHistogram to hide initializations from the user. 00338240 Remove some computations in histogram clustering 44f91b0d Speed DispatchAlpha_SSE2 up ee8e8c62 Fix member naming for VP8LHistogram a1ad3f1e Merge "Remove now unused ExtraCostCombined" into main 321561b4 Remove now unused ExtraCostCombined e0ae21d2 WebPMemoryWriterClear: use WebPMemoryWriterInit a4183d94 Remove the computation of ExtraCost when comparing histograms f2b3f527 Get AVX2 into WebP lossless 7c70ff7a Clean dsp/lossless includes 9dd5ae81 Use the full register in PredictorSub13_SSE2 613be8fc Makefile.vc: add /MP to CFLAGS 1d86819f Merge changes I1437390a,I10a20de5,I1ac777d1 into main 743a5f09 enc_neon: enable vld1q_u8_x4 for clang & msvc 565da148 pngdec.c: add support for 'eXIf' tag 319860e9 pngdec.c: support ImageMagick app1 exif text data 815fc1e1 pngdec.c: add missing #ifdef for png_get_iCCP 980b708e enc_neon: fix build w/aarch64 gcc < 9.4.0 73b728cb cmake: bump minimum version to 3.16 6a22b670 Add a function to validate a WebPDecoderConfig 7ed2b10e Use consistently signed stride types. 654bfb04 Avoid nullptr arithmetic in VP8BitReaderSetBuffer f8f24107 Fix potential "divide by zero" in examples found by coverity 2af6c034 Merge tag 'v1.5.0' a4d7a715 update ChangeLog (tag: v1.5.0, origin/1.5.0) c3d85ce4 update NEWS ad14e811 tests/fuzzer/*: add missing include 74cd026e fuzz_utils.cc: fix build error w/WEBP_REDUCE_SIZE a027aa93 mux_demux_api_fuzzer.cc: fix -Wshadow warning 25e17c68 update ChangeLog (tag: v1.5.0-rc1) aa2684fc update NEWS 36923846 bump version to 1.5.0 ceea8ff6 update AUTHORS e4f7a9f0 img2webp: add a warning for unused options 1b4c967f Merge "Properly check the data size against the end of the RIFF chunk" into main 9e5ecfaf Properly check the data size against the end of the RIFF chunk da0d9c7d examples: exit w/failure w/no args fcff86c7 {gif,img}2webp: sync -m help w/cwebp b76c4a84 man/img2webp.1: sync -m text w/cwebp.1 & gif2webp.1 30633519 muxread: fix reading of buffers > riff size 4c85d860 yuv.h: update RGB<->YUV coefficients in comment 0ab789e0 Merge changes I6dfedfd5,I2376e2dc into main 03236450 {ios,xcframework}build.sh: fix compilation w/Xcode 16 61e2cfda rework AddVectorEq_SSE2 7bda3deb rework AddVector_SSE2 2ddaaf0a Fix variable names in SharpYuvComputeConversionMatrix a3ba6f19 Makefile.vc: fix gif2webp link error f999d94f gif2webp: add -sharp_yuv/-near_lossless dfdcb7f9 Merge "lossless.h: fix function declaration mismatches" into main (tag: webp-rfc9649) 78ed6839 fix overread in Intra4Preds_NEON d516a68e lossless.h: fix function declaration mismatches 87406904 Merge "Improve documentation of SharpYuvConversionMatrix." into main fdb229ea Merge changes I07a7e36a,Ib29980f7,I2316122d,I2356e314,I32b53dd3, ... into main 0c3cd9cc Improve documentation of SharpYuvConversionMatrix. 169dfbf9 disable Intra4Preds_NEON 2dd5eb98 dsp/yuv*: use WEBP_RESTRICT qualifier 23bbafbe dsp/upsampling*: use WEBP_RESTRICT qualifier 35915b38 dsp/rescaler*: use WEBP_RESTRICT qualifier a32b436b dsp/lossless*: use WEBP_RESTRICT qualifier 04d4b4f3 dsp/filters*: use WEBP_RESTRICT qualifier b1cb37e6 dsp/enc*: use WEBP_RESTRICT qualifier 201894ef dsp/dec*: use WEBP_RESTRICT qualifier 02eac8a7 dsp/cost*: use WEBP_RESTRICT qualifier 84b118c9 Merge "webp-container-spec: normalize notes & unknown chunk link" into main 052cf42f webp-container-spec: normalize notes & unknown chunk link 220ee529 Search for best predictor transform bits 78619478 Try to reduce the sampling for the entropy image 14f09ab7 webp-container-spec: reorder chunk size - N text a78c5356 Remove a useless malloc for entropy image bc491763 Merge "Refactor predictor finding" into main 34f92238 man/{cwebp,img2webp}.1: rm 'if needed' from -sharp_yuv 367ca938 Refactor predictor finding a582b53b webp-lossless-bitstream-spec: clarify some text 0fd25d84 Merge "anim_encode.c: fix function ref in comment" into main f8882913 anim_encode.c: fix function ref in comment 40e4ca60 specs_generation.md: update kramdown command line 57883c78 img2webp: add -exact/-noexact per-frame options 1c8eba97 img2webp,cosmetics: add missing '.' spacers to help 2e81017c Convert predictor_enc.c to fixed point 94de6c7f Merge "Fix fuzztest link errors w/-DBUILD_SHARED_LIBS=1" into main 51d9832a Fix fuzztest link errors w/-DBUILD_SHARED_LIBS=1 7bcb36b8 Merge "Fix static overflow warning." into main 8e0cc14c Fix static overflow warning. cea68462 README.md: add security report note 615e5874 Merge "make VP8LPredictor[01]_C() static" into main 233e86b9 Merge changes Ie43dc5ef,I94cd8bab into main 1a29fd2f make VP8LPredictor[01]_C() static dd9d3770 Do*Filter_*: remove row & num_rows parameters ab451a49 Do*Filter_C: remove dead 'inverse' code paths f9a480f7 {TrueMotion,TM16}_NEON: remove zero extension 04834aca Merge changes I25c30a9e,I0a192fc6,I4cf89575 into main 39a602af webp-lossless-bitstream-spec: normalize predictor transform ref f28c837d Merge "webp-container-spec: align anim pseudocode w/prose" into main 74be8e22 Fix implicit conversion issues 0c01db7c Merge "Increase the transform bits if possible." into main f2d6dc1e Increase the transform bits if possible. caa19e5b update link to issue tracker c9dd9bd4 webp-container-spec: align anim pseudocode w/prose 8a7c8dc6 WASM: Enable VP8L_USE_FAST_LOAD f0c53cd9 WASM: don't use USE_GENERIC_TREE eef903d0 WASM: Enable 64-bit BITS caching 6296cc8d iterator_enc: make VP8IteratorReset() static fbd93896 histogram_enc: make VP8LGetHistogramSize static cc7ff545 cost_enc: make VP8CalculateLevelCosts[] static 4e2828ba vp8l_dec: make VP8LClear() static d742b24a Intra16Preds_NEON: fix truemotion saturation c7bb4cb5 Intra4Preds_NEON: fix truemotion saturation 952a989b Merge "Remove TODO now that log is using fixed point." into main dde11574 Remove TODO now that log is using fixed point. a1ca153d Fix hidden myerr in my_error_exit 3bd94202 Merge changes Iff6e47ed,I24c67cd5,Id781e761 into main d27d246e Merge "Convert VP8LFastSLog2 to fixed point" into main 4838611f Disable msg_code use in fuzzing mode 314a142a Use QuantizeBlock_NEON for VP8EncQuantizeBlockWHT on Arm 3bfb05e3 Add AArch64 Neon implementation of Intra16Preds baa93808 Add AArch64 Neon implementation of Intra4Preds 41a5e582 Fix errors when compiling code as C++ fb444b69 Convert VP8LFastSLog2 to fixed point c1c89f51 Fix WEBP_NODISCARD comment and C++ version 66408c2c Switch the histogram_enc.h API to fixed point ac1e410d Remove leftover tiff dep b78d3957 Disable TIFF on fuzztest. cff21a7d Do not build statically on oss-fuzz. 6853a8e5 Merge "Move more internal fuzzers to public." into main 9bc09db4 Merge "Convert VP8LFastLog2 to fixed point" into main 0a9f1c19 Convert VP8LFastLog2 to fixed point db0cb9c2 Move more internal fuzzers to public. ff2b5b15 Merge "advanced_api_fuzzer.cc: use crop dims in OOM check" into main c4af79d0 Put 0 at the end of a palette and do not store it. 0ec80aef Delete last references to delta palettization 96d79f84 advanced_api_fuzzer.cc: use crop dims in OOM check c35c7e02 Fix huffman fuzzer to not leak. f2fe8dec Bump fuzztest dependency. 9ce982fd Fix fuzz tests to work on oss-fuzz 3ba8af1a Do not escape quotes anymore in build.sh ea0e121b Allow centipede to be used as a fuzzing engine. 27731afd make VP8I4ModeOffsets & VP8MakeIntra4Preds static ddd6245e oss-fuzz/build.sh: use heredoc for script creation 50074930 oss-fuzz/build.sh,cosmetics: fix indent 20e92f7d Limit the possible fuzz engines. 4f200de5 Switch public fuzz tests to fuzztest. 64186bb3 Add huffman_fuzzer to .gitignore 0905f61c Move build script from oss-fuzz repo to here. e8678758 Fix link to Javascript documentation 5e5b8f0c Fix SSE2 Transform_AC3 function name 45129ee0 Revert "Check all the rows." ee26766a Check all the rows. 7ec51c59 Increase the transform bits if possible. 3cd16fd3 Revert "Increase the transform bits if possible." 971a03d8 Increase the transform bits if possible. 1bf198a2 Allow transform_bits to be different during encoding. 1e462ca8 Define MAX_TRANSFORM_BITS according to the specification. 64d1ec23 Use (MIN/NUM)_(TRANSFORM/HUFFMAN)_BITS where appropriate a90160e1 Refactor histograms in predictors. a7aa7525 Fix some function declarations 68ff4e1e Merge "jpegdec: add a hint for EOF/READ errors" into main 79e7968a jpegdec: add a hint for EOF/READ errors d33455cd man/*: s/BUGS/REPORTING BUGS/ a67ff735 normalize example exit status edc28909 upsampling_{neon,sse41}: fix int sanitizer warning 3cada4ce ImgIoUtilReadFile: check ftell() return dc950585 Merge tag 'v1.4.0' 845d5476 update ChangeLog (tag: v1.4.0, origin/1.4.0) 8a6a55bb update NEWS cf7c5a5d provide a way to opt-out/override WEBP_NODISCARD cc34288a update ChangeLog (tag: v1.4.0-rc1) f13c0886 NEWS: fix date 74555950 Merge "vwebp: fix window title when options are given" into 1.4.0 d781646c vwebp: fix window title when options are given c2e394de update NEWS f6d15cb7 bump version to 1.4.0 57c388b8 update AUTHORS b3d1b2cb Merge changes I26f4aa22,I83386b6c,I320ed1a2 into main 07216886 webp-container-spec: fix VP8 chunk ref ('VP8'->'VP8 ') f88666eb webp_js/*.html: fix canvas mapping e2c8f233 cmake,wasm: simplify SDL2 related flags d537cd37 cmake: fix vwebp_sdl compile w/libsdl-org release 6c484cbf CMakeLists.txt: add missing WEBP_BUILD_EXTRAS check 7b0bc235 man/cwebp.1: add more detail to -partition_limit 3c0011bb WebPMuxGetChunk: add an assert 955a3d14 Merge "muxread,MuxGet: add an assert" into main 00abc000 muxread,MuxGet: add an assert 40e85a0b Have the window title reflect the filename. 1bf46358 man/cwebp.1: clarify -pass > 1 behavior w/o -size/-psnr eba03acb webp-container-spec: replace 'above' with 'earlier' a16d30cb webp-container-spec: clarify chunk order requirements 8a7e9112 Merge "CMakeLists.txt: apply cmake-format" into main 7fac6c1b Merge "Copy C code to not have multiplication overflow" into main e2922e43 Merge "Check for the presence of the ANDROID_ABI variable" into main 501d9274 Copy C code to not have multiplication overflow fba7d62e CMakeLists.txt: apply cmake-format 661c1b66 Merge "windows exports: use dllexport attribute, instead of visibility." into main 8487860a windows exports: use dllexport attribute, instead of visibility. 8ea678b9 webp/mux.h: data lifetime note w/copy_data=0 79e05c7f Check for the presence of the ANDROID_ABI variable 45f995a3 Expose functions for managing non-image chunks on WebPAnimEncoder 1fb9f3dc gifdec: fix ErrorGIFNotAvailable() declaration 4723db65 cosmetics: s/SANITY_CHECK/DCHECK/ f4b9bc9e clear -Wextra-semi-stmt warnings 713982b8 Limit animdecoder_fuzzer to 320MB cbe825e4 cmake: fix sharpyuv simd files' build f99305e9 Makefile.vc: add ARM64 support 5efd6300 mv SharpYuvEstimate420Risk to extras/ e78e924f Makefile.vc: add sharpyuv_risk_table.obj d7a0506d Add YUV420 riskiness metric. 89c5b917 Merge "BuildHuffmanTable check sorted[] array bounds before writing" into main 34c80749 Remove alpha encoding pessimization. 13d9c30b Add a WEBP_NODISCARD 24d7f9cb Switch code to SDL2. 0b56dedc BuildHuffmanTable check sorted[] array bounds before writing a429c0de sharpyuv: convert some for() to do/while f0cd7861 DoSharpArgbToYuv: remove constant from loop 339231cc SharpYuvConvertWithOptions,cosmetics: fix formatting 307071f1 Remove medium/large code model-specific inline asm deadc339 Fix transfer functions where toGamma and toLinear are swapped. e7b78d43 Merge "Fix bug in FromLinearLog100." into main 15a1309e Merge "webp-lossless-bitstream-spec: delete extra blank line" into main 54ca9752 Fix bug in FromLinearLog100. d2cb2d8c Dereference after NULL check. e9d50107 webp-lossless-bitstream-spec: delete extra blank line 78657971 Merge changes Ief442c90,Ie6e9c9a5 into main e30a5884 webp-lossless-bitstream-spec: update variable names 09ca1368 Merge "webp-container-spec: change assert to MUST be TRUE" into main 38cb4fc0 iosbuild,xcframeworkbuild: add SharpYuv framework 40afa926 webp-lossless-bitstream-spec: simplify abstract 9db21143 webp-container-spec: change assert to MUST be TRUE cdbf88ae Fix typo in API docs for incremental decoding 05c46984 Reformat vcpkg build instructions. 8534f539 Merge "Never send VP8_STATUS_SUSPENDED back in non-incremental." into main 35e197bd Never send VP8_STATUS_SUSPENDED back in non-incremental. 61441425 Add vcpkg installation instructions dce8397f Fix next is invalid pointer when WebPSafeMalloc fails 57c58105 Cmake: wrong public macro WEBP_INCLUDE_DIRS c1ffd9ac Merge "vp8l_enc: fix non-C90 code" into main a3965948 Merge changes If628bb93,Ic79f6309,I45f0db23 into main f80e9b7e vp8l_enc: fix non-C90 code accd141d Update lossless spec for two simple codes. ac17ffff Fix non-C90 code. 433c7dca Fix static analyzer warnings. 5fac76cf Merge tag 'v1.3.2' ca332209 update ChangeLog (tag: v1.3.2) 1ace578c update NEWS 63234c42 bump version to 1.3.2 a35ea50d Add a fuzzer for ReadHuffmanCodes 95ea5226 Fix invalid incremental decoding check. 2af26267 Fix OOB write in BuildHuffmanTable. 902bc919 Fix OOB write in BuildHuffmanTable. 7ba44f80 Homogenize "__asm__ volatile" vs "asm volatile" 68e27135 webp-container-spec: reorder example chunk layout 943b932a Merge changes I6a4d0a04,Ibc37b91e into main 1cc94f95 decode.h: wrap idec example in /* */ 63acdd1e decode.h: fix decode example aac5c5d0 ReadHuffmanCode: rm redundant num code lengths check a2de25f6 webp-lossless-bitstream-spec: normalize list item case 68820f0e webp-lossless-bitstream-spec: normalize pixel ref cdb31aa8 webp-lossless-bitstream-spec: add missing periods 0535a8cf webp-lossless-bitstream-spec: fix grammar b6c4ce26 normalize numbered list item format dd7364c3 Merge "palette.c: fix msvc warnings" into main c63c5df6 palette.c: fix msvc warnings 0a2cad51 webp-container-spec: move terms from intro section dd88d2ff webp-lossless-bitstream-spec: color_cache -> color cache 6e750547 Merge changes I644d7d39,Icf05491e,Ic02e6652,I63b11258 into main 67a7cc2b webp-lossless-bitstream-spec: fix code blocks 1432ebba Refactor palette sorting computation. cd436142 webp-lossless-bitstream-spec: block -> chunk 3cb66f64 webp-lossless-bitstream-spec: add some missing commas 56471a53 webp-lossless-bitstream-spec: normalize item text in 5.1 af7fbfd2 vp8l_dec,ReadTransform: improve error status reporting 7d8e0896 vp8l_dec: add VP8LSetError() a71ce1cf animencoder_fuzzer: fix error check w/Nallocfuzz e94b36d6 webp-lossless-bitstream-spec: relocate details from 5.1 84628e56 webp-lossless-bitstream-spec: clarify image width changes ee722997 alpha_dec: add missing VP8SetError() 0081693d enc_dec_fuzzer: use WebPDecode() 0fcb311c enc_dec_fuzzer: fix WebPEncode/pic.error_code check 982c177c webp-lossless-bitstream-spec: fix struct member refs 56cf5625 webp-lossless-bitstream-spec: use RFC 7405 for ABNF 6c6b3fd3 webp-lossless-bitstream-spec,cosmetics: delete blank lines 29b9eb15 Merge changes Id56ca4fd,I662bd1d7 into main 47c0af8d ReadHuffmanCodes: rm max_alphabet_size calc b92deba3 animencoder_fuzzer: no WebPAnimEncoderAssemble check w/nallocfuzz 6be9bf8b animencoder_fuzzer: fix leak on alloc failure 5c965e55 vp8l_dec,cosmetics: add some /*param=*/ comments e4fc2f78 webp-lossless-bitstream-spec: add validity note for max_symbol 71916726 webp-lossless-bitstream-spec: fix max_symbol definition eac3bd5c Have the palette code be in its own file. e2c85878 Add an initializer for the SharpYuvOptions struct. 4222b006 Merge tag 'v1.3.1' 25d94f47 Implement more transfer functions in libsharpyuv 2153a679 Merge changes Id0300937,I5dba5ccf,I57bb68e0,I2dba7b4e,I172aca36, ... into main 4298e976 webp-lossless-bitstream-spec: add PredictorTransformOutput cd7e02be webp-lossless-bitstream-spec: fix RIFF-header ABNF 6c3845f9 webp-lossless-bitstream-spec: split LZ77 Backward Ref section 7f1b6799 webp-lossless-bitstream-spec: split Meta Prefix Codes section 7b634d8f webp-lossless-bitstream-spec: note transform order 6d6d4915 webp-lossless-bitstream-spec: update transformations text fd7bb21c update ChangeLog (tag: v1.3.1-rc2, tag: v1.3.1) e1adea50 update NEWS 6b1c722a lossless_common.h,cosmetics: fix a typo 08d60d60 webp-lossless-bitstream-spec: split code length section 7a12afcc webp-lossless-bitstream-spec: rm unused anchor 43393320 enc/*: normalize WebPEncodingSetError() calls 287fdefe enc/*: add missing WebPEncodingSetError() calls c3bd7cff EncodeAlphaInternal: add missing error check 14a9dbfb webp-lossless-bitstream-spec: refine single node text 64819c7c Implement ExtractGreen_SSE2 d49cfbb3 vp8l_enc,WriteImage: add missing error check 2e5a9ec3 muxread,MuxImageParse: add missing error checks ebb6f949 cmake,emscripten: explicitly set stack size 59a2b1f9 WebPDecodeYUV: check u/v/stride/uv_stride ptrs 8e965ccb Call png_get_channels() to see if image has alpha fe80fbbd webp-container-spec: add some missing commas e8ed3176 Merge "treat FILTER_NONE as a regular Unfilter[] call" into main 03a7a048 webp-lossless-bitstream-spec: rm redundant statement c437c7aa webp-lossless-bitstream-spec: mv up prefix code group def e4f17a31 webp-lossless-bitstream-spec: fix section reference e2ecd5e9 webp-lossless-bitstream-spec: clarify ABNF syntax 8b55425a webp-lossless-bitstream-spec: refine pixel copy text 29c9f2d4 webp-lossless-bitstream-spec: minor wording updates 6b02f660 treat FILTER_NONE as a regular Unfilter[] call 7f75c91c webp-container-spec: fix location of informative msg f6499943 webp-container-spec: consistently quote FourCCs 49918af3 webp-container-spec: minor wording updates 7f0a3419 update ChangeLog (tag: v1.3.1-rc1) bab7efbe update NEWS 7138bf8f bump version to 1.3.1 435b4ded update AUTHORS 47351229 update .mailmap 46bc4fc9 Merge "Switch ExtraCost to ints and implement it in SSE." into main 828b4ce0 Switch ExtraCost to ints and implement it in SSE. ff6c7f4e CONTRIBUTING.md: add C style / cmake-format notes dd530437 add .cmake-format.py adbe2cb1 cmake,cosmetics: apply cmake-format 15b36508 doc/webp-container-spec: rm future codec comment c369c4bf doc/webp-lossless-bitstream-spec: improve link text 1de35f47 doc/webp-container-spec: don't use 'currently' bb06a16e doc/webp-container-spec: prefer present tense 9f38b71e doc/webp-lossless-bitstream-spec: prefer present tense 7acb6b82 doc/webp-container-spec: avoid i.e. & e.g. 4967e7cd doc/webp-lossless-bitstream-spec: avoid i.e. & e.g. e3366659 Merge "Do not find_package image libraries if not needed." into main 428588ef clarify single leaf node trees and use of canonical prefix coding 709ec152 Do not find_package image libraries if not needed. 8dd80ef8 fuzz_utils.h: lower kFuzzPxLimit w/ASan 8f187b9f Clean message calls in CMake cba30078 WebPConfig.cmake.in: use calculated include path 6cf9a76a Merge "webp-lossless-bitstream-spec: remove use of 'dynamics'" into main 740943b2 Merge "Specialize and optimize ITransform_SSE2 using do_two" into main 2d547e24 Compare kFuzzPxLimit to max_num_operations ac42dde1 Specialize and optimize ITransform_SSE2 using do_two 17e0ef1d webp-lossless-bitstream-spec: remove use of 'dynamics' ed274371 neon.h,cosmetics: clear a couple lint warnings 3fb82947 cpu.h,cosmetics: segment defines 0c496a4f cpu.h: add WEBP_AARCH64 8151f388 move VP8GetCPUInfo declaration to cpu.c 916548c2 Make kFuzzPxLimit sanitizer dependent 4070b271 advanced_api_fuzzer: reduce scaling limit 761f49c3 Merge "webp-lossless-bitstream-spec: add missing bits to ABNF" into main 84d04c48 webp-lossless-bitstream-spec: add missing bits to ABNF 0696e1a7 advanced_api_fuzzer: reduce scaling limit 93d88aa2 Merge "deps.cmake: remove unneeded header checks" into main 118e0035 deps.cmake: remove unneeded header checks 4c3d7018 webp-lossless-bitstream-spec: condense normal-prefix-code a6a09b32 webp-lossless-bitstream-spec: fix 2 code typos 50ac4f7c Merge "cpu.h: enable NEON w/_M_ARM64EC" into main 4b7d7b4f Add contribution instructions 0afbd97b cpu.h: enable NEON w/_M_ARM64EC 349f4353 Merge changes Ibd89e56b,Ic57e7f84,I89096614 into main 8f7513b7 upsampling_neon.c: fix WEBP_SWAP_16BIT_CSP check cbf624b5 advanced_api_fuzzer: reduce scaling limit 89edfdd1 Skip slow scaling in libwebp advanced_api_fuzzer 859f19f7 Reduce libwebp advanced_api_fuzzer threshold a4f04835 Merge changes Ic389aaa2,I329ccd79 into main 1275fac8 Makefile.vc: fix img2webp link w/dynamic cfg 2fe27bb9 img2webp: normalize help output 24bed3d9 cwebp: reflow -near_lossless help text 0825faa4 img2webp: add -sharp_yuv/-near_lossless d64e6d7d Merge "PaletteSortModifiedZeng: fix leak on error" into main 0e12a22d Merge "EncodeAlphaInternal: clear result->bw on error" into main 0edbb6ea PaletteSortModifiedZeng: fix leak on error 41ffe04e Merge "Update yapf style from "chromium" to "yapf"" into main 2d9d9265 Update yapf style from "chromium" to "yapf" a486d800 EncodeAlphaInternal: clear result->bw on error 1347a32d Skip big scaled advanced_api_fuzzer 52b6f067 Fix scaling limit in advanced_api_fuzzer.c 73618428 Limit scaling in libwebp advanced_api_fuzzer.c b54d21a0 Merge "CMakeLists.txt: allow CMAKE_INSTALL_RPATH to be set empty" into main 31c28db5 libwebp{,demux,mux}.pc.in: Requires -> Requires.private d9a505ff CMakeLists.txt: allow CMAKE_INSTALL_RPATH to be set empty bdf33d03 Merge tag 'v1.3.0' b5577769 update ChangeLog (tag: v1.3.0-rc1, tag: v1.3.0) 0ba77244 update NEWS e763eb1e bump version to 1.3.0 2a8686fc update AUTHORS 106a57c1 Merge "*/Android.mk: add a check for NDK_ROOT" into main c5e841c4 Merge "extras: WebpToSDL -> WebPToSDL" into main dbc30715 Merge "xcframeworkbuild.sh: bump MACOSX_CATALYST_MIN_VERSION" into main 6fc1a9f9 */Android.mk: add a check for NDK_ROOT d3e151fc doc/api.md,webp_js/README.md: Webp -> WebP ed92a626 extras: WebpToSDL -> WebPToSDL 6eb0189b xcframeworkbuild.sh: bump MACOSX_CATALYST_MIN_VERSION 1d58575b CMake: align .pc variables with autoconf e5fe2cfc webp-lossless-bitstream-spec,cosmetics: reflow paragraphs 0ceeeab9 webp-lossless-bitstream-spec: add amendment note 607611cd Merge "webp-container-spec: normalize section title case" into main f853685e lossless: SUBTRACT_GREEN -> SUBTRACT_GREEN_TRANSFORM 786497e4 webp-lossless-bitstream-spec: fix inv color txfm description c6ac672d webp-lossless-bitstream-spec: fix num_code_lengths check b5700efb webp-lossless-bitstream-spec,cosmetics: grammar/capitalization d8ed8c11 webp-container-spec: normalize section title case 52ec0b8f Merge changes Ie975dbb5,Ifc8c93af,I6ca7c5d6,I2e8d66f5,I152477b8 into main 5097ef62 webp-container-spec,cosmetics: grammar/capitalization e3ba2b1f webp-lossless-bitstream-spec,cosmetics: reflow abstract 1e8e3ded webp-lossless-bitstream-spec: reword abstract re alpha 017cb6fa webp-container-spec,cosmetics: normalize range syntax f6a4684b webp-lossless-bitstream-spec,cosmetics: normalize range syntax 54ebd5a3 webp-lossless-bitstream-spec: limit dist map lut to 69 cols 44741f9c webp-lossless-bitstream-spec: fix dist mapping example fad0ece7 pnmdec.c: use snprintf instead of sprintf 3f73e8f7 sharpyuv: add SharpYuvGetVersion() ce2f2d66 SharpYuvConvert: fix a race on SharpYuvGetCPUInfo a458e308 sharpyuv_dsp.h: restore sharpyuv_cpu.h include 9ba800a7 Merge changes Id72fbf3b,Ic59d23a2 into main 979c0ebb sharpyuv: add SharpYuvGetCPUInfo 8bab09a4 Merge "*.pc.in: rename lib_prefix to webp_libname_prefix" into main 769387c5 cpu.c,cosmetics: fix a typo a02978c2 sharpyuv/Makefile.am+cmake: add missing -lm 28aedcb9 *.pc.in: rename lib_prefix to webp_libname_prefix c42e6d5a configure.ac: export an empty lib_prefix variable dfc843aa Merge "*.pc.in: add lib prefix to lib names w/MSVC" into main 2498209b *.pc.in: add lib prefix to lib names w/MSVC ac252b61 Merge "analysis_enc.c: fix a dead store warning" into main 56944762 analysis_enc.c: fix a dead store warning d34f9b99 Merge "webp-lossless-bitstream-spec: convert BNF to ABNF" into main dc05b4db Merge changes I96bc063c,I45880467,If9e18e5a,I6ee938e4,I0a410b28, ... into main 83270c7f webp-container-spec: add prose for rendering process 73b19b64 webp-container-spec: note reserved fields MUST be ignored 57101d3f webp-lossless-bitstream-spec: improve 'small' color table stmt dfd32e45 webp-container-spec: remove redundant sentence 8a6185dd doc/webp-*: fix some punctuation, grammar 72776530 webp-lossless-bitstream-spec: convert BNF to ABNF d992bb08 cmake: rename cpufeatures target to cpufeatures-webp 3ed2b275 webp-container-spec: clarify background color note 951c292d webp-container-spec: come too late -> out of order 902dd787 webp-container-spec: prefer hex literals a8f6b5ee webp-container-spec: change SHOULD to MUST w/ANIM chunk 1dc59435 webp-container-spec: add unknown fields MUST be ignored 280a810f webp-container-spec: make padding byte=0 a MUST 41f0bf68 webp-container-spec: update note on trailing data 6bdd36db webp-container-spec: clarify Chunk Size is in bytes 87e36c48 Merge "webp_js/README.md,cosmetics: reflow some lines" into main 5b01f321 Merge "Update Windows makefile to build libsharpyuv library." into main 19b1a71c webp_js/README.md,cosmetics: reflow some lines 780db756 Update Windows makefile to build libsharpyuv library. e407d4b3 CMakeLists.txt: replace GLUT_glut_LIBRARY w/GLUT::GLUT abf73d62 Merge "WebPConfig.cmake.in: add find_dependency(Threads)" into main 25807fb4 Merge "cmake: restore compatibility with cmake < 3.12" into main 5dbc4bfa WebPConfig.cmake.in: add find_dependency(Threads) b2a175dd Merge "Update wasm instructions." into main cb90f76b Update wasm instructions. 02d15258 cmake: restore compatibility with cmake < 3.12 5ba046e2 CMake: add_definitions -> add_compile_options e68765af dsp,neon: use vaddv in a few more places e8f83de2 Set libsharpyuv include dir to 'webp' subdirectory. 15a91ab1 cmake,cosmetics: apply cmake-format 0dd49d1a CMakeLists.txt: set @ONLY in configure_file() calls 62b1bfe8 Merge changes I2877e7bb,I777cad70,I15af7d1a,I686e6740,If10538a9, ... into main 95c8fe5f Merge changes Iecea3603,I9dc228ab into main e7c805cf picture_csp_enc.c: remove SafeInitSharpYuv 6af8845a sharpyuv: prefer webp/types.h 639619ce cmake: fix dll exports 782ed48c sharpyuv,SharpYuvInit: add mutex protection when available cad0d5ad sharyuv_{neon,sse2}.c: merge WEBP_USE_* sections ef70ee06 add a few missing includes for NULL f0f9eda4 sharpyuv.h: remove 9b902cba Merge "picture_csp_enc.c,CheckNonOpaque: rm unneeded local" into main 9c1d457c cmake/cpu.cmake: remove unused variable 9ac25bcb CMakeLists.txt,win32: match naming convention used by nmake 76c353ba picture_csp_enc.c,CheckNonOpaque: rm unneeded local 5000de54 Merge "cwebp: fix WebPPictureHasTransparency call" into main e1729309 Merge "WebPPictureHasTransparency: add missing pointer check" into main 00ff988a vp8l_enc,AddSingleSubGreen: clear int sanitizer warnings e2fecc22 dsp/lossless_enc.c: clear int sanitizer warnings 129cf9e9 dsp/lossless.c: clear int sanitizer warnings ad7d1753 dsp/lossless_enc.c: clear int sanitizer warnings 5037220e VP8LSubtractGreenFromBlueAndRed_C: clear int sanitizer warnings 2ee786c7 upsampling_sse2.c: clear int sanitizer warnings 4cc157d4 ParseOptionalChunks: clear int sanitizer warning 892cf033 BuildHuffmanTable: clear int sanitizer warning 3a9a4d45 VP8GetSigned: clear int sanitizer warnings 704a3d0a dsp/lossless.c: quiet int sanitizer warnings 1a6c109c WebPPictureHasTransparency: add missing pointer check c626e7d5 cwebp: fix WebPPictureHasTransparency call 866e349c Merge tag 'v1.2.4' c170df38 Merge "Create libsharpyuv.a in makefile.unix." into main 9d7ff74a Create libsharpyuv.a in makefile.unix. 0d1f1254 update ChangeLog (tag: v1.2.4) fcbc2d78 Merge "doc/*.txt: restrict code to 69 columns" into main 4ad0e189 Merge "webp-container-spec.txt: normalize fourcc spelling" into main 980d2488 update NEWS 9fde8127 bump version to 1.2.4 7a0a9935 doc/*.txt: restrict code to 69 columns c040a615 webp-container-spec.txt: normalize fourcc spelling aff1c546 dsp,x86: normalize types w/_mm_cvtsi128_si32 calls ab540ae0 dsp,x86: normalize types w/_mm_cvtsi32_si128 calls 8980362e dsp,x86: normalize types w/_mm_set* calls (2) e626925c lossless: fix crunch mode w/WEBP_REDUCE_SIZE 83539239 dsp,x86: normalize types w/_mm_set* calls 8a4576ce webp-container-spec.txt: replace & with & db870881 Merge "webp-container-spec.txt: make reserved 0 values a MUST" into main 01d7d378 webp-lossless-bitstream-spec: number all sections 337cf69f webp-lossless-bitstream-spec: mv Nomenclature after Intro 79be856e Merge changes I7111d1f7,I872cd62c into main 5b87983a webp-container-spec.txt: make reserved 0 values a MUST bd939123 Merge changes I7a25b1a6,I51b2c2a0,I87d0cbcf,I6ec60af6,I0a3fe9dc into main 04764b56 libwebp.pc: add libsharpyuv to requires 7deee810 libsharpyuv: add pkg-config file 1a64a7e6 webp-container-spec.txt: clarify some SHOULDs bec2c88a webp-container-spec.txt: move ChunkHeader to terminology c9359332 webp-container-spec.txt: clarify 'VP8 '/'XMP ' fourccs 70fe3063 webp-container-spec.txt: rightsize table entries ddbf3f3f webp-container-spec.txt: update 'key words' text c151e95b utils.h,WEBP_ALIGN: make bitmask unsigned 748e92bb add WebPInt32ToMem 3fe15b67 Merge "Build libsharpyuv as a full installable library." into main 4f402f34 add WebPMemToInt32 a3b68c19 Build libsharpyuv as a full installable library. b4994eaa CMake: set rpath for shared objects 94cd7117 Merge "CMake: fix dylib versioning" into main e91451b6 Fix the lossless specs a bit more. 231bdfb7 CMake: fix dylib versioning bfad7ab5 CMakeLists.txt: correct libwebpmux name in WebPConfig.cmake c2e3fd30 Revert "cmake: fix webpmux lib name for cmake linking" 7366f7f3 Merge "lossless: fix crunch mode w/WEBP_REDUCE_SIZE" into main 84163d9d lossless: fix crunch mode w/WEBP_REDUCE_SIZE d01c1eb3 webp-lossless-bitstream-spec,cosmetics: normalize capitalization 8813ca8e Merge tag 'v1.2.3' 3c4a0fbf update ChangeLog (tag: v1.2.3) 56a480e8 dsp/cpu.h: add missing extern "C" 62b45bdd update ChangeLog (tag: v1.2.3-rc1) 8764ec7a Merge changes Idb037953,Id582e395 into 1.2.3 bcb872c3 vwebp: fix file name display in windows unicode build 67c44ac5 webpmux: fix -frame option in windows unicode build 8278825a makefile.unix: add sharpyuv objects to clean target 14a49e01 update NEWS 34b1dc33 bump version to 1.2.3 0b397fda update AUTHORS c16488ac update .mailmap 5a2d929c Merge "unicode.h: set console mode before using wprintf" into main 169f867f unicode.h: set console mode before using wprintf a94b855c Merge "libsharpyuv: add version defines" into main f83bdb52 libsharpyuv: add version defines bef0d797 unicode_gif.h: fix -Wdeclaration-after-statement 404c1622 Rename Huffman coding to prefix coding in the bitstream spec 8895f8a3 Merge "run_static_analysis.sh: fix scan-build archive path" into main 92a673d2 Merge "Add -fvisibility=hidden flag in CMakeLists." into main 67c1d722 Merge "add WEBP_MSAN" into main 1124ff66 Add -fvisibility=hidden flag in CMakeLists. e15b3560 add WEBP_MSAN ec9e782a sharpyuv: remove minimum image size from sharpyuv library 7bd07f3b run_static_analysis.sh: fix scan-build archive path 5ecee06f Merge "sharpyuv: increase precision of gamma<->linear conversion" into main f81dd7d6 Merge changes I3d17d529,I53026880,I1bd61639,I6bd4b25d,Icfec8fba into main 2d607ee6 sharpyuv: increase precision of gamma<->linear conversion 266cbbc5 sharpyuv: add 32bit version of SharpYuvFilterRow. 9fc12274 CMake: add src to webpinfo includes 7d18f40a CMake: add WEBP_BUILD_WEBPINFO to list of checks for exampleutil 11309aa5 CMake: add WEBP_BUILD_WEBPMUX to list of checks for exampleutil 4bc762f7 CMake: link imageioutil to exampleutil after defined 0d1b9bc4 WEBP_DEP_LIBRARIES: use Threads::Threads 20ef48f0 Merge "sharpyuv: add support for 10/12/16 bit rgb and 10/12 bit yuv." into main 93c54371 sharpyuv: add support for 10/12/16 bit rgb and 10/12 bit yuv. 53cf2b49 normalize WebPValidatePicture declaration w/definition d3006f4b sharpyuv: slightly improve precision ea967098 Merge changes Ia01bd397,Ibf3771af into main 11bc8410 Merge changes I2d317c4b,I9e77f6db into main 30453ea4 Add an internal WebPValidatePicture. 6c43219a Some renamings for consistency. 4f59fa73 update .mailmap e74f8a62 webp-lossless-bitstream-spec,cosmetics: normalize range syntax 5a709ec0 webp-lossless-bitstream-spec,cosmetics: fix code typo a2093acc webp-lossless-bitstream-spec: add amendment note 86c66930 webp-lossless-bitstream-spec: fix BNF 232f22da webp-lossless-bitstream-spec: fix 'simple code' snippet 44dd765d webp-lossless-bitstream-spec: fix ColorTransform impl 7a7e33e9 webp-lossless-bitstream-spec: fix TR-pixel right border note 86f94ee0 Update lossless spec with Huffman codes. a3927cc8 sharpyuv.c,cosmetics: fix indent 6c45cef7 Make sure the stride has a minimum value in the importer. 0c8b0e67 sharpyuv: cleanup/cosmetic changes dc3841e0 {histogram,predictor}_enc: quiet int -> float warnings a19a25bb Replace doubles by floats in lossless misc cost estimations. 42888f6c Add an option to enable static builds. 7efcf3cc Merge "Fix typo in color constants: Marix -> Matrix" into main 8f4b5c62 Fix typo in color constants: Marix -> Matrix 90084d84 Merge "demux,IsValidExtendedFormat: remove unused variable" into main ed643f61 Merge changes I452d2485,Ic6d75475 into main 8fa053d1 Rename SharpYUV to SharpYuv for consistency. 99a87562 SharpYuvComputeConversionMatrix: quiet int->float warnings deb426be Makefile.vc: add sharpyuv_csp.obj to SHARPYUV_OBJS 779597d4 demux,IsValidExtendedFormat: remove unused variable 40e8aa57 Merge "libsharpyuv: add colorspace utilities" into main 01a05de1 libsharpyuv: add colorspace utilities 2de4b05a Merge changes Id9890a60,I376d81e6,I1c958838 into main b8bca81f Merge "configure.ac: use LT_INIT if available" into main e8e77b9c Merge changes I479bc487,I39864691,I5d486c2c,I186d13be into main 7e7d5d50 Merge ".gitignore: add Android Studio & VS code dirs" into main 10c50848 normalize label indent 89f774e6 mux{edit,internal}: fix leaks on error 2d3293ad ExUtilInitCommandLineArguments: fix leak on error ec34fd70 anim_util: fix leaks on error e4717287 gif2webp: fix segfault on OOM e3cfafaf GetBackwardReferences: fail on alloc error a828a59b BackwardReferencesHashChainDistanceOnly: fix segfault on OOM fe153fae VP8LEncodeStream: fix segfault on OOM 919acc0e .gitignore: add Android Studio & VS code dirs efa0731b configure.ac: use LT_INIT if available 0957fd69 tiffdec: add grayscale support e685feef Merge "Make libsharpyuv self-contained by removing dependency on cpu.c" into main 841960b6 Make libsharpyuv self-contained by removing dependency on cpu.c 617cf036 image_dec: add WebPGetEnabledInputFileFormats() 7a68afaa Let SharpArgbToYuv caller pass in an RGB>YUV conversion matrix. 34bb332c man/cwebp.1: add note about crop/resize order f0e9351c webp-lossless-bitstream-spec,cosmetics: fix some typos 5ccbd6ed vp8l_dec.c,cosmetics: fix a few typos c3d0c2d7 fix ios build scripts after sharpyuv dep added d0d2292e Merge "Make libwebp depend on libsharpyuv." into main 03d12190 alpha_processing_neon.c: fix 0x01... typo d55d447c Make libwebp depend on libsharpyuv. e4cbcdd2 Fix lossless encoding for MIPS. 924e7ca6 alpha_processing_neon.c: fix Dispatch/ExtractAlpha_NEON 0fa0ea54 Makefile.vc: use /MANIFEST:EMBED 29cc95ce Basic version of libsharpyuv in libwebp, in C. a30f2190 examples/webpmux.c: fix a couple of typos 66b3ce23 Fix bad overflow check in ReadTIFF() 54e61a38 Markdownify libwebp docs and reorganize them. b4533deb CMakeLists.txt,cosmetics: break long line b9d2f9cd quant_enc.c: use WEBP_RESTRICT qualifier ec178f2c Add progress hook granularity in lossless 26139c73 Rename MAX_COST to MAX_BIT_COST in histogram_enc.c 13b82816 cmake: fix webpmux lib name for cmake linking 88b6a396 webp-container-spec.txt,cosmetics: normalize formatting 6f496540 Merge tag 'v1.2.2' 4074acf8 dsp.h: bump msvc arm64 version requirement to 16.6 b0a86089 update ChangeLog (tag: v1.2.2) 6db8248c libwebp: Fix VP8EncTokenLoop() progress 827a307f BMP enc: fix the transparency case db25f1b4 libwebp: Fix VP8EncTokenLoop() progress 286e7fce libwebp: do not destroy jpeg codec twice on error 6e8a4126 libwebp: do not destroy jpeg codec twice on error faf21968 Merge "BMP enc: fix the transparency case" into main 480cd51d BMP enc: fix the transparency case 9195ea05 update ChangeLog (tag: v1.2.2-rc2) 4acae017 update NEWS 883f0633 man/img2webp.1: update date 567e1f44 Reword img2webp synopsis command line 1b0c15db man/img2webp.1: update date 17bade38 Merge "Reword img2webp synopsis command line" into main a80954a1 Reword img2webp synopsis command line f084244d anim_decode: fix alpha blending with big-endian b217b4ff webpinfo: fix fourcc comparison w/big-endian ec497b75 Merge "anim_decode: fix alpha blending with big-endian" into main e4886716 anim_decode: fix alpha blending with big-endian e3cb052c webpinfo: fix fourcc comparison w/big-endian a510fedb patch-check: detect duplicated files f035d2e4 update ChangeLog (tag: v1.2.2-rc1) 7031946a update NEWS 973390b6 bump version to 1.2.2 abd6664f update AUTHORS 5b7e7930 Merge "add missing USE_{MSA,NEON} checks in headers" into main 02ca04c3 add missing USE_{MSA,NEON} checks in headers e94716e2 xcframeworkbuild.sh: place headers in a subdir c846efd8 patch-check: commit subject length check b6f756e8 update http links 8f5cb4c1 update rfc links 8ea81561 change VP8LPredictorFunc signature to avoid reading 'left' 6b1d18c3 webpmux: fix the -bgcolor description 3368d876 Merge "webpmux: add "-set bgcolor A,R,G,B"" into main f213abf6 webpinfo: print the number of warnings 50c97c30 webpmux: add "-set bgcolor A,R,G,B" 2c206aaf Remove CMakeLists.txt check in compile.sh 96e3dfef Merge "infra/common.sh: add shard_should_run()" into main 0e0f74b7 infra/common.sh: add shard_should_run() 35b7436a Jenkins scripts port: update shell function comments 21d24b4c webp-container-spec.txt: remove 'experimental' markers cdcf8902 Merge "Port Jenkins script: compile" into main dc683cde Jenkins scripts port: static analysis 0858494e Port Jenkins script: compile c2cf6a93 Jenkins scripts port: android compilation df0e808f presubmit: Add pylint-2.7 and .pylintrc 676c57db patch-check: shfmt 7bb7f747 patch-check: Add shellcheck abcd1797 Reformat docstrings and imports edaf0895 Port Jenkins scripts: compile js b9622063 Set CheckPatchFormatted flags to fail on diffs e23cd548 dsp.h: enable NEON w/VS2019+ ARM64 targets 3875c7de CMakeLists.txt: set minimum version to 3.7 1a8f0d45 Have a hard-coded value for memset in TrellisQuantizeBlock. 93480160 Speed up TrellisQuantizeBlock 45eaacc9 Convert deprecated uint32 to uint32_t. 42592af8 webp,cmake: Remove unnecessary include dirs e298e05f Add patch-check steps in PRESUBMIT.py 29148919 Merge tag 'v1.2.1' 9ce5843d update ChangeLog (tag: v1.2.1) d9191588 fuzzer/*: normalize src/ includes c5bc3624 fuzzer/*: normalize src/ includes 53b6f762 fix indent d2caaba4 fix indent 731246ba update ChangeLog (tag: v1.2.1-rc2) d250f01d dsp/*: use WEBP_HAVE_* to determine Init availability 1fe31625 dsp/*: use WEBP_HAVE_* to determine Init availability 3a4d3ecd update NEWS b2bc8093 bump version to 1.2.1 e542fc7a update AUTHORS e0241154 Merge "libwebp/CMake: Add to webp incl" into main edea6444 libwebp/CMake: Add to webp incl ece18e55 dsp.h: respect --disable-sse2/sse4.1/neon a89a3230 wicdec: support alpha from WebP WIC decoder 26f4aa01 Merge "alpha_processing: fix visual studio warnings" into main 8f594663 alpha_processing: fix visual studio warnings 46d844e6 Merge "cpu.cmake: fix compiler flag detection w/3.17.0+" into main 298d26ea Merge changes I593adf92,If20675e7,Ifac68eac into main a1e5dae0 alpha_processing*: use WEBP_RESTRICT qualifier 327ef24f cpu.cmake: fix compiler flag detection w/3.17.0+ f70819de configure: enable libwebpmux by default dc7e2b42 configure: add informational notices when disabling binaries 9df23ddd configure: move lib flag checks before binaries a2e18f10 Merge "WebPConfig.config.in: correct WEBP_INCLUDE_DIRS" into main e1a8d4f3 Merge "bit_reader_inl_utils: uniformly apply WEBP_RESTRICT" into main 4de35f43 rescaler.c: fix alignment 0f13eec7 bit_reader_inl_utils: uniformly apply WEBP_RESTRICT 277d3074 Fix size_t overflow in WebPRescalerInit 97adbba5 WebPConfig.config.in: correct WEBP_INCLUDE_DIRS b60d4603 advanced_api_fuzzer: add extreme config value coverage 72fe52f6 anim_encode.c,cosmetics: normalize indent 116d235c anim_encode: Fix encoded_frames_[] overflow 6f445b3e CMake: set CMP0072 to NEW b1cf887f define WEBP_RESTRICT for MSVC 3e265136 Add WEBP_RESTRICT & use it in VP8BitReader f6d29247 vp8l_dec::ProcessRows: fix int overflow in multiply de3b4ba8 CMake: add WEBP_BUILD_LIBWEBPMUX 7f09d3d1 CMakeLists.txt: rm libwebpmux dep from anim_{diff,dump} 4edea4a6 Init{RGB,YUV}Rescaler: fix a few more int overflows c9e26bdb rescaler_utils: set max valid scaled w/h to INT_MAX/2 28d488e6 utils.h: add SizeOverflow() 695bdaa2 Export/EmitRescaledRowsRGBA: fix pointer offset int overflow 685d073e Init{RGB,YUV}Rescaler: fix int overflows in multiplication d38bd0dd WebPFlipBuffer: fix integer overflow 109ff0f1 utils: allow MALLOC_LIMIT to indicate a max a2fce867 WebPRescalerImportRowExpand_C: promote some vals before multiply 776983d4 AllocateBuffer: fix int multiplication overflow check 315abbd6 Merge "Revert "Do not use a palette for one color images."" eae815d0 Merge changes Ica3bbf75,I82f82954 afbca5a1 Require Emscripten 2.0.18 3320416b CMakeLists,emscripten: use EXPORTED_RUNTIME_METHODS 29145ed6 Update README instructions for using Emscripten 1f579139 cosmetics: remove use of 'sanity' / 'master' 29b6129c WebPAnimEncoderNewInternal: remove some unnecessary inits b60869a1 Revert "Do not use a palette for one color images." 6fb4cddc demux: move padded size calc post unpadded validation 05b72d42 vp8l_enc.c: normalize index types b6513fba Do not use a palette for one color images. 98bbe35b Fix multi-threading with palettes. b1674240 Add modified Zeng's method to palette sorting. 88c90c45 add CONTRIBUTING.md 6a9916d7 WebPRescalerInit: add missing int64_t promotion b6cf52d5 WebPIoInitFromOptions: treat use_scaling as a bool 3b12b7f4 WebPIoInitFromOptions: treat use_cropping as a bool 595fa13f add WebPCheckCropDimensions() 8fdaecb0 Disable cross-color when palette is used. 8933bac2 WebPIoInitFromOptions: respect incoming bypass_filtering val 7d416ff0 webpdec,cosmetics: match error text to function call ec6cfeb5 Fix typo on WebPPictureAlloc() in README 7e58a1a2 *.cmake: add license header 5651a6b2 cmake: fix .so versioning 25ae67b3 xcframeworkbuild.sh: add arm64 simulator target 5d4ee4c3 cosmetics: remove use of the term 'dummy' 01b38ee1 faster CollectColorXXXTransforms_SSE41 652aa344 Merge "Use BitCtz for FastSLog2Slow_C" 0320e1e3 add the missing default BitsCtz() code 8886f620 Use BitCtz for FastSLog2Slow_C fae41617 faster CombinedShannonEntropy_SSE2 5bd2704e Introduce the BitCtz() function. fee64287 Merge "wicdec,icc: treat unsupported op as non-fatal" 33ddb894 lossless_sse{2,41}: remove some unneeded includes b27ea852 wicdec,icc: treat unsupported op as non-fatal b78494a9 Merge "Fix undefined signed shift." e79974cd Fix undefined signed shift. a8853394 SSE4.1 versions of BGRA to RGB/BGR color-space conversions a09a6472 SSE4.1 version of TransformColorInverse 401da22b Merge "pngdec: check version before using png_get_chunk_malloc_max" 26907822 pngdec: check version before using png_get_chunk_malloc_max 06c1e72e Code cleanup 8f0d41aa Merge changes Id135bbf4,I99e59797 373eb170 gif2webp: don't store loop-count if there's only 1 frame 759b9d5a cmake: add WEBP_USE_THREAD option 926ce921 cmake: don't install binaries from extras/ 9c367bc6 WebPAnimDecoderNewInternal: validate bitstream before alloc 47f64f6e filters_sse2: import Chromium change cc3577e9 fuzzer/*: use src/ based include paths 004d77ff Merge tag 'v1.2.0' fedac6cc update ChangeLog (tag: v1.2.0-rc3, tag: v1.2.0) 170a8712 Fix check_c_source_compiles with pthread. ceddb5fc Fix check_c_source_compiles with pthread. 85995719 disable CombinedShannonEntropy_SSE2 on x86 289757fe TiffDec: enforce stricter mem/dimension limit on tiles 8af7436f Merge "{ios,xcframework}build.sh: make min version(s) more visible" into 1.2.0 e56c3c5b pngdec: raise memory limit if needed 8696147d pngdec: raise memory limit if needed 13b8e9fe {ios,xcframework}build.sh: make min version(s) more visible a9225410 animdecoder_fuzzer: fix memory leak d6c2285d update gradle to 6.1.1 8df77fb1 animdecoder_fuzzer: fix memory leak 52ce6333 update NEWS 28c49820 bump version to 1.2.0 7363dff2 webp/encode.h: restore WEBP_ENCODER_ABI_VERSION to v1.1.0 826aafa5 update AUTHORS 63258823 animdecoder_fuzzer: validate canvas size 9eb26381 CMake: remove duplicate "include(GNUInstallDirs)" 2e7bed79 WebPPicture: clarify the ownership of user-owned data. cccf5e33 webpmux: add an '-set loop ' option c9a3f6a1 Merge changes Ie29f9867,I289c54c4 319f56f1 iosbuild.sh: sync some aspects of xcframeworkbuild.sh e8e8db98 add xcframeworkbuild.sh ae545534 dsp.h: allow config.h to override MSVC SIMD autodetection fef789f3 Merge "cmake: fix per-file assembly flags" fc14fc03 Have C encoding predictors use decoding predictors. 7656f0b3 README,cosmetics: fix a couple typos d2e245ea cmake: disable webp.js if WEBP_ENABLE_SIMD=1 96099a79 cmake: fix per-file assembly flags 5abb5582 Merge "cmake: fix compilation w/Xcode generator" 8484a120 cmake: fix compilation w/Xcode generator d7bf01c9 Merge changes Ifcae0f38,Iee2d7401 36c81ff6 WASM-SIMD: port 2 patches from rreverser@'s tree 988b02ab Merge "Couple of fixes to allow SIMD on Emscripten" 26faf770 wicdec: fail with animated images ab2d08a8 [cd]webp: document lack of animated webp support 52273943 Couple of fixes to allow SIMD on Emscripten 8870ba7f Fix skia bug #10952 4b3c6953 Detect if StoreFrame read more than anmf_payload_size bytes 17fd4ba8 webp/decode.h,cosmetics: normalize 'flip' comment 411d3677 remove some unreachable break statements 3700ffd7 WebPPictureHasTransparency: remove unreachable return 83604bf3 {animencoder,enc_dec}_fuzzer: convert some abort()s to returns eb44119c Merge changes I8ae09473,I678c8b1e 9f6055fc fuzz_utils.h: rename max() to Max() 695788e7 fuzz_utils.h: make functions WEBP_INLINE 906c1fcd make ImgIoUtilReadFile use WebPMalloc instead of malloc 8cb7e536 rename demux_api_fuzzer.c -> mux_demux_api_fuzzer.c 443db47d add animdecoder_fuzzer.cc 36a6eea3 Merge "import fuzzers from oss-fuzz/chromium" ec5f12c1 Makefile.vc: remove deprecated /Gm option 64425a08 picture_tools_enc: fix windows build warning bd94090a import fuzzers from oss-fuzz/chromium cf847cba use WEBP_DSP_INIT_FUNC for Init{GammaTables*,GetCoeffs} 55a080e5 Add WebPReplaceTransparentPixels() in dsp 84739717 GetBackgroundColorGIF: promote to uint32_t before << 24 def64e92 cwebp: Fix -print_psnr for near_lossless cf2f88b3 Add palette and spatial for q >= 75 and -m 5 f0110bae Add no-color cache configuration to the cruncher 749a8b99 Better estimate of the cache cost. 4f9f00cc Use spatial predictors on top of palette no matter what. 7658c686 Add spatial prediction on top of palette in cruncher. 133ff0e3 webp_js: force WASM=0 option explicitly e3c259a2 Fix integer overflow in EmitFancyRGB. b3ff0bde man/{gif2,img2}webp,webpmux: normalize some wording f9b30586 fix ABI breakage introduced by 6a0ff358 1d58dcfc README.webp_js: update note about emscripten version 44070266 README.webp_js: s/fastcomp/upstream/ 2565fa8f README.webp_js: update cmake command 47309ef5 webp: WEBP_OFFSET_PTR() 687ab00e DC{4,8,16}_NEON: replace vmovl w/vaddl 1b92fe75 DC16_NEON,aarch64: use vaddlv 53f3d8cf dec_neon,DC8_NEON: use vaddlv instead of movl+vaddv 27d08240 Fix integer overflow in WebPAnimDecoderGetNext() 69776e38 Merge "remove call to MBAnalyzeBestIntra4Mode for method >= 5" a99078c1 remove call to MBAnalyzeBestIntra4Mode for method >= 5 22e404cc CMakeLists.txt: fix set(CACHE) argument order 71690b52 fix MSVC warning 6a0ff358 Enc: add a qmin / qmax range for quality factor 0fa56f30 Merge tag 'v1.1.0' 6cf504d0 PNM decoding: handle max_value != 255 d7844e97 update ChangeLog (tag: v1.1.0-rc2, tag: v1.1.0) 7f006436 Makefile.vc: fix webp_quality.exe link cf047e83 Makefile.vc: fix webp_quality.exe link c074c653 update NEWS 30f09551 bump version to 1.1.0 a76694a1 update AUTHORS 6e3ef7b3 extras: fix WEBP_SWAP_16BIT_CSP check 47178dbd extras: add WebPUnmultiplyARGB() convenience function 22cbae33 idec_dec: fix 0 offset of NULL pointer 290dd0b4 muxread: fix 0 offset of NULL pointer 0df474ac Merge "lossless_(enc_|)sse2: avoid offsetting a NULL pointer" c6b75a19 lossless_(enc_|)sse2: avoid offsetting a NULL pointer 295e5e38 fix UBSAN warning e2575e05 DC8_NEON,aarch64: use vaddv b0e09e34 dec_neon: Fix build failure under some toolchains cf0e903c dsp/lossless: Fix non gcc ARM builds bb7bc40b Remove ubsan errors. 78881b76 CMake: fix GLUT library link 9f750f7a cmake: fix BUILD_SHARED_LIBS build on mac 17850e74 libwebp: Remove char-subscripts warning in pnmdec.c 2fa2552d Merge "Expose WebPMalloc() in addition to WebPFree()" a4df4aae Expose WebPMalloc() in addition to WebPFree() 853ea3d8 imageio/tiff: Return error before allocating bad tile size af650c0b Fix a Wxor-used-as-pow false positive 601ef17c libwebp.py: update to swig 3.0.12 0e48d889 bugfix: last alpha rows were incorrectly decoded 24d2ccb4 webp: Fix imageio ReadPNM() TUPLTYPE fab8f9cf cosmetics: normalize '*' association 94138e0e update .gitignore 0fe1a89d update ChangeLog (tag: v1.0.3-rc1, tag: v1.0.3) 2ad0916d update NEWS 1287362b bump version to 1.0.3 7b968cc2 update AUTHORS 9d6988f4 Fix the oscillating prediction problem at low quality 312f74d0 makefile.unix: allow *_LIBS to be overridden w/EXTRA_LIBS 92dbf237 filters_sse2,cosmetics: shorten some long lines a277d197 filters_sse2.c: quiet integer sanitizer warnings 804540f1 Fix cpufeatures in CMake. bf00c15b Add CMake option for bittrace. a788b498 filters_sse2.c: quiet integer sanitizer warnings e6a92c5e filters.c: quiet integer sanitizer warnings ec1cc40a lossless.c: remove U32 -> S8 conversion warnings 1106478f remove conversion U32 -> S8 warnings 812a6b49 lossless_enc: fix some conversion warning 4627c1c9 lossless_enc,TransformColorBlue: quiet uint32_t conv warning c84673a6 lossless_enc_sse{2,41}: quiet signed conv warnings 776a7757 dec_sse2: quiet signed conv warnings bd39c063 Merge "thread_utils: release mutex before signaling" 0550576f Merge "(alpha_processing,enc}_sse2: quiet signed conv warnings" 6682f2c4 thread_utils: release mutex before signaling e78dea75 (alpha_processing,enc}_sse2: quiet signed conv warnings 9acf18ba iosbuild.sh: add WebP{Demux,Mux}.framework b9be7e65 vwebp: remove the -fit option (and make it default) 1394a2bb Merge "README.webp_js: update Emscripten.cmake note" dd3e7f8a README.webp_js: update Emscripten.cmake note 32cf8801 predictor_enc,GetBestGreenRedToBlue: quiet implicit conv warnings e1c8acb5 Merge "vwebp: add a -fit option" cbd23dd5 vwebp: add a -fit option 2e672351 bit_writer_utils,Flush: quiet implicit conversion warnings 1326988d swig: update libwebp_python_wrap.c 0e7f8548 update generated swig files 17ed1438 Merge "PutLE{16,24}: quiet implicit conversion warnings" 24686538 PutLE{16,24}: quiet implicit conversion warnings 153bb3a0 fix some clang-7 warnings: ab2dc893 Rescaler: fix rounding error aa65f89a HistogramCombineStochastic: fix free of uninit value af0bac64 Merge "encode.h: mention 'exact' default in WebPEncodeLossless*" 6d2e11ec encode.h: mention 'exact' default in WebPEncodeLossless* 8c3f04fe AndroidCPUInfo: reorder terms in conditional fcfd9c71 BitTrace: if BITTRACE is > 0, record and print syntax bits used 067031ea Speedups for unused Huffman groups. 01ac46ba libwebp: Display "libjpeg error:" in imageio/jpegdec d9a662e1 WebPRescalerGetScaledDimensions: round scaled dimension up 62eb3f08 libwebp: Fix missing '{' in README e05f785a Merge "unicode,INIT_WARGV: add missing cast" 63c9a69f tag the VP8LHashPix() function for potential uint roll-over 2b7214ab unicode,INIT_WARGV: add missing cast bf424b46 tag the GetPixPairHash64() function for potential uint roll-over 7d05d6ca Have the color cache computation be u32-bit only. 6bcf8769 Remove BINARYEN_METHOD in wasm settings. 2b98df90 update ChangeLog (tag: v1.0.2-rc1, tag: v1.0.2) 61e372b7 update NEWS 7ae658a0 bump version to 1.0.2 51c4907d update AUTHORS 666bd6c6 man/cwebp.1: refine near-lossless text 561cdce5 Clarify the doc about GetFeatures. aec2cf02 near_lossless: fix fuzzing-detected integer overflow 928a75de webp: Fix VP8LBitWriterClone() bug 5173d4ee neon IsFlat 5b081219 IsFlat: inline when possible 381b7b54 IsFlat: use int for thresh 6ed15ea1 fix unprobable leak in webp_sdl.c 22bbb24e Merge "IsFlat: return int" 8b3fb238 Merge tag 'v1.0.1' f435de95 IsFlat: return int 41521aed utils.h: only define WEBP_NEED_LOG_TABLE_8BIT when needed 9f4d4a3f neon: GetResidualCost 0fd7514b neon: SetResidualCoeffs f95a996c Simpler histogram clustering. e85d3313 update ChangeLog (tag: v1.0.1-rc2, tag: v1.0.1) fa8210e4 Fix pair update in stochastic entropy merging. fd198f73 add codereview.settings 825389ac README.mux: add a reference to the AnimDecoder API 3be698c3 CMake: fix webp_js compilation 485ff86f Fix pair update in stochastic entropy merging. 4cd0582d CMake: fix webp_js compilation 4cbb4caf update NEWS f5a5918d bump version to 1.0.1 d61385db Speed-up: Make sure we only initialize histograms when needed. 6752904b Speed-up: Make sure we only initialize histograms when needed. 0c570316 update AUTHORS 301a2dda img2webp: add help note about arguments from a file f0abab92 Speedups for empty histograms. f2dfd925 Split HistogramAdd to only have the high level logic in C. 06b7bc7d Fix compilation on windows and clang-cl+ninja. b6284d82 img2webp: add help note about arguments from a file decf6f6b Speedups for empty histograms. dea3e899 Split HistogramAdd to only have the high level logic in C. 632798ae Merge "Fix compilation on windows and clang-cl+ninja." dc1a9518 Merge "libwebp: Unicode command tools on Windows" 9cf9841b libwebp: Unicode command tools on Windows 98179495 remove some minor TODOs a376e7b9 Fix compilation on windows and clang-cl+ninja. cbf82cc0 Remove AVX2 files. 5030e902 Merge "TIFF decoder: remove unused KINV definition" ac543311 Remove a few more useless #defines 123d3306 TIFF decoder: remove unused KINV definition ef1094b0 Merge "- install pkg-config files during the CMake build" b911fbc9 libwebp: Remove duplicate GIFDisplayError in anim_util eee00b66 - install pkg-config files during the CMake build ac3ec8c9 Merge "Clean-up the common sources in dsp." 3e13da7b Clean-up the common sources in dsp. 5c395f1d libwebp: cmake-format all e7a69729 libwebp: Add extras targets in CMakeLists.txt e52485d6 libwebp: Rename macros in webpmux.c 92dc0f09 clean-up MakeInputImageCopy() 39952de2 VP8IteratorImport: add missing 'const' 382af7a2 clean-up WebPBlendAlpha 14d020f6 libwebp: Use ExUtilGet*() in anim_diff 0d92ff25 libwebp: remove useless variable in gif2webp 556cb1b4 Merge "CMake: Set WEBP_BUILD_GIF2WEBP to off" da26ee49 CMake: Set WEBP_BUILD_GIF2WEBP to off b2a867c0 cwebp: Don't premultiply during -resize if -exact 637141bc pngdec: fix build w/libpng < 1.4.x bc5092b1 pngdec: set memory functions 50d8345a Fix CMake math library. 6aa3e8aa Fix math library on Visual Studio. d71df4e2 Fix math library finding in CMake. de08d727 cosmetics: normalize include guard comment 009562b4 vwebp: Fix bug when Dispose then NoBlend frames 423f2579 Fix up CMake to create targets. 907208f9 Wait for all threads to be done in DecodeRemaining. 4649b3c4 vwebp: Add background color display option 78ad57a3 Fix bad glClearColor parameters da96d8d9 Allow for a non-initialized alpha decompressor in DoRemap. 2563db47 fix rescaling rounding inaccuracy 211f37ee fix endian problems in pattern copy 5f0f5c07 Make sure partition #0 is read before VP8 data in IDecode. de98732b fix GetColorf() bug 4338cd36 misc fixes in libwebpmux e00af13e fix signatures after a9ceda7ff1 a9ceda7f Speed-up chunk list operations. 2281bbf6 Merge "Better handling of bogus Huffman codes." 39cb9aad Better handling of bogus Huffman codes. 89cc9d37 Merge "fix read-overflow while parsing VP8X chunk" 95fd6507 fix read-overflow while parsing VP8X chunk 9e729fe1 Fix VP8IoTeardownHook being called twice on worker sync failure 29fb8562 Merge "muxread,anmf: fail on multiple image chunks" eb82ce76 muxread,anmf: fail on multiple image chunks 1344a2e9 fix alpha-filtering crash when image width is larger than radius be738c6d muxread,ChunkVerifyAndAssign: validate chunk_size 2c70ad76 muxread,CreateInternal: fix riff size checks 569001f1 Fix for thread race heap-use-after-free c56a02d9 Android.mk: use LOCAL_EXPORT_C_INCLUDES w/public libs 15795596 CMakeLists.txt,cosmetics: normalize if() formatting 1a44c233 Merge "cmake: add support for webpmux" e9569ad7 Merge "configure,*am,cosmetics: s/WANT_/BUILD_/" 35c7de6f cmake: add support for webpmux 0f25e61c WebpToSDL(): fix the return value in case of error 5d8985de configure,*am,cosmetics: s/WANT_/BUILD_/ 895fd28f Merge "man/Makefile.am: add img2webp.1" 5cf3e2af man/Makefile.am: add img2webp.1 2a9de5b9 Add build rules for anim_diff & anim_dump utils. 71ed73cf fix invalid check for buffer size af0e4fbb gif2webp: fix transcode of loop count=65535 dce5d764 Limit memory allocation when reading invalid Huffman codes. f9df0081 Merge "cmake: quiet glut deprecation warnings on OS X" dc39b16f webpmux.1: correct grammar c7aa1264 cwebp.c: fix a missing \n 53aa51e9 Merge tag 'v1.0.0' 698b8844 update ChangeLog (tag: v1.0.0) 8d510751 webp-container-spec: correct frame duration=0 note e6b2164e vwebp: Copy Chrome's behavior w/frame duration == 0 094b3b28 cmake: quiet glut deprecation warnings on OS X 71c39a06 webp-container-spec: correct frame duration=0 note fd3d5756 vwebp: Copy Chrome's behavior w/frame duration == 0 b0c966fb Build vwebp from CMake. d20b7707 update ChangeLog (tag: v1.0.0-rc3) 0d5fad46 add WEBP_DSP_INIT / WEBP_DSP_INIT_FUNC d77bf512 add WEBP_DSP_INIT / WEBP_DSP_INIT_FUNC c1cb86af fix 16b overflow in SSE2 e577feb7 makefile.unix: add DEBUG flag for compiling w/ debug-symbol 99be34b3 cwebp,get_disto: fix bpp output e122e511 cwebp,get_disto: fix bpp output f5565ca8 cmake: Make sure we use near-lossless by default. d898dc14 fix bug in WebPImport565: alpha value was not set 1c8f358d Fix CMake with WASM. a0215fb7 webp_js: fix webp_js demo html 882784b0 update ChangeLog (tag: v1.0.0-rc2) 2f930e08 Revert "Use proper targets for CMake." 8165e8fb Use proper targets for CMake. 3f157dd5 Remove some very hard TODOs. abb47760 Merge "Use proper targets for CMake." cd758a17 {de,}mux/Makefile.am: add missing headers e155dda0 Use proper targets for CMake. b892b8ba makefile.unix,dist: use ascii for text output 64a57d05 add -version option to anim_dump,anim_diff and img2webp 994be82d Merge "Remove some very hard TODOs." 4033e1d7 Remove some very hard TODOs. fc1b8e3a webp_js: fix webp_js demo html 15aa48d9 update ChangeLog (tag: v1.0.0-rc1) e607dabc update AUTHORS 38410c08 [CFI] Remove function pointer casts 978eec25 [CFI] Remove function pointer casts c57b2736 bump version to 1.0.0 cba28853 update NEWS c909d531 Merge "remove some deprecation warning on MacOSX" 217443c7 remove some deprecation warning on MacOSX b672bdfa configure: quiet glut deprecation warnings on OS X daa9fcaf configure: use sdl-config if available dd174cae Merge "imagedec: support metadata reading for WebP image decoding" 641cedcc imagedec: support metadata reading for WebP image decoding 065b2ce1 anim_diff: add a couple missing newlines in Help() c4cc1147 Merge "gif2webp: force low duration frames to 100ms" 09333097 gif2webp: force low duration frames to 100ms e03f0ec3 sharp_yuv: use 14b fixed-point precision for gamma b2db361c image_enc,WebPWritePNG: move locals after setjmp 74e82ec6 Merge "WebPPictureDistortion: fix big-endian results order" 645d04ca Merge "cwebp,get_disto: report bpp" 120f58c3 Merge "lossless*sse2: improve non-const 16-bit vector creation" a7fe9412 WebPPictureDistortion: fix big-endian results order e26fe066 cwebp,get_disto: report bpp 9df64e28 Merge changes Id5b4a1a4,Ia20ce844 8043504f lossless*sse2: improve non-const 16-bit vector creation 1e3dfc48 Import: extract condition from loop 3b07d327 Import,RGBA: fix for BigEndian import 551948e4 Remove unused argument in VP8LBitsEntropy. 3005237a ReadWebP: fix for big-endian 499c395a Merge "anim_diff: expose the -max_diff option" f69dcd69 Merge "remove WEBP_EXPERIMENTAL_FEATURES" 07d884d5 anim_diff: expose the -max_diff option f4dd9256 remove WEBP_EXPERIMENTAL_FEATURES 94a8377b extract the command-line parsing helpers to example_util fc09e6e2 PNM decoder: prevent unsupported depth=2 PAM case. 6de58603 MIPS64: Fix defined-but-not-used errors with WEBP_REDUCE_CSP cbde5728 gif2webp: add support for reading from stdin cf1c5054 Add an SSE4 version of some lossless color transforms. 45a8b5eb Fix lint error with man page. cff38e8f Merge "PNG decoder: handle gAMA chunk" 59cb1a48 Merge "enable dc error-diffusion always" 78318b30 PNG decoder: handle gAMA chunk 664c21dd Merge "remove some TODOs" 815652de enable dc error-diffusion always aec45cec remove some TODOs 5715dfce fix block-count[] increment in case of large image c2d04f3e enable DC error-diffusion always for multi-pass 96bf07c5 use DC error diffusion for U/V at low-quality 1c59020b fix missing sse41 targets in makefile.unix 7a8e814b cosmetics: s/color_space/colorspace/ 05f6fe24 upsampling: rm asserts w/REDUCE_CSP+OMIT_C_CODE b4cf5597 Merge "Upsampling SSE2/SSE4 speedup." ccbeb32c Makefile.vc: add missing sse41 files 55403a9a Upsampling SSE2/SSE4 speedup. 807b53c4 Implement the upsampling/yuv functions in SSE41 84101a81 Fix wasm WebP compilation 8bebd2a3 fix warning on MSVC a7f93fe3 webpmux: allow reading argument from a file b69f18a7 gif2webp.1: fix -loop_compatibility layout 72d530c0 Merge "fix lossless decoding w/WEBP_REDUCE_SIZE" 296c7dc4 fix lossless decoding w/WEBP_REDUCE_SIZE 0d5d029c Merge "ImgIoUtilReadFile: fix file leak upon error" ae568ce7 ImgIoUtilReadFile: fix file leak upon error 796b5a8a Merge tag 'v0.6.1' 6b7a95fd update ChangeLog (tag: v0.6.1) f66955de WEBP_REDUCE_CSP: restrict colorspace support 1af0df76 Merge "WEBP_REDUCE_CSP: restrict colorspace support" 6de20df0 WEBP_REDUCE_CSP: restrict colorspace support a289d8e7 update ChangeLog (tag: v0.6.1-rc2) c10a493c vwebp: disable double buffering on windows & mac 0d4466c2 webp_to_sdl.c: fix file mode 1b27bf8b WEBP_REDUCE_SIZE: disable all rescaler code 126be109 webpinfo: add -version option 0df22b9e WEBP_REDUCE_SIZE: disable all rescaler code 9add62b5 bump version to 0.6.1 d3e26144 update NEWS 2edda639 README: add webpinfo section 9ca568ef Merge "right-size some tables" 31f1995c Merge "SSE2 implementation of HasAlphaXXX" a80c46bd SSE2 implementation of HasAlphaXXX 083507f2 right-size some tables 2e5785b2 anim_utils.c: remove warning when !defined(WEBP_HAVE_GIF) b299c47e add WEBP_REDUCE_SIZE f593d71a enc: disable pic->stats/extra_info w/WEBP_DISABLE_STATS 541179a9 Merge "predictor_enc: fix build w/--disable-near-lossless" 5755a7ec predictor_enc: fix build w/--disable-near-lossless eab5bab7 add WEBP_DISABLE_STATS 8052c585 remove some petty TODOs from vwebp. c245343d move LOAD8x4 and STORE8x2 closer to their use location b9e734fd dec,cosmetics: normalize function naming style c188d546 dec: harmonize function suffixes 28c5ac81 dec_sse41: harmonize function suffixes e65b72a3 Merge "introduce WebPHasAlpha8b and WebPHasAlpha32b" b94cee98 dec_sse2: remove HE8uv_SSE2 44a0ee3f introduce WebPHasAlpha8b and WebPHasAlpha32b aebf59ac Merge "WebPPictureAllocARGB: align argb allocation" c184665e WebPPictureAllocARGB: align argb allocation 3daf7509 WebPParseHeaders: remove obsolete animation TODO 80285d97 cmake: avoid security warnings under msvc 650eac55 cmake: don't set -Wall with MSVC c462cd00 Remove useless code. 01a98217 Merge "remove WebPWorkerImpl declaration from the header" 3c49fc47 Merge "thread_utils: fix potentially bad call to Execute" fde2782e thread_utils: fix potentially bad call to Execute 2a270c1d remove WebPWorkerImpl declaration from the header f1f437cc remove mention of 'lossy-only parameters' from the doc 3879074d Merge "WebPMemToUint32: remove ptr cast to int" 04b029d2 WebPMemToUint32: remove ptr cast to int b7971d0e dsp: avoid defining _C functions w/NEON builds 6ba98764 webpdec: correct alloc size check w/use_argb 5cfb3b0f normalize include guards f433205e Merge changes Ia17c7dfc,I75423abb,Ia2f716b4,I161caa14,I4210081a, ... 8d033b14 {dec,enc}_neon: harmonize function suffixes x2 0295e981 upsampling_neon: harmonize function suffixes d572c4e5 yuv_neon: harmonize function suffixes ab9c2500 rescaler_neon: harmonize function suffixes 93e0ce27 lossless_neon: harmonize function suffixes 22fbc50e lossless_enc_neon: harmonize function suffixes 447875b4 filters_neon,cosmetics: fix indent e51bdd43 remove unused VP8TokenToStats() function 785da7ea enc_neon: harmonize function suffixes bc1a251f dec_neon: harmonize function suffixes 61e535f1 dsp/lossless: workaround gcc-4.8 bug on arm 68b2eab7 cwebp: fix alpha reporting w/lossless & metadata 30042faa WebPDemuxGetI: add doc details around WebPFormatFeature 0a17f471 Merge "WIP: list includes as descendants of the project dir" a4399721 WIP: list includes as descendants of the project dir 08275708 Merge "Make sure we reach the full range for alpha blending." d361a6a7 yuv_sse2: harmonize function suffixes 6921aa6f upsampling_sse2: harmonize function suffixes 08c67d3e ssim_sse2: harmonize function suffixes 582a1b57 rescaler_sse2: harmonize function suffixes 2c1b18ba lossless_sse2: harmonize function suffixes 0ac46e81 lossless_enc_sse2: harmonize function suffixes bc634d57 enc_sse2: harmonize function suffixes bcb7347c dec_sse2: harmonize function suffixes e14ad93c Make sure we reach the full range for alpha blending. 7038ca8d demux,StoreFrame: restore hdr size check to min req fb3daad6 cpu: fix ssse3 check be590e06 Merge "Fix CMake redefinition for HAVE_CPU_FEATURES_H" 35f736e1 Fix CMake redefinition for HAVE_CPU_FEATURES_H a5216efc Fix integer overflow warning. a9c8916b decode.h,WebPIDecGetRGB: clarify output ptr validity 3c74c645 gif2webp: handle 1-frame case properly + fix anim_diff c7f295d3 Merge "gif2webp: introduce -loop_compatibility option" b4e04677 gif2webp: introduce -loop_compatibility option f78da3de add LOCAL_CLANG_PREREQ and avoid WORK_AROUND_GCC w/3.8+ 01c426f1 define WEBP_USE_INTRINSICS w/gcc-4.9+ 8635973d use sdl-config (if available) to determine the link flags e9459382 use CPPFLAGS before CFLAGS 4a9d788e Merge "Android.mk,mips: fix clang build with r15" 4fbdc9fb Android.mk,mips: fix clang build with r15 a80fcc4a ifdef code not used by Chrome/Android. 3993af12 Fix signed integer overflows. f66f94ef anim_dump: small tool to dump frames from animated WebP 6eba857b Merge "rationalize the Makefile.am" c5e34fba function definition cleanup 3822762a rationalize the Makefile.am 501ef6e4 configure style fix: animdiff -> anim_diff f8bdc268 Merge "protect against NULL dump_folder[] value in ReadAnimatedImage()" 23bfc652 protect against NULL dump_folder[] value in ReadAnimatedImage() 8dc3d71b cosmetics,ReadAnimatedWebP: correct function comment 5bd40066 Merge changes I66a64a0a,I4d2e520f 7945575c cosmetics,webpinfo: remove an else after a return 8729fa11 cosmetics,cwebp: remove an else after a return f324b7f9 cosmetics: normalize fn proto & decl param names 869eb369 CMake cleanups. 289e62a3 Remove declaration of unimplemented VP8ApplyNearLosslessPredict 20a94186 pnmdec,PAM: validate depth before calculating bytes_per_px 34130afe anim_encode: fix integer overflow 42c79aa6 Merge "Encoder: harmonize function suffixes" b09307dc Encoder: harmonize function suffixes bed0456d Merge "SSIM: harmonize the function suffix" 54f6a3cf lossless_sse2.c: fix some missed suffix changes 088f1dcc SSIM: harmonize the function suffix 86fc4dd9 webpdec: use ImgIoUtilCheckSizeArgumentsOverflow 08ea9ecd imageio: add ability restrict max image size 6f9daa4a jpegdec,ReadError: fix leaks on error a0f72a4f VP8LTransformColorFunc: drop an non-respected 'const' from the signature. 8c934902 Merge "Lossess dec: harmonize the function suffixes" 622242aa Lossess dec: harmonize the function suffixes 1411f027 Lossless Enc: harmonize the function suffixes 24ad2e3c add const to two variables 46efe062 Merge "Allow the lossless cruncher to work for alpha." 8c3f9a47 Speed-up LZ77. 1aef4c71 Allow the lossless cruncher to work for alpha. b8821dbd Improve the box LZ77 speed. 7beed280 add missing ()s to macro parameters 6473d20b Merge "fix Android standalone toolchain build" dcefed95 Merge "build.gradle: fix arm64 build" 0c83a8bc Merge "yuv: harmonize suffix naming" c6d1db4b fix Android standalone toolchain build 663a6d9d unify the ALTERNATE_CODE flag usage 73ea9f27 yuv: harmonize suffix naming c71b68ac build.gradle: fix arm64 build c4568b47 Rescaler: harmonize the suffix naming 6cb13b05 Merge "alpha_processing: harmonize the naming suffixes to be _C()" 83a3e69a Merge "simplify WEBP_EXTERN macro" 7295fde2 Merge "filters: harmonize the suffixes naming to _SSE2(), _C(), etc." 8e42ba4c simplify WEBP_EXTERN macro 331ab34b cost*.c: harmonize the suffix namings b161f670 filters: harmonize the suffixes naming to _SSE2(), _C(), etc. dec5e4d3 alpha_processing: harmonize the naming suffixes to be _C() 6878d427 fix memory leak in SDL_Init() 461ae555 Merge "configure: fix warnings in sdl check" 62486a22 configure: test for -Wundef 92982609 dsp.h: fix -Wundef w/__mips_dsp_rev 0265cede configure: fix warnings in sdl check 88c73d8a backward_references_enc.h: fix WINDOW_SIZE_BITS check 4ea49f6b rescaler_sse2.c: fix WEBP_RESCALER_FIX -> _RFIX typo 1b526638 Clean-up some CMake 87f57a4b Merge "cmake: fix gif lib detection when cross compiling" b34a9db1 cosmetics,dec_sse2: remove some redundant comments 471c5755 cmake: fix gif lib detection when cross compiling c793417a cmake: disable gif2webp if gif lib isn't found dcbc1c88 cmake: split gif detection from IMG deps 66ad84f0 Merge "muxread: remove unreachable code" 50ec3ab7 muxread: remove unreachable code 7d67a164 Lossy encoding: smoothen transparent areas to improve compression e50650c7 Merge "fix signature for DISABLE_TOKEN_BUFFER compilation" 671d2567 fix signature for DISABLE_TOKEN_BUFFER compilation d6755580 cpu.cmake: use unique flag to test simd disable flags 28914528 Merge "Remove the argb* files." 8acb4942 Remove the argb* files. 3b62347b README: correct cmake invocation note 7ca0df13 Have the SSE2 version of PackARGB use common code. 7b250459 Merge "Re-use the transformed image when trying several LZ77 in lossless." e132072f Re-use the transformed image when trying several LZ77 in lossless. 5d7a50ef Get code to compile in C++. 7b012987 configure: test for -Wparentheses-equality f0569adb Fix man pages for multi-threading. f1d5a397 multithread cruncher: only copy stats when picture->stats != NULL f8c2ac15 Multi-thread the lossless cruncher. a88c6522 Merge "Integrate a new LZ77 looking for matches in the neighborhood of a pixel only." 8f6df1d0 Unroll Predictors 10, 11 and 12. 355c3d1b Integrate a new LZ77 looking for matches in the neighborhood of a pixel only. a1779a01 Refactor LZ77 handling in preparation for a new method. 67de68b5 Android.mk/build.gradle: fix mips build with clang from r14b f209a548 Use the plane code and not the distance when computing statistics. b903b80c Split cost-based backward references in its own file. 498cad34 Cosmetic changes in backward reference. e4eb4587 lossless, VP8LTransformColor_C: make sure no overflow happens with colors. af6deaff webpinfo: handle alpha flag mismatch 7caef29b Fix typo that creeped in. 39e19f92 Merge "near lossless: fix unsigned int overflow warnings." 9bbc0891 near lossless: fix unsigned int overflow warnings. e1118d62 Merge "cosmetics,FindClosestDiscretized: use uint in mask creation" 186bc9b7 Merge "webpinfo: tolerate ALPH+VP8L" b5887297 cosmetics,FindClosestDiscretized: use uint in mask creation f1784aee near_lossless,FindClosestDiscretized: use unsigned ops 0d20abb3 webpinfo: tolerate ALPH+VP8L 972104b3 webpmux: tolerate false positive Alpha flag dd7e83cc tiffdec,ReadTIFF: ensure data_size is < tsize_t max d988eb7b tiffdec,MyRead: quiet -Wshorten-64-to-32 warning dabda707 webpinfo: add support to parse Alpha bitstream 4c117643 webpinfo: correct background color output, BGRA->ARGB defc98d7 Doc: clarify the role of quality in WebPConfig. d78ff780 Merge "Fix code to compile with C++." c8f14093 Fix code to compile with C++. 497dc6a7 pnmdec: sanitize invalid header output d78e5867 Merge "configure: test for -Wconstant-conversion" 481e91eb Merge "pnmdec,PAM: set bytes_per_px based on depth when missing" 93b12753 configure: test for -Wconstant-conversion 645f0c53 pnmdec,PAM: set bytes_per_px based on depth when missing e9154605 Merge "vwebp: activate GLUT double-buffering" 818d795b vwebp: activate GLUT double-buffering d63e6f4b Add a man page for webpinfo 4d708435 Merge "NEON: implement ConvertRGB24ToY/BGR24/ARGB/RGBA32ToUV/ARGBToUV" faf42213 NEON: implement ConvertRGB24ToY/BGR24/ARGB/RGBA32ToUV/ARGBToUV b4d576fa Install man pages with CMake. cbc1b921 webpinfo: add features to parse bitstream header e644c556 Fix bad bit writer initialization. b62cdad2 Merge "Implement a cruncher for lossless at method 6." da3e4dfb use the exact constant for the gamma transfer function a9c701e0 Merge "tiffdec: fix EXTRASAMPLES check" adab8ce0 Implement a cruncher for lossless at method 6. 1b92b237 Merge "Fix VP8ApplyNearLossless to respect const and stride." 1923ff02 tiffdec: fix EXTRASAMPLES check 97cce5ba tiffdec: only request EXTRASAMPLES w/> 3 samples/px 0dcd85b6 Fix VP8ApplyNearLossless to respect const and stride. f7682189 yuv: rationalize the C/SSE2 function naming 52245424 NEON implementation of some Sharp-YUV420 functions 690efd82 Avoid several backward reference copies. 4bb1f607 src/dec/vp8_dec.h, cosmetics: fix comments 285748be cmake: build/install webpinfo 78fd199c backward_references_enc.c: clear -Wshadow warnings ae836410 WebPLog2FloorC: clear -Wshadow warning d0b7404e Merge "WASM support" 134e314f WASM support c08adb6f Merge "VP8LEnc: remove use of BitsLog2Ceiling()" 28c37ebd VP8LEnc: remove use of BitsLog2Ceiling() 2cb58ab2 webpinfo: output format as a human readable string bb175a93 Merge "rename some symbols clashing with MSVC headers" 39eda658 Remove a duplicated pixel hash implementation. 36b8274d rename some symbols clashing with MSVC headers 274daf54 Add webpinfo tool. ec5036e4 add explicit reference to /usr/local/{lib,inc} 18f0dfac Merge "fix TIFF encoder regarding rgbA/RGBA" 4e2b0b50 Merge "webpdec.h: fix a doc typo" e2eeabff Merge "Install binaries, libraries and headers in CMake." 836607e6 webpdec.h: fix a doc typo 9273e441 fix TIFF encoder regarding rgbA/RGBA 17e3c11f Add limited PAM decoding support 5f624871 Install binaries, libraries and headers in CMake. 976adac1 Merge "lossless incremental decoding: fix missing eos_ test" f8fad4fa lossless incremental decoding: fix missing eos_ test 27415d41 Merge "vwebp_sdl: fix the makefile.unix" 49566182 Merge "ImgIoUtilWriteFile(): use ImgIoUtilSetBinaryMode" 6f75a51b Analyze the transform entropy on the whole image. a5e4e3af Use palette only if we can in entropy analysis. 75a9c3c4 Improve compression by better entropy analysis. 39cf6f4f vwebp_sdl: fix the makefile.unix 699b0416 ImgIoUtilWriteFile(): use ImgIoUtilSetBinaryMode 7d985bd1 Fix small entropy analysis bug. 6e7caf06 Optimize the color cache size. 833c9219 More efficient stochastic histogram merge. 5183326b Refactor the greedy histogram merge. 99f6f462 Merge "histogram_enc.c,MyRand: s/ul/u/ for unsigned constants" 80a22186 ssim.c: remove dead include a128dfff histogram_enc.c,MyRand: s/ul/u/ for unsigned constants 693bf74e move the SSIM calculation code in ssim.c / ssim_sse2.c 10d791ca Merge "Fix the random generator in HistogramCombineStochastic." fa63a966 Fix the random generator in HistogramCombineStochastic. 16be192f VP8LSetBitPos: remove the eos_ setting 027151ca don't erase the surface before blitting. 4105d565 disable WEBP_USE_XXX optimisations when EMSCRIPTEN is defined 9ee32a75 Merge "WebP-JS: emscripten-based Javascript decoder" ca9f7b7d WebP-JS: emscripten-based Javascript decoder 868aa690 Perform greedy histogram merge in a unified way. 5b393f2d Merge "fix path typo for vwebp_sdl in Makefile.vc" e0012bea CMake: only use libwebpdecoder for building dwebp 84c2a7b0 fix path typo for vwebp_sdl in Makefile.vc 1b0e4abf Merge "Add a flag to disable SIMD optimizations." 32263250 Add a flag to disable SIMD optimizations. b494fdec optimize the ARGB->ARGB Import to use memcpy f1536039 Merge "ReadWebP: decode directly into a pre-allocated buffer" e69ed291 ReadWebP: decode directly into a pre-allocated buffer 57d8de8a Merge "vwebp_sdl: simple viewer based on SDL" 5cfd4ebc LZ77 interval speedups. Faster, smaller, simpler. 1e7ad88b PNM header decoder: add some basic numerical validation 17c7890c Merge "Add a decoder only library for WebP in CMake." be733786 Merge "Add clang build fix for MSA" 03cda0e4 Add a decoder only library for WebP in CMake. aa893914 Add clang build fix for MSA 31a92e97 Merge "imageio: add limited PNM support for reading" dcf9d82a imageio: add limited PNM support for reading 6524fcd6 vwebp_sdl: simple viewer based on SDL 6cf24a24 get_disto: fix reference file read 43d472aa Merge tag 'v0.6.0' 50d1a848 update ChangeLog (tag: v0.6.0, origin/0.6.0) 20a7fea0 extras/Makefile.am: fix libwebpextras.la reference 415f3ffe update ChangeLog (tag: v0.6.0-rc3) 3c6d1224 update NEWS ee4a4141 update AUTHORS 32ed856f Fix "all|no frames are keyframes" settings. 1c3190b6 Merge "Fix "all|no frames are keyframes" settings." f4dc56fd disable GradientUnfilter_NEON 4f3e3bbd disable GradientUnfilter_NEON 2dc0bdca Fix "all|no frames are keyframes" settings. 0d8e0588 img2webp: treat -loop as a no-op w/single images b0450139 ReadImage(): restore size reporting 0ad3b4ef update ChangeLog (tag: v0.6.0-rc2) 6451709e img2webp,get_disto: fix image decode w/WIC builds 92504d21 get_disto: make ReadPicture() return a bool c3e4b3a9 update NEWS 3363eb6d man/img2webp.1: fix formatting warning 4d1312f2 update NEWS 36c42ea4 bump version to 0.6.0 bb498a51 update AUTHORS 84cef16f Makefile.vc: fix CFG=debug-dynamic build 919f9e2f Merge "add .rc files for windows dll versioning" f1ae8af4 Merge ".gitignore: add img2webp" 4689ce16 cwebp: add a -sharp_yuv option for 'sharp' RGB->YUV conversion 79bf46f1 rename the pretentious SmartYUV into SharpYUV eb1dc89a silently expose use_delta_palette in the WebPConfig API c85b0dde .gitignore: add img2webp 43d3f01a add .rc files for windows dll versioning 668e1dd4 src/{dec,enc,utils}: give filenames a unique suffix 0e6b7f33 Merge "iosbuild.sh: only add required headers to framework" 29ed6f9a iosbuild.sh: only add required headers to framework 71c53f1a NEON: speed-up strong filtering 73f567ea Merge "get_disto: remove redundant reader check" 9e14276f Merge "makefiles: prune get_disto & webp_quality deps" 99965bac Merge "Makefile.vc: add get_disto.exe, webp_quality.exe" d4912238 get_disto: remove redundant reader check ea482409 makefiles: prune get_disto & webp_quality deps 2ede5a19 Makefile.vc: add get_disto.exe, webp_quality.exe a345068a ARM: speed up bitreader by avoiding tables 1dc82a6b Merge "introduce a generic GetCoeffs() function pointer" 8074b89e introduce a generic GetCoeffs() function pointer 749a45a5 Merge "NEON: implement alpha-filters (horizontal/vertical/gradient)" 74c053b5 Merge "NEON: fix overflow in SSE NxN calculation" 0a3aeff7 Merge "dsp: WebPExtractGreen function for alpha decompression" 1de931c6 NEON: implement alpha-filters (horizontal/vertical/gradient) 9b3aca40 NEON: fix overflow in SSE NxN calculation 1c07a3c6 dsp: WebPExtractGreen function for alpha decompression 9ed5e3e5 use pointers for WebPRescaler's in WebPDecParams db013a8d Merge "ARM: don't use USE_GENERIC_TREE" fcd4784d use a 8b table for C-version for clz() fbb5c473 ARM: don't use USE_GENERIC_TREE 8fda5612 Merge "add a kSlowSSSE3 feature for CPUInfo" 86bbd245 add a kSlowSSSE3 feature for CPUInfo 7c2779e9 Get code to fully compile in C++. 250c3586 Merge "When compiling as C++, avoid narrowing warnings." c0648ac2 When compiling as C++, avoid narrowing warnings. 0d55f60c 40% faster ApplyAlphaMultiply_SSE2 49d0280d NEON: implement several alpha-processing functions 48b1e85f SSE2: 15% faster alpha-processing functions e3b8abbc fix warning from static analysis. 28fe054e SSE2: 30% faster ApplyAlphaMultiply() f44acd25 Merge "Properly compute the optimal color cache size." 527844fe Properly compute the optimal color cache size. be0ef639 fix a comment typo 8874b162 Fix a non-deterministic color cache size computation. d712e20d Do not allow a color cache size bigger than the number of colors. ecff04f6 re-introduce some comments in Huffman Cost. 259e9828 replace 'ptr + y * stride' by 'ptr += stride' 00b08c88 Merge "NEON: 5% faster conversion to RGB565 and RGBA4444" 0e7f4447 Merge "NEON: faster fancy upsampling" b016cb91 NEON: faster fancy upsampling 1cb63801 Call the C function to finish off lossless SSE loops only when necessary. 875fafc1 Implement BundleColorMap in SSE2. 3674d49e Merge "remove Clang warnings with unused arch arguments." f04eb376 Merge tag 'v0.5.2' 341d711c NEON: 5% faster conversion to RGB565 and RGBA4444 abb54827 remove Clang warnings with unused arch arguments. ece9684f update ChangeLog (tag: v0.5.2-rc2, tag: v0.5.2) aa7744ca anim_util: quiet implicit conv warnings in 32-bit d9120271 jpegdec: correct ContextFill signature 24eb3940 Remove some errors when compiling the code as C++. a4a8e5f3 vwebp: clear canvas during resize w/o animation 67c25ad5 vwebp: clear canvas during resize w/o animation a4bbe4b3 fix indentation 31ca2a80 tiffdec: restore libtiff 3.9.x compatibility b2f77b57 update NEWS 5ab6d9de AnimEncoder: avoid freeing uninitialized memory pointer. f29bf582 WebPAnimEncoder: If 'minimize_size' and 'allow_mixed' on, try lossy + lossless. 3ebe1c00 AnimEncoder: avoid freeing uninitialized memory pointer. df780e0e fix a potential overflow with MALLOC_LIMIT 58fc5078 Merge "PredictorSub: implement fully-SSE2 version" 9cc42167 PredictorSub: implement fully-SSE2 version 0aa1f35c remove dependency of imageio/ to stopwatch.h cb9ec84b Merge "remove the dependency to stop_watch.[ch] in imageio" dc0c01fb Merge "anim_util: quiet implicit conv warnings in 32-bit" 827d3c50 Merge "fix a potential overflow with MALLOC_LIMIT" 1e2e25b0 anim_util: quiet implicit conv warnings in 32-bit 218460cd bump version to 0.5.2 de7d654d update AUTHORS & .mailmap 273367c1 Merge "dsp/lossless.c,cosmetics: fix indent" 76bbcf2e fix a potential overflow with MALLOC_LIMIT 8ac1abfe Merge "jpegdec: correct ContextFill signature" cb215aed remove the dependency to stop_watch.[ch] in imageio 2423017a dsp/lossless.c,cosmetics: fix indent 74a12b10 iosbuild.sh: add WebPDecoder.framework + encoder a9cc7621 Merge "iosbuild.sh: add WebPDecoder.framework + encoder" fbba5bc2 optimize predictor #1 in plain-C For some reason, gcc has hard time inlining this one... 9ae0b3f6 Merge "SSE2: slightly (~2%) faster Predictor #1" c1f97bd7 SSE2: slightly (~2%) faster Predictor #1 ea664b89 SSE2: 10% faster Predictor #11 be7dcc08 AnimEncoder: Correctly skip a frame when sub-rectangle is empty. 40885830 Fix assertions in WebPRescalerExportRow() 1d5046d1 iosbuild.sh: add WebPDecoder.framework + encoder cec72014 jpegdec: correct ContextFill signature 8f38c72e fix a typo in WebPPictureYUVAToARGB's doc 33ca93f9 systematically call WebPDemuxReleaseIterator() on dec->prev_iter_ 76e19073 doc: use two's complement explicitly for uint8->int8 conversion f91ba963 Anim_encoder: correctly handle enc->prev_candidate_undecided_ 25d74e65 WebPPictureDistortion(): free() -> WebPSafeFree() 03f1c008 mux/Makefile.am: add missing -lm 58410cd6 fix bug in RefineUsingDistortion() e168af8c fix filtering auto-adjustment ed9dec41 fix doc and code snippet for WebPINewDecoder() doc 3c49178f prevent 32b overflow for very large canvas_width / height 9595f290 fix anim_util.c compilation when HAVE_GIF is not defined. 7ec9552c Make gif transparent color to be transparent black b3fb8bb6 slightly faster Predictor #11 in NEON 9871335f Add a CMake option for WEBP_SWAP_16BIT_CSP. 0ae32226 Fix missing cpu-features for Android. ab4c8056 cpu.cmake: improve webp_check_compiler_flag output eec5fa3a Provide support for CMake on Android studio 2.2. 004d5690 Split the main CMake file. 4fe5d588 Android.mk: use -fvisibility=hidden bd63a31a vwebp: ensure setenv() is available in stdlib.h 363a5681 vwebp: handle window resizing properly a0d2753f lower WEBP_MAX_ALLOCABLE_MEMORY default 31fe11a5 fix infinite loop in case of PARTITION0 overflow 532215dd Change the rule of picking UV mode in MBAnalyzeBestUVMode() 9c75dbd3 cwebp.1: improve some grammar af2e05cb vwebp: Clear previous frame when a key triggers a redraw 26ffa296 Add descriptions of default configuration in help info. 7416280d Fix an unsigned integer overflow error in enc/cost.h 13cf1d2e Do token recording and counting in a single loop eb9a4b97 Reset segment id if we decide not to update segment map 42ebe3b7 configure: fix NEON flag detection under gcc 6 76ebbfff NEON: implement predictor #13 95b12a08 Merge "Revert Average3 and Average4" 54ab2e75 Revert Average3 and Average4 fe12330c 3-5% faster Predictor #5, #6, #7 and #10 for NEON fbfb3bef ~2% faster predictor #10 for NEON d4b7d801 lossless_sse2: use the local functions a5e3b225 Lossless decoder SSE2 improvements. 58a1f124 ~2% faster predictor #12 in NEON. 906c3b63 Merge "Implement lossless transforms in NEON." d23abe4e Implement lossless transforms in NEON. 2e6cb6f3 Give more flexibility to the predictor generating macro. 28e0bb70 Merge "Fix race condition in multi-threading initialization." 64704530 Fix race condition in multi-threading initialization. bded7848 img2webp: fix default -lossless value and use pic.argb=1 0e61a513 Merge "img2webp: convert a sequence of images to an animated webp" 1cc79e92 AnimEncoder: Correctly skip a frame when sub-rectangle is empty. 03f40955 img2webp: convert a sequence of images to an animated webp ea72cd60 add missing 'extern' keyword for predictor dcl 67879e6d SSE implementation of decoding predictors. 34aee990 Merge "vwebp: make 'd' key toggle the debugging of fragments" a41296ae Fix potentially uninitialized value. c85adb33 vwebp: make 'd' key toggle the debugging of fragments 4239a148 Make the lossless predictors work on a batch of pixels. bc18ebad fix extra 'const's in signatures 71e2f5ca Remove memcpy in lossless decoding. 7474d46e Do not use a register array in SSE. 67748b41 Improve latency of FTransform2. 16951b19 Merge "Provide an SSE implementation of ConvertBGRAToRGB" 6540cd0e Provide an SSE implementation of ConvertBGRAToRGB de568abf Android.mk: use -fvisibility=hidden 3c2a61b0 remove some unneeded casts 9ac063c3 add dsp functions for SmartYUV 22efabdd Merge "smart_yuv: switch to planar instead of packed r/g/b processing" 1d6e7bf3 smart_yuv: switch to planar instead of packed r/g/b processing 0a3838ca fix bug in RefineUsingDistortion() c0699515 webpmux -duration: set default 'end' value equal to 'start' 83cbfa09 Import: use relative pointer offsets a1ade40e PreprocessARGB: use relative pointer offsets fd4d090f ConvertWRGBToYUV: use relative pointer offsets 9daad459 ImportYUVAFromRGBA: use relative pointer offsets f90c60d1 Merge "add a "-duration duration,start,end" option to webpmux" 3f182d36 add a "-duration duration,start,end" option to webpmux 342e15f0 Import: use relative pointer offsets 1147ab4e PreprocessARGB: use relative pointer offsets e4cd4daf fix filtering auto-adjustment e7152856 fix doc and code snippet for WebPINewDecoder() doc de9fa507 ConvertWRGBToYUV: use relative pointer offsets deb1b831 ImportYUVAFromRGBA: use relative pointer offsets c284780f imageio_util: add ImgIoUtilCheckSizeArgumentsOverflow e375080d gifdec,Remap: avoid out of bounds colormap read c222a053 additional fix for stride type as size_t bb233617 fix potential overflow when width * height * 4 >= (1<<32) 883d41fb gif2webp: fix crash with NULL extension data cac9a36a gifdec,Remap: avoid out of bounds colormap read 4595e01f Revert "gifdec,Remap: avoid out of bounds colormap read" fb52d443 gifdec: make some constants unsigned f048d38d gifdec,Remap: avoid out of bounds colormap read 31b1e343 fix SSIM metric ... by ignoring too-dark area 2f51b614 introduce WebPPlaneDistortion to compute plane distortion 0104d730 configure: fix NEON flag detection under gcc 6 265abbe9 Merge "additional fix for stride type as size_t" f7601aa6 Merge "Introduce a generic WebPGetImageReader(type) function" ce873320 Introduce a generic WebPGetImageReader(type) function 2a2773ea imageio/*dec,Read*: add input parameter checks 9f5c8eca additional fix for stride type as size_t 4eb5df28 remove unused stride fields from VP8Iterator 11bc423a MIN_LENGTH cleanups. 273d035a Merge "fix a typo in WebPPictureYUVAToARGB's doc" 4db82a17 Merge "fix potential overflow when width * height * 4 >= (1<<32)" e2affacc fix potential overflow when width * height * 4 >= (1<<32) dc789ada fix a typo in WebPPictureYUVAToARGB's doc 539f5a68 Fix non-included header in config.c. aaf2a6a6 systematically call WebPDemuxReleaseIterator() on dec->prev_iter_ 20ef9915 Merge "imageio_util: add ImgIoUtilCheckSizeArgumentsOverflow" bc86b7a8 imageio_util: add ImgIoUtilCheckSizeArgumentsOverflow 806f6279 gif2webp: fix crash with NULL extension data 68ae5b67 Add libwebp/src/mux/animi.h 28ce3043 Remove some errors when compiling the code as C++. b34abcb8 Favor keeping the areas locally similar in spatial prediction mode selection ba843a92 fix some SSIM calculations 51b71fd2 Merge "vwebp: ensure setenv() is available in stdlib.h" fb01743a get_disto: fix the r/g/b order for luma calculation bfab8947 vwebp: ensure setenv() is available in stdlib.h 9310d192 vwebp: handle window resizing properly f79450ca Speedup ApplyMap. cfdda7c6 Merge "prevent 32b overflow for very large canvas_width / height" e36396ba Merge "get_disto: new option to compute SSIM map and convert to gray" 18a9a0ab Add an API to import a color-mapped image. 30d43706 Speed-up Combined entropy for palettized histograms. 36aa087b get_disto: new option to compute SSIM map and convert to gray 86a84b35 2x faster SSE2 implementation of SSIMGet b8384b53 lower WEBP_MAX_ALLOCABLE_MEMORY default 1c364400 prevent 32b overflow for very large canvas_width / height eee0cce1 Merge "Small LZ77 speedups." 5f1caf29 Small LZ77 speedups. 1effde7b fix anim_util.c compilation when HAVE_GIF is not defined. a2fe9bf4 Speedup TrellisQuantizeBlock(). 573cce27 smartYUV improvements 21e7537a fix infinite loop in case of PARTITION0 overflow 053a1565 Merge "Change the rule of picking UV mode in MBAnalyzeBestUVMode()" 1377ac2e Change the rule of picking UV mode in MBAnalyzeBestUVMode() 7c1fb7d0 fix uint32_t initialization (0. -> 0) bfff0bf3 speed-up SSIM calculation 64577de8 De-VP8L-ize GetEntropUnrefinedHelper. a7be7328 Merge "refactor the PSNR / SSIM calculation code" 50c3d7da refactor the PSNR / SSIM calculation code d6228aed indentation fix after I7055d3ee3bd7ed5e78e94ae82cb858fa7db3ddc0 dd538b19 Remove unused declaration. 6cc48b17 Move some lossless logic out of dsp. 78363e9e Merge "Remove a redundant call to InitLeft() in VP8IteratorReset()" ffd01929 Refactor VP8IteratorNext(). c4f6d9c9 Remove a redundant call to InitLeft() in VP8IteratorReset() c27d8210 Merge "smartYUV: simplify main loop" 07795296 smartYUV: simplify main loop c9b45863 Split off common lossless dsp inline functions. 490ae5b1 smartYUV: improve initial state for faster convergence 894232be smartYUV: fix and simplify the over-zealous stop criterion 8de08483 Remove unused code in webpi.h 41cab7fe imageio/Android.mk: correct imagedec dependencies 82c91c70 Merge "libimageenc.a: extract image-saving code from dwebp" af1ad3e2 libimageenc.a: extract image-saving code from dwebp dd7309e3 Merge "doc: use two's complement explicitly for uint8->int8 conversion" 6105777e Merge "add gif2webp to CMake" 13ae011e doc: use two's complement explicitly for uint8->int8 conversion 4bda0cfb add gif2webp to CMake 6029c7fe Merge "remove mention of fragment, frgm, FRGM, etc." 545c147f remove mention of fragment, frgm, FRGM, etc. 5b46f7fc cwebp.1: improve some grammar 9e478f80 dec/vp8l.c: add assertions in EmitRescaledRowsRGBA/YUVA 43bd8958 Make gif transparent color to be transparent black 0887fc2d Merge "get_disto: add a '-o file' option to save a diff map" 0de48e18 get_disto: add a '-o file' option to save a diff map 0a57ad0d cosmetics: WebPSafeAlloc -> WebPSafeMalloc 0a4699bc Merge "WebPPictureDistortion(): free() -> WebPSafeFree()" 29fedbf5 Anim_encoder: correctly handle enc->prev_candidate_undecided_ 32dead4e WebPPictureDistortion(): free() -> WebPSafeFree() 85cd5d06 Smarter LZ77 for uniform regions. 6585075f Change PixelsAreSimilar() to handle black pixels correctly. c0a27fd2 vwebp: Clear previous frame when a key triggers a redraw 57a5e3b6 webp_quality should return '0' in case of success. 7f1b897b Faster stochastic histogram merging. 48c810b8 Merge "remove WEBP_FORCE_ALIGNED and use memcpy() instead." 3884972e remove WEBP_FORCE_ALIGNED and use memcpy() instead. 485cac1a switch libimagedec.a and libimageio_util.a to avoid undefined symbol 005e15b1 Merge "{extras,mux}/Makefile.am: add missing -lm" 6ab496ed fix some 'unsigned integer overflow' warnings in ubsan 8a4ebc6a Revert "fix 'unsigned integer overflow' warnings in ubsan" 9d4f209f Merge changes I25711dd5,I43188fab e44f5248 fix 'unsigned integer overflow' warnings in ubsan 27b5d991 Fix assertions in WebPRescalerExportRow() 74f6f9e7 Add descriptions of default configuration in help info. aaf2530c {extras,mux}/Makefile.am: add missing -lm 1269dc7c Refactor VP8LColorCacheContains() 40872fb2 dec_neon,NeedsHev: micro optimization 7b54e26b Add a CMake option for WEBP_SWAP_16BIT_CSP. d2223d8d Fix missing cpu-features for Android. bf16a4b4 Merge "cpu.cmake: improve webp_check_compiler_flag output" ee1057e3 cpu.cmake: improve webp_check_compiler_flag output b551e587 cosmetics: add {}s on continued control statements d2e4484e dsp/Makefile.am: put msa source in correct lib c7f66c82 Merge "utils/thread.c,cosmetics: join a few lines" 98d8f295 Merge "examples/Makefile.am,cosmetics: sort binary targets" 39f4ffbc utils/thread.c,cosmetics: join a few lines a86ce2b1 Merge "extras/Makefile.am: don't install libwebpextras" 6fa9fe24 extras/Makefile.am: don't install libwebpextras 0b2c58a9 Fix an unsigned integer overflow error in enc/cost.h d7ce4a2e examples/Makefile.am,cosmetics: sort binary targets 386e4ba2 Reset segment id if we decide not to update segment map 7b87e848 Merge "Add MSA optimized YUV to RGB upsampling functions" d3ddacb6 Add MSA optimized YUV to RGB upsampling functions eb98d8d8 webp_quality: detect lossless format and features ebee57f4 move imageio/example_util.[hc] (back to) examples/ 99542bbf webpdec: s/ExUtil// da573cf4 imageio_util: s/ExUtil/ImgIoUtil/ bdda5bd4 split example_util.h 15ed462b .gitignore: add extras/{get_disto,webp_quality} 7be57489 Merge "VP8EstimateQuality(): roughty estimate webp bitstream quality factor" 57020525 Makefile.vc: add missing imageio target e8ab6a82 VP8EstimateQuality(): roughty estimate webp bitstream quality factor fee7b3d6 Merge "'extras/get_disto' example: compute PSNR between two files" 1e7d4401 'extras/get_disto' example: compute PSNR between two files 4cecab63 pngdec.c,jpegdec.[hc]: remove unnecessary includes 259f0434 makefile.unix: normalize image decode lib name ed34c39b fix: examples/libexample_dec.a => imageio/libexample_dec.a 33d8d0d4 Merge "move examples/{example_util,image_dec} to imageio/" c960b82e Merge "extras.h: correct include guard" fe3cd28a Merge ".gitignore: add .gradle, /build" 45fbeba5 Merge "Do token recording and counting in a single loop" 4f33c820 .gitignore: add .gradle, /build c379b55a move examples/{example_util,image_dec} to imageio/ 5108d9aa extras.h: correct include guard ad497fbc move src/extras to the top-level 0c0fb832 Do token recording and counting in a single loop 9ac74f92 Add MSA optimized rescaling functions cb19dbc1 Add MSA optimized color transform functions 3f4042b5 WebPAnimEncoder: If 'minimize_size' and 'allow_mixed' on, try lossy + lossless. 5e2eb89e cosmetics,dsp/*msa.c: associate '*' with the type 5b60db5c FastMBAnalyze() for quick i16/i4 decision 567e6977 Add MSA optimized CollectHistogram function c54ab8dd Add MSA optimized quantization functions ec6f68c5 Merge "Remove QuantizeBlockWHT() in enc.c" 2a5c417c Apply the RLE heuristic to LZ77. 91b59e88 Remove QuantizeBlockWHT() in enc.c fe572737 Add MSA optimized SSE functions 6b53ca87 cosmetics,(dec|enc)_sse2.c: fix indent b15d00d9 Merge "Add MSA optimized encoder IntraChromaPreds function" afe3cec8 Add MSA optimized encoder IntraChromaPreds function fc8cad9f reduce the number of malloc/free cycles in huffman.c 7b4b05e0 Add MSA optimized encoder Intra16Preds function c18787a0 Add MSA optimized encoder Intra4Preds function 479d1908 webpmux: Also print compression info per frame. a80e8cfd Provide support for CMake on Android studio 2.2. 6c628410 Split the main CMake file. bbb6ecd9 Merge "Add MSA optimized distortion functions" 7915396f Add MSA optimized distortion functions 652e944f Merge "build.gradle: remove tab" c0991a14 io,EmitRescaledAlphaYUV: factor out a common expr 48bf5ed1 build.gradle: remove tab bfef6c9f Merge tag 'v0.5.1' 3d97bb75 update ChangeLog (tag: v0.5.1) deb54d91 Clarify the expected 'config' lifespan in WebPIDecode() 435308e0 Add MSA optimized encoder transform functions dce64bfa Add MSA optimized alpha filter functions 429120d0 Add MSA optimized color transform functions c7e2d245 update ChangeLog (tag: v0.5.1-rc5) 55b2fede normalize the macros' "do {...} while (0)" constructs 701c772e Add MSA optimized colorspace conversion functions c7eb06f7 Fix corner case in CostManagerInit. f918cb10 fix rescaling bug: alpha plane wasn't filled with 0xff ab7937a5 gif2webp: normalize the number of .'s in the help message 3cdec847 vwebp: normalize the number of .'s in the help message bdf6241e cwebp: normalize the number of .'s in the help message 06a38c7b fix rescaling bug: alpha plane wasn't filled with 0xff 319e37be Improve lossless compression. 6a197937 Add MSA optimized intra pred chroma functions 447adbce 'our bug tracker' -> 'the bug tracker' 97b9e644 normalize the number of .'s in the help message 293d786f Added MSA optimized intra prediction 16x16 functions 0afa0ce2 Added MSA optimized intra prediction 4x4 functions a6621bac Added MSA optimized simple edge filtering functions bb50bf42 pngdec,ReadFunc: throw an error on invalid read 38063af1 decode.h,WebPGetInfo: normalize function comment 1ebf193c Added MSA optimized chroma edge filtering functions 9ad2352d Merge "Added MSA optimized edge filtering functions" 60751096 Added MSA optimized edge filtering functions 9e8e1b7b Inline GetResidual for speed. 7d58d1b7 Speed-up uniform-region processing. 8ec7032b simplify HistogramCombineEntropyBin() 23e29cb1 Merge "Fix a boundary case in BackwardReferencesHashChainDistanceOnly." into 0.5.1 472a049b remove bin_map[] allocation altogether 0bb23b2c free -> WebPSafeFree() a977b4b5 Merge "rewrite the bin_map clustering to use less memory" 3591ba66 rewrite the bin_map clustering to use less memory e6ac450c utils.[hc]: s/MAX_COLOR_COUNT/MAX_PALETTE_SIZE/ e7b91772 Merge "DecodeImageData(): change the incorrect assert" into 0.5.1 2abfa54f DecodeImageData(): change the incorrect assert 5a48fcd8 Merge "configure: test for -Wfloat-conversion" 0174d18d Fix a boundary case in BackwardReferencesHashChainDistanceOnly. 6a9c262a Merge "Added MSA optimized transform functions" cfbcc5ec Make sure to consider small distances in LZ77. 5e60c42a Added MSA optimized transform functions 3dc28d76 configure: test for -Wfloat-conversion f2a0946a add some asserts to delimit the perimeter of CostManager's operation 9a583c66 fix invalid-write bug for alpha-decoding f66512db make gradlew executable 6fda58f1 backward_references: quiet double->int warning a48cc9d2 Merge "Fix a compression regression for images with long uniform regions." into 0.5.1 cc2720c1 Merge "Revert an LZ77 boundary constant." into 0.5.1 059aab4f Fix a compression regression for images with long uniform regions. b0c7e49e Check more backward matches with higher quality. a3611513 Revert an LZ77 boundary constant. 8190374c README: fix typo 7551db44 update NEWS 0fb2269c bump version to 0.5.1 f4537610 update AUTHORS & .mailmap 3259571e Refactor GetColorPalette method. 1df5e260 avoid using tmp histogram in PreparePair() 7685123a fix comment typos a246b921 Speedup backward references. 76d73f18 Merge "CostManager: introduce a free-list of ~10 intervals" eab39d81 CostManager: introduce a free-list of ~10 intervals 4c59aac0 Merge "mips msa webp configuration" 043c33f1 Merge "Improve speed and compression in backward reference for lossless." 71be9b8c Merge "clarify variable names in HistogramRemap()" 0ba7fd70 Improve speed and compression in backward reference for lossless. 0481d42a CostManager: cache one interval and re-use it when possible 41b7e6b5 Merge "histogram: fix bin calculation" 96c3d624 histogram: fix bin calculation fe9e31ef clarify variable names in HistogramRemap() ce3c8247 disable near-lossless quantization if palette is used e11da081 mips msa webp configuration 5f8f998d mux: Presence of unknown chunks should trigger VP8X chunk output. cadec0b1 Merge "Sync mips32 and dsp_r2 YUV->RGB code with C verison" d9637758 Compute the hash chain once and for all for lossless compression. 50a48665 Sync mips32 and dsp_r2 YUV->RGB code with C verison eee788e2 Merge "introduce a common signature for all image reader function" d77b877c introduce a common signature for all image reader function ca8d9519 remove some obsolete TODOs ae2a7222 collect all decoding utilities from examples/ in libexampledec.a 0b8ae852 Merge "Move DitherCombine8x8 to dsp/dec.c" 77cad885 Merge "ReadWebP: avoid conversion to ARGB if final format is YUVA" ab8d6698 ReadWebP: avoid conversion to ARGB if final format is YUVA f8b7ce9e Merge "test pointer to NULL explicitly" 5df6f214 test pointer to NULL explicitly 77f21c9c Move DitherCombine8x8 to dsp/dec.c c9e6d865 Add gradle support c65f41e8 Revert "Add gradle support" bf731ede Add gradle support 08333b85 WebPAnimEncoder: Detect when canvas is modified, restore only when needed. 0209d7e6 Merge "speed-up MapToPalette() with binary search" fdd29a3d speed-up MapToPalette() with binary search cf4a651b Revert "Refactor GetColorPalette method." 0a27aca3 Merge changes Idfa8ce83,I19adc9c4 f25c4406 WebPAnimEncoder: Restore original canvas between multiple encodes. 169004b1 Refactor GetColorPalette method. 576362ab VP8LDoFillBitWindow: support big-endian in fast path ac49e4e4 bit_reader.c: s/VP8L_USE_UNALIGNED_LOAD/VP8L_USE_FAST_LOAD/ d39ceb58 VP8LDoFillBitWindow: remove stale TODO 2ec2de14 Merge "Speed-up BackwardReferencesHashChainDistanceOnly." 3e023c17 Speed-up BackwardReferencesHashChainDistanceOnly. f2e1efbe Improve near lossless compression when a prediction filter is used. e15afbce dsp.h: fix ubsan macro name e53c9ccb dsp.h: add WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW af81fdb7 utils.h: quiet -fsanitize=undefined warnings ea0be354 dsp.h: remove utils.h include cd276aec utils/*.c: ../utils/utils.h -> ./utils.h c8927131 utils/Makefile.am: add some missing headers ea24e026 Merge "dsp.h: add WEBP_UBSAN_IGNORE_UNDEF" 369e264e dsp.h: add WEBP_UBSAN_IGNORE_UNDEF 0d020a78 Merge "add runtime NEON detection" 5ee2136a Merge "add VP8LAddPixels() to lossless.h" 47435a61 add VP8LAddPixels() to lossless.h 8fa6ac68 remove two ubsan warnings 74fb56fb add runtime NEON detection 4154a839 MIPS update to new Unfilter API c80b9fc8 Merge "cherry-pick decoder fix for 64-bit android devices" 6235147e cherry-pick decoder fix for 64-bit android devices d41b8c43 configure: test for -Wformat-* w/-Wformat present 5f95589f Fix WEBP_ALIGN in case the argument is a pointer to a type larger than a byte. 2309fd5c replace num_parts_ by num_parts_minus_one_ (unsigned) 9629f4bc SimplifySegments: quiet -Warray-bounds warning de47492e Merge "update the Unfilter API in dsp to process one row independently" 2102ccd0 update the Unfilter API in dsp to process one row independently e3912d56 WebPAnimEncoder: Restore canvas before evaluating blending possibility. 6e12e1e3 WebPAnimEncoder: Fix for single-frame optimization. 602f344a Merge changes I1d03acac,Ifcb64219 95ecccf6 only apply color-mapping for alpha on the cropped area 47dd0708 anim_diff: Add an experimental option for max inter-frame diff. aa809cfe only allocate alpha_plane_ up to crop_bottom row 31f2b8d8 WebPAnimEncoder: FlattenSimilarPixels(): look for similar 774dfbdc perform alpha filtering within the decoding loop a4cae68d lossless decoding: only process decoded row up to last_row 238cdcdb Only call WebPDequantizeLevels() on cropped area cf6c713a alpha: preparatory cleanup b95ac0a2 Merge "VP8GetHeaders(): initialize VP8Io with sane value for crop/scale dimensions" 89231394 VP8GetHeaders(): initialize VP8Io with sane value for crop/scale dimensions 5828e199 use_8b_decode -> use_8b_decode_ 8dca0247 fix bug in alpha.c that was triggering a memory error in incremental mode 9a950c53 WebPAnimEncoder: Disable filtering when blending is used with lossy encoding. eb423903 WebPAnimEncoder: choose max diff for framerect based on quality. ff0a94be WebPAnimEncoder lossy: ignore small pixel differences for frame rectangles. f8040084 gif2webp: Remove the 'prev_to_prev_canvas' buffer. 6d8c07d3 Merge "WebPDequantizeLevels(): use stride in CountLevels()" d96fe5e0 WebPDequantizeLevels(): use stride in CountLevels() ec1b2407 WebPPictureImport*: check output pointer c0768769 Merge "Revert "Re-enable encoding of alpha plane with color cache for next release."" 41f14bcb WebPPictureImport*: check src pointer 64eed387 Pass stride parameter to WebPDequantizeLevels() 97934e24 Revert "Re-enable encoding of alpha plane with color cache for next release." e88c4ca0 fix -m 2 mode-cost evaluation (causing partition0 overflow) 4562e83d Merge "add extra meaning to WebPDecBuffer::is_external_memory" abdb109f add extra meaning to WebPDecBuffer::is_external_memory 875aec70 enc_neon,cosmetics: break long comment 71e856cf GetMBSSIM,cosmetics: fix alignment a90edffb fix missing 'extern' for SSIM function in dsp/ 423ecaf4 move some SSIM-accumulation function for dsp/ f08e6624 Merge "Fix FindClosestDiscretized in near lossless:" 0d40cc5e enc_neon,Disto4x4: remove an unnecessary transpose e8feb20e Fix FindClosestDiscretized in near lossless: 82006430 anim_util: quiet static analysis warning a6f23c49 Merge "AnimEncoder: Support progress hook and user data." a5193774 Merge "Near lossless feature: fix some comments." da98d31c AnimEncoder: Support progress hook and user data. 33357131 Near lossless feature: fix some comments. 0beed01a cosmetics: fix indent after 2f5e898 6753f35c Merge "FTransformWHT optimization." 6583bb1a Improve SSE4.1 implementation of TTransform. 7561d0c3 FTransformWHT optimization. 7ccdb734 fix indentation after patch #328220 6ec0d2a9 clarify the logic of the error path when decoding fails. 8aa352b2 Merge "Remove an unnecessary transposition in TTransform." db860884 Merge "remove useless #include" 9960c316 Remove an unnecessary transposition in TTransform. 6e36b511 Small speedup in FTransform. 9dbd4aad Merge "fix C and SIMD flags completion." e60853ea Add missing common_sse2.h file to makefile.unix 696eb2b0 fix C and SIMD flags completion. 2b4fe33e Merge "fix multiple allocation for transform buffer" 2f5e8986 fix multiple allocation for transform buffer bf2b4f11 Regroup common SSE code + optimization. 4ed650a1 force "-pass 6" if -psnr or -size is used but -pass isn't. 3ef1ce98 yuv_sse2: fix -Wconstant-conversion warning a7a03e9f Merge changes I4852d18f,I51ccb85d 5e122bd6 gif2webp: set enc_options.verbose = 0 w/-quiet ab3c2583 anim_encode,DefaultEncoderOptions: init verbose 8f0dee77 Merge "configure: fix builtin detection w/-Werror" 4a7b85a9 cmake: fix builtin detection w/-Werror b74657fb configure: fix builtin detection w/-Werror 3661b980 Add a CMakeLists.txt 75f4af4d remove useless #include 6c1d7631 avoid Yoda style for comparison 8ce975ac SSE optimization for vector mismatch. 7db53831 Merge tag 'v0.5.0' 37f04949 update ChangeLog (tag: v0.5.0-rc1, tag: v0.5.0, origin/0.5.0) 7e7b6ccc faster rgb565/rgb4444/argb output 4c7f565f update NEWS 1f62b6b2 update AUTHORS e224fdc8 update mailmap 71100500 bump version to 0.5.0 230a685e README: update help text, repo link d48e427b Merge "demux: accept raw bitstreams" 99a01f4f Merge "Unify some entropy functions." 4b025f10 Merge "configure: disable asserts by default" 92cbddf8 Merge "fix PrintBlockInfo()" ca509a33 Unify some entropy functions. 367bf903 fix PrintBlockInfo() b0547ff0 move back common constants for lossless_enc*.c into the .h fb4c7832 lossless: simpler alpha cleanup preprocessing ba7f4b68 Merge "anim_diff: add brief description of options" 47ddd5a4 Move some codec logic out of ./dsp . b4106c44 anim_diff: add brief description of options 357f455d yuv_sse2: fix 32-bit visual studio build b9d80fa4 configure: disable asserts by default 7badd3da cosmetic fix: sizeof(type) -> sizeof(*var) 80ce27d3 Speed up 24-bit packing / unpacking in YUV / RGB conversions. 68eebcb0 remove a TODO about rotation 2dee2966 remove few obsolete TODO about aligned loads in SSE2 e0c0bb34 remove TODO about unused ref_lf_delta[] 9cf1cc2b remove few TODO: * 256 -> RD_DISTO_MULT * don't use TDisto for UV mode picking 79189645 Merge changes from topic 'demux-fragment-cleanup' 47399f92 demux: remove GetFragment() d3cfb79a demux: remove dead fragment related TODO ab714b8a demux, Frame: remove is_fragment_ field b105921c yuv_sse2, cosmetics: fix indent 466c92e8 demux,WebPIterator: remove fragment_num/num_fragments 11714ff1 demux: remove WebPDemuxSelectFragment c0f7cc47 fix for bug #280: UMR in next->bits 578beeb8 Merge "enc/Makefile.am: add missing headers" 1a819f00 makefile.unix: make visibility=hidden the default d4f9c2ef enc/Makefile.am: add missing headers 846caff4 configure: check for -fvisibility=hidden 3f3ea2c5 demux: accept raw bitstreams d6dad5d0 man cwebp: add precision about exactness of the 'lossless' mode 46bb1e34 Merge "gifdec: remove utils.h include" 2b882e94 Merge "Makefile.vc: define WEBP_HAVE_GIF for gifdec.c" 892b9238 Merge "man/*, AUTHORS: clarify origin of the tool" e5687a18 Merge "fix optimized build with -mcmodel=medium" e56e6859 Makefile.vc: define WEBP_HAVE_GIF for gifdec.c 4077d944 gifdec: remove utils.h include b5e30dac man/*, AUTHORS: clarify origin of the tool b275e598 fix optimized build with -mcmodel=medium 64da45a9 cosmetics, cwebp: fix indent 038a060d Merge "add disto-based refinement for UV mode (if method = 1 or 2)" 2835089d Provide an SSE2 implementation of CombinedShannonEntropy. e6c93519 add disto-based refinement for UV mode (if method = 1 or 2) 04507dc9 Merge "fix undefined behaviour during shift, using a cast" 793c5261 Merge "wicdec: add support for reading from stdin" d3d16397 Optimize the heap usage in HistogramCombineGreedy. 202a710b fix undefined behaviour during shift, using a cast 14d27a46 improve method #2 by merging DistoRefine() and SimpleQuantize() cb1ce996 Merge "10% faster table-less SSE2/NEON version of YUV->RGB conversion" ac761a37 10% faster table-less SSE2/NEON version of YUV->RGB conversion 79fcf29a wicdec: add support for reading from stdin 015f173f Merge "cwebp: add support for stdin input" a9947c32 cwebp: add support for stdin input 7eb01ff3 Merge "Improved alpha cleanup for the webp encoder when prediction transform is used." fb8c9106 Merge "introduce WebPMemToUint32 and WebPUint32ToMem for memory access" bd91af20 Merge "bit_reader: remove aarch64 BITS TODO" 6c702b81 Speed up hash chain initialization using memset. 4c60f63c make ReadPNG and ReadJPEG take a filename instead of a FILE 464ed10f bit_reader: remove aarch64 BITS TODO d478e589 Merge "configure: update issue tracker" 69381113 Improved alpha cleanup for the webp encoder when prediction transform is used. 2c08aac8 introduce WebPMemToUint32 and WebPUint32ToMem for memory access 010ca3d1 Fix FindMatchLength with non-aligned buffers. a90e1e3f README: add prerequisites for an autoconf build 458f0866 configure: update issue tracker 33914595 vwebp: work around the transparent background with GLUT bug e4a7eed4 cosmetics: fix indent 08375129 Merge "Make a separate case for low_effort in CopyImageWithPrediction" aa2eb2d4 Merge "cosmetics: fix indent" b7551e90 cosmetics: fix indent 5bda52d4 Make a separate case for low_effort in CopyImageWithPrediction 66fa598a Merge "configure: fix intrinsics build w/older gcc" 5ae220be backward_references.c: Fixed compiler warning 1556da09 Merge "configure: restore 2 warnings" 71a17e58 configure: restore 2 warnings 9eeabc07 configure: fix intrinsics build w/older gcc 363babe2 Merge "fix some warning about unaligned 32b reads" a1411782 Optimization in hash chain comparison for 64 bit Arrays were compared 32 bits at a time, it is now done 64 bits at a time. Overall encoding speed-up is only of 0.2% on @skal's small PNG corpus. It is of 3% on my initial 1.3 Mp desktop screenshot image. 829bd141 Combine Huffman cost and bit entropy into one loop a7a954c8 Merge "lossless: make prediction in encoder work per scanline" 61b605b4 Merge "fix of undefined multiply (int32 overflow)" 239421c5 lossless: make prediction in encoder work per scanline f5ca40e0 fix of undefined multiply (int32 overflow) 5cd2ef4c Merge changes from topic 'win-threading-compat' 76ce9187 Makefile.vc: enable WEBP_USE_THREAD for windows phone d2afe974 thread: use CreateThread for windows phone 0fd0e12b thread: use WaitForSingleObjectEx if available 63fadc9f thread: use InitializeCriticalSectionEx if available 110ad583 thread: use native windows cond var if available 912c9fdf dec/webp: use GetLE(24|32) from utils f1694481 utils/GetLE32: correct uint32 promotion 158763de Merge "always call WebPInitSamplers(), don't try to be smart" 3770f3bb Merge "cleanup the YFIX/TFIX difference by removing some code and #define" a40f60a9 Merge "3% speed improvement for lossless webp encoder for low effort mode:" ed1c2bc6 always call WebPInitSamplers(), don't try to be smart b8c44f1a 3% speed improvement for lossless webp encoder for low effort mode: 997e1038 cleanup the YFIX/TFIX difference by removing some code and #define d73d1c8b Merge "Make discarding invisible RGB values (cleanup alpha) the default." 1f9be97c Make discarding invisible RGB values (cleanup alpha) the default. f240117b Make dwebp listen more to the -quiet flag b37b0179 fix for issue #275: don't compare to out-of-bound pointers 21735e06 speed-up trivial one-symbol decoding case for lossless 397863bd Refactor CopyPlane() and CopyPixels() methods: put them in utils. 6ecd72f8 Re-enable encoding of alpha plane with color cache for next release. 1f7148a4 Merge "remove unused fields from WebPDecoderOptions and WebPBitstreamFeatures" 6ae395fa Merge "use ExReadFile() for ReadYUV()" 8076a00e gitignore list: add anim_diff. 1c1702d8 use ExReadFile() for ReadYUV() 775d3a37 remove unused fields from WebPDecoderOptions and WebPBitstreamFeatures c13245c7 AnimEncoder: Add a GetError() method. 688b265d AnimDecoder API: Add a GetDemuxer() method. 1aa4e3d6 WebPAnimDecoder: add an option to enable multi-threaded decoding. 3584abca AnimDecoder: option to decode to common color modes. afd5a62c Merge "mux.h does NOT need to include encode.h" 8550d443 Merge "migrate anim_diff tool from C++ to C89" 96201e50 migrate anim_diff tool from C++ to C89 945cfa3b mux.h does NOT need to include encode.h 8da07e8d Merge "~2x faster SSE2 RGB24toY, BGR24toY, ARGBToY|UV" bfd3fc02 ~2x faster SSE2 RGB24toY, BGR24toY, ARGBToY|UV 02432427 man/cwebp.1, cosmetics: escape '-'s 96f5b423 man/cwebp: group lossy-only options 52fdbdfe extract some RGB24 to Luma conversion function from enc/ to dsp/ ab8c2300 add missing \n 8304179a sync NEWS with 0.4.4 5bd04a08 sync versions with 0.4.4 8f1fcc15 Merge "Move ARGB->YUV functions from dec/vp8l.c to dsp/yuv.c" 25bf2ce5 fix some warning about unaligned 32b reads 922268fd s/TIFF/WebP fa8927ef Move ARGB->YUV functions from dec/vp8l.c to dsp/yuv.c 9b373598 Merge "for ReadXXXX() image-readers, use the value of pic->use_argb" f7c507a5 Merge "remove unnecessary #include "yuv.h"" 7861578b for ReadXXXX() image-readers, use the value of pic->use_argb 14e4043b remove unnecessary #include "yuv.h" 469ba2cd vwebp: fix incorrect clipping w/NO_BLEND 4b9186b2 update issue tracker url d64d376c change WEBP_ALIGN_CST value to 31 f717b828 vp8l.c, cosmetics: fix indent after 95509f9 927ccdc4 Merge "fix alignment of allocated memory in AllocateTransformBuffer" fea94b2b fix alignment of allocated memory in AllocateTransformBuffer 5aa8d61f Merge "MIPS: rescaler code synced with C implementation" e7fb267d MIPS: rescaler code synced with C implementation 93c86ed5 Merge "format_constants.h: MKFOURCC, correct cast" 5d791d26 format_constants.h: MKFOURCC, correct cast 65726cd3 dsp/lossless: Average2, make a constant unsigned d26d9def Use __has_builtin to check clang support 12ec204e moved ALIGN_CST into util/utils.h and renamed WEBP_ALIGN_xxx a2640838 Merge "rescaler: ~20% faster SSE2 implementation for lossless ImportRowExpand" 3fb600d5 Merge "wicdec: fix alpha detection w/64bpp BGRA/RGBA" 67c547fd rescaler: ~20% faster SSE2 implementation for lossless ImportRowExpand 99e3f812 Merge "large re-organization of the delta-palettization code" 95509f99 large re-organization of the delta-palettization code 74fb458b fix for weird msvc warning message ae49ad86 Merge "SSE2 implementation of ImportRowShrink" 932fd4df SSE2 implementation of ImportRowShrink badfcbaa wicdec: fix alpha detection w/64bpp BGRA/RGBA 35cafa6c Merge "iosbuild: fix linking with Xcode 7 / iOS SDK 9" b0c9d8af label rename: NO_CHANGE -> NoChange b4e731cd neon-implementation for rescaler code db1321a6 iosbuild: fix linking with Xcode 7 / iOS SDK 9 6dfa5e3e rescaler: better handling of the fxy_scale=0 special case. 55c05293 Revert "rescaler: better handling of the fxy_scale=0 special case." 9f226bf8 rescaler: better handling of the fxy_scale=0 special case. f7b8f907 delta_palettization.*: add copyright c1e1b710 Changed delta palette to compress better 0dd28267 Merge "Add delta_palettization feature to WebP" 48f66b66 Add delta_palettization feature to WebP 27933e2a anim_encoder: drop a frame if it has same pixels as the prev frame. df9f6ec8 Merge "webpmux/DisplayInfo: send non-error output to stdout" 8af4993b Merge "rescaler_mips_dsp_r2: cosmetics, fix indent" 2b9d2495 Merge "rescaler: cosmetics, join two lines" cc020a8c webpmux/DisplayInfo: send non-error output to stdout a288e746 configure: add -Wshorten-64-to-32 c4c3cf2d pngdec: fix type conversion warnings bef8e97d webpmux: fix type conversion warning 5a84460d rescaler_mips_dsp_r2: cosmetics, fix indent acde0aae rescaler: cosmetics, join two lines 306ce4fd rescaler: move the 1x1 or 2x1 handling one level up cced974b remove _mm_set_epi64x(), which is too specific 56668c9f fix warnings about uint64_t -> uint32_t conversion 76a7dc39 rescaler: add some SSE2 code 1df1d0ee rescaler: harmonize function protos 9ba1894b rescaler: simplify ImportRow logic 5ff0079e fix rescaler vertical interpolation cd82440e VP8LAllocateHistogramSet: align histogram[] entries a406b1dd Merge "fix memory over-allocation in lossless rescaler init" 0fde33e3 add missing const in VP8InitFrame signature ac7d5e8d fix memory over-allocation in lossless rescaler init 017f8ccc Loosen the buffer size checks for Y/U/V/A too. 15ca5014 loosen the padding check on buffer size d623a870 dec_neon: add whitespace around stringizing operator 29377d55 dsp/mips: cosmetics: add whitespace around XSTR macro eebaf97f dsp/mips: add whitespace around stringizing operator d39dc8f3 Create a WebPAnimDecoder API. 03fb7522 gif2webp: print output file size 14efabbf Android: limit use of cpufeatures 7b83adbe preparatory cosmetics for Rescaler code fix and clean-up 77fb41c2 dec/vp8l/DecodeAlphaData: remove redundant cast 90fcfcd9 Insert less hash chain entries from the beginnings of long copies. bd55604d SSE2: add yuv444 converters, re-using yuv_sse2.c 41a5d99d add a -quiet option to 'dwebp' 80ab3edb Merge "README: update dwebp help output after 1e595fe" 32b71b2e README: update dwebp help output after 1e595fe 3ec11827 use the DispatchAlpha() call from dsp c5f00621 incorporate bzero() into WebPRescalerInit() instead of call site 3ebcdd41 remove duplicate "#include " 1e595fe1 dwebp: add -resize as a synonym for -scale 24a96932 dec: allow 0 as a scaling dimension b9187242 utils/rescaler: add WebPRescalerGetScaledDimensions 923e8eda Merge "update NEWS" 020fd099 Merge "WebPPictureDistortion: support ARGB format for 'pic' when computing distortion." 6a5292f6 update NEWS 56a2e9f5 WebPPictureDistortion: support ARGB format for 'pic' when computing distortion. 0ae582e4 configure: test and add -Wunreachable-code c2f9dc06 bit_writer: convert VP8L macro values to immediates b969f888 Reduce magic in palette reordering acb297e9 anim_diff: add a -raw_comparison flag 155c1b22 Merge changes I76f4d6fe,I45434639 717e4d5a mips32/mipsDSPr2: function ImportRow rebased 7df93893 fix rescaling bug (uninitialized read, see bug #254). 5cdcd561 lossless_enc_neon: add VP8LTransformColor a53c3369 lossless_neon: add VP8LTransformColorInverse 99131e7f Merge changes I9fb25a89,Ibc648e9e c4556766 simplify the main loop for downscaling 2a010f99 lossless_neon: remove predictors 5-13 ca221bbc ll_enc_neon: enable VP8LSubtractGreenFromBlueAndRed 585d93db Container spec: clarify ordering of ALPH chunk. 01d61fd9 lossless: ~20 % speedup f722c8f0 lossless: Speed up ComputeCacheEntropy by 40 % 1ceecdc8 add a VP8LColorCacheSet() method for color cache 17eb6099 lossless: Allow copying from prev row in rle-mode. f3a7a5bf lossless: bit writer optimization d97b9ff7 Merge changes from topic 'lossless-enc-improvements' 0250dfcc msvc: fix pointer type warning in BitsLog2Floor 52931fd5 lossless: combine the Huffman code with extra bits c4855ca2 lossless: Inlining add literal 8e9c94de lossless: simplify HashChainFindCopy heuristics 888429f4 lossless: 0.5 % compression density improvement 7b23b198 lossless: Add zeroes into the predicted histograms. 85b44d8a lossless: encoding, don't compute unnecessary histo d92453f3 lossless: Remove about 25 % of the speed degradation 2cce0317 Faster alpha coding for webp 5e75642e lossless: rle mode not to accept lengths smaller than 4. 84326e4a lossless: Less code for the entropy selection 16ab951a lossless: 0.37 % compression density improvement 822f113e add WebPFree() to the API 0ae2c2e4 SSE2/SSE41: optimize SSE_16xN loops 39216e59 cosmetics: fix indent after 32462a07 559e54ca Merge "SSE2: slightly faster FTransformWHT" 8ef9a63b SSE2: slightly faster FTransformWHT f27f7735 lossless_neon: enable VP8LAddGreenToBlueAndRed 36e9c4bc SSE2: minor cosmetrics on in-loop filter code 4741fac4 dsp/lossless_*sse2: remove some unnecessary inlines 1819965e fix warning ("left shift of negative value") using a cast 70170014 SSE2: speed-up some lossless-encoding functions abcb0128 Merge "SSE2: slightly faster (~5%) AddGreenToBlueAndRed()" 2df5bd30 Merge "Speedup to HuffmanCostCombinedCount" 9e356d6b SSE2: slightly faster (~5%) AddGreenToBlueAndRed() fc6c75a2 SSE2: 53% faster TransformColor[Inverse] 49073da6 SSE2: 46% speed-up of TransformColor[Inverse] 32462a07 Speedup to HuffmanCostCombinedCount f3d687e3 SSE4.1 implementation of some lossless encoding functions bfc300c7 SSE4.1 implementation of some alpha-processing functions 7f9c98f2 Merge "sse2 in-loop: simplify SignedShift8b() a bit" ef314a5d dec_sse2/GetNotHEV: micro optimization a729cff9 sse2 in-loop: simplify SignedShift8b() a bit 422ec9fb simplify Load8x4() a bit 8df238ec Merge "remove some duplicate FlipSign()" 751506c4 remove some duplicate FlipSign() 65ef5afc Merge "lossless: 0.13% compression density gain" 2beef2f2 lossless: 0.13% compression density gain 3033f24c lossless: 0.06 % compression density improvement 64960da9 dec_neon: add VE8uv / VE16 14dbd87b dec_neon: add HE8uv / HE16 ac768011 introduce FTransform2 to perform two transforms at a time. aa6065ae dec_neon: use vld1_dup(mem) rather than vdup(mem[0]) 8b63ac78 Merge "dec_neon: add TM16" f51be09e Merge "dec_neon/TrueMotion: simply left border load" dc48196b dec_neon: add TM16 ea95b305 dec_neon/TrueMotion: simply left border load f262d612 speed-up SetResidualSSE2 bf46d0ac fix mips2 build target 929a0fdc enc_sse2/TTransform: simplify abs calculation 17dbd058 enc_sse2/CollectHistogram: simplify abs calculation a6c15936 dec_neon: add DC16 intra predictors 03b4f50d Makefile.vc: add anim_diff build support. 1b989874 Merge changes I9cd84125,Iee7e387f,I7548be72 acd7b5af Introduce a test tool anim_diff. f274a96c dsp/enc_sse2: add luma4 intra predictors 040b11bd dsp/enc_sse2: add chroma intra predictors aee021bb dsp/enc_sse2: add luma16 intra predictors 9e00a499 makefile.unix: remove superclean target cefc9c09 makefile.unix: clean up after extras target 4c9af023 dec_neon: add DC8uvNoTopLeft dd55b873 Merge "doc/webp-container-spec: update repo browser link" f0486968 doc/webp-container-spec: update repo browser link 9287761d Merge "GetResidualCostSSE2: simplify abs calculation" 0e009366 dsp/cpu.c(x86): check maximum supported cpuid feature b243a4bc GetResidualCostSSE2: simplify abs calculation 6d4602b8 Merge "fix typo: constitutes -> constitute" 5fe1fe37 fix typo: constitutes -> constitute b83bd7c4 Merge "populate 'libwebpextras' with: import gray, rgb565 and rgb4444 functions" b0114a32 Merge "histogram.h: cosmetics: remove unnecessary includes" feab45ef gifdec: Move inclusion of webp/config.h to header. dbba67d1 histogram.h: cosmetics: remove unnecessary includes e978fec6 Merge "VP8LBitReader: fix remaining ubsan error with large shifts" d6fe5884 Merge "ReconstructRow: move some one-time inits out of the main loop" a21d647c ReconstructRow: move some one-time inits out of the main loop 7a01c3c3 VP8LBitReader: fix remaining ubsan error with large shifts 7fa67c9b change GetPixPairHash64() return type to uint32_t ec1fb9f8 Merge "dsp/enc.c: cosmetics: move DST() def closer to use" 7073bfb3 Merge "split 64-mult hashing into two 32-bit multiplies" 0768b252 dsp/enc.c: cosmetics: move DST() def closer to use 6a48b8f0 Merge "fix MSVC size_t->int conversion warning" 1db07cde Merge "anim_encode: cosmetics: fix alignment" e28271a3 anim_encode: cosmetics: fix alignment 7fe357b8 split 64-mult hashing into two 32-bit multiplies af74c145 populate 'libwebpextras' with: import gray, rgb565 and rgb4444 functions 61214134 remove VP8Residual::cost unused field e2544823 fix MSVC size_t->int conversion warning b69a6c35 vwebp: don't redefine snprintf with VS2015+ 0ac29c51 AnimEncoder API: Consistent use of trailing underscores in struct. d4845550 AnimEncoder API: Use timestamp instead of duration as input to Add(). 9904e365 dsp/dec_sse2: DC8uv / DC8uvNoLeft speedup 7df20497 dsp/dec_sse2: DC16 / DC16NoLeft speedup 8e515dfe Merge "makefile.unix: add some missing headers" db12250f cosmetics: vp8enci.h: break long line bf516a87 makefile.unix: add some missing headers b44eda3f dsp: add DSP_INIT_STUB 03e76e96 clarify the comment about double-setting the status in SetError() 9fecdd71 remove unused EmitRGB() 43f010dd move ReconstructRow to top 82d98020 add a dec/common.h header to collect common enc/dec #defines 5d4744a2 Merge "enc_sse41: add Disto4x4 / Disto16x16" e38886a7 mux.h: Bump up ABI version 46305ca6 configure: add --disable- 2fc8b658 CPPFLAGS->CFLAGS for detecting sse4.1 in preprocessor 1a338fb3 enc_sse41: add Disto4x4 / Disto16x16 94055503 encoding SSE4.1 stub for StoreHistogram + Quantize + SSE_16xN c64659e1 remove duplicate variables after the lossless{_enc}.c split 67ba7c7a enc_sse2: call local FTransform in CollectHistogram 18249799 dsp: s/VP8LSetHistogramData/VP8SetHistogramData/ ede5e158 cosmetics: dsp/lossless.h: reorder prototypes 553051f7 dsp/lossless: split enc/dec functions 9064adc8 Merge "conditionally add -msse4.1 in Makefile.unix" cecf5096 dsp/yuv*.c: rework WEBP_USE_ ifdef 6584d398 dsp/upsampling*.c: rework WEBP_USE_ ifdef 80809422 dsp/rescaler*.c: rework WEBP_USE_ ifdef 1d93ddec dsp/lossless*.c: rework WEBP_USE_ ifdef 73805ff2 dsp/filters*.c: rework WEBP_USE_ ifdef fbdcef24 dsp/enc*.c: rework WEBP_USE_ ifdef 66de69c1 dsp/dec*.c: rework WEBP_USE_ ifdef 48e4ffd1 dsp/cost*.c: rework WEBP_USE_ ifdef 29fd6f90 dsp/argb*.c: rework WEBP_USE_ ifdef 80ff3813 dsp/alpha*.c: rework WEBP_USE_ ifdef bf09cf1e conditionally add -msse4.1 in Makefile.unix e9570dd9 stub for SSE4.1 support. 4a95384b Merge "dsp: add sse4.1 detection" cabf4bd2 dsp: add sse4.1 detection 4ecba1ab thread.h: rename interface param b8d706c8 Merge "sync versions with 0.4.3" ae64a711 Merge "add shell for libwebpextras" 92a5da9c sync versions with 0.4.3 9d4e2d16 Merge "~30% faster smart-yuv (-pre 4) with early-out criterion" b1bdbbab ~30% faster smart-yuv (-pre 4) with early-out criterion 7efb9748 Merge "Disable NEON code on Native Client" ac4f5784 Disable NEON code on Native Client 0873f85b AnimEncoder API: Support input frames in YUV(A) format. 5c176d2d add shell for libwebpextras 44bd9561 fix signature for VP8RecordCoeffTokens() c9b8ea0e small cosmetics on TokenBuffer. 76394c09 Merge "MIPS: dspr2: added optimization for TrueMotion" 0f773693 WebPPictureRescale: add a note about 0 width/height 241bb5d9 MIPS: dspr2: added optimization for TrueMotion 6cef0e4f examples/Android.mk: add webpmux_example target 53c16ff0 Android.mk: add webpmux target 21852a00 Android.mk: add webpdemux target 8697a3bc Android.mk: add webpdecoder{,_static} targets 4a670491 Android.mk: split source lists per-directory b5e79422 MIPS: dspr2: Added optimization for some convert functions 0f595db6 MIPS: dspr2: Added optimization for some convert functions 8a218b4a MIPS: [mips32|dspr2]: GetResidualCost rebased ef987500 Speedup method StoreImageToBitMask by 5%. 602a00f9 fix iOS arm64 build with Xcode 6.3 23820507 1-2% faster encoding by removing an indirection in GetResidualCost() eddb7e70 MIPS: dspr2: added otpimization for DC8uv, DC8uvNoTop and DC8uvNoLeft 73ba2915 MIPS: dspr2: added optimization for functions RD4 and LD4 c7129da5 Merge "4-5% faster encoding using SSE2 for GetResidualCost" 94380d00 MIPS: dspr2: added optimizaton for functions VE4 and DC4 2a407092 4-5% faster encoding using SSE2 for GetResidualCost 17e19862 Merge "MIPS: dspr2: added optimization for simple filtering functions" 3ec404c4 Merge "dsp: normalize WEBP_TSAN_IGNORE_FUNCTION usage" b969f5df dsp: normalize WEBP_TSAN_IGNORE_FUNCTION usage d7b8e711 MIPS: dspr2: added optimization for simple filtering functions 235f774e Merge "MIPS: dspr2: Added optimization for function VP8LTransformColorInverse_C" 42a8a628 MIPS: dspr2: Added optimization for function VP8LTransformColorInverse_C b442bef3 Merge "ApplyFiltersAndEncode: only copy lossless stats" b510fbfe doc/webp-container-spec: note MSB order for chunk diagrams 9bc0f922 ApplyFiltersAndEncode: only copy lossless stats 3030f115 Merge "dsp/mips: add some missing TSan annotations" dfcf4593 Merge "MIPS: dspr2: Added optimization for function VP8LAddGreenToBlueAndRed_C" 55c75a25 dsp/mips: add some missing TSan annotations 2cb879f0 MIPS: dspr2: Added optimization for function VP8LAddGreenToBlueAndRed_C e1556010 move some cost tables from enc/ to dsp/ c3a03168 Merge "picture_csp: fix build w/USE_GAMMA_COMPRESSION undefined" 39537d7c Merge "VP8LDspInitMIPSdspR2: add missing TSan annotation" 1dd419ce picture_csp: fix build w/USE_GAMMA_COMPRESSION undefined 43fd3543 VP8LDspInitMIPSdspR2: add missing TSan annotation c7233dfc Merge "VP8LDspInit: remove memcpy" 0ec4da96 picture_csp::InitGammaTables*: add missing TSan annotations 35579a49 VP8LDspInit: remove memcpy 97f6aff8 VP8YUVInit: add missing TSan annotation f9016d66 dsp/enc::InitTables: add missing TSan annotation e3d9771a VP8EncDspCostInit*: add missing TSan annotations d97c143d Merge "doc/webp-container-spec: cosmetics" 309b7908 MIPS: mips32: Added optimization for function SetResidualCoeffs a987faed MIPS: dspr2: added optimization for function GetResidualCost e7d3df23 doc/webp-container-spec: cosmetics be6635e9 Merge "VP8TBufferClear: remove some misleading const's" 02971e72 Merge "VP8EmitTokens: remove unnecessary param void cast" 3b77e5a7 VP8TBufferClear: remove some misleading const's aa139c8f VP8EmitTokens: remove unnecessary param void cast c24d8f14 cosmetics: upsampling_sse2: add const to some casts 1829c42c cosmetics: lossless_sse2: add const to some casts 183168f3 cosmetics: enc_sse2: add const to some casts 860badca cosmetics: dec_sse2: add const to some casts 0254db97 cosmetics: argb_sse2: add const to some casts 1aadf856 cosmetics: alpha_processing_sse2: add const to some casts 1579de3c vwebp: clear canvas at the beginning of each loop 4b9fa5d0 Merge "webp-container-spec: clarify background clear on loop" 4c82284d Updated the near-lossless level mapping. 56039479 webp-container-spec: clarify background clear on loop 19f0ba0e Implement true-motion prediction in SSE2 774d4cb7 make VP8PredLuma16[] array non-const d7eabb80 Merge "MIPS: dspr2: Added optimization for function CollectHistogram" fe42739c Use integers for kmin/kmax for simplicity. b9df35f7 AnimEncode API: kmax=0 should imply all keyframes. 6ce296da MIPS: dspr2: Added optimization for function CollectHistogram 2c906c40 vwebp: remove unnecessary static Help() prototype be0fd1d5 Merge "dec/vp8: clear 'dither_' on skipped blocks" e96170fe Merge "vwebp/animation: display last frame on end-of-loop" 0f017b56 vwebp/animation: display last frame on end-of-loop c86b40cc enc/near_lossless.c: fix alignment 66935fb9 dec/vp8: clear 'dither_' on skipped blocks b7de7946 Merge "lossless_neon: enable subtract green for aarch64" 77724f70 SSE2 version of GradientUnfilter 416e1cea lossless_neon: enable subtract green for aarch64 72831f6b Speedup AnalyzeAndInit for low effort compression. a6597483 Speedup Analyze methods for lossless compression. 98c81386 Enable Near-lossless feature. c6b24543 AnimEncoder API: Fix for kmax=1 and default kmin case. 022d2f88 add SSE2 variants for alpha filtering functions 2db15a95 Temporarily disable encoding of alpha plane with color cache. 1d575ccd Merge "Lossless decoding: Remove an unnecessary if condition." cafa1d88 Merge "Simplify backward refs calculation for low-effort." 7afdaf84 Alpha coding: reorganize the filter/unfiltering code 4d6d7285 Simplify backward refs calculation for low-effort. ec0d1be5 Cleaup Near-lossless code. 9814ddb6 Remove the post-transform near-lossless heuristic. 4509e32e Lossless decoding: Remove an unnecessary if condition. f2ebc4a8 Merge "Regression fix for lossless decoding" 783a8cda Regression fix for lossless decoding 9a062b8e AnimEncoder: Bugfix for kmin = 1 and kmax = 2. 0f027a72 simplify smart RGB->YUV conversion code 0d5b334e BackwardReferencesHashChainFollowChosenPath: remove unused variable f480d1a7 Fix to near lossless artefacts on palettized images. d4615d08 Merge changes Ia1686828,I399fda40 cb4a18a7 rename HashChainInit into HashChainReset f079e487 use uint16_t for chosen_path[] da091212 MIPS: dspr2: Added optimization for function FTransformWHT b8c20135 Merge "wicdec: (msvs) quiet some /analyze warnings" 9b228b54 wicdec: (msvs) quiet some /analyze warnings daeb276a Merge "MIPS: dspr2: Added optimization for MultARGBRow function" cc087424 Merge "dsp/cpu: (msvs) add include for __cpuidex" 4a82aab5 Merge changes I87544e92,I0bb6cda5 7a191398 dwebp/WritePNG: mark png variables volatile 775dfad2 dwebp: include setjmp.h w/WEBP_HAVE_PNG 47d26be7 dwebp: correct sign in format strings f0e0677b VP8LEncodeStream: add an assert c5f7747f VP8LColorCacheCopy: promote an int before shifting 0de5f33e dsp/cpu: (msvs) add include for __cpuidex 7d850f7b MIPS: dspr2: Added optimization for MultARGBRow function 54875293 MIPS: dspr2: added optimization for function QuantizeBlock 4fbe9cf2 dsp/cpu: (msvs) avoid immintrin.h on _M_ARM 3fd59039 simplify/reorganize arguments for CollectColorBlueTransforms b9e356b9 Disable costly TraceBackwards for method=0. a7e7caa4 MIPS: dspr2: added optimization for function TransformColorRed 2cb39180 Merge "MIPS: dspr2: added optimization for function TransformColorBlue" 279e6613 Merge "dsp/cpu: add include for _xgetbv() w/MSVS" b6c0428e dsp/cpu: add include for _xgetbv() w/MSVS d1c4ffae gif2webp: Move GIF decoding related code to a support library. 07c39559 Merge "AnimEncoder API: Add info in README.mux" 7b161973 MIPS: dspr2: added optimization for function TransformColorBlue d7c4b02a cpu: fix AVX2 detection for gcc/clang targets 9d299469 AnimEncoder API: Add info in README.mux d581ba40 follow-up: clean up WebPRescalerXXX dsp function f8740f0d dsp: s/USE_INTRINSICS/WEBP_USE_INTRINSICS/ ce73abe0 Merge "introduce a separate WebPRescalerDspInit to initialize pointers" ab66beca introduce a separate WebPRescalerDspInit to initialize pointers 205c7f26 fix handling of zero-sized partition #0 corner case cbcdd5ff Merge "move rescaler functions to rescaler* files in src/dsp/" bf586e88 Merge changes I230b3532,Idf3057a7 6dc79dc2 Merge "anim_encode: fix type conversion warnings" 11fce25a Merge "dec_neon: remove returns from void functions" c4e63f99 Makefile.vc: add gif2webp target 4f43d38c enable NEON for Windows ARM builds 3f6615ac Makefile.vc: add rudimentary Windows ARM support e7c5954c dec_neon: remove returns from void functions f79c163b anim_encode: fix type conversion warnings 0f54f1ec Remove gif2webp_util which is no longer needed. cbcbedd0 move rescaler functions to rescaler* files in src/dsp/ ac79ed19 webpmux: remove experimental fragment handling e8694d4d mux: remove experimental FRGM parsing 9e92b6ea AnimEncoder API: Optimize single-frame animated images abbae279 Merge "Move over gif2webp to the new AnimEncoder API." a28c4b36 MIPS: move WORK_AROUND_GCC define to appropriate place 012d2c60 MIPS: dspr2: added optimization for functions SSEAxB 67720c8b Move over gif2webp to the new AnimEncoder API. 9241ecf4 MIPS: dspr2: added optimization for function Average 9422211d Merge "Tune BackwardReferencesLz77 for low_effort (m=0)." df40057b Merge "Speedup VP8LGetHistoImageSymbols for low effort (m=0) mode." ea08466d Tune BackwardReferencesLz77 for low_effort (m=0). b0b973c3 Speedup VP8LGetHistoImageSymbols for low effort (m=0) mode. c6d32927 argb_sse2: cosmetics 67f601cd make the 'last_cpuinfo_used' variable names unique b9489861 AnimEncoder API: Init method for default options. 856f8ec1 Merge "AnimEncoder API: Remove AnimEncoderFrameOptions." c537514d Merge "AnimEncoder API: GenerateCandidates bugfix." dc0ce039 Merge "AnimEncoder API: Compute change rectangle for first frame too." f00b639b Merge "AnimEncoder API: In Assemble(), always set animation parameters." 29ed796c Merge "AnimEncoder lib cleanup: prev to prev canvas not needed." 9f0dd6e5 Merge "WebPAnimEncoder API: Header and implementation" 5e56bbe0 AnimEncoder API: Remove AnimEncoderFrameOptions. b902c3ea AnimEncoder API: GenerateCandidates bugfix. ef3c39bb AnimEncoder API: Compute change rectangle for first frame too. eec423ab AnimEncoder API: In Assemble(), always set animation parameters. ae1c046e AnimEncoder lib cleanup: prev to prev canvas not needed. 4b997ae4 WebPAnimEncoder API: Header and implementation 72208bec move argb_*.o build target to encoder list 95920538 Merge "multi-thread fix: lock each entry points with a static var" 4c1b300a Merge "SSE2 implementation of VP8PackARGB" fbcc2004 Merge "add -Wformat-nonliteral and -Wformat-security" 80d950d9 add -Wformat-nonliteral and -Wformat-security 04c20e75 Merge "MIPS: dspr2: added optimization for function Intra4Preds" a437694a multi-thread fix: lock each entry points with a static var ca7f60db SSE2 implementation of VP8PackARGB 72d573f6 simplify the PackARGB signature 4e2589ff demux: restore strict fragment flag check 4ba8e074 Merge "webp-container-spec: remove references to fragments" e752f0a6 Merge "demux: remove experimental FRGM parsing" f8abb112 Merge changes I109ec4d9,I73fe7743 ae2188a4 MIPS: dspr2: added optimization for function Intra4Preds 1f4b8642 move VP8EncDspARGBInit() call closer to where it's needed 14108d78 dec_neon: add DC8uvNoTop / DC8uvNoLeft d8340da7 dec_neon: add DC8uv a66e66c7 webp-container-spec: remove references to fragments 7ce8788b MIPS: dspr2: added optimization for function MakeARGB32 012e623d demux: remove experimental FRGM parsing 87c3d531 method=0: Don't evaluate any predictor 6f4fcb98 Merge "MIPS: dspr2: added optimization for function ImportRow" 24284459 replace unneeded calls to HistogramCopy() by swaps bdf7b40c MIPS: dspr2: added optimization for function ImportRow e66a9225 Merge "MIPS: dspr2: added optimization for function ExportRowC" c279fec1 MIPS: dspr2: added optimization for function ExportRowC 31a9cf64 Speedup WebP lossless compression for low effort (m=0) mode with following: - Disable Cross-Color transform. - Evaluate predictors #11 (paeth), #12 and #13 only. 9275d91c MIPS: dspr2: added optimization for function TrueMotion 26106d66 Merge "enc_neon: fix building with non-Xcode clang (iOS)" 1c4e3efe unroll the kBands[] indirection to remove a dereference in GetCoeffs() a3946b89 enc_neon: fix building with non-Xcode clang (iOS) 8ed9c00d Merge "simplify the Histogram struct, to only store max_value and last_nz" bad77571 simplify the Histogram struct, to only store max_value and last_nz 3cca0dc7 MIPS: dspr2: Added optimization for DCMode function 37e395fd MIPS: fix functions to use generic BPS istead of hardcoded value 9475bef4 PickBestUV: fix VP8Copy16x8 invocation 441f273f Merge changes I55f8da52,Id73a1e96 4a279a68 cosmetics: add some missing != NULL comparisons 66ad3725 factorize BPS definition in dsp.h and add VP8Copy16x8 432e5b55 make ALIGN_xxx naming consistent 57606047 encoder: switch BPS to 32 instead of 16 1b66bbe9 MIPS: dspr2: added optimization for function TransformColor_C c6d0f9e7 histogram: cosmetics f399d307 Merge changes I6eac17e5,I32d2b514 9de9074c dec_neon: add TM8uv 8e517eca bit_reader/kVP8NewRange: range_t -> uint8_t e1857139 dsp: initialize VP8PredChroma8 in VP8DspInit() e0c809ad Move Entropy methods to lossless.c a96ccf8f iosbuild: add x64_64 simulator support a0df5510 Remove handling for WEBP_HINT_GRAPH 413dfc0c Move static method definition before its usage. 0f235665 Update BackwardRefsWithLocalCache. d69e36ec Remove TODOs from lossless encoder code. fdaac8e0 Optmize VP8LGetBackwardReferences LZ77 references. 2f0e2ba8 MIPS: dspr2: added optimization for function Select a3e79a46 Merge "WebPEncode: Support encoding same pic twice (even if modified)" e4f4dddb WebPEncode: Support encoding same pic twice (even if modified) cbc3fbb4 Merge "Updated VP8LGetBackwardReferences and color cache." 95a9bd85 Updated VP8LGetBackwardReferences and color cache. 54f2c14c MIPS: dspr2: added optimization for function FTransform aa42f423 MIPS: dspr2: Added optimization for function VP8LSubtractGreenFromBlueAndRed 11a25f75 Merge "FlattenSimilarBlocks should only be tried when blending is possible." 5cccdadf FlattenSimilarBlocks should only be tried when blending is possible. 95ca44a7 MIPS: dspr2: added optimization for Disto4x4 4171b672 backward_references.c: reindent after c8581b0 c8581b06 Optimize BackwardReferences for RLE encoding. 5798eee6 MIPS: dspr2: unfilters bugfix (Ie7b7387478a6b5c3f08691628ae00f059cf6d899) 4167a3f5 Optimize backwardreferences d18554c3 Merge "webp/types.h: use inline for clang++/-std=c++11" 7489b0e7 gif2webp: Add '-min-size' option to get best compression. 77bdddf0 Speed up BackwardReferences 6638710b webp/types.h: use inline for clang++/-std=c++11 abf04205 Enable entropy based merge histo for (q<100) 572022a3 filters_mips_dsp_r2.c: disable unfilters a28e21b1 MIPS: dspr2: Added optimization for function ClampedAddSubtractFull 18d5a1ef MIPS: dspr2: added optimization for function ClampedAddSubtractHalf 829a8c19 MIPS: dspr2: added optimization for ITransform c94ed49e gif2webp: Use the default hint instead of WEBP_HINT_GRAPH. 653ace55 Increase the MAX_COLOR_CACHE_BITS from 9 to 10. 919220c7 Change the logic adjusting the Histogram bits. 53b096c0 Merge "Fix bug in VP8LCalculateEstimateForCacheSize." e912bd55 Fix bug in VP8LCalculateEstimateForCacheSize. 541d7839 Merge "dec_neon: add RD4 intra predictor" f8cd0672 Merge "Makefile.vc: add a 'legacy' RTLIBCFG option" 22881c99 dec_neon: add RD4 intra predictor 613d281e update NEWS 1304eb34 Merge "dec_neon: DC4: use pair-wise adds for top row" 34c20c06 Makefile.vc: add a 'legacy' RTLIBCFG option 7083006b Merge "dsp/dec_{neon,sse2}: VE4: normalize variable names" 0db9031c dsp/dec_{neon,sse2}: VE4: normalize variable names b5bc1530 dec_neon: DC4: use pair-wise adds for top row 5b90d8fe Unify the API between VP8BitWriter and VP8LBitWriter f7ada560 Merge changes I2e06907b,Ia9ed4ca6,I782282ff 5beb6bf0 Merge "dec_neon: add VE4 intra predictor" eba6ce06 dec_neon: add DC4 intra predictor 79abfbd9 dec_neon: add TM4 intra predictor fe395f0e dec_neon: add LD4 intra predictor 32de385e dec_neon: add VE4 intra predictor 72395ba9 Merge "Modify CostModel to allocate optimal memory." 65e5eb8a gif2webp: Support GIF_DISPOSE_RESTORE_PREVIOUS e4c829ef gif2webp: Handle frames with odd offsets + disposal to background. c2b5a039 Modify CostModel to allocate optimal memory. b7a33d7e implement VE4/HE4/RD4/... in SSE2 97c76f1f make VP8PredLuma4[] non-const and initialize array in VP8DspInit() 0ea8c6c2 Merge "PrintReg: output to stderr" d7ff2f97 Merge "stopwatch.h: fix includes" f85ec712 PrintReg: output to stderr 54edbf65 stopwatch.h: fix includes 139142e4 Optimize BackwardReferenceHashChainFollowPath. 5f36b68d enc/backward_references.c: fix indent e0e9960d Merge "sync version numbers to 0.4.2 release" 64ac5144 sync version numbers to 0.4.2 release c24f8954 Simplify and speedup Backward refs computation. d1c359ef fix shared object build with -fvisibility=hidden a4c3a31b WEBP_TSAN_IGNORE_FUNCTION: fix gcc compat warning f358eeb8 add code for testing random incremental decoding in dwebp 80247291 mark some init function as being safe for thread_sanitizer. 79b5bdbf bit_reader.h: cosmetics: fix a typo 6c673681 Improved near-lossless mode. 0ce27e71 enc_mips32: workaround gcc-4.9 bug aca1b98f enc/vp8l.c: fix indent ca005027 Evaluate non-palette compression for palette image c8a87bb6 AssignSegments: quiet -Warray-bounds warning 32f67e30 Merge "enc_neon: initialize vectors w/vdup_n_u32" fabc65da 1-3% faster encoding optimizing SSE_NxN functions 7534d716 enc_neon: initialize vectors w/vdup_n_u32 5f813912 Merge "Fix return code of EncodeImageInternal()" e321abe4 Fix return code of EncodeImageInternal() f82cb06a optimize palette ordering f545feee don't set the alpha value for histogram index image 2d9b0a44 add WebPDispatchAlphaToGreen() to dsp 1bd4c2ad Merge "Change Entropy based Histogram Combine heuristic." e295b8f1 Merge "iosbuild: cleanup" 1be4e760 Merge "iosbuild: output autoconf req. on failure" d5e498d4 Change Entropy based Histogram Combine heuristic. 47a2d8e1 fix MSVC float->int conversion warning 041956f6 iosbuild: cleanup 767eb402 iosbuild: output autoconf req. on failure 35ad48b8 HistoHeapInit: correct positions allocation size 45d9635f lossless: entropy clustering for high qualities. dc37df8c fix type warning for VS9_x64 9f7d9e6d iosbuild: make iOS 6 the minimum requirement fdd6528b Remove unused VP8LDecoder member variable ea3bba5a Merge "rewrite Disto4x4 in enc_neon.c with intrinsic" f060dfc4 add lossless incremental decoding support ab70794d rewrite Disto4x4 in enc_neon.c with intrinsic d4471637 MIPS: dspr2: added optimization for function FilterLoop24 2aef54d4 Merge "prepare VP8LDecodeImage for incremental decode" aed0f5a2 Merge "MIPS: dspr2: added optimization for function FilterLoop26" 28630685 prepare VP8LDecodeImage for incremental decode 248f3aed remove br->error_ field 49e15044 MIPS: dspr2: added optimization for function FilterLoop26 38128cb9 iobuild.sh: only install .h files in Headers c792d412 Premultiply with alpha during U/V downsampling 0cc811d7 gif2webp: Background color correction d7167ff7 Amend the lossless spec according to issue #205, #206 and #224 b901416b Record the lossless size stats. cddd3340 Add a WebPExtractAlpha function to dsp 0716a98e fix indent after I0204949917836f74c0eb4ba5a7f4052a4797833b f9ced95a Optimize lossless decoding for trivial(ARB) codes. 924fcfd9 Merge "webpmux: simplify InitializeConfig()" c0a462ca webpmux: simplify InitializeConfig() 6986bb5e webpmux: fix indent f89e1690 webpmux: fix exit status on numeric value parse error 2172cb62 Merge "webpmux: fix loop_count range check" e3b343ec Merge "examples: warn on invalid numeric parameters" 0e23c487 webpmux: fix loop_count range check 6208338a Merge "fix loop bug in DispatchAlpha()" d51f3e40 gif2webp: Handle frames with missing graphic control extension 690b491a fix loop bug in DispatchAlpha() 96d43a87 examples: warn on invalid numeric parameters 3101f537 MIPS: dspr2: added optimization for TransformOne a6bb9b17 SSE2 for inverse Mult(ARGB)Row and ApplyAlphaMultiply d84a8ffd Remove default initialization of decoder status. be70b86c configure: simplify libpng-config invocation e0a99321 Rectify bug in lossless incremental decoding. e2502a97 MIPS: dspr2: added optimization for TransformAC3 24e1072a MIPS: dspr2: added optimization for TransformDC c0e84df8 Merge "Slightly faster lossless decoding (1%)" 8dd28bb5 Slightly faster lossless decoding (1%) f0103595 MIPS: dspr2: added optimization for ColorIndexInverseTransforms d3242aee make VP8LSetBitPos() set br->eos_ flag a9decb55 Lossless decoding: fix eos_ flag condition 3fea6a28 fix erroneous dec->status_ setting 80b8099f MIPS: dspr2: add some specific mips code to commit I2c3f2b12f8df15b785fad5a9c56316e954ae0c53 e5640625 Merge "further refine the COPY_PATTERN optim for DecodeAlpha" 854509fe enc/histogram.c: reindent after f4059d0 34421964 Merge "~3-5% faster encoding optimizing PickBestIntra*()" 865069c1 further refine the COPY_PATTERN optim for DecodeAlpha a5956228 added C-level optimization for DecodeAlphaData function 187d379d add a fallback to ALPHA_NO_COMPRESSION a48a2d76 ~3-5% faster encoding optimizing PickBestIntra*() a6140194 ExUtilReadFromStdin: (windows) open stdin in bin mode e80eab1f webpmux: (windows) open stdout in binary mode e9bfb116 cwebp: (windows) open stdout in binary mode 5927e15b example_util: add ExUtilSetBinaryMode 30f3b75b webpmux man page: Clarify some title, descriptions and examples 77d4c7e3 address cosmetic comments from patch #71380 f75dfbf2 Speed up Huffman decoding for lossless 637b3888 dsp/lossless: workaround gcc-4.9 bug on arm 8323a903 dsp.h: collect gcc/clang version test macros e6c4b52f move static initialization of WebPYUV444Converters[] to the Init function. 49911d4d Merge "fix indentation" f4059d0c Code cleanup for HistogramRemap. e632b092 fix indentation f5c04d64 Merge "add a DispatchAlpha() for SSE2 that handles 8 pixels at a time" fc98edd9 add a DispatchAlpha() for SSE2 that handles 8 pixels at a time 73d361dd introduce VP8EncQuantize2Blocks to quantize two blocks at a time 0b21c30b MIPS: dspr2: added optimization for EmitAlphaRGB 953acd56 enc_neon: enable QuantizeBlock for aarch64 f4ae1437 MIPS: mips32: code rebase 56977154 MIPS: dspr2: added optimizations for VP8YuvTo* 2523aa73 SmartRGBYUV: fix odd-width problem with pixel replication ee52dc4e fix some MSVC64 warning about float conversion 3fca851a cpu: check for _MSC_VER before using msvc inline asm e2a83d71 faster RGB->YUV conversion function (~7% speedup) de2d03e1 Merge "Add smart RGB->YUV conversion option -pre 4" 3fc4c539 Add smart RGB->YUV conversion option -pre 4 b4dc4069 MIPS: dspr2: added optimization for (un)filters 137e6090 Merge "configure: add work around for gcc-4.9 aarch64 bug" b61c9cec MIPS: dspr2: Optimization of some simple point-sampling functions e2b8cec0 configure: add work around for gcc-4.9 aarch64 bug 98c54107 MIPS: mips32r2: added optimization for BSwap32 dab702b3 Update PATENTS to reflect s/VP8/WebM/g b564f7c7 Merge "MIPS: detect mips32r6 and disable mips32r1 code" b7e5a5c4 MIPS: detect mips32r6 and disable mips32r1 code 63c2fc02 Correctly use the AC_CANONICAL_* macros bb07022b Merge "cosmetics" e300c9d8 cosmetics 0e519eea Merge "cosmetics: remove some extraneous 'extern's" 3ef0f08a Merge "vp8enci.h: cosmetics: fix '*' placement" 4c6dde37 bit_writer: cosmetics: rename kFlush() -> Flush() f7b4c48b cosmetics: remove some extraneous 'extern's b47fb00a vp8enci.h: cosmetics: fix '*' placement b5a36cc9 add -near_lossless [0..100] experimental option 0524d9e5 dsp: detect mips64 & disable mips32 code d3485d96 cwebp.1: fix quality description placement 29a9fe22 Merge tag 'v0.4.1' 8af27718 update ChangeLog (tag: v0.4.1, origin/0.4.1) e09e9ff6 Record & log the image pre-processing time. f59c0b4b iosbuild.sh: specify optimization flags 8d34ea3e update ChangeLog (tag: v0.4.1-rc1) dbc3da66 makefile.unix: add vwebp.1 to the dist target 89a7c83c update ChangeLog ffe67ee9 Merge "update NEWS for the next release" into 0.4.1 2def1fe6 gif2webp: dust up the help message fb668d78 remove -noalphadither option from README/vwebp.1 e49f693b update NEWS for the next release cd013580 Merge "update AUTHORS" into 0.4.1 268d01eb update AUTHORS 85213b9b bump version to 0.4.1 695f80ae Merge "restore mux API compatibility" into 0.4.1 862d296c restore mux API compatibility 8f6f8c5d remove the !WEBP_REFERENCE_IMPLEMENTATION tweak in Put8x8uv d713a696 Merge changes If4debc15,I437a5d5f into 0.4.1 c2fc52e4 restore encode API compatibility 793368e8 restore decode API compatibility b8984f31 gif2webp: fix compile with giflib 5.1.0 222f9b1a gif2webp: simplify giflib version checking d2cc61b7 Extend MakeARGB32() to accept Alpha channel. 4595b62b Merge "use explicit size of kErrorMessages[] arrays" 157de015 Merge "Actuate memory stats for PRINT_MEMORY_INFO" fbda2f49 JPEG decoder: delay conversion to YUV to WebPEncode() call 0b747b1b use explicit size of kErrorMessages[] arrays 3398d81a Actuate memory stats for PRINT_MEMORY_INFO 6f3202be Merge "move WebPPictureInit to picture.c" 6c347bbb move WebPPictureInit to picture.c fb3acf19 fix configure message for multi-thread 40b086f7 configure: check for _beginthreadex 1549d620 reorder the YUVA->ARGB and ARGB->YUVA functions correctly c6461bfd Merge "extract colorspace code from picture.c into picture_csp.c" 736f2a17 extract colorspace code from picture.c into picture_csp.c 645daa03 Merge "configure: check for -Wformat-security" abafed86 configure: check for -Wformat-security fbadb480 split monolithic picture.c into picture_{tools,psnr,rescale}.c c76f07ec dec_neon/TransformAC3: initialize vector w/vcreate bb4fc051 gif2webp: Allow single-frame animations 46fd44c1 thread: remove harmless race on status_ in End() 5a1a7264 Merge "configure: check for __builtin_bswapXX()" 6781423b configure: check for __builtin_bswapXX() 6450c48d configure: fix iOS builds 6422e683 VP8LFillBitWindow: enable fast path for 32-bit builds 4f7f52b2 VP8LFillBitWindow: respect WEBP_FORCE_ALIGNED e458badc endian_inl.h: implement htoleXX with BSwapXX f2664d1a endian_inl.h: add BSwap16 6fbf5345 Merge "configure: add --enable-aligned" dc0f479d configure: add --enable-aligned 9cc69e2b Merge "configure: support WIC + OpenGL under mingw64" 257adfb0 remove experimental YUV444 YUV422 and YUV400 code 10f4257c configure: support WIC + OpenGL under mingw64 380cca4f configure.ac: add AC_C_BIGENDIAN ee70a901 endian_inl.h: add BSwap64 47779d46 endian_inl.h: add BSwap32 d5104b1f utils: add endian_inl.h 58ab6224 Merge "make alpha-detection loop in IsKeyFrame() in good x/y order" 9d562902 make alpha-detection loop in IsKeyFrame() in good x/y order 516971b1 lossless: Remove unaligned read warning b8b596f6 Merge "configure.ac: add an autoconf version prerequisite" 34b02f8c configure.ac: add an autoconf version prerequisite e59f5360 neon: normalize vdup_n_* usage 6ee7160d Merge changes I0da7b3d3,Idad2f278,I4accc305 abc02f24 Merge "fix (uncompiled) typo" bc03670f neon: add INIT_VECTOR4 6c1c632b neon: add INIT_VECTOR3 dc7687e5 neon: add INIT_VECTOR2 4536e7c4 add WebPMuxSetCanvasSize() to the mux API 824eab10 fix (uncompiled) typo 1f3e5f1e remove unused 'shift' argument and QFIX2 define 8e867051 Merge "VP8LoadNewBytes: use __builtin_bswap32 if available" 1b6a2635 Merge "Fix handling of weird GIF with canvas dimension 0x0" 1da3d461 VP8LoadNewBytes: use __builtin_bswap32 if available 1582e402 Fix handling of weird GIF with canvas dimension 0x0 b8811dac Merge "rename interface -> winterface" db8b8b5f Fix logic in the GIF LOOP-detection parsing 25aaddc8 rename interface -> winterface 5584d9d2 make WebPSetWorkerInterface() check its arguments a9ef7ef9 Merge "cosmetics: update thread.h comments" c6af9991 Merge "dust up the help message" 0a8b8863 dust up the help message a9cf3191 cosmetics: update thread.h comments 27bfeee4 QuantizeBlock SSE2 Optimization: 2bc0dc3e Merge "webpmux: warn when odd frame offsets are used" 3114ebe4 Merge changes Id8edd3c1,Id418eb96,Ide05e3be c0726634 webpmux: warn when odd frame offsets are used c5c6b408 Merge "add alpha dithering for lossy" d5146784 examples/Android.mk: add cwebp ca0fa7c7 Android.mk: move dwebp to examples/Android.mk 73d8fca0 Android.mk: add ENABLE_SHARED flag 6e93317f muxread: fix out of bounds read 8b0f6a48 Makefile.vc: fix CFLAGS assignment w/HAVE_AVX2=1 bbe32df1 add alpha dithering for lossy 79020767 Merge "make error-code reporting consistent upon malloc failure" 77bf4410 make error-code reporting consistent upon malloc failure 7a93c000 **/Makefile.am: remove unused AM_CPPFLAGS 24e30805 Add an interface abstraction to the WebP worker thread implementation d6cd6358 Merge "fix orig_rect==NULL case" 2bfd1ffa fix orig_rect==NULL case 059e21c1 Merge "configure: move config.h to src/webp/config.h" f05fe006 properly report back encoding error code in WebPFrameCacheAddFrame() 32b31379 configure: move config.h to src/webp/config.h 90090d99 Merge changes I7c675e51,I84f7d785 ae7661b3 makefiles: define WEBP_HAVE_AVX2 when appropriate 69fce2ea remove the special casing for res->first in VP8SetResidualCoeffs 6e61a3a9 configure: test for -msse2 b9d2efc6 rename upsampling_mips32.c to yuv_mips32.c bdfeebaa dsp/yuv: move sse2 functions to yuv_sse2.c 46b32e86 Merge "configure: set WEBP_HAVE_AVX2 when available" 88305db4 Merge "VP8RandomBits2: prevent signed int overflow" 73fee88c VP8RandomBits2: prevent signed int overflow db4860b3 enc_sse2: prevent signed int overflow 3fdaf4d2 Merge "real fix for longjmp warning" 385e3340 real fix for longjmp warning 230a0555 configure: set WEBP_HAVE_AVX2 when available a2ac8a42 restore original value_/range_ field order 5e2ee56f Merge "remove libwebpdspdecode dep on libwebpdsp_avx2" 61362db5 remove libwebpdspdecode dep on libwebpdsp_avx2 42c447ae Merge "lossy bit-reader clean-up:" 479ffd8b Merge "remove unused #include's" 9754d39a Merge "strong filtering speed-up (~2-3% x86, ~1-2% for NEON)" 158aff9b remove unused #include's 09545eea lossy bit-reader clean-up: ea8b0a17 strong filtering speed-up (~2-3% x86, ~1-2% for NEON) 6679f899 Optimize VP8SetResidualCoeffs. ac591cf2 fix for gcc-4.9 warnings about longjmp + local variables 4dfa86b2 dsp/cpu: NaCl has no support for xgetbv 4c398699 Merge "cwebp: fallback to native webp decode in WIC builds" 33aa497e Merge "cwebp: add some missing newlines in longhelp output" c9b340a2 fix missing WebPInitAlphaProcessing call for premultiplied colorspace output 57897bae Merge "lossless_neon: use vcreate_*() where appropriate" 6aa4777b Merge "(enc|dec)_neon: use vcreate_*() where appropriate" 0d346e41 Always reinit VP8TransformWHT instead of hard-coding 7d039fc3 cwebp: fallback to native webp decode in WIC builds d471f424 cwebp: add some missing newlines in longhelp output bf0e0030 lossless_neon: use vcreate_*() where appropriate 9251c2f6 (enc|dec)_neon: use vcreate_*() where appropriate 399b916d lossy decoding: correct alpha-rescaling for YUVA format 78c12ed8 Merge "Makefile.vc: add rudimentary avx2 support" dc5b122f try to remove the spurious warning for static analysis ddfefd62 Makefile.vc: add rudimentary avx2 support a8911643 Merge "simplify VP8LInitBitReader()" fdbcd44d simplify VP8LInitBitReader() 7c004287 makefile.unix: add rudimentary avx2 support 515e35cf Merge "add stub dsp/enc_avx2.c" a05dc140 SSE2: yuv->rgb speed-up for point-sampling 178e9a69 add stub dsp/enc_avx2.c 1b99c09c Merge "configure: add a test for -mavx2" fe728071 configure: add a test for -mavx2 e46a247c cpu: fix check for __cpuidex availability 176fda26 fix the bit-writer for lossless in 32bit mode 541784c7 dsp.h: add a check for AVX2 / define WEBP_USE_AVX2 bdb151ee dsp/cpu: add AVX2 detection ab9f2f86 Merge "revamp the point-sampling functions by processing a full plane" a2f8b289 revamp the point-sampling functions by processing a full plane ef076026 use decoder's DSP functions for autofilter 2b5cb326 Merge "dsp/cpu: add AVX detection" df08e67e dsp/cpu: add AVX detection e2f405c9 Merge "clean-up and slight speed-up in-loop filtering SSE2" f60957bf clean-up and slight speed-up in-loop filtering SSE2 9fc3ae46 .gitattributes: treat .ppm as binary 3da924b5 Merge "dsp/WEBP_USE_NEON: test for __aarch64__" c7164490 Android.mk: always include *_neon.c in the build a577b23a dsp/WEBP_USE_NEON: test for __aarch64__ 54bfffca move RemapBitReader() from idec.c to bit_reader code 34168ecb Merge "remove all unused layer code" f1e77173 remove all unused layer code b0757db7 Code cleanup for VP8LGetHistoImageSymbols. 5fe628d3 make the token page size be variable instead of fixed 8192 f948d08c memory debug: allow setting pre-defined malloc failure points ca3d746e use block-based allocation for backward refs storage, and free-lists 1ba61b09 enable NEON intrinsics in aarch64 builds b9d2bb67 dsp/neon.h: coalesce intrinsics-related defines b5c75258 iosbuild: add support for iOSv7/aarch64 9383afd5 Reduce number of memory allocations while decoding lossless. 888e63ed Merge "dsp/lossless: prevent signed int overflow in left shift ops" 8137f3ed Merge "instrument memory allocation routines for debugging" 2aa18736 instrument memory allocation routines for debugging d3bcf72b Don't allocate VP8LHashChain, but treat like automatic object bd6b8619 dsp/lossless: prevent signed int overflow in left shift ops b7f19b83 Merge "dec/vp8l: prevent signed int overflow in left shift ops" 29059d51 Merge "remove some uint64_t casts and use." e69a1df4 dec/vp8l: prevent signed int overflow in left shift ops cf5eb8ad remove some uint64_t casts and use. 38e2db3e MIPS: MIPS32r1: Added optimization for HistogramAdd. e0609ade dwebp: fix exit code on webp load failure bbd358a8 Merge "example_util.h: avoid forward declaring enums" 8955da21 example_util.h: avoid forward declaring enums 6d6865f0 Added SSE2 variants for Average2/3/4 b3a616b3 make HistogramAdd() a pointer in dsp c8bbb636 dec_neon: relocate some inline-asm defines 4e393bb9 dec_neon: enable intrinsics-only functions ba99a922 dec_neon: use positive tests for USE_INTRINSICS 69058ff8 Merge "example_util: add ExUtilDecodeWebPIncremental" a7828e8b dec_neon: make WORK_AROUND_GCC conditional on version 3f3d717a Merge "enc_neon: enable intrinsics-only functions" de3cb6c8 Merge "move LOCAL_GCC_VERSION def to dsp.h" 1b2fe14d example_util: add ExUtilDecodeWebPIncremental ca49e7ad Merge "enc_neon: move Transpose4x4 to dsp/neon.h" ad900abd Merge "fix warning about size_t -> int conversion" 4825b436 fix warning about size_t -> int conversion 42b35e08 enc_neon: enable intrinsics-only functions f937e012 move LOCAL_GCC_VERSION def to dsp.h 5e1a17ef enc_neon: move Transpose4x4 to dsp/neon.h c7b92a5a dec_neon: (WORK_AROUND_GCC) delete unused Load4x8 8e5f90b0 Merge "make ExUtilLoadWebP() accept NULL bitstream param." 05d4c1b7 Merge "cwebp: add webpdec" ddeb6ac8 cwebp: add webpdec 35d7d095 Merge "Reduce memory footprint for encoding WebP lossless." 0b896101 Reduce memory footprint for encoding WebP lossless. f0b65c9a make ExUtilLoadWebP() accept NULL bitstream param. 9c0a60cc Merge "dwebp: move webp decoding to example_util" 1d62acf6 MIPS: MIPS32r1: Added optimization for HuffmanCost functions. 4a0e7390 dwebp: move webp decoding to example_util c0220460 Merge "Bugfix: Incremental decode of lossy-alpha" 8c7cd722 Bugfix: Incremental decode of lossy-alpha 7955152d MIPS: fix error with number of registers. b1dabe37 Merge "Move the HuffmanCost() function to dsp lib" 75b12006 Move the HuffmanCost() function to dsp lib 2772b8bd MIPS: fix assembler error revealed by clang's debug build 6653b601 enc_mips32: fix unused symbol warning in debug 8dec1209 enc_mips32: disable ITransform(One) in debug builds 98519dd5 enc_neon: convert Disto4x4 to intrinsics fe9317c9 cosmetics: 953b0746 enc_neon: cosmetics a9fc697c Merge "WIP: extract the float-calculation of HuffmanCost from loop" 3f84b521 Merge "replace some mult-long (vmull_u8) with mult-long-accumulate (vmlal_u8)" 4ae0533f MIPS: MIPS32r1: Added optimizations for ExtraCost functions. b30a04cf WIP: extract the float-calculation of HuffmanCost from loop a8fe8ce2 Merge "NEON intrinsics version of CollectHistogram" 95203d2d NEON intrinsics version of CollectHistogram 7ca2e74b replace some mult-long (vmull_u8) with mult-long-accumulate (vmlal_u8) 41c6efbd fix lossless_neon.c 8ff96a02 NEON intrinsics version of FTransform 0214f4a9 Merge "MIPS: MIPS32r1: Added optimizations for FastLog2" baabf1ea MIPS: MIPS32r1: Added optimizations for FastLog2 3d49871d NEON functions for lossless coding 3fe02915 MIPS: MIPS32r1: Added optimizations for SSE functions. c503b485 Merge "fix the gcc-4.6.0 bug by implementing alternative method" abe6f487 fix the gcc-4.6.0 bug by implementing alternative method 5598bdec enc_mips32.c: fix file mode 2b1b4d5a MIPS: MIPS32r1: Add optimization for GetResidualCost f0a1f3cd Merge "MIPS: MIPS32r1: Added optimization for FTransform" 7231f610 MIPS: MIPS32r1: Added optimization for FTransform 869eaf6c ~30% encoding speedup: use NEON for QuantizeBlock() f758af6b enc_neon: convert FTransformWHT to intrinsics 7dad095b MIPS: MIPS32r1: Added optimization for Disto4x4 (TTransform) 2298d5f3 MIPS: MIPS32r1: Added optimization for QuantizeBlock e88150c9 Merge "MIPS: MIPS32r1: Add optimization for ITransform" de693f25 lossless_neon: disable VP8LConvert* functions 4143332b NEON intrinsics for encoding 0ca2914b MIPS: MIPS32r1: Add optimization for ITransform 71bca5ec dec_neon: use vst_lane instead of vget_lane bf061052 Intrinsics NEON version of TransformOne 19c6f1ba Merge "dec_neon: use vld?_lane instead of vset?_lane" 7a94c0cf upsampling_neon: drop NEON suffix from local functions d14669c8 upsampling_sse2: drop SSE2 suffix from local functions 2ca42a4f enc_sse2: drop SSE2 suffix from local functions d038e619 dec_sse2: drop SSE2 suffix from local functions fa52d752 dec_neon: use vld?_lane instead of vset?_lane c520e77d cosmetic: fix long line 4b0f2dae Merge "add intrinsics NEON code for chroma strong-filtering" e351ec07 add intrinsics NEON code for chroma strong-filtering aaf734b8 Merge "Add SSE2 version of forward cross-color transform" c90a902e Add SSE2 version of forward cross-color transform bc374ff3 Use histogram_bits to initalize transform_bits. 2132992d Merge "Add strong filtering intrinsics (inner and outer edges)" 5fbff3a6 Add strong filtering intrinsics (inner and outer edges) d4813f0c Add SSE2 function for Inverse Cross-color Transform 26029568 dec_neon: add strong loopfilter intrinsics cca7d7ef Merge "add intrinsics version of SimpleHFilter16NEON()" 1a05dfa7 windows: fix dll builds d6c50d8a Merge "add some colorspace conversion functions in NEON" 4fd7c82e SSE2 variants of Subtract-Green: Rectify loop condition 97e5fac3 add some colorspace conversion functions in NEON b9a7a45f add intrinsics version of SimpleHFilter16NEON() daccbf40 add light filtering NEON intrinsics af444608 fix typo in STORE_WHT 6af6b8e1 Tune HistogramCombineBin for large images. af93bdd6 use WebPSafe[CM]alloc/WebPSafeFree instead of [cm]alloc/free 51f406a5 lossless_sse2: relocate VP8LDspInitSSE2 proto 0f4f721b separate SSE2 lossless functions into its own file 514fc251 VP8LConvertFromBGRA: use conversion function pointers 6d2f3527 dsp/dec: TransformDCUV: use VP8TransformDC defc8e1b Merge "fix out-of-bound read during alpha-plane decoding" fbed3643 Merge "dsp: reuse wht transform from dec in encoder" d8467084 Merge "Add SSE2 version of ARGB -> BGR/RGB/... conversion functions" 207d03b4 fix out-of-bound read during alpha-plane decoding d1b33ad5 2-5% faster trellis with clang/MacOS (and ~2-3% on ARM) 369c26dd Add SSE2 version of ARGB -> BGR/RGB/... conversion functions df230f27 dsp: reuse wht transform from dec in encoder 80e218d4 Android.mk: fix build with APP_ABI=armeabi-v7a-hard 59daf083 Merge "cosmetics:" 53622008 cosmetics: 3e7f34a3 AssignSegments: quiet array-bounds warning 3c2ebf58 Merge "UpdateHistogramCost: avoid implicit double->float" cf821c82 UpdateHistogramCost: avoid implicit double->float 312e638f Extend the search space for GetBestGreenRedToBlue 1c58526f Fix few nits fef22704 Optimize and re-structure VP8LGetHistoImageSymbols 068b14ac Optimize lossless decoding. 5f0cfa80 Do a binary search to get the optimum cache bits. 24ca3678 Merge "allow 'cwebp -o -' to emit output to stdout" e12f874e allow 'cwebp -o -' to emit output to stdout 2bcad89b allow some more stdin/stout I/O 84ed4b3a fix cwebp.1 typos after patch #69199 65b99f1c add a -z option to cwebp, and WebPConfigLosslessPreset() function 30176619 4-5% faster trellis by removing some unneeded calculations. 687a58ec histogram.c: reindent after b33e8a0 06d456f6 Merge "~3-4% faster lossless encoding" c60de260 ~3-4% faster lossless encoding 42eb06fc Merge "few cosmetics after patch #69079" 82af8264 few cosmetics after patch #69079 b33e8a05 Refactor code for HistogramCombine. ca1bfff5 Merge "5-10% encoding speedup with faster trellis (-m 6)" 5aeeb087 5-10% encoding speedup with faster trellis (-m 6) 82ae1bf2 cosmetics: normalize VP8GetCPUInfo checks e3dd9243 Merge "Refactor GetBestPredictorForTile for future tuning." 206cc1be Refactor GetBestPredictorForTile for future tuning. 3cb84062 Merge "speed-up trellis quant (~5-10% overall speed-up)" b66f2227 Merge "lossy encoding: ~3% speed-up" 4287d0d4 speed-up trellis quant (~5-10% overall speed-up) 390c8b31 lossy encoding: ~3% speed-up 9a463c4a Merge "dec_neon: convert TransformWHT to intrinsics" e8605e96 Merge "dec_neon: add ConvertU8ToS16" 4aa3e412 MIPS: MIPS32r1: rescaler bugfix c16cd99a Speed up lossless encoder. 9d6b5ff1 dec_neon: convert TransformWHT to intrinsics 2ff0aae2 dec_neon: add ConvertU8ToS16 77a8f919 fix compilation with USE_YUVj flag 4acbec1b Merge changes I3b240ffb,Ia9370283,Ia2d28728 2719bb7e dec_neon: TransformAC3: work on packed vectors b7b60ca1 dec_neon: add SaturateAndStore4x4 b7685d73 Rescale: let ImportRow / ExportRow be pointer-to-function e02f16ef dec_neon.c: convert TransformDC to intrinsics 9cba963f add missing file 8992ddb7 use static clipping tables 0235d5e4 1-2% faster quantization in SSE2 b2fbc36c fix VC12-x64 warning 6e37cb94 Merge "cosmetics: backward_references.c: reindent after a7d2ee3" a42ea974 cosmetics: backward_references.c: reindent after a7d2ee3 6c327442 Merge "fix missing __BIG_ENDIAN__ definition on some platform" a8b6aad1 fix missing __BIG_ENDIAN__ definition on some platform fde2904b Increase initial buffer size for VP8L Bit Writer. a7d2ee39 Optimize cache estimate logic. 7fb6095b Merge "dec_neon.c: add TransformAC3" bf182e83 VP8LBitWriter: use a bit-accumulator 3f40b4a5 Merge "MIPS: MIPS32r1: clang macro warning resolved" 1684f4ee WebP Decoder: Mark some truncated bitstreams as invalid acbedac4 MIPS: MIPS32r1: clang macro warning resolved 228e4877 dec_neon.c: add TransformAC3 393f89b7 Android.mk: avoid gcc-specific flags with clang 32aeaf11 revamp VP8LColorSpaceTransform() a bit 0c7cc4ca Merge "Don't dereference NULL, ensure HashChain fully initialized" 391316fe Don't dereference NULL, ensure HashChain fully initialized 926ff402 WEBP_SWAP_16BIT_CSP: remove code dup 1d1cd3bb Fix decode bug for rgbA_4444/RGBA_4444 color-modes. 939e70e7 update AUTHORS file 8934a622 cosmetics: *_mips32.c dd438c9a MIPS: MIPS32r1: Optimization of some simple point-sampling functions. PATCH [6/6] 53520911 Added support for calling sampling functions via pointers. d16c6974 MIPS: MIPS32r1: Optimization of filter functions. PATCH [5/6] 04336fc7 MIPS: MIPS32r1: Optimization of function TransformOne. PATCH [4/6] 92d8fc7d MIPS: MIPS32r1: Optimization of function WebPRescalerImportRow. PATCH [3/6] bbc23ff3 parse one row of intra modes altogether a2f608f9 Merge "MIPS: MIPS32r1: Optimization of function WebPRescalerExportRow. [2/6]" 88230854 MIPS: MIPS32r1: Optimization of function WebPRescalerExportRow. [2/6] c5a5b028 decode mt+incremental: fix segfault in debug builds 9882b2f9 always use fast-analysis for all methods. 000adac0 Merge "autoconf: update ax_pthread.m4" 2d2fc37d update .gitignore 5bf4255a Merge "Make it possible to avoid automagic dependencies" c1cb1933 disable NEON for arm64 platform 73a304e9 Make it possible to avoid automagic dependencies 4d493f8d MIPS: MIPS32r1: Decoder bit reader function optimized. PATCH [1/6] c741183c make WebPCleanupTransparentArea work with argb picture 5da18552 add a decoding option to flip image vertically 00c3c4e1 Merge "add man/vwebp.1" 2c6bb428 add man/vwebp.1 ea59a8e9 Merge "Merge tag 'v0.4.0'" 7574bed4 fix comments related to array sizes 0b5a90fd dwebp.1: fix option formatting effcb0fd Merge tag 'v0.4.0' 7c76255d autoconf: update ax_pthread.m4 fff2a11b make -short work with -print_ssim, -print_psnr, etc. 68e7901d update ChangeLog (tag: v0.4.0-rc1, tag: v0.4.0, origin/0.4.0) 256e4333 update NEWS description with new general features 29625340 Merge "gif2webp: don't use C99 %zu" into 0.4.0 3b9f9dd0 gif2webp: don't use C99 %zu b5b2e3c7 cwebp: fix metadata output w/lossy+alpha ad26df1a makefile.unix: clean up libgif2webp_util.a c3b45570 update Changelog ca841121 Merge "bump version to 0.4.0" into 0.4.0 8c524db8 bump version to 0.4.0 eec2398c update AUTHORS & .mailmap b9bbf6a1 update NEWS for 0.4.0 c72e0811 Merge "dec/webp.c: don't wait for data before reporting w/h" 5ad65314 dec/frame.c: fix formatting f7fc4bc8 dec/webp.c: don't wait for data before reporting w/h 66a32af5 Merge "NEON speed up" 26d842eb NEON speed up f307f98b Merge "webpmux: let -- stop parameter parsing" fe051da7 Merge "README: add a section on gif2webp" 6fd2bd62 Merge "manpage pedantry" 4af19007 README: add a section on gif2webp 6f36ade9 manpage pedantry f9016cb9 README: update dwebp options b4fa0a47 webpmux: let -- stop parameter parsing a9a20acf gif2webp: Add a multi-threaded encode option 495bef41 fix bug in TrellisQuantize 605a7127 simplify __cplusplus ifdef 33109f99 Merge "drop: ifdef __cplusplus checks from C files" 7f9de0b9 Merge changes I994a5587,I8467bb71,I13b50688,I1e2c9c7b 5459030b gif2webp: let -- stop parameter parsing a4b0aa06 vwebp: let -- stop parameter parsing 98af68fe cwebp: let -- stop parameter parsing a33831e2 dwebp: let -- stop parameter parsing 36301249 add some checks on error paths ce4c7139 Merge "autoconf: add --disable-wic" 5227d991 drop: ifdef __cplusplus checks from C files f6453559 dwebp.1: fix typo f91034f2 Merge "cwebp: print metadata stats when no output file is given" d4934553 gif2webp: Backward compatibility for giflib version <= 4.1.3 4c617d32 gif2webp: Disable output of ICC profile by default 73b731fb introduce a special quantization function for WHT 41c0cc4b Make Forward WHT transform use 32bit fixed-point calculation a3359f5d Only compute quantization params once 70490437 cwebp: print metadata stats when no output file is given d513bb62 * fix off-by-one zthresh calculation * remove the sharpening for non luma-AC coeffs * adjust the bias a little bit to compensate for this ad9dec0c Merge "cosmetics: dwebp: fix local function name format" f737f037 Merge "dwebp: remove a dead store" 3c3a70da Merge "makefile.unix: install binaries in $(DESTDIR)/bin/" 150b655f Merge "Android.mk: add some release compile flags" dbebd33b cosmetics: dwebp: fix local function name format 27749951 dwebp: remove a dead store a01e04fe autoconf: add --disable-wic 5009b227 makefile.unix: install binaries in $(DESTDIR)/bin/ bab30fca Merge "fix -print_psnr / ssim options" ebef7fb3 fix -print_psnr / ssim options cb637855 Merge "fix bug due to overzealous check in WebPPictureYUVAToARGB()" 8189885b Merge "EstimateBestFilter: use an int to iterate WEBP_FILTER_TYPE" 4ad7d335 Android.mk: add some release compile flags c12e2369 cosmetics: fix a few typos 6f104034 fix bug due to overzealous check in WebPPictureYUVAToARGB() 3f6c35c6 EstimateBestFilter: use an int to iterate WEBP_FILTER_TYPE cc55790e Merge changes I8bb7a4dc,I2c180051,I021a014f,I8a224a62 c536afb5 Merge "cosmetics: fix some typos" cbdd3e6e add a -dither dithering option to the decoder e8124012 Updated iosbuild.sh for XCode 5.x 4931c329 cosmetics: fix some typos 05aacf77 mux: add some missing casts 617d9348 enc/vp8l: add a missing cast 46db2865 idec: add some missing casts b524e336 ErrorStatusLossless: correct return type cb261f79 fix a descaling bug for vertical/horizontal U/V interpolation bcb3955c Merge changes I48968468,I181bc736 73f52133 gif2webp: Add a mixed compression mode 6198715e demux: split chunk parsing from ParseVP8X d2e3f4e6 demux: add a tail pointer for chunks 87cffcc3 demux: cosmetics: s/has_frames/is_animation/ e18e6677 demux: strictly enforce the animation flag c4f39f4a demux: cosmetics: remove a useless break 61cb884d demux: (non-exp) fail if the fragmented flag is set ff379db3 few % speedup of lossless encoding df3649a2 remove all disabled code related to P-frames 6d0cb3de Merge "gif2webp: kmin = 0 should suppress key-frame addition." 36555983 gif2webp: kmin = 0 should suppress key-frame addition. 7708e609 Merge "detect flatness in blocks and favor DC prediction" 06b1503e Merge "add comment about the kLevelsFromDelta[][] LUT generation" 5935259c add comment about the kLevelsFromDelta[][] LUT generation e3312ea6 detect flatness in blocks and favor DC prediction ebc9b1ee Merge "VPLBitReader bugfix: Catch error if bit_pos > LBITS too." 96ad0e0a VPLBitReader bugfix: Catch error if bit_pos > LBITS too. a014e9c9 tune quantization biases toward higher precision 1e898619 add helpful PrintBlockInfo() function 596a6d73 make use of 'extern' consistent in function declarations c8d48c6e Merge "extract random utils to their own file util/random.[ch]" 98aa33cf extract random utils to their own file util/random.[ch] 432a723e Merge "swig: add basic go bindings" fab618b5 Merge "rename libwebp.i -> libwebp.swig" e4e7fcd6 swig: add basic go bindings d3408720 Merge "fast auto-determined filtering strength" f8bfd5cd fast auto-determined filtering strength ac0bf951 small clean-up in ExpandMatrix() 1939607e rename libwebp.i -> libwebp.swig 43148b6c filtering: precompute ilimit and hev_threshold 18f992ec simplify f_inner calculation a little 241d11f1 add missing const 86c0031e add a 'format' field to WebPBitstreamFeatures dde91fde Demux: Correct the extended format validation 5d6c5bd2 add entry for '-resize' option in cwebp's man 7c098d18 Use some gamma-curve range compression when computing U/V average 0b2b0504 Use deterministic random-dithering during RGB->YUV conversion 8a2fa099 Add a second multi-thread method 7d6f2da0 Merge "up to 20% faster multi-threaded decoding" 266f63ea Merge "libwebp.jar: build w/Java 1.6 for Android compat" 0532149c up to 20% faster multi-threaded decoding 38efdc2e Simplify the gif2webp tool: move the optimization details to util de899516 libwebp.jar: build w/Java 1.6 for Android compat cb221552 Decode a full row of bitstream before reconstructing dca8a4d3 Merge "NEON/simple loopfilter: avoid q4-q7 registers" 9e84d901 Merge "NEON/TransformWHT: avoid q4-q7 registers" fc10249b NEON/simple loopfilter: avoid q4-q7 registers 2f09d63e NEON/TransformWHT: avoid q4-q7 registers 77585a2b Merge "use a macrofunc for setting NzCoeffs bits" d155507c Merge "use HINT_GRAPH as image_hint for gif source" 9c561646 Merge "only print GIF_DISPOSE_WARNING once" 05879865 use HINT_GRAPH as image_hint for gif source 0b28d7ab use a macrofunc for setting NzCoeffs bits f9bbc2a0 Special-case sparse transform 00125196 gif2webp: detect and flatten uniformly similar blocks 0deaf0fa only print GIF_DISPOSE_WARNING once 6a8c0eb7 Merge "small optimization in segment-smoothing loop" f7146bc1 small optimization in segment-smoothing loop 5a7533ce small gif2webp fix 4df0c89e Merge changes Ic697660c,I27285521 5b2e6bd3 Android.mk: add a dwebp target f910a84e Android.mk: update build flags 63f9aba4 special-case WHT transform when there's only DC 80911aef Merge "7-8% faster decoding by rewriting GetCoeffs()" 606c4304 gif2webp: Improved compression for lossy animated WebP fb887f7f gif2webp: Different kmin/kmax defaults for lossy and lossless 2a981366 7-8% faster decoding by rewriting GetCoeffs() 92d47e4c improve VP8L signature detection by checking the version bits too 5cd43e43 Add -incremental option to dwebp 54b8e3f6 webpmux: DisplayInfo(): remove unnecessary error checks. 40ae3520 fix memleak in WebPIDelete() d9662658 mux.h doc: WebPMuxGetFrame() can return WEBP_MUX_MEMORY_ERROR too. 0e6747f8 webpmux -info: display dimensions and has_alpha per frame d78a82c4 Sanity check for underflow 8498f4bf Merge "remove -Wshadow warnings" e89c6fc8 Avoid a potential memleak 3ebe1757 Merge "break down the proba 4D-array into some handy structs" 6a44550a break down the proba 4D-array into some handy structs 2f5e8934 remove -Wshadow warnings bf3a29b3 Merge "add proper WEBP_HAVE_GIF and WEBP_HAVE_GL flags" 2b0a7593 Merge "fix some warnings from static analysis" 22dd07ce mux.h: Some doc corrections 79ff0346 add proper WEBP_HAVE_GIF and WEBP_HAVE_GL flags d51f45f0 fix some warnings from static analysis d134307b fix conversion warning on MSVC d538cea8 gif2webp: Support a 'min' and 'max' key frame interval 80b54e1c allow search with token buffer loop and fix PARTITION0 problem b7d4e042 add VP8EstimateTokenSize() 10fddf53 enc/quant.c: silence a warning 399cd456 Merge "fix compile error on ARM/gcc" 9f24519e encoder: misc rate-related fixes c663bb21 Merge "simplify VP8IteratorSaveBoundary() arg passing" fa46b312 Demux.h: Correct a method name reference f8398c9d fix compile error on ARM/gcc f691f0e4 simplify VP8IteratorSaveBoundary() arg passing 42542be8 up to 6% faster encoding with clang compiler 93402f02 multi-threaded segment analysis 7e2d6595 Merge "remove the PACK() bit-packing tricks" c13fecf9 remove the PACK() bit-packing tricks 2fd091c9 Merge "use NULL for lf_stats_ testing, not bool" b11c9d62 dwebp: use default dct_method 4bb8465f Merge "(de)mux.h: wrap pseudo-code in /* */" cfb56b17 make -pass option work with token buffers 5416aab4 (de)mux.h: wrap pseudo-code in /* */ 35dba337 use NULL for lf_stats_ testing, not bool 733a7faa enc->Iterator memory cleanup e81fac86 Add support for "no blend" in webpmux binary 3b80bc48 gif2webp: Separate out each step into a method bef7e9cc Add doc precision about demux object keeping pointers to data. 61405a14 dwebp: enable stdout output with WIC 6eabb886 Merge "Animated WebP: add "do no blend" option to spec" be20decb fix compilation for BITS 24 e58cc137 Merge "dwebp: s/unsigned char/uint8_t/" 72501d43 dwebp: s/unsigned char/uint8_t/ 2c9633e8 Merge "gif2webp: Insert independent frames at regular intervals." f0d6a14b gif2webp: Insert independent frames at regular intervals. b25a6fbf yuv.h: fix indent ede3602e Merge "cosmetics: fix indent" 3a65122a dwebp: fix stdout related output 388a7249 cosmetics: fix indent 4c7322c8 Merge "dsp: msvc compatibility" d50c7e32 Merge "5-7% faster SSE2 versions of YUV->RGB conversion functions" b8ab7847 Merge "simplify upsampler calls: only allow 'bottom' to be NULL" df6cebfa 5-7% faster SSE2 versions of YUV->RGB conversion functions ad6ac32d simplify upsampler calls: only allow 'bottom' to be NULL a5e8afaf output to stdout if file name is "-" f358450f dsp: msvc compatibility 43a7c8eb Merge "cosmetics" 4c5f19c1 Merge "bit_reader.h: cosmetics" f72fab70 cosmetics 14dd5e78 fix const-ness b20aec49 Merge "Support for 'do not blend' option in vwebp" dcf65222 Support for 'do not blend' option in vwebp d5bad033 Animated WebP: add "do no blend" option to spec a2f5f73d Merge "Support for "Do not blend" in mux and demux libraries" e081f2f3 Pack code & extra_bits to Struct (VP8LPrefixCode). 6284854b Support for "Do not blend" in mux and demux libraries f486aaa9 Merge "slightly faster ParseIntraMode" d1718632 slightly faster ParseIntraMode 3ceca8ad bit_reader.h: cosmetics 69257f70 Create LUT for PrefixEncode. 988b7084 add WebPWorkerExecute() for convenient bypass 06e24987 Merge "VP8EncIterator clean-up" de4d4ad5 VP8EncIterator clean-up 7bbe9529 Merge "cosmetics: thread.c: drop a redundant comment" da411485 cosmetics: thread.c: drop a redundant comment feb4b6e6 thread.h: #ifdef when checking WEBP_USE_THREAD 8924a3a7 thread.c: drop WebPWorker prefix from static funcs 1aed8f2a Merge "fix indent" 4038ed15 fix indent 1693fd9b Demux: A new state WEBP_DEMUX_PARSE_ERROR 8dcae8b3 fix rescaling-with-alpha inaccuracy 11249abf Merge changes I9b4dc36c,I4e0eef4d 52508a1f Mux: support parsing unknown chunks within a frame/fragment. 05db0572 WebPMuxSetChunk: remove unused variable 8ba1bf61 Stricter check for presence of alpha when writing lossless images a03c3516 Demux: WebPIterator now also denotes if the frame has alpha. 6df743a3 Decoder: handle fragments case correctly too. faa4b07e Support for unknown chunks in mux library 7d60bbc6 Speed up HashChainFindCopy function. 66740140 Speedup Alpha plane encoding. b7346a1e 0.1 % speedup to decoding c606182e webp-container-spec: Tighten language added by last a34a5029 pngdec: output error messages from libpng e84c625d Merge "Detect canvas and image size mismatch in decoder." f626fe2e Detect canvas and image size mismatch in decoder. f5fbdee0 demux: stricter image bounds check 30c8158a add extra assert in Huffman decode code 8967b9f3 SSE2 for lossless decoding (critical) functions. 699d80ea Jump-lookup for Huffman coding c34307ab fix some VS9 warnings about type conversion eeada35c pngdec: add missing include 54b65108 gif2webp: If aligning to even offsets, extra pixels should be transparent 0bcf5ce3 Merge "remove a malloc() in case we're using only FILTER_NONE for alpha" 2c07143b remove a malloc() in case we're using only FILTER_NONE for alpha a4d5f59d Faster lossless decoding fd53bb75 Merge "alternate LUT-base reverse-bits code" d1c166ef Merge "Container spec: a clarification on background color." fdb91779 Rename a method 5e967532 Container spec: a clarification on background color. 30e77d0f Merge branch '0.3.0' 1b631e29 alternate LUT-base reverse-bits code 24cc307a ~20% faster lossless decoding 313d853f Speedup for decoding lossless WebP photographs: 24ee098a change the bytes_per_pixels_ field into more evocative use_8b_decode 2a04b034 update ChangeLog (tag: v0.3.1-rc2, tag: v0.3.1) 7288950b Regression fix for alpha channels using color cache: 2e377b53 wicdec: silence a format warning ad9e42a6 muxedit: silence some uninitialized warnings 3307c163 Don't set alpha-channel to 0xff for alpha->green uplift 5130770c Merge "wicdec: silence a format warning" a37eff47 Regression fix for alpha channels using color cache: 241cf99b Merge "muxedit: silence some uninitialized warnings" c8f9c84d Regression fix for alpha unfiltering: 14cd5c6c muxedit: silence some uninitialized warnings a368db81 dec/vp8l: quiet vs9 x64 type conversion warning ffae9f31 wicdec: silence a format warning 8cf0701e Alpha encoding: never filter in case of NO_COMPRESSION 825e73b1 update ChangeLog (tag: v0.3.1-rc1) abf6f691 update NEWS 5a92c1a5 bump version to 0.3.1 86daf77c store top Y/U/V samples in packed fashion 67bc353e Revert "add WebPBlendAlpha() function to blend colors against background" 068db59e Intertwined decoding of alpha and RGB 38cc0114 Simplify forward-WHT + SSE2 version 3fa595a5 Support decoding upto given row in DECODE_DATA_FUNC 520f005f DequantizeLevels(): Add 'row' and 'num_rows' args 47374b82 Alpha unfilter for given set of rows f32097e0 probe input file and quick-check for WebP format. a2aed1d0 configure: improve gl/glut library test c7e89cbb update copyright text a00380d2 configure: remove use of AS_VAR_APPEND a94a88dd fix EXIF parsing in PNG a71e5d84 add doc precision for WebPPictureCopy() and WebPPictureView() 8287012e remove datatype qualifier for vmnv e1908430 fix a memory leak in gif2webp 0b18b9ee fix two minor memory leaks in webpmux db5095d5 remove some cruft from swig/libwebp.jar 850e956f README: update swig notes bddd9b0a swig/python: add minimal documentation d573a8d5 swig: add python encode support 6b931875 swig/java: reduce wrapper function code duplication 6fe536f4 swig/java: rework uint8_t typemap a2ea4643 Fix the bug in ApplyPalette. 7bb28d2a webp/lossless: fix big endian BGRA output f036d4bf Speed up ApplyPalette for ARGB pixels. 8112c8cf remove some warnings: cc128e0b Further reduce memory to decode lossy+alpha images 07db70d2 fix for big-endian eda8a7de gif2webp: Fix signed/unsigned comparison mismatch 31f346fe Makefile.vc: fix libwebpdemux dll variable typo 6c76d28e swig: add python (decode) support b4f5bb6c swig: cosmetics 498d4dd6 WebP-Lossless encoding improvements. 26e72442 swig: ifdef some Java specific code 8ecec686 configure: add warning related flags e676b043 configure: add GLUT detection; build vwebp b0ffc437 Alpha decoding: significantly reduce memory usage 20aa7a8d configure: add --enable-everything b8307cc0 configure.ac: add some helper macros 980e7ae9 Remove the gcc compilation comments 7f25ff99 gif2webp: Fix ICC and XMP support d8e53211 Add missing name to AUTHORS 11edf5e2 Demux: Fix a potential memleak c7b92184 don't forward declare enums 7a650c6a prevent signed int overflow in left shift ops 31bea324 add precision about dynamic output reallocation with IDecoder c22877f7 Add incremental support for extended format files 5051245f Makefile.vc: have 'all' target build everything 8191deca Makefile.vc: flags cleanup b9d74735 Makefile.vc: drop /FD flag 5568dbcf update gitignore f4c7b654 WebPEncode: An additional check. Start VP8EncLoop/VP8EncTokenLoop only if VP8EncStartAlpha succeeded. 1fb04bec pngdec: Avoid a double-free. dcbb1ca5 add WebPBlendAlpha() function to blend colors against background bc9f5fbe configure.ac: add AM_PROG_AR for automake >= 1.12 bf867bf2 Tuned cross_color parameter (step) for lower qual 90e2ec5a Merge "probe input file and quick-check for WebP format." 7180d7ff Merge "update copyright text" 830f72b7 probe input file and quick-check for WebP format. 2ccf58d6 configure: improve gl/glut library test d640614d update copyright text c2113ad4 Merge "configure: remove use of AS_VAR_APPEND" 9326a56f configure: remove use of AS_VAR_APPEND ea63d619 fix a type warning on VS9 x86 bec11092 fix EXIF parsing in PNG b6e65f3d Merge "fix warnings for vs9 x64" 438946dc fix warnings for vs9 x64 f4710e3b collect macroblock reconstruction data in VP8MBData struct 23d28e21 add doc precision for WebPPictureCopy() and WebPPictureView() 518f2cd7 cosmetics: gif2webp: fix indent af358e68 Merge "remove datatype qualifier for vmnv" 3fe91635 remove datatype qualifier for vmnv 764fdffa fix a memory leak in gif2webp 3e59a74d fix two minor memory leaks in webpmux 47b9862f Merge "README: update swig notes" 325d15ff remove some cruft from swig/libwebp.jar 4a7627c2 README: update swig notes 5da81e33 Merge "swig/python: add minimal documentation" f39e08f2 Merge "swig: add python encode support" 6ca4a3e3 Merge "swig/java: reduce wrapper function code duplication" 8f8702b0 Merge "swig/java: rework uint8_t typemap" 91413be2 reduce memory for VP8MB and remove bitfields use 7413394e Fix the memory leak in ApplyFilters. 2053c2cf simplify the alpha-filter testing loop 825b64db swig/python: add minimal documentation 14677e11 swig: add python encode support a5c297c8 swig/java: reduce wrapper function code duplication ad4a367d swig/java: rework uint8_t typemap 0d25876b use uint8_t for inv_palette[] afa3450c Fix the bug in ApplyPalette. 2d6ac422 Merge "webp/lossless: fix big endian BGRA output" 2ca83968 webp/lossless: fix big endian BGRA output 742110cc Speed up ApplyPalette for ARGB pixels. 2451e47d misc code cleanup 83db4043 Merge "swig: add python (decode) support" eeeea8b5 Merge "swig: cosmetics" d5f9b8f3 Merge "libwebp: fix vp8 encoder mem alloc offsetting" d8edd835 libwebp: fix vp8 encoder mem alloc offsetting 8983b83e remove use of bit-fields in VP8FInfo 87a4fca2 remove some warnings: ba8f74e2 Merge "fix for big-endian" a65067fa Merge "Further reduce memory to decode lossy+alpha images" 64c84486 Further reduce memory to decode lossy+alpha images 332130b9 Mux: make a few methods static 44370617 fix for big-endian 5199eab5 Merge "add uncompressed TIFF output support" a3aede97 add uncompressed TIFF output support f975b67f Merge "gif2webp: Fix signed/unsigned comparison mismatch" 5fbc734b Merge "GetFeatures: Detect invalid VP8X/VP8/VP8L data" d5060c87 Merge "mux.h: A comment fix + some consistency fixes" 352d0dee GetFeatures: Detect invalid VP8X/VP8/VP8L data 3ef79fef Cosmetic: "width * height" 043e1ae4 gif2webp: Fix signed/unsigned comparison mismatch 5818cff7 mux.h: A comment fix + some consistency fixes 1153f888 Merge "swig: ifdef some Java specific code" 3eeedae1 Makefile.vc: fix libwebpdemux dll variable typo f980faf4 swig: add python (decode) support 7f5f42bb swig: cosmetics 8eae188a WebP-Lossless encoding improvements. c7247c4c swig: ifdef some Java specific code 4cb234d5 Merge "Mux: make ValidateForSingleImage() method static" ed6f5308 Merge "Add GetCanvasSize() method to mux" 1d530c9a Mux: make ValidateForSingleImage() method static bba4c2b2 configure: add warning related flags fffefd18 Add GetCanvasSize() method to mux 732da8d0 Merge "configure: add GLUT detection; build vwebp" 0e513f7a configure: add GLUT detection; build vwebp 55d1c150 Merge "Alpha decoding: significantly reduce memory usage" 13d99fb5 Merge "configure: add --enable-everything" 2bf698fe Merge "configure.ac: add some helper macros" edccd194 Alpha decoding: significantly reduce memory usage 3cafcc9a configure: add --enable-everything 4ef14477 configure.ac: add some helper macros a4e1cdbb Remove the gcc compilation comments 6393fe4b Cosmetic fixes 9c4ce971 Simplify forward-WHT + SSE2 version 878b9da5 fix missed optim 00046171 VP8GetInfo(): Check for zero width or height. 9bf31293 align VP8Encoder::nz_ allocation 5da165cf fix CheckMode() signature 0ece07dc Merge "explicitly pad bitfields to 32-bits" 9dbc9d19 explicitly pad bitfields to 32-bits 5369a80f Merge "prevent signed int overflow in left shift ops" 70e39712 Merge "cosmetics: remove unnecessary ';'s" d3136ce2 Merge "don't forward declare enums" b26e5ad5 gif2webp: Fix ICC and XMP support 46089b20 Add missing name to AUTHORS 94328d64 Demux: Fix a potential memleak 96e948d7 don't forward declare enums f4f90880 prevent signed int overflow in left shift ops 0261545e cosmetics: remove unnecessary ';'s 7ebdf110 Merge "Fix few missing comparisons to NULL" 1579989e Fix few missing comparisons to NULL ea1b21cf Cleaned up VP8GetHeaders() so that it parses only frame header b66caee4 dwebp: add support for BMP output ff885bfe add precision about dynamic output reallocation with IDecoder 79241d5a Merge "Makefile.vc: have 'all' target build everything" ac1c729b Merge "Makefile.vc: flags cleanup" 118a055c Merge "Makefile.vc: drop /FD flag" ecad0109 Merge "update gitignore" a681b4f4 Rename PRE_VP8 state to WEBP_HEADER ead4d478 Add incremental support for extended format files 69d0f926 Makefile.vc: have 'all' target build everything 52967498 Makefile.vc: flags cleanup c61baf0c Makefile.vc: drop /FD flag 3a15125d update gitignore 5167ca47 Merge "WebPEncode: An additional check. Start VP8EncLoop/VP8EncTokenLoop only if VP8EncStartAlpha succeeded." 67708d67 WebPEncode: An additional check. Start VP8EncLoop/VP8EncTokenLoop only if VP8EncStartAlpha succeeded. b68912af pngdec: Avoid a double-free. 82abbe12 Merge "configure.ac: add AM_PROG_AR for automake >= 1.12" e7d9548c add WebPBlendAlpha() function to blend colors against background ed4dc717 configure.ac: add AM_PROG_AR for automake >= 1.12 df4a406d Merge branch '0.3.0' 1e0d4b8c Update ChangeLog (tag: v0.3.0-rc7, tag: v0.3.0) d52b405d Cosmetic fixes 6cb4a618 misc style fix 68111ab0 add missing YUVA->ARGB automatic conversion in WebPEncode() e9a7990b Cosmetic fixes 403bfe82 Container spec: Clarify frame disposal 2aaa423b Merge "add missing YUVA->ARGB automatic conversion in WebPEncode()" 07d87bda add missing YUVA->ARGB automatic conversion in WebPEncode() 142c4629 misc style fix 3e7a13a0 Merge "Container spec: clarify the background color field" into 0.3.0 14af7745 container doc: add a note about the 'ANMF' payload cc635efa Container spec: clarify the background color field e3e33949 container doc: move RIFF description to own section 4299f398 libwebp/mux: fix double free 33f9a692 Merge "demux: keep a frame tail pointer; used in AddFrame" into 0.3.0 a2a7b959 use WebPDataCopy() instead of re-coding it. 6f18f12f demux: keep a frame tail pointer; used in AddFrame e5af49e9 add doc precision about WebPParseHeaders() return codes db46daab Merge "Makefile.vc: fix dynamic builds" into 0.3.0 53c77afc Merge "gif2webp: Bgcolor fix for a special case" into 0.3.0 a5ebd143 gif2webp: Bgcolor fix for a special case 6378f238 Merge "vwebp/animation: fix background dispose" into 0.3.0 3c8eb9a8 fix bad saturation order in QuantizeBlock 04c7a2ec vwebp/animation: fix background dispose 81a50695 Makefile.vc: fix dynamic builds 5f25c396 update ChangeLog (tag: v0.3.0-rc6) 14d42af2 examples: don't use C99 %zu 5ccf1fe5 update ChangeLog 2560c243 update NEWS f43bafc3 Merge changes Iecccb09c,If5ee9fd2,I3e181ce4 into 0.3.0 a788644f dwebp: warn when decoding animated webp's 302efcdb Decode: return more meaningful error for animation ad452735 WebPBitstreamFeatures: add has_animation field 783dfa49 disable FRGM decoding for good in libwebpmux 4b956be0 Update ChangeLog ad8b86d7 update NEWS 3e084f63 Merge "demux cosmetics: comments/rename internal function" into 0.3.0 d3f8c621 Merge "move WebPFeatureFlags declaration" into 0.3.0 7386fe50 Merge "libwebp{demux,mux}: install mux_types.h" into 0.3.0 d6cd4e90 Merge "bump decode abi" into 0.3.0 17f8da5c bump decode abi 97684ae2 Merge "add doc precision about WebPDemuxPartial()" into 0.3.0 f933fd2a move WebPFeatureFlags declaration 289bc47b libwebp{demux,mux}: install mux_types.h 224e8d46 add doc precision about WebPDemuxPartial() 4c18e80c demux cosmetics: comments/rename internal function 7cfd1bf1 update AUTHORS 401f7b85 Merge "speed-up lossless (~3%) with ad-hoc histogram cost evaluation" into 0.3.0 1fc8ffca Merge "makefile.unix: dist related changes" into 0.3.0 8a89c6ed Merge changes I466c377f,Ib761ebd3,I694857fc into 0.3.0 f4ffb2d5 speed-up lossless (~3%) with ad-hoc histogram cost evaluation 723847d5 gif2webp: only write error messages to stderr 701b9e2a makefile.unix: dist related changes bb85b437 Merge "update NEWS" into 0.3.0 59423a24 gif2webp: fix crash on open failure with libgif5 9acb17de gif2webp: silence a unused param warning 7d9fdc23 Merge "README updates" into 0.3.0 5621934e Merge "build: fix install race on shared headers" into 0.3.0 70809d89 Merge "bump version to 0.3.0" into 0.3.0 d851cd1d demux: make the parse a bit more strict 28bb4107 update NEWS cef93882 bump version to 0.3.0 9048494d build: fix install race on shared headers 1e67e8ef README updates 42b611a4 Merge "configure: drop experimental from mux/demux" into 0.3.0 096a8e32 Merge "vwebp: add color profile support" into 0.3.0 ddfee5dc vwebp: add color profile support 0d6927d3 Merge "Mark fragment options as experimental in webpmux" into 0.3.0 5dbd4030 Mark fragment options as experimental in webpmux a0a6648c configure: drop experimental from mux/demux ee65bad8 Merge "add support for BITS > 32" into 0.3.0 744930db add support for BITS > 32 7dd288f0 cwebp: fix build 19a8dd01 Merge "Makefile.vc: add vwebp.exe target" into 0.3.0 50eeddad Merge "examples: normalize icc related program arguments" into 0.3.0 757f637f Merge "Makefile.vc: add libwebpdecoder target" into 0.3.0 b65c4b7c Makefile.vc: add libwebpdecoder target f8db7b4a Merge "vwebp: replace doubles w/floats where appropriate" into 0.3.0 d99aa56f Makefile.vc: add vwebp.exe target 013023e7 vwebp: replace doubles w/floats where appropriate 9b3db894 README.mux: add version reference 7b6a26cf Merge "cwebp: output metadata statistics" into 0.3.0 d8dc72a0 examples: normalize icc related program arguments 7bfc9056 Merge "make alpha unfilter work in-place" into 0.3.0 0037b2d2 Merge "add LUT-free reference code for YUV->RGB conversion." into 0.3.0 166bf744 Merge "demux: disable fragment parsing" into 0.3.0 126974b4 add LUT-free reference code for YUV->RGB conversion. 0aef3ebd make alpha unfilter work in-place 14ef5005 Merge "Remove 'status: experimental' from container spec" into 0.3.0 d40c98e1 Merge "webpmux binary: tiny style fix" into 0.3.0 0bc42689 cwebp: output metadata statistics bc039803 Merge "autoconf: normalize experimental define" into 0.3.0 d1e21b13 Remove 'status: experimental' from container spec 7681bb96 webpmux binary: tiny style fix a3dd3d0f avoid installing example_util.h 252320e2 demux: disable fragment parsing 537bde05 autoconf: normalize experimental define 5e338e0b Merge changes I33e8a613,I8e8a7b44 into 0.3.0 d9d0ea1b Merge changes If21e3ec7,I991fc30b into 0.3.0 627f5ca6 automake: add reference to libwebp for mux/demux eef73d07 don't consolidate proba stats too often 05ec4cc2 libwebp{,decoder}.pc: add pthread flags 1bfcf5bf add libwebpmux.pc 26ca843d add libwebpdemux.pc 69e25906 Merge "Tune Lossless compression for lower qualities." 0478b5d2 Tune Lossless compression for lower qualities. 39f7586f add a mention of parallel alpha encoding in the NEWS 5a21d967 Merge "1.5x-2x faster encoding for method 3 and up" 9bfbdd14 1.5x-2x faster encoding for method 3 and up 27dc741b Correct frame options order in README.mux be2fd173 Mux: fix a scenario with bad ANMF/FRGM size 19eb012c Merge "Demux: Add option to get frame count using GetI()" 7368b8cb Merge "WebPGetFeatures() out of if condition for clarity." f604c9a4 Merge "fix windows build" 153f94e8 fix windows build 847b4924 Merge "vwebp: use magenta for 'i'nfo display" 25ea46bd Merge "vwebp: add keyboard shortcuts to help output" bea7ccaf vwebp: use magenta for 'i'nfo display 8fab161a webpmux: correct -frame param order in help output 03cc23d6 vwebp: add keyboard shortcuts to help output 068eba8d Demux: Add option to get frame count using GetI() 988b8f56 WebPGetFeatures() out of if condition for clarity. 6933d910 Merge "gif2webp: Be lenient about background color index." 4d0f7c55 Merge "WebPGetFeatures() behavior change:" fdeeb01d gif2webp: Be lenient about background color index. ad250320 Merge "multi-threaded alpha encoding for lossy" 4e32d3e1 Merge "fix compilation of token.c" f817930a multi-threaded alpha encoding for lossy 88050351 fix compilation of token.c fc816219 code using the actual values for num_parts_, not the ones from config 72655350 Merge "move the config check from .c to .h" dd9e76f7 move the config check from .c to .h 956b217a WebPGetFeatures() behavior change: df02e4ce WebPDemuxGetI behavior change: 633c004d Merge "rebalance method tools (-m) for methods [0..4]" 58ca6f65 rebalance method tools (-m) for methods [0..4] 7648c3cc Merge "describe rd-opt levels introduce VP8RDLevel enum" 67fb1003 Merge "autoconf: enable silent-rules by default" a5042a32 GetVersion() methods for mux and demux 5189957e describe rd-opt levels introduce VP8RDLevel enum 4e094ace autoconf: enable silent-rules by default b7eaa85d inline VP8LFastLog2() and VP8LFastSLog2 for small values 5cf7792e split quant_levels.c into decoder and encoder version e5d3ffe2 Merge "Update code example in README.mux" ac5a9156 Update code example in README.mux 38a91e99 Add example code snippet for demux API 5f557f3c README.mux: add info about Demux API and vwebp c0ba0903 backward_references: avoid signed integer overflow 943386db disable SSE2 for now 9479fb7d lossless encoding speedup ec2030a8 merge two lines together b67956c0 Merge "Remove ReadOneBit() and ReadSymbolUnsafe()" 1667bded Remove ReadOneBit() and ReadSymbolUnsafe() 3151669b wicdec + dwebp cosmetics: normalize formatting 92668da6 change default filtering parameters: * type is now 'strong' * strength is now '60' b7490f85 introduce WEBP_REFERENCE_IMPLEMENTATION compile option 33838857 faster decoding (3%-6%) 5c3e381b Merge "add a -jpeg_like option" c2311046 remove unused declaration of VP8Zigzag 36152957 Merge "wicdec: add alpha support for paletted formats" c9f16490 wicdec: add alpha support for paletted formats 1262f81e Merge "wicdec: silence some warnings" e7ea61eb wicdec: silence some warnings 23c0f354 fix missing intptr_t->int cast for MSVC e895059a add a -jpeg_like option 1f803f64 Merge "Tune alpha quality mapping to more reasonable values." 1267d498 Tune alpha quality mapping to more reasonable values. 043076e2 Merge "speed-up lossless in BackwardTrace" f3a44dcd remove one malloc from TraceBackwards() 0fc1a3a0 speed-up lossless in BackwardTrace 7c732e59 cwebp: centralize WebPCleanupTransparentArea() 7381254e Merge "wicdec: add ICC profile extraction" e83ff7de wicdec: add ICC profile extraction 146c6e3b Merge "cosmetics: pngdec: normalize default label location" a8f549d7 Merge "manpages: italicize option parameters" e118db83 Merge "encode.h: note the need to free() WebPMemoryWriter" 1dfee6db cosmetics: pngdec: normalize default label location 14c38200 manpages: italicize option parameters 7defbfad encode.h: note the need to free() WebPMemoryWriter 88d382a0 cwebp: cleanup after memory_writer 12d6cecf fix extra space in dwebp.1 man b01681a9 Fix for demuxer frame iteration: 56c12aa6 Demuxer creation fix: 66c810bc add a -yuv option to dwebp (very similar to -pgm) 841a3ba5 Merge "Remove -Wshadow warnings." 8fd02527 Merge "upsampling_neon.c: fix build" 6efed268 Remove -Wshadow warnings. 60904aa6 Merge "allow WebPINewRGB/YUVA to be passed a NULL output buffer." b7adf376 allow WebPINewRGB/YUVA to be passed a NULL output buffer. 27f8f742 upsampling_neon.c: fix build 06b9cdf1 gitignore: add IOS related directories f112221e Merge "Fix more comments for iobuild.sh" fe4d25dd Fix more comments for iobuild.sh 1de3e252 Merge "NEON optimised yuv to rgb conversion" 090b708a NEON optimised yuv to rgb conversion daa06476 Merge "Add ios build script for building iOS library." 79fe39e2 Add ios build script for building iOS library. 126c035f remove some more -Wshadow warnings 522e9d61 Merge "cwebp: enable '-metadata'" 76ec5fa1 cwebp: enable '-metadata' aeb91a9d Merge "cosmetics: break a few long lines" be7c96b0 cosmetics: break a few long lines cff8ddb6 Merge "add libwebpdecoder.pc" 93148ab8 Merge "libwebp.pc.in: detab" 6477f955 Merge "Makefile.vc: normalize path separator" bed1ed7c add libwebpdecoder.pc 46168b2d libwebp.pc.in: detab a941a346 Fixed few nits in the build files. dd7a49b2 Makefile.vc: normalize path separator 9161be86 Merge "cwebp: extract WIC decoding to its own module" 08e7c58e Merge "Provide an option to build decoder library." 0aeba528 Provide an option to build decoder library. 757ebcb1 catch malloc(0)/calloc(0) with an assert 152ec3d2 Merge "handle malloc(0) and calloc(0) uniformly on all platforms" a452a555 cwebp: extract WIC decoding to its own module 2b252a53 Merge "Provide option to swap bytes for 16 bit colormodes" 94a48b4b Provide option to swap bytes for 16 bit colormodes 42f8f934 handle malloc(0) and calloc(0) uniformly on all platforms 8b2152c5 Merge "add an extra assert to check memory bounds" 0d19fbff remove some -Wshadow warnings cd22f655 add an extra assert to check memory bounds 8189feda Merge "Add details and reference about the YUV->RGB conversion" 1d2702b1 Merge "Formatting fixes in lossless bitstream spec" 8425aaee Formatting fixes in lossless bitstream spec a556cb1a Add details and reference about the YUV->RGB conversion d8f21e0b add link to SSIM description on Wikipedia 18e9167e Merge "WebP-lossless spec clarifications:" 98e25b9b Merge "cwebp: add -metadata option" f01c2a53 WebP-lossless spec clarifications: f4a97970 Merge "Disto4x4 and Disto16x16 in NEON" 47b7b0ba Disto4x4 and Disto16x16 in NEON 7eaee9f1 cwebp: add -metadata option 36c52c2c tiffdec: use toff_t for exif ifd offset 7c8111e4 Merge "cwebp/tiffdec: add TIFF metadata extraction" e6409adc Remove redundant include from dsp/lossless code. 1ab5b3a7 Merge "configure: fix --with-gifincludedir" 03c749eb configure: fix --with-gifincludedir 8b650635 multiple libgif versions support for gif2webp 476e293f gif2webp: Use DGifOpenFileName() b50f277b tiffdec: correct format string 2b9048e3 Merge "tiffdec: check error returns for width/height" a1b5a9a3 Merge "cwebp/tiff: use the first image directory" 079423f5 tiffdec: check error returns for width/height d62824af Merge "cwebp/jpegdec: add JPEG metadata extraction" 03afaca4 Merge "cwebp: add PNG metadata extraction" 2c724968 cwebp/jpegdec: add JPEG metadata extraction dba64d91 cwebp: add PNG metadata extraction 1f075f89 Lossless spec corrections/rewording/clarifications 2914ecfd cwebp/tiffdec: add TIFF metadata extraction d82a3e33 More corrections/clarifications in lossless spec: bd002557 cwebp/tiff: use the first image directory df7aa076 Merge "Cleanup around jpegdec" 0f57dcc3 decoding speed-up (~1%) bcec339b Lossless bitstream clarification: 6bf20874 add examples/metadata.c 207f89c0 Merge "configure: add libwebpdemux status to summary" 1bd287a6 Cleanup around jpegdec 91455679 Merge "cosmetics: use '== 0' in size checks" d6b88b76 cosmetics: use '== 0' in size checks d3dace2f cosmetics: jpegdec 2f69af73 configure: add libwebpdemux status to summary 1c1c5646 cwebp: extract tiff decoding to its own module 6a871d66 cwebp: extract jpeg decoding to its own module 2ee228f9 cwebp: extract png decoding to its own module 4679db00 Merge "cwebp: add metadata framework" 63aba3ae cwebp: add metadata framework 931bd516 lossless bitstream: block size bits correction e4fc4c1c lossless bitstream: block size bits correction d65ec678 fix build, move token.c to src/enc/ 657f5c91 move token buffer to its own file (token.c) c34a3758 introduce GetLargeValue() to slim-fast GetCoeffs(). d5838cd5 faster non-transposing SSE2 4x4 FTransform f76191f9 speed up GetResidualCost() ba2aa0fd Add support for BITS=24 case 2e7f6e8e makefile.unix: Dependency on libraries dca84219 Merge "Separate out mux and demux code and libraries:" 23782f95 Separate out mux and demux code and libraries: bd56a01f configure: add summary output 90e5e319 dwebp manual: point to webpmux, gif2webp. 540790ca gif2webp.c: add a note about prerequisites d1edf697 cwebp man page: meaning of '-q' for lossy/lossless 79efa1d0 Add man page for gif2webp utility 2243e40c Merge "gif2webp build support with autoconf tools" c40efca1 gif2webp build support with autoconf tools 6523e2d4 WebP Container: 4da788da Merge "simplify the fwd transform" 42c3b550 simplify the fwd transform 41a6ced9 user GLfloat instead of float b5426119 fix indentation 68f282f7 * handle offset in anim viewer 'vwebp' * fix gif2webp to handle disposal method and odd offset correctly 118cb312 Merge "add SSE2 version of Sum of Square error for 16x16, 16x8 and 8x8 case" 8a7c3cc8 Merge "Change the order of -frame argument to be more natural" 99e0a707 Merge "Simplify the texture evaluation Disto4x4()" 0f923c3f make the bundling work in a tmp buffer e5c3b3f5 Simplify the texture evaluation Disto4x4() 48600084 Change the order of -frame argument to be more natural 35bfd4c0 add SSE2 version of Sum of Square error for 16x16, 16x8 and 8x8 case a7305c2e Clarification for unknown chunks 4c4398e2 Refine WebP Container Spec wrt unknown chunks. 2ca642e0 Rectify WebPMuxGetFeatures: 7caab1d8 Some cosmetic/comment fixes. 60b2651a Merge "Write a GIF to WebP converter based on libgif." c7127a4d Merge "Add NEON version of FTransformWHT" 11b27212 Write a GIF to WebP converter based on libgif. e9a15a37 ExUtilWriteFile() to write memory segment to file 74356eb5 Add a simple cleanup step in mux assembly: 51bb1e5d mux.h: correct WebPDemuxSelectFragment() prototype 22a0fd9d Add NEON version of FTransformWHT fa30c863 Update mux code to match the spec wrt animation d9c5fbef by-pass Analysis pass in case segments=1 d2ad4450 Merge changes Ibeccffc3,Id1585b16 5c8be251 Merge "Chunk fourCCs for XMP/EXIF" a00a3daf Use 'frgm' instead of 'tile' in webpmux parameters 81b8a741 Design change in ANMF and FRGM chunks: f903cbab Chunk fourCCs for XMP/EXIF 812933d6 Tune performance of HistogramCombine 52ad1979 Animation specification in container spec 001b9302 Image fragment specification in container spec 391f9db9 Ordering of description of bits in container spec d5735776 Metadata specification in container spec 1c4609b1 Merge commit 'v0.2.1' 0ca584cb Merge "Color profile specification in container spec" e8b41ad1 add NEON asm version for WHT inverse transform af6f0db2 Color profile specification in container spec a61a824b Merge "Add NULL check in chunk APIs" 0e8b7eed fix WebPPictureView() unassigned strides 75e5f17e ARM/NEON: 30% encoding speed-up 02b43568 Add NULL check in chunk APIs a0770727 mux struct naming 6c66dde8 Merge "Tune Lossless encoder" ab5ea217 Tune Lossless encoder 74fefc8c Update ChangeLog (tag: v0.2.1, origin/0.2.0) 92f8059c Rename some chunks: 3bb4bbeb Merge "Mux API change:" d0c79f05 Mux API change: abc06044 Merge "update NEWS" into 0.2.0 57cf313b update NEWS 25f585c4 bump version to 0.2.1 fed7c048 libwebp: validate chunk size in ParseOptionalChunks 552cd9bc cwebp (windows): fix alpha image import on XP b14fea99 autoconf/libwebp: enable dll builds for mingw 4a8fb272 [cd]webp: always output windows errors d6621580 fix double to float conversion warning 72b96a69 cwebp: fix jpg encodes on XP 734f762a VP8LAllocateHistogramSet: fix overflow in size calculation f9cb58fb GetHistoBits: fix integer overflow b30add20 EncodeImageInternal: fix uninitialized free 3de58d77 fix the -g/O3 discrepancy for 32bit compile 77aa7d50 fix the BITS=8 case e5970bda Make *InitSSE2() functions be empty on non-SSE2 platform ef5cc47e make *InitSSE2() functions be empty on non-SSE2 platform c4ea259d make VP8DspInitNEON() public 8344eadf Merge "libwebp: validate chunk size in ParseOptionalChunks" 4828bb93 Merge "cwebp (windows): fix alpha image import on XP" 30763333 libwebp: validate chunk size in ParseOptionalChunks 70481898 AccumulateLSIM: fix double -> float warnings eda8ee4b cwebp (windows): fix alpha image import on XP c6e98658 Merge "add EXPERIMENTAL code for YUV-JPEG colorspace" f0360b4f add EXPERIMENTAL code for YUV-JPEG colorspace f86e6abe add LSIM metric to WebPPictureDistortion() c3aa215a Speed up HistogramCombine for lower qualities. 1765cb1c Merge "autoconf/libwebp: enable dll builds for mingw" a13562e8 autoconf/libwebp: enable dll builds for mingw 9f469b57 typo: no_fancy -> no_fancy_upsampling 1a27f2f8 Merge "fix double to float conversion warning" cf1e90de Merge "cwebp: fix jpg encodes on XP" f2b5d19b [cd]webp: always output windows errors e855208c fix double to float conversion warning ecd66f77 cwebp: fix jpg encodes on XP 7b3eb372 Tune lossless compression to get better gains. ce8bff45 Merge "VP8LAllocateHistogramSet: fix overflow in size calculation" ab5b67a1 Merge "EncodeImageInternal: fix uninitialized free" 7fee5d12 Merge "GetHistoBits: fix integer overflow" a6ae04d4 VP8LAllocateHistogramSet: fix overflow in size calculation 80237c43 GetHistoBits: fix integer overflow 8a997235 EncodeImageInternal: fix uninitialized free 0b9e6829 minor cosmetics a792b913 fix the -g/O3 discrepancy for 32bit compile 73ba4357 Merge "detect and merge similar segments" fee66275 detect and merge similar segments 0c44f415 src/webp/*.h: don't forward declare enums in C++ d7a5ac86 vwebp: use demux interface 931e0ea1 Merge "replace 'typedef struct {} X;" by "typedef struct X X; struct X {};"" 8f216f7e remove cases of equal comparison for qsort() 28d25c82 replace 'typedef struct {} X;" by "typedef struct X X; struct X {};" 2afee60a speed up for ARM using 8bit for boolean decoder 5725caba new segmentation algorithm 2cf1f815 Merge "fix the BITS=8 case" 12f78aec fix the BITS=8 case 6920c71f fix MSVC warnings regarding implicit uint64 to uint32 conversions f6c096aa webpmux binary: Rename 'xmp' option to 'meta' ddfe871a webpmux help correction b7c55442 Merge "Make *InitSSE2() functions be empty on non-SSE2 platform" 1c04a0d4 Common APIs for chunks metadata and color profile. 2a3117a1 Merge "Create WebPMuxFrameInfo struct for Mux APIs" 5c3a7231 Make *InitSSE2() functions be empty on non-SSE2 platform 7c6e60f4 make *InitSSE2() functions be empty on non-SSE2 platform c7eb4576 make VP8DspInitNEON() public ab3234ae Create WebPMuxFrameInfo struct for Mux APIs e3990fd8 Alignment fixes e55fbd6d Merge branch '0.2.0' 4238bc0a Update ChangeLog (tag: v0.2.0) c655380c dec/io.c: cosmetics fe1958f1 RGBA4444: harmonize lossless/lossy alpha values 681cb30a fix RGBA4444 output w/fancy upsampling f06c1d8f Merge "Alignment fix" into 0.2.0 f56e98fd Alignment fix 6fe843ba avoid rgb-premultiply if there's only trivial alpha values 528a11af fix the ARGB4444 premultiply arithmetic a0a48855 Lossless decoder fix for a special transform order 62dd9bb2 Update encoding heuristic w.r.t palette colors. 6f4272b0 remove unused ApplyInverseTransform() 93bf0faa Update ChangeLog (tag: v0.2.0-rc1) 5934fc59 update AUTHORS 014a711d update NEWS 43b0d610 add support for ARGB -> YUVA conversion for lossless decoder 33705ca0 bump version to 0.2.0 c40d7ef1 fix alpha-plane check + add extra checks a06f8023 MODE_YUVA: set alpha to opaque if the image has none 52a87dd7 Merge "silence one more warning" into 0.2.0 3b023093 silence one more warning f94b04f0 move some RGB->YUV functions to yuv.h 4b71ba03 README: sync [cd]webp help output c9ae57f5 man/dwebp.1: add links to output file format details 292ec5cc quiet a few 'uninitialized' warnings 4af3f6c4 fix indentation 9b261bf5 remove the last NOT_HAVE_LOG2 instances 323dc4d9 remove use of log2(). Use VP8LFastLog2() instead. 8c515d54 Merge "harness some malloc/calloc to use WebPSafeMalloc and WebPSafeCalloc" into 0.2.0 d4b4bb02 Merge changes I46090628,I1a41b2ce into 0.2.0 bff34ac1 harness some malloc/calloc to use WebPSafeMalloc and WebPSafeCalloc a3c063c7 Merge "extra size check for security" into 0.2.0 5e796300 Merge "WebPEncode: clear stats at the start of encode" into 0.2.0 f1edf62f Merge "rationalize use of color-cache" into 0.2.0 c1933317 extra size check for security 906be657 rationalize use of color-cache dd1c3873 Add image-hint for low-color images. 4eb7aa64 Merge "WebPCheckMalloc() and WebPCheckCalloc():" into 0.2.0 80cc7303 WebPCheckMalloc() and WebPCheckCalloc(): 183cba83 check VP8LBitWriterInit return cbfa9eec lossless: fix crash on user abort 256afefa cwebp: exit immediately on version mismatch 475d87d7 WebPEncode: clear stats at the start of encode a7cc7291 fix type and conversion warnings 7d853d79 add stats for lossless d39177b7 make QuantizeLevels() store the sum of squared error 5955cf5e replace x*155/100 by x*101581>>16 7d732f90 make QuantizeLevels() store the sum of squared error e45a446a replace x*155/100 by x*101581>>16 159b75d3 cwebp output size consistency: cbee59eb Merge commit 'v0.1.99' 1889e9b6 dwebp: report -alpha option 3bc3f7c0 Merge "dwebp: add PAM output support" into 0.2.0 d919ed06 dwebp: add PAM output support 85e215d3 README/manpages/configure: update website link c3a207b9 Update ChangeLog (tag: v0.1.99) d1fd7826 Merge "add extra precision about default values and behaviour" into 0.2.0 efc826e0 add extra precision about default values and behaviour 9f29635d header/doc clean up ff9fd1ba Makefile.vc: fix webpmux.exe *-dynamic builds 8aacc7b0 remove INAM, ICOP, ... chunks from the test webp file. 2fc13015 harmonize authors as "Name (mail@address)" 4a9f37b7 Merge "update NEWS" into 0.2.0 7415ae13 makefile.unix: provide examples/webpmux target ce82cedc update NEWS 641e28e8 Merge "man/cwebp.1: wording, change the date" into 0.2.0 c37c23e5 README: cosmetics 3976dcd5 man/cwebp.1: wording, change the date 3e5bbe1c Merge "rename 'use_argb_input' to 'use_argb'" into 0.2.0 ce90847a Merge "add some padding bytes areas for later use" into 0.2.0 2390dabc Merge "fixing the findings by Frederic Kayser to the bitstream spec" into 0.2.0 02751591 add a very crude progress report for lossless a4b9b1c6 Remove some unused enum values. dd108176 rename 'use_argb_input' to 'use_argb' 90516ae8 add some padding bytes areas for later use d03b2503 fixing the findings by Frederic Kayser to the bitstream spec ce156afc add missing ABI compatibility checks 9d45416a Merge "Doc: container spec text tweaks" into 0.2.0 4e2e0a8c Doc: container spec text tweaks f7f16a29 add ABI compatibility check 2a775570 Merge "swig: add WebPEncodeLossless* wrappers" into 0.2.0 a3ec6225 mux.h: remove '* const' from function parameters 31426eba encode.h: remove '* const' from function parameters 9838e5d5 decode.h: remove '* const' from function parameters 4972302d swig: add WebPEncodeLossless* wrappers 9ff00cae bump encoder/decoder versions c2416c9b add lossless quick encoding functions to the public API 4c1f5d64 Merge "NEWS: mention decode_vp8.h is no longer installed" into 0.2.0 6cb2277d NEWS: mention decode_vp8.h is no longer installed d5e5ad63 move decode_vp8.h from webp/ to dec/ 8d3b04a2 Merge "header clean-up" into 0.2.0 02201c35 Merge "remove one malloc() by making color_cache non dynamic" into 0.2.0 d708ec14 Merge "move MIN/MAX_HISTO_BITS to format_constants.h" into 0.2.0 ab2da3e9 Merge "add a malloc() check" into 0.2.0 2d571bd8 add a malloc() check 7f0c178e remove one malloc() by making color_cache non dynamic 6569cd7c Merge "VP8LFillBitWindow: use 64-bit path for msvc x64 builds" into 0.2.0 23d34f31 header clean-up 2a3ab6f9 move MIN/MAX_HISTO_BITS to format_constants.h 985d3da6 Merge "shuffle variables in HashChainFindCopy" into 0.2.0 cdf885c6 shuffle variables in HashChainFindCopy c3b014db Android.mk: add missing lossless files 8c1cc6b5 makefile.unix dist: explicitly name installed includes 7f4647ee Merge "clarify the colorspace naming and byte ordering of decoded samples" into 0.2.0 cbf69724 clarify the colorspace naming and byte ordering of decoded samples 857650c8 Mux: Add WebPDataInit() and remove WebPImageInfo ff771e77 don't install webp/decode_vp8.h 596dff78 VP8LFillBitWindow: use 64-bit path for msvc x64 builds 3ca7ce98 Merge "doc: remove non-finalized chunk references" into 0.2.0 1efaa5a3 Merge "bump versions" into 0.2.0 51fa13e1 Merge "README: update cwebp help output" into 0.2.0 12f9aede README: update cwebp help output f0b5defb bump versions 4c42a61b update AUTHORS 6431a1ce doc: remove non-finalized chunk references 8130c4cc Merge "build: remove libwebpmux from default targets/config" 23b44438 Merge "configure: broaden test for libpng-config" 85bff2cd Merge "doc: correct lossless prefix coding table & code" 05108f6e Merge "More spec/code matching in mux:" 6808e69d More spec/code matching in mux: bd2b46f5 Merge "doc/webp-container-spec: light cosmetics" 20ead329 doc/webp-container-spec: light cosmetics 1d40a8bc configure: add pthread detection b5e9067a fix some int <-> size_t mix for buffer sizes e41a7596 build: remove libwebpmux from default targets/config 0fc2baae configure: broaden test for libpng-config 45b8272c Merge "restore authorship to lossless bitstream doc" 06ba0590 restore authorship to lossless bitstream doc 44a09a3c add missing description of the alpha filtering methods 63db87dd Merge "vwebp: add checkboard background for alpha display" a73b8978 vwebp: add checkboard background for alpha display 939158ce Merge "vwebp: fix info display" b35c07d9 vwebp: fix info display 48b39eb1 fix underflow for very short bitstreams 7e622984 cosmetics: param alignment, manpage wording 1bd7dd50 Merge changes I7b0afb0d,I7ecc9708 ac69e63e Merge "Updated cwebp man's help for Alpha & Lossless." c0e8859d Get rid of image_info_ from WebPChunk struct. 135ca69e WebP Container Spec: eb6f9b8a Updated cwebp man's help for Alpha & Lossless. 0fa844fb cosmetic fixes on assert and 'const' where applicable 7f22bd25 check limit of width * height is 32 bits 16c46e83 autoconf/make: cosmetics: break long lines ab22a07a configure: add helper macro to define --with-* c17699b3 configure: add libtiff test 0e09732c Merge "cwebp: fix crash with yuv input + lossless" 88a510ff Merge "fix big-endian VP8LWriteBits" da99e3bf Merge "Makefile.vc: split mux into separate lib" 7bda392b cwebp: fix crash with yuv input + lossless f56a369a fix big-endian VP8LWriteBits 54169d6c Merge "cwebp: name InputFileFormat members consistently" e2feefa9 Makefile.vc: split mux into separate lib 27caa5aa Merge "cwebp: add basic TIFF support" d8921dd4 cwebp: name InputFileFormat members consistently 6f76d246 cwebp: add basic TIFF support 4691407b Merge changes If39ab7f5,I3658b5ae cca7c7b8 Fixed nit: 10 -> 10.f 5d09a244 WebPMuxCreate() error handling: 777341c3 Fix a memleak in WebPMuxCreate() 61c9d161 doc: correct lossless prefix coding table & code 4c397579 Merge "mark VP8{,L}{GetInfo,CheckSignature} as WEBP_EXTERN" e4e36cc6 Merge "Mux: Allow only some frames/tiles to have alpha." ad2aad3c Merge "WebP Decoding error handling:" 97649c8f Mux: Allow only some frames/tiles to have alpha. f864be3b Lower the quality settings for Alpha encoding. 3ba81bbe WebP Decoding error handling: fcc69923 add automatic YUVA/ARGB conversion during WebPEncode() 802e012a fix compilation in non-FANCY_UPSAMPLING mode e012dfd9 make width/height coding match the spec 228d96a5 mark VP8{,L}{GetInfo,CheckSignature} as WEBP_EXTERN 637a314f remove the now unused *KeepA variants d11f6fcc webpmux returns error strings rather than numbers fcec0593 makefile.unix: cwebp: fix OSX link 6b811f1b Merge "doc: remove lossless pdf" c9634821 doc: remove lossless pdf b9ae4f0d cosmetics after mux changes b74ed6e, b494ad5 b494ad50 Mux: only allow adding frame/tiles at the end. 2c341b0e Merge "Added image characteristic hint for the codec." d373076a Added image characteristic hint for the codec. 2ed2adb5 Merge "msvc: add intrinsic based BitsLog2Floor" e595e7c5 Merge "add demux.c to the makefiles" da47b5bd Merge "demux: add {Next,Prev}Chunk" e5f46742 add demux.c to the makefiles 4708393c demux: add {Next,Prev}Chunk e8a0a821 demux: quiet msvc warnings 7f8472a6 Update the WebP Container Spec. 31b68fe6 cleanup WebPPicture struct and API 9144a186 add overflow check before calling malloc() 81720c91 consistency cosmetics 2ebe8394 Merge "Add kramdown version information to README" 71443084 enc/vp8l.c: fix build b7ac19fe Add kramdown version information to README efdcb667 Merge "Edit for consistency, usage and grammar." 08220102 Enable alpha in vvwebp 8de9a084 Merge "Mux API change:" b74ed6e7 Mux API change: 233a589e take picture->argb_stride into account for lossless coding 04e33f17 Edit for consistency, usage and grammar. a575b4bc Merge "cosmetics: add missing const" 8d99b0f4 Merge "cosmetics: remove unimplemented function proto" 69d02217 cosmetics: add missing const 5b08318b cosmetics: remove unimplemented function proto b7fb0ed5 Log warning for unsupported options for lossless. e1f769fe msvc: add intrinsic based BitsLog2Floor 8a69c7d8 Bug-fix: Clamp backward dist to 1. b5b6ac97 Merge "Bring the special writer 'WebPMemoryWriter' to public API" a6a1909f Merge "Fix floating point exception with cwebp -progress" f2cee067 Fix floating point exception with cwebp -progress 91b7a8c7 Bring the special writer 'WebPMemoryWriter' to public API 310e2972 support resize and crop for RGBA input a89835d3 Merge changes Ice662960,Ie8d7aa90,I2d996d5e,I01c04772 ce614c0c Merge "dec/vp8: avoid setting decoder status twice" 900285da dec/vp8: avoid setting decoder status twice 8227adc8 Merge changes I6f02b0d0,I5cbc9c0a,I9dd9d4ed,Id684d2a1 dcda59c1 Merge "demux: rename SetTile to SelectTile" 622ef12e demux: rename SetTile to SelectTile 81ebd375 Merge "demux: add {Next,Prev}Frame" 02dd37a2 demux: add {Next,Prev}Frame 4b79fa59 Merge "Limit the maximum size of huffman Image to 16MB." 9aa34b34 Manually number "chapters," as chapter numbers are used in the narrative. 2a4c6c29 Re-wrap at <= 72 columns a45adc19 Apply inline emphasis and monospacing, per gdoc / PDF 91011206 Incorporate gdoc changes through 2012-06-08 7a182487 Removed CodeRay syntax declarations ... b3ec18c5 Provide for code-block syntax highlighting. 709d7702 Replace high ASCII artifacts (curly quotes, etc.). 930e8abb Lossless WebP doc largely ported to markdown text. 18cae37b msvc: silence some build warnings b3923084 Limit the maximum size of huffman Image to 16MB. f180df2a Merge "libwebp/demux: add Frame/Chunk iteration" 2bbe1c9a Merge "Enable lossless encoder code" d0601b01 Merge changes I1d97a633,I81c59093 78f3e345 Enable lossless encoder code d974a9cc Merge "libwebp/demux: add simple format parsing" 26bf2232 Merge "libwebp: add WebPDemux stub functions" 2f666688 Merge "modify WebPParseHeaders to allow reuse by GetFeatures" b402b1fb libwebp/demux: add Frame/Chunk iteration ad9ada3b libwebp/demux: add WebPDemuxGetI 2f2d4d58 libwebp/demux: add extended format parsing 962dcef6 libwebp/demux: add simple format parsing f8f94081 libwebp: add WebPDemux stub functions fb47bb5c Merge "NumNamedElements() should take an enum param." 7c689805 Fix asserts in Palette and BackwardReference code. fbdcb7ea NumNamedElements() should take an enum param. fb4943bd modify WebPParseHeaders to allow reuse by GetFeatures 3697b5ce write an ad-hoc EncodeImageInternal variant eaee9e79 Bug-Fix: Decode small (less than 32 bytes) images. 0bceae48 Merge "cwebp: fix alpha reporting in stats output" 0424b1ef Rebase default encoding settings. c71ff9e3 cwebp: fix alpha reporting in stats output e2ffe446 Merge "Stop indefinite recursion for Huffman Image." 70eb2bd6 Stop indefinite recursion for Huffman Image. f3bab8eb Update vwebp 6d5c797c Remove support for partial files in Mux. f1df5587 WebPMuxAssemble() returns WebPData*. 814a0639 Rename 'Add' APIs to 'Set'. bbb0218f Update Mux psuedo-code examples. 4fc4a47f Use WebPData in MUX set APIs c67bc979 Merge "add WebPPictureImportRGBX() and WebPPictureImportBGRX()" 27519bc2 add WebPPictureImportRGBX() and WebPPictureImportBGRX() f80cd27e factorize code in Import() 9b715026 histogram: add log2 wrapper 8c34378f Merge "fix some implicit type conversion warnings" 42f6df9d fix some implicit type conversion warnings 250c16e3 Merge "doc: update lossless pdf" 9d9daba4 Merge "add a PDF of the lossless spec" 8fbb9188 prefer webp/types.h over stdint.h 0ca170c2 doc: update lossless pdf 0862ac6e add a PDF of the lossless spec 437999fb introduce a generic WebPPictureHasTransparency() function d2b6c6c0 cosmetic fixes after Idaba281a b4e6645c Merge "add colorspace for premultiplied alpha" 48f82757 add colorspace for premultiplied alpha 069f903a Change in lossless bit-stream. 5f7bb3f5 Merge "WebPReportProgress: use non-encoder specific params" f18281ff WebPReportProgress: use non-encoder specific params 9ef32283 Add support for raw lossless bitstream in decoder. 7cbee29a Fix bug: InitIo reseting fancy_upsampling flag. 880fd98c vwebp: fix exit w/freeglut 1875d926 trap two unchecked error conditions 87b4a908 no need to have mux.h as noinst clause in enc/ 88f41ec6 doc: fix bit alignment in VP8X chunk 52f5a4ef Merge "fix bug with lossy-alpha output stride" 3bde22d7 fix bug with lossy-alpha output stride 42d61b6d update the spec for the lossy-alpha compression methods. e75dc805 Move some more defines to format_constants.h c13f6632 Move consts to internal header format_constants.h 7f2dfc92 use a bit-set transforms_seen_ instead of looping 18da1f53 modulate alpha-compression effort according to config.method f5f2fff6 Merge "Alpha flag fix for lossless." c975c44e Alpha flag fix for lossless. 4f067fb2 Merge "Android: only build dec_neon with NEON support" 255c66b4 Android: only build dec_neon with NEON support 8f9117a9 cosmetics: signature fixes 39bf5d64 use header-less lossless bitstream for alpha channel 75d7f3b2 Merge "make input data be 'const' for VP8LInverseTransform()" 9a721c6d make input data be 'const' for VP8LInverseTransform() 9fc64edc Disallow re-use of same transformation. 98ec717f use a function pointer for ProcessRows() f7ae5e37 cosmetics: join line 140b89a3 factor out buffer alloc in AllocateARGBBuffers() a107dfa8 Rectify WebPParseOptionalChunks(). 237eab67 Add two more color-spaces for lossless decoding. 27f417ab fix orthographic typo 489ec335 add VP8LEncodeStream() to compress lossless image stream fa8bc3db make WebPEncodingSetError() take a const picture 638528cd bitstream update for lossy alpha compression d73e63a7 add DequantizeLevels() placeholder ec122e09 remove arch-dependent rand() d40e7653 fix alignment 1dd6a8b6 Merge "remove tcoder, switch alpha-plane compression to lossless" 3e863dda remove tcoder, switch alpha-plane compression to lossless 8d77dc29 Add support for lossless in mux: 831bd131 Make tile size a function of encoding method. 778c5228 Merge "remove some variable shadowing" 817c9dce Few more HuffmanTreeToken conversions. 37a77a6b remove some variable shadowing 89c07c96 Merge "normalize example header includes" 4aff411f Merge "add example_util.[hc]" 00b29e28 normalize example header includes 061263a7 add example_util.[hc] c6882c49 merge all tree processing into a single VP8LProcessTree() 9c7a3cf5 fix VP8LHistogramNumCodes to handle the case palette_code_bits == 0 b5551d2e Merge "Added HuffmanTreeCode Struct for tree codes." 8b85d01c Added HuffmanTreeCode Struct for tree codes. 093f76d8 Merge "Allocate single memory in GetHuffBitLengthsAndCodes." 41d80494 Allocate single memory in GetHuffBitLengthsAndCodes. 1b04f6d2 Correct size in VP8L header. 2924a5ae Makefile.vc: split object lists based on directory c8f24165 Merge "add assert(tokens)" 43239947 add assert(tokens) 9f547450 Catch an error in DecodeImageData(). ac8e5e42 minor typo and style fix 9f566d1d clean-up around Huffman-encode c579a710 Introduce CHUNK_SIZE_BYTES in muxi.h. 14757f8a Make sure huffman trees always have valid symbols 41050618 makefile.unix: add support for building vwebp 48b37721 Merge "fixed signed/unsigned comparison warning" 57f696da Merge "EncodeImageInternal: fix potential leak" d972cdf2 EncodeImageInternal: fix potential leak 5cd12c3d fixed signed/unsigned comparison warning cdca30d0 Merge "cosmetics: shorten long line" e025fb55 cosmetics: shorten long line 22671ed6 Merge "enc/vp8l: fix double free on error" e1b9b052 Merge "cosmetics: VP8LCreateHuffmanTree: fix indent" a8e725f8 enc/vp8l: fix double free on error 27541fbd cosmetics: VP8LCreateHuffmanTree: fix indent 1d38b258 cwebp/windows: use MAKE_REFGUID where appropriate 817ef6e9 Merge "cwebp: fix WIC/Microsoft SDK compatibility issue" 902d3e3b cwebp: fix WIC/Microsoft SDK compatibility issue 89d803c4 Merge "Fix a crash due to wrong pointer-integer arithmetic." cb1bd741 Merge "Fix a crash in lossless decoder." de2fe202 Merge "Some cleanup in VP8LCreateHuffmanTree() (and related functions CompareHuffmanTrees() and SetBitDepths()): - Move 'tree_size' initialization and malloc for 'tree + tree_pool' outside the loop. - Some renames/tweaks for readability." ce69177a Fix a crash due to wrong pointer-integer arithmetic. e40a3684 Fix a crash in lossless decoder. 3927ff3a remove unneeded error condition for WebPMuxNumNamedElements() 2c140e11 Some cleanup in VP8LCreateHuffmanTree() (and related functions CompareHuffmanTrees() and SetBitDepths()): - Move 'tree_size' initialization and malloc for 'tree + tree_pool' outside the loop. - Some renames/tweaks for readability. 861a5b7b add support for animation eb5c16cc Merge "Set correct encode size in encoder's stats." 4abe04a2 fix the return value and handle missing input file case. 2fafb855 Set correct encode size in encoder's stats. e7167a2b Provide one entry point for backward references. c4ccab64 Print relevant lossless encoding stats in cwebp. e3302cfd GetHuffBitLengthsAndCodes: reduce level of indirection b5f2a9ed enc/vp8l: fix uninitialized variable warning 7885f8b2 makefile.unix: add lossless encoder files 1261a4c8 Merge "cosmetics" 3926b5be Merge "dsp/cpu.c: Android: fix crash on non-neon arm builds" 834f937f dsp/cpu.c: Android: fix crash on non-neon arm builds 126e1606 cosmetics e38602d2 Merge branch 'lossless_encoder' e8d3d6a0 split StoreHuffmanCode() into smaller functions d0d88990 more consolidation: introduce VP8LHistogramSet 1a210ef1 big code clean-up and refactoring and optimization 41b5c8ff Some cosmetics in histogram.c ada6ff77 Approximate FastLog between value range [256, 8192] ec123ca3 Forgot to update out_bit_costs to symbol_bit_costs at one instance. cf33ccd1 Evaluate output cluster's bit_costs once in HistogramRefine. 781c01f4 Simple Huffman code changes. a2849bc5 Lossless decoder: remove an unneeded param in ReadHuffmanCodeLengths(). b39e7487 Reducing emerging palette size from 11 to 9 bits. bfc73db4 Move GetHistImageSymbols to histogram.c 889a5786 Improve predict vs no-predict heuristic. 01f50663 code-moving and clean-up 31035f3b reduce memory usage by allocating only one histo fbb501b8 Restrict histo_bits to ensure histo_image size is under 32MB 8415ddf3 further simplification for the meta-Huffman coding e4917299 A quick pass of cleanup in backward reference code 83332b3c Make transform bits a function of encode method (-m). 72920caa introduce -lossless option, protected by USE_LOSSLESS_ENCODER c6ac4dfb Run TraceBackwards for higher qualities. 412222c8 Make histo_bits and transform_bits function of quality. 149b5098 Update lossless encoder strategy: 0e6fa065 cache_bits passed to EncodeImageInternal() e38b40a9 Factorize code for clearing HtreeGroup. 6f4a16ea Removing the indirection of meta-huffman tables. 3d33ecd1 Some renaming/comments related to palette in lossless encoder. 4d02d586 Lossless encoder: correction in Palette storage 4a636235 fix a memleak in EncodeImageInternal() 0993a611 Full and final fix for prediction transform afd2102f Fix cross-color transform in lossless encoder b96d8740 Need to write a '0' bit at the end of transforms. 54dad7e5 Color cache size should be counted as 0 when cache bits = 0 4f0c5caf Fix prediction transform in lossless encoder. 36dabdad Fix memory leak in method EncodeImageInternal for histogram_image. 352a4f49 Get rid of PackLiteralBitLengths() d673b6b9 Change the predictor function to pass left pixel b2f99465 Fix CopyTileWithPrediction() 84547f54 Add EncodeImageInternal() method. 6b38378a Guard the lossless encoder (in flux) under a flag 09f7532c Fix few nits (const qualifiers) 648be393 Added implementation for various lossless functions 32714ce3 Add VP8L prefix to backward ref & histogram methods. fcba7be2 Fixed header file tag (WEBP_UTILS_HUFFMAN_ENCODE_H_) bc703746 Add backward_ref, histogram & huffman encode modules from lossless. fdccaadd Fixing nits 227110c4 libwebp interface changes for lossless encoding. 50679acf minor style fixes b38dfccf remove unneeded reference to NUM_LITERAL_CODES 8979675b harmonize header description c04eb7be tcoder.c: define NOT_HAVE_LOG2 for MSVC builds 9a214fa1 Merge "VP8[L]GetInfo: check input pointers" 5c5be8ba VP8[L]GetInfo: check input pointers 0c188fec Merge changes I431acdfe,I713659b7 b3515c62 mux: drop 'chunk' from ChunkInfo member names aea7923c muxi.h: remove some unused defines 01422492 update NEWS file for next release 29e3f7ec Merge "dec: remove deprecated WebPINew()" 4718e449 Merge "muxedit: a few more size_t changes" 82654f96 Merge "muxedit: remove a few redundant NULL checks" 02f27fbd dec: remove deprecated WebPINew() ccddb3fc muxedit: remove a few redundant NULL checks a6cdf710 muxedit: a few more size_t changes a3846892 Merge "mux: remove unused LIST_ID" 11ae46ae alpha.c: quiet some size_t -> int conversion warnings dee46692 mux: remove unused LIST_ID 03f1f493 mux: add version checked entry points 6a0abdaa Merge "doc: tile/alpha corrections" c8139fbe Merge "few cosmetics" 68338737 Merge "lossless: remove some size_t -> int conversions" 5249e94a doc: tile/alpha corrections d96e722b huffman: quiet int64 -> int conversion warning 532020f2 lossless: remove some size_t -> int conversions 23be6edf few cosmetics 1349edad Merge "configure: AC_ARG_* use AS_HELP_STRING" bfbcc60a configure: AC_ARG_* use AS_HELP_STRING 1427ca8e Merge "Makefile.am: header file maintenance" 087332e3 Merge "remove unused parameter 'round' from CalcProba()" 9630e168 remove unused parameter 'round' from CalcProba() 92092eaa Merge "bit_reader.h: correct include" a87fc3f6 Merge "mux: ensure # images = # tiles" 53af99b1 Merge "mux: use size_t consistently" 39a57dae Makefile.am: header file maintenance 1bd0bd0d bit_reader.h: correct include 326a3c6b mux: ensure # images = # tiles 95667b8d mux: use size_t consistently 231ec1fb Removing the indirection of meta-huffman tables. 15ebcbaa check return pointer from MuxImageGetListFromId b0d6c4a7 Merge "configure: remove test for zlib.h" 8cccac50 Merge "dsp/lossless: silence some build warnings" b08819a6 dsp/lossless: silence some build warnings 7ae22521 Android.mk: SSE2 & NEON updates 0a49e3f3 Merge "makefile.unix add missing header files" 2e75a9a1 Merge "decode.h: use size_t consistently" fa13035e configure: remove test for zlib.h d3adc81d makefile.unix add missing header files 262fe01b Merge "makefile.unix & Android.mk: cosmetics" 4cce137e Merge "enc_sse2 add missing stdlib.h include" 80256b85 enc_sse2 add missing stdlib.h include 9b3d1f3a decode.h: use size_t consistently 64083d3c Merge "Makefile.am: cosmetics" dceb8b4d Merge changes If1331d3c,I86fe3847 0e33d7bf Merge "webp/decode.h: fix prototypes" fac0f12e rename BitReader to VP8LBitReader fbd82b5a types.h: centralize use of stddef.h 2154835f Makefile.am: cosmetics 1c92bd37 vp8io: use size_t for buffer size 90ead710 fix some more uint32_t -> size_t typing cbe705c7 webp/decode.h: fix prototypes 3f8ec1c2 makefile.unix & Android.mk: cosmetics 217ec7f4 Remove tabs in configure.ac b3d35fc1 Merge "Android.mk & Makefile.vc: add new files" 0df04b9e Android.mk & Makefile.vc: add new files e4f20c5b Merge "automake: replace 'silent-rules' w/AM_SILENT_RULES" 8d254a09 cosmetics 6860c2ea fix some uint32_t -> size_t typing 4af1858a Fix a crash due to max symbol in a tree >= alphabet size 6f01b830 split the VP8 and VP8L decoding properly f2623dbe enable lossless decoder b96efd7d add dec/vp8i.h changes from experimental 19f6398e add dec/vp8l{i.h,.c} from experimental c4ae53c8 add utils/bit_reader.[hc] changes from experimental 514d0089 add dsp/lossless.[hc] from experimental 9c67291d add utils/huffman.[hc] from experimental 337914a0 add utils/color_cache.[hc] from experimental b3bf8fe7 the read-overflow code-path wasn't reporting as an error 1db888ba take colorspace into account when cropping 61c2d51f move the rescaling code into its own file and make enc/ and dec/ use it. efc2016a Make rescaler methods generic 3eacee81 Move rescaler methods out of io.c. a69b893d automake: replace 'silent-rules' w/AM_SILENT_RULES 6f7bf645 issue 111: fix little-endian problem in bit-reader ed278e22 Removed unnecessary lookup cd8c3ba7 fix some warnings: down-cast and possibly-uninitialized variable 0a7102ba ~1% improvement of alpha compression 3bc1b141 Merge "Reformat container doc" dc17abdc mux: cosmetics cb5810df Merge "WebPMuxGetImage: allow image param to be NULL" 506a4af2 mux: cosmetics 135e8b19 WebPMuxGetImage: allow image param to be NULL de556b68 Merge "README.mux: reword some descriptions" 0ee2aeb9 Makefile.vc: use batch mode rules d9acddc0 msvc: move {i,p}db creation to object directory 237c9aa7 Merge "expose WebPFree function for DLL builds" b3e4054f silence msvc debug build warning 45feb55d expose WebPFree function for DLL builds 11316d84 README.mux: reword some descriptions 4be52f4a factorize WebPMuxValidate 14f6b9f6 mux: light cleanup 5e96a5db add more param checks to WebPPictureDistortion() 8abaf820 Merge "silence some type size related warnings" 1601a39b silence some type size related warnings f3abe520 Merge "idec: simplify buffer size calculation" a9c5cd4c idec: simplify buffer size calculation 7b06bd7f Merge "configure/automake: add silent-rules option" e9a7d145 Reformat container doc d4e5c7f3 configure/automake: add silent-rules option 5081db78 configure/automake: no -version-info for convenience libs 85b6ff68 Merge "idec: fix WebPIUpdate failure" 7bb6a9cc idec: fix internal state corruption 89cd1bb8 idec: fix WebPIUpdate failure 01b63806 4-5% faster decoding, optimized byte loads in arithmetic decoder. 631117ea Merge "cosmetics & warnings" a0b2736d cosmetics & warnings f73947f4 use 32bit for storing dequant coeffs, instead of 16b. b9600308 Merge "store prediction mode array as uint8_t[16], not int[16]." 7b67881a store prediction mode array as uint8_t[16], not int[16]. cab8d4dc Merge "NEON TransformOne" ba503fda NEON TransformOne 9f740e3b Merge "gcc warning fix: remove the 'const' qualifier." f76d3587 gcc warning fix: remove the 'const' qualifier. e78478d6 Merge "webpmux: make more use of WebPData" f85bba3d Merge "manpages: add BUGS section" 48a43bbf Merge "makefile.unix: variable cosmetics" c274dc96 makefile.unix: variable cosmetics 1f7b8595 re-organize the error-handling in the main loop a bit 1336fa71 Only recompute level_cost_[] when needed 771ee449 manpages: add BUGS section 0f7820e6 webpmux: make more use of WebPData 974aaff3 examples: logging updates 6c14aadd Merge "better token buffer code" f4054250 better token buffer code 18d959fa Merge "mux: add WebPData type" eec4b877 mux: add WebPData type 0de3096b use 16bit counters for recording proba counts 7f23678d fix for LevelCost + little speed-up 7107d544 further speed-up/cleanup of RecordCoeffs() and GetResidualCost() fd221040 Introduce Token buffer (unused for now) 5fa148f4 Merge "speed-up GetResidualCost()" 28a9d9b4 speed-up GetResidualCost() 11e7dadd Merge "misc cosmetics" 378086bd misc cosmetics d61479f9 add -print_psnr and -print_ssim options to cwebp. 2e3e8b2e add a WebPCleanupTransparentArea() method 552c1217 Merge "mux: plug some memory leaks on error" a2a81f7d Merge "fix Mach-O shared library build" b3482c43 Merge "fix gcc-4.0 apple 32-bit build" e4e3ec19 fix gcc-4.0 apple 32-bit build b0d2fecf mux: plug some memory leaks on error f0d2c7a7 pass of cosmetics b309a6f9 fix Mach-O shared library build 241ddd38 doc: delete mux container pdf 8b1ba272 doc: update VP8 decode guide link 7e4371c5 WebPMuxCreate: fix unchecked malloc eb425586 Merge "have makefile.unix clean up src/webp/*~ too" a85c3631 Merge "correct EncodeAlpha documentation" a33842fd Merge "Update webp container spec with alpha filter options." 8d6490da Incremental support for some of the mux APIs. b8375abd have makefile.unix clean up src/webp/*~ too b5855fc7 correct EncodeAlpha documentation dba37fea Update webp container spec with alpha filter options. 2e74ec8b fix compile under MINGW 716d1d7f fix suboptimal MAX_LEN cut-off limit 57cab7b8 Harmonize the alpha-filter predictions at boundary 3a989534 Merge "Fix bug for Alpha in RGBA_4444 color-mode." 8ca2076d Introduce a 'fast' alpha mode 221a06bb Fix bug for Alpha in RGBA_4444 color-mode. ad1e163a cosmetics: normalize copyright headers c77424d7 cosmetics: light include cleanup 9d0e17c9 fix msvc build breakage after 252028a 7c4c177c Some readability fixes for mux library d8a47e66 Merge "Add predictive filtering option for Alpha." 252028aa Add predictive filtering option for Alpha. 9b69be1c Merge "Simplify mux library code" a056170e Simplify mux library code 992187a3 improve log2 test e852f832 update Android.mk file list a90cb2be reduce number of copies and mallocs in alpha plane enc/dec b1662b05 fix some more type conversion warnings w/MSVC 223d8c60 fix some uint64_t -> int conversion warnings with MSC c1a0437b Merge "simplify checks for enabling SSE2 code" f06817aa simplify checks for enabling SSE2 code 948d4fe9 silence a msvc build warning 91179549 vwebp: msvc build tweaks 7937b409 simple WebP viewer, based on OpenGL 6aac1df1 add a bunch of missing 'extern "C"' 421eb99d Merge "Remove assigned-but-not-used variable "br"" 91e27f45 better fitting names for upsampling functions a5d7ed5c Remove assigned-but-not-used variable "br" f62d2c94 remove unused 'has_alpha' from VP8GetInfo() signature 08e86582 trap alpha-decoding error b361eca1 add cut-off to arith coder probability update. 8666a93a Some bug-fixes for images with alpha. 273a12a0 fix off-by-1 diff in case cropping and simple filtering 2f741d1e webpmux: ReadImage: fix ptr free in error case 721f3f48 fix alpha decode 60942c8c fix the has_alpha_ order 30971c9e Implement progress report (and user abort) eda520a9 cosmetics after 9523f2a 38bd5bb5 Merge "Better alpha support in webpmux binary" ccbaebfe Merge "Updated the includes to relative paths." d71fbdcc fix small typo in error message array cdf97aa2 Better alpha support in webpmux binary 885f25bc Updated the includes to relative paths. a0ec9aac Update WebP encoder (cwebp) to support Alpha. 667b769a Fixed the include for types.h within mux.h 9523f2a5 Add Alpha Encode support from WebPEncode. 16612ddd Merge "Add Alpha Decode support from WebPDecode." d117a940 Add Alpha Decode support from WebPDecode. 67228734 cosmetics after e1947a9 e1947a92 Add Alpha encode/decode code. afc4c5d6 simplify code by introducing a CopyPlane() helper func 113b3128 Merge "MUX API Updates" c398f595 MUX API Updates 5acf04ef remove orphan source file 059f03ef Merge "dec: validate colorspace before using as array index" 70a03989 Merge "factorize some code" 9b243b3d factorize some code 372e2b46 Correct a bug in ReadPNG() with GRAY_ALPHA images 469d6eb9 Merge "Makefile.am: remove redundant noinst_HEADERS" 9fe3372f dec: validate colorspace before using as array index 8962030f remove orphan source file ced3e3f4 Makefile.am: remove redundant noinst_HEADERS 964387ed use WEBP_INLINE for inline function declarations 90880a11 Merge "manpages: break long lines" b5910895 Merge "manpages: minor formatting updates" 4c451e4a Merge "Rectify the Chunk parsing logic." 04e84cf1 examples: slight cleanup 099717ce manpages: break long lines 1daf39bb manpages: minor formatting updates abd030b5 fix missing "(void)" in function signature f6a7d758 remove useless test f07b2138 Rectify the Chunk parsing logic. b8634f7d webpmux: fix lib link order 42c2e682 Fix missing coma (on uncompiled code) d8329d41 Android.mk: add missing source files 13a54df5 Merge "More aggressive copy-edit; add TODO; validate HTML5" 868b96ae More aggressive copy-edit; add TODO; validate HTML5 767afea2 configure: check for a symbol contained in libpng 408b8918 Merge "Linewrap at 72 cols. Casual copy-edit." 3ae318c7 Merge "Restore (most) emphasis; add emphasis to normative RFC 2119 terms (MUST, etc.)" 918eb2d8 Merge "Basic container doc source clean-up; fix lists and pseudocode blocks." 03bec9e0 Linewrap at 72 cols. Casual copy-edit. 2678d819 Restore (most) emphasis; add emphasis to normative RFC 2119 terms (MUST, etc.) 428674da Basic container doc source clean-up; fix lists and pseudocode blocks. 6a77d928 Merge "Makefile.vc: cosmetics" 28c38e8c Merge "Makefile.vc: condense directory creation rules" 55be2cf8 Initial import of container spec document, from pdftotext transform. a82a788b Makefile.vc: cosmetics c8f41ce5 Makefile.vc: condense directory creation rules 2b877cd0 Some fixes to Makefile.vc to support the src\mux directory. 3eb969b3 Merge "Add Makefile.vc for Mux library & binary." e78e971e Add Makefile.vc for Mux library & binary. 6aedde58 Add manual for WebPMux tool. 8a360d0a Merge "Added WebPMux Binary." a4f32cae Added WebPMux Binary. f3bf4c76 Added Mux Container Spec & README for MUX-API. 9f761cfa Changed function signature for WebPMuxCreate 5f31b5ec Merge "Add Mux library for manipulating WebP container." 2315785f Add Mux library for manipulating WebP container. 7e198abb update ChangeLog (tag: v0.1.3) dfc9c1ea Harmonize the dates 28ad70c5 Fix PNG decoding bug 846e93c5 Update AUTHORS & add .mailmap 563e52d6 cosmetics after '76036f5 Refactor decoder library' 76036f54 Refactor decoder library 377ef43c configure.ac: update AC_INIT params 7a8d8762 use a user-visible MACRO for max width/height. d4e9f559 NEON decode support in WebP 0ee683b5 update libtool version-info fdbe02c5 windows: match _cond_destroy logic w/return variable name 206b686b README: correct advanced decode api pseudo-code 6a32a0f5 make VP8BitReader a typedef, for better re-use b112e836 create a libwebputils under src/utils ee697d9f harmonize the include guards and #endif comments a1ec07a6 Fixing compiler error in non x86 arch. dcfa509a Fixed recursive inclusion of bit_writer.h and vp8enci.h. e06ac088 create a separate libwebpdsp under src/dsp ebeb412a use unsigned int for bitfields 341cc56a make kNewRange a static array 227a91e5 README: minor wording update 05bd8e6a add man pages to dist 812dfa1a bump up versions in preparations for 0.1.3 a5b78c81 wrap alpha-related options under WEBP_EXPERIMENTAL_FEATURES flag 34dc7907 regen ChangeLog for 0.1.3-rc2 7c436630 Silence some (more) Visual Studio warnings. 60306e8c add top-level gitattributes 2aa6b80e Slience some Visual Studio warnings. 4cbbb290 Merge "bump up version for next freeze" a3291674 bump up version for next freeze c7e86aba cosmetics: fix comment line lengths c9e037ab makefile.unix: add simple dist target 87d58ce9 makefile.unix: rule maintenance d477de77 mend fac15ec7 Update NEWS & README for next release V0.1.3 6215595c Merge "add a -partition_limit option to limit the number of bits used by intra4x4" 3814b76c Merge "reorganize chunk-parsing code" 900286e0 add a -partition_limit option to limit the number of bits used by intra4x4 cd12b4b0 add the missing cost for I4/I16 mode selection dfcc2136 reorganize chunk-parsing code 3cf20306 initialize pointers to function within VP8DspInit() d21b4795 Merge "windows: add decode threading support" 473ae953 fix hang on thread creation failure fccca420 windows: add decode threading support a31f843a Use the exact PNG_INCLUDES/PNG_LIBS when testing for -lpng ad9b45f1 Merge "Makefile.vc: rule maintenance" 565a2cab Makefile.vc: rule maintenance 2d0da681 makefile.unix: disable Wvla by default fc7815d6 multi-thread decoding: ~25-30% faster acd8ba42 io->teardown() was not always called upon error c85527b1 Merge "Makefile.vc: add DLL configs" e1e9be35 cosmetics: spelling/grammar in README and lib headers b4d0ef8f Makefile.vc: add DLL configs 998754a7 remove unused nb_i4_ and nb_i16_ fields. 9f01ce3a rename WebPDecBuffer::memory -> private_memory fb5d659b fix an overflow bug in LUT calculation d646d5c7 swig: add WebPDecodeARGB 78aeed40 add missing WebPDecodeARGBInto() and switch ARGB4444 to RGBA4444 as was intended cd7c5292 explicitly mark library functions as extern 19db59f8 add support for RGB565, ARGB4444 and ARGB colorspace (decoder) c915fb2a encoder speed-up: hardcode special level values c558bdad Rename and improve the API to retrieve decoded area bf599d74 Merge "makefile.unix: disable -Wvla by default" c9ea03d7 SSE2 version of strong filtering 993af3e2 makefile.unix: disable -Wvla by default 3827e1bc Merge "examples: (windows/WIC) add alpha support" e291fae0 SSE2 functions for the fancy upsampler. a06bbe2e add WebPISetIOHooks() to set some custom hooks on the incremental decoder object. 7643a6f2 Merge "makefile.unix: use uname to detect OSX environment" 5142a0be export alpha channel (if present) when dumping to PGM format 14d5731c makefile.unix: use uname to detect OSX environment 08057062 examples: quiet warnings 3cfe0888 examples: (windows/WIC) add alpha support 13ed94b8 add compile warning for variable-length-array 5a18eb1a Merge "add Advanced Decoding Interface" 5c4f27f9 add missing \n f4c4e416 80 cols fix d2603105 add Advanced Decoding Interface bd2f65f6 sse2 version of the complex filter 96ed9ce0 perform two idct transforms at a time when possible 01af7b69 use aligned stored 0e1d1fdf Merge "Makefile.vc: add experimental target" 2a1292a6 Makefile.vc: add experimental target 23bf351e Enable decode SSE2 for Visual Studio 131a4b7b dec/dsp_sse2: fix visual studio compile 00d9d680 swig: file reorganization 7fc7e0d9 Merge "swig/java: basic encode support" 3be57b16 fix MSVC compile for WEBP_EXPERIMENTAL_FEATURES 40a7e347 dec/dsp: disable sse2 for Visual Studio builds e4d540c8 add SSE2 code for transform 54f2170a swig/java: basic encode support c5d4584b call function pointers instead of C-version ea43f045 Merge "configure: mingw32 targets: test for WIC support" a11009d7 SSE2 version of simple in-loop filtering 42548da9 shave one unneeded filter-cache line 31f9dc6f configure: mingw32 targets: test for WIC support 19559699 Merge "split expression in two." 415dbe46 split expression in two. e29072a8 configure: test for zlib only w/--enable-experimental b2b0090b Simplify Visual Studio ifdefs ca7a2fd6 Add error reporting from encoding failures. 6c9405db Merge "Makefile.vc: require CFG with clean target" 0424ecd9 Makefile.vc: require CFG with clean target 003417c7 Enable SSE2 for Visual Studio builds af10db4a little speed up for VP8BitUpdate() e71418f8 more MSVC files to ignore 46d90363 cosmetics edf59ab3 typo fix 72229f5f Add support for x64 and SSE2 builds under Windows. 92e5c6e1 VP8GetInfo() + WebPResetDecParams() 416b7a6b raise the fixed-point precision for the rescaler aa87e4e0 fix alignment eb66670c disable WEBP_EXPERIMENTAL_FEATURES c5ae7f65 typo fix: USE_ => WEBP_ d041efae swig: add libwebp.jar/libwebp_java_wrap.c f6fb3877 add swig interface e9273902 align buffer for double too 842c009b fix -strong option d0a70387 Merge "cosmetics" fc0a02e5 fix the dichotomy loop 38369c03 cosmetics 8dfc4c6f factorize and unify GetAlpha() between the C and SSE2 version 6d0e66c2 prepare experimentation with yuv444 / 422 79cc49f5 add a --enable-experimental option to './configure' d7575238 sse2 version of CollectHistogram() c1c728d6 add an extra #ifdef WEBP_EXPERIMENTAL_FEATURES to avoid 'unused variable' warning 60c61d2d always call VP*EncDeleteAlpha() unconditionnally, for simplicity 0f8c6384 simply don't call WriteExtensions() if WEBP_EXPERIMENTAL_FEATURES is not defined 47c661d5 rename swap -> swap_rb 10d55bbb move chunk[] declaration out of the for() loop 517cec21 fix indentation f7d9e261 fix merge problems 8fd42b3a add a stride 'a_stride' for the alpha plane b8dcbf2f fix alpha-plane copy and crop methods cdef89de fix some 'unused variable' warning fb29c262 SSE2 version of the fwd transform and the squared sum metric 2ab4b72f EXPERIMENTAL: add support for alpha channel cfbf88a6 add SSE2 functions. ~2x faster encoding on average. e7ff3f9a merge two ITransforms together when applicable and change the TTransform to return the sum directly. ca554137 fix WebPIDecGetRGB() to accept any RGB(A) mode, not just MODE_RGB 8aa50efd fix some 'man' typos d3f3bdda update ChangeLog (tag: v0.1.2) d7e9a69c update contributor list 261abb8e add a 'superclean' section 276ae825 Remove files not mean to be in git, and update .gitignore 24868455 build: prepare libwebp.pc 14ceb6e8 add "-version" description to man pages b247a3b2 Create the m4 directory, and also place .gitignore in there for libtool. cdd734c9 Resolve automake warnings c5fa726e build: add pkgconfig files b20aaca2 build: just use autoreconf, avoid calling tools manually 4b0b0d66 cwebp: use modern functions efbc6c41 update Android.mk 7777570b better version of ChangeLog fa70d2b7 update version number in the DOC f8db5d5d more C89-fixes 0de013b3 fix typos 650ffa3b add version getters for decoder and encoder be4867d2 doc for incremental decoding 56732a1b add idec.obj in MSVC makefile 208afb5e add c++ guards 8bf76fe0 add incremental decoding 1f288328 'inline' isn't defined in strict ansi c89 8b77c632 move the quantization function to dsp.c b2c3575c add a 'last_y' field to WebPDecParams 2654c3da correctly pass along the exact same status returned from ParsePartitions 4704146a add missing precision in the man 6d978a6c add error messages 6463e6ab add some install instructions, and fix intel-mac flags 05fb7bfc Merge ".gitignore: initial version" c33f0195 .gitignore: initial version e532b9ab Makefile: allow out of tree builds 4c0da7aa enable sparse dc/ac transforms 07dbb8d5 clarify the return logic 5c69e1bb fix bigger-by-1 array 7c5267e3 fix a (harmless) typo: non_zero_ -> non_zero_ac_ bc752135 fix missing free() af3e2aaa remove trailing spaces 13e50da6 make the bitreader preload at least 8bits, instead of post-load them (this makes initialization easier and will be helpful for incremental decoding). Modify ParsePartitions() to accommodate for truncated input. f4888f77 emit 9 - nb_bits trailing zeros instead of 8 3db65255 separate block-parsing into a visible VP8DecodeMB() a871de02 add missing extern "C" b3ce8c52 remove a gcc warning about type pun by using a proper union'd type e1863715 update after addition of webpi.h 3e856e2d Extract some useful functions around decoding buffer WebPDecParams. d5bc05a4 make the filtering process match libvpx and ffvp8 dd60138d add man pages for cwebp(1) and dwebp(1) c4fa3644 fix header 5b70b378 * add an option to bypass_filtering in VP8Io. b97a4003 simplify QuantizeBlock code a bit 84b58ebb add more checks around picture allocation b65a3e10 remove absolute_delta_ field and syntax code 0744e842 Dont' open output file until we're sure the input file is valid d5bd54c7 fix typo and buggy line f7a9549d Add a simple top-level makefile.unix for quick & easy build. 5f36b944 update the doc for the -f option f61d14aa a WebP encoder converts PNG & JPEG to WebP 81c96621 oops: forgotten call to Initialize() + move the error message to a more useful place 87ffa005 typo: fix a missing 'R', was confusing. b04b857a * add decoding measurement using stopwatch.h (use -v option) * support PNG output through WIC on Win32 746a4820 * make (*put)() hook return a bool for abort request. * add an enum for VP8Status() to make things clearer 73c973e6 * strengthen riff/chunk size checks * don't consider odd-sized chunks being an error 1dc4611a add support for PNG output (default) regularize include guards 860641df fix a typo: sizeof(kYModeProbaInter0) => sizeof(kUVModeProbaInter0) 3254fc52 fix some petty constness fix the ./configure file too 504d3393 fix eof_ mis-initialization 2bc0778f leftover Makefile.* from previous commit d2cf04e4 move Makefile.am one level below, to src/dec fix typos here and there dwebp is now an installed program ade92de8 typo: vp8.h -> decode_vp8.h d7241241 forgot to declare types.h to be installed 6421a7a4 move the decoder sourcetree to a sub-location src/dec to make room for future libs sources a9b3eab6 correct layout name is IMC4. 2330522c handle corner case of zero-dimensions 280c3658 make VP8Init() handle short buffers (< 2 bytes) correctly b1c9e8b4 handle error cases more robustly 0e94935c Merge "table-less version of clip_8b()" 1e0a2d25 table-less version of clip_8b() e12109ee dwebp: change -yuv option to -raw change the layout to IMC2 d72180a4 speed-up fancy upscaler 9145f3bc reset eof_ at construction time a7ee0559 simplify the logic of GetCoeffs() f67b5939 lot of cosmetics ea27d7c6 fix endian problem on PowerPC beb0a1ba fix signature of VP8StoreBlock b128c5e2 Merge "fancy chroma upscaling" 6a37a2aa fancy chroma upscaling ff565edc fix two numeric typos 5a936a0a use uintptr_t for casting pointers to ints e14a0301 for cross_compiling=yes to prevent executing any binary 83b545ee add vc9+ makefile 296f6914 fix output loop for small height cbfbb5c3 convert to plain-C f09f96ee Fix declaration after statement warning 5981ee55 Fix UV plane ac/dc quantizer transposition c8d15efa convert to ANSI-C c3f41cb4 Initial commit PK1]/Q~~doc/alt-libwebp/README.mdnu[# WebP Codec ``` __ __ ____ ____ ____ / \\/ \/ _ \/ _ )/ _ \ \ / __/ _ \ __/ \__\__/\____/\_____/__/ ____ ___ / _/ / \ \ / _ \/ _/ / \_/ / / \ \ __/ \__ \____/____/\_____/_____/____/v1.6.0 ``` WebP codec is a library to encode and decode images in WebP format. This package contains the library that can be used in other programs to add WebP support, as well as the command line tools 'cwebp' and 'dwebp' to compress and decompress images respectively. See https://developers.google.com/speed/webp for details on the image format. The latest source tree is available at https://chromium.googlesource.com/webm/libwebp It is released under the same license as the WebM project. See https://www.webmproject.org/license/software/ or the "COPYING" file for details. An additional intellectual property rights grant can be found in the file PATENTS. ## Building See the [building documentation](doc/building.md). ## Encoding and Decoding Tools The examples/ directory contains tools to encode and decode images and animations, view information about WebP images, and more. See the [tools documentation](doc/tools.md). ## APIs See the [APIs documentation](doc/api.md), and API usage examples in the `examples/` directory. ## Bugs Please report all bugs to the [issue tracker](https://issues.webmproject.org). For security reports, select 'Security report' from the Template dropdown. Patches welcome! See [how to contribute](CONTRIBUTING.md). ## Discuss Email: webp-discuss@webmproject.org Web: https://groups.google.com/a/webmproject.org/group/webp-discuss PK1];< >>doc/alt-libwebp/NEWSnu[- 6/30/2025 version 1.6.0 This is a binary compatible release. API changes: - libwebp: WebPValidateDecoderConfig * additional x86 (AVX2, SSE2), general optimizations and compression improvements for lossless * `-mt` returns same results as single-threaded lossless (regressed in 1.5.0, #426506716) * miscellaneous warning, bug & build fixes (#393104377, #397130631, #398288323, #398066379, #427503509) Tool updates: * cwebp can restrict the use of `-resize` with `-resize_mode` (#405437935) - 12/19/2024 version 1.5.0 This is a binary compatible release. API changes: - `cross_color_transform_bits` added to WebPAuxStats * minor lossless encoder speed and compression improvements * lossless encoding does not use floats anymore * additional Arm optimizations for lossy & lossless + general code generation improvements * improvements to WASM performance (#643) * improvements and corrections in webp-container-spec.txt and webp-lossless-bitstream-spec.txt (#646, #355607636) * further security related hardening and increased fuzzing coverage w/fuzztest (oss-fuzz: #382816119, #70112, #70102, #69873, #69825, #69508, #69208) * miscellaneous warning, bug & build fixes (#499, #562, #381372617, #381109771, #42340561, #375011696, #372109644, chromium: #334120888) Tool updates: * gif2webp: add -sharp_yuv & -near_lossless * img2webp: add -exact & -noexact * exit codes normalized; running an example program with no arguments will output its help and exit with an error (#42340557, #381372617) - 4/12/2024: version 1.4.0 This is a binary compatible release. * API changes: - libwebpmux: WebPAnimEncoderSetChunk, WebPAnimEncoderGetChunk, WebPAnimEncoderDeleteChunk - libsharpyuv: SharpYuvOptionsInit, SharpYuvConvertWithOptions - extras: SharpYuvEstimate420Risk * further security related hardening in libwebp & examples * some minor optimizations in the lossless encoder * added WEBP_NODISCARD to report unused result warnings; enable with -DWEBP_ENABLE_NODISCARD=1 * improvements and corrections in webp-container-spec.txt and webp-lossless-bitstream-spec.txt (#611) * miscellaneous warning, bug & build fixes (#615, #619, #632, #635) - 9/13/2023: version 1.3.2 This is a binary compatible release. * security fix for lossless decoder (chromium: #1479274, CVE-2023-4863) - 6/23/2023: version 1.3.1 This is a binary compatible release. * security fixes for lossless encoder (#603, chromium: #1420107, #1455619, CVE-2023-1999) * improve error reporting through WebPPicture error codes * fix upsampling for RGB565 and RGBA4444 in NEON builds * img2webp: add -sharp_yuv & -near_lossless * Windows builds: - fix compatibility with clang-cl (#607) - improve Arm64 performance with cl.exe - add Arm64EC support * fix webp_js with emcc >= 3.1.27 (stack size change, #614) * CMake fixes (#592, #610, #612) * further updates to the container and lossless bitstream docs (#581, #611) - 12/16/2022: version 1.3.0 This is a binary compatible release. * add libsharpyuv, which exposes -sharp_yuv/config.use_sharp_yuv functionality to other libraries; libwebp now depends on this library * major updates to the container and lossless bitstream docs (#448, #546, #551) * miscellaneous warning, bug & build fixes (#576, #583, #584) - 8/4/2022: version 1.2.4 This is a binary compatible release. * restore CMake libwebpmux target name for compatibility with 1.2.2 (#575) * fix lossless crunch mode encoding with WEBP_REDUCE_SIZE (chromium: #1345547, #1345595, #1345772, #1345804) - 6/30/2022: version 1.2.3 This is a binary compatible release. * security fix for lossless encoder (#565, chromium:1313709) * improved progress granularity in WebPReportProgress() when using lossless * improved precision in Sharp YUV (-sharp_yuv) conversion * many corrections to webp-lossless-bitstream-spec.txt (#551) * crash/leak fixes on error/OOM and other bug fixes (#558, #563, #569, #573) - 1/11/2022: version 1.2.2 This is a binary compatible release. * webpmux: add "-set bgcolor A,R,G,B" * add ARM64 NEON support for MSVC builds (#539) * fix duplicate include error in Xcode when using multiple XCFrameworks in a project (#542) * doc updates and bug fixes (#538, #544, #548, #550) - 7/20/2021: version 1.2.1 This is a binary compatible release. * minor lossless encoder improvements and x86 color conversion speed up * add ARM64 simulator support to xcframeworkbuild.sh (#510) * further security related hardening in libwebp & examples (issues: #497, #508, #518) (chromium: #1196480, #1196773, #1196775, #1196777, #1196778, #1196850) (oss-fuzz: #28658, #28978) * toolchain updates and bug fixes (#498, #501, #502, #504, #505, #506, #509, #533) * use more inclusive language within the source (#507) - 12/23/2020: version 1.2.0 * API changes: - libwebp: encode.h: add a qmin / qmax range for quality factor (cwebp adds -qrange) * lossless encoder improvements * SIMD support for Wasm builds * add xcframeworkbuild.sh, supports Mac Catalyst builds * import fuzzers from oss-fuzz & chromium (#409) * webpmux: add an '-set loop ' option (#494) * toolchain updates and bug fixes (#449, #463, #470, #475, #477, #478, #479, #488, #491) - 12/18/2019: version 1.1.0 * API changes: - libwebp: WebPMalloc (issue #442) - extras: WebPUnmultiplyARGB * alpha decode fix (issue #439) * toolchain updates and bug fixes (chromium: #1026858, #1027136, #1027409, #1028620, #1028716, #995200) (oss-fuzz: #19430, #19447) - 7/4/2019: version 1.0.3 This is a binary compatible release. * resize fixes for Nx1 sizes and the addition of non-opaque alpha values for odd sizes (issues #418, #434) * lossless encode/decode performance improvements * lossy compression performance improvement at low quality levels with flat content (issue #432) * python swig files updated to support python 3 Tool updates: vwebp will now preserve the aspect ratio of images that exceed monitor resolution by scaling the image to fit (issue #433) - 1/14/2019: version 1.0.2 This is a binary compatible release. * (Windows) unicode file support in the tools (linux and mac already had support, issue #398) * lossless encoder speedups * lossy encoder speedup on ARM * lossless multi-threaded security fix (chromium:917029) - 11/2/2018: version 1.0.1 This is a binary compatible release. * lossless encoder speedups * big-endian fix for alpha decoding (issue #393) * gif2webp fix for loop count=65535 transcode (issue #382) * further security related hardening in libwebp & libwebpmux (issues #383, #385, #386, #387, #388, #391) (oss-fuzz #9099, #9100, #9105, #9106, #9111, #9112, #9119, #9123, #9170, #9178, #9179, #9183, #9186, #9191, #9364, #9417, #9496, #10349, #10423, #10634, #10700, #10838, #10922, #11021, #11088, #11152) * miscellaneous bug & build fixes (issues #381, #394, #396, #397, #400) - 4/2/2018: version 1.0.0 This is a binary compatible release. * lossy encoder improvements to avoid chroma shifts in various circumstances (issues #308, #340) * big-endian fixes for decode, RGBA import and WebPPictureDistortion Tool updates: gifwebp, anim_diff - default duration behavior (<= 10ms) changed to match web browsers, transcoding tools (issue #379) img2webp, webpmux - allow options to be passed in via a file (issue #355) - 11/24/2017: version 0.6.1 This is a binary compatible release. * lossless performance and compression improvements + a new 'cruncher' mode (-m 6 -q 100) * ARM performance improvements with clang (15-20% w/ndk r15c, issue #339) * webp-js: emscripten/webassembly based javascript decoder * miscellaneous bug & build fixes (issue #329, #332, #343, #353, #360, #361, #363) Tool updates / additions: added webpinfo - prints file format information (issue #330) gif2webp - loop behavior modified to match Chrome M63+ (crbug.com/649264); '-loop_compatibility' can be used for the old behavior - 1/26/2017: version 0.6.0 * lossless performance and compression improvements * miscellaneous performance improvements (SSE2, NEON, MSA) * webpmux gained a -duration option allowing for frame timing modification * new img2webp utility allowing a sequence of images to be converted to animated webp * API changes: - libwebp: WebPPictureSharpARGBToYUVA WebPPlaneDistortion - libwebpmux / gif2webp: WebPAnimEncoderOptions: kmax <= 0 now disables keyframes, kmax == 1 forces all keyframes. See mux.h and the gif2webp manpage for details. - 12/13/2016: version 0.5.2 This is a binary compatible release. This release covers CVE-2016-8888 and CVE-2016-9085. * further security related hardening in the tools; fixes to gif2webp/AnimEncoder (issues #310, #314, #316, #322), cwebp/libwebp (issue #312) * full libwebp (encoder & decoder) iOS framework; libwebpdecoder WebP.framework renamed to WebPDecoder.framework (issue #307) * CMake support for Android Studio (2.2) * miscellaneous build related fixes (issue #306, #313) * miscellaneous documentation improvements (issue #225) * minor lossy encoder fixes and improvements - 6/14/2016: version 0.5.1 This is a binary compatible release. * miscellaneous bug fixes (issues #280, #289) * reverted alpha plane encoding with color cache for compatibility with libwebp 0.4.0->0.4.3 (issues #291, #298) * lossless encoding performance improvements * memory reduction in both lossless encoding and decoding * force mux output to be in the extended format (VP8X) when undefined chunks are present (issue #294) * gradle, cmake build support * workaround for compiler bug causing 64-bit decode failures on android devices using clang-3.8 in the r11c NDK * various WebPAnimEncoder improvements - 12/17/2015: version 0.5.0 * miscellaneous bug & build fixes (issues #234, #258, #274, #275, #278) * encoder & decoder speed-ups on x86/ARM/MIPS for lossy & lossless - note! YUV->RGB conversion was sped-up, but the results will be slightly different from previous releases * various lossless encoder improvements * gif2webp improvements, -min_size option added * tools fully support input from stdin and output to stdout (issue #168) * New WebPAnimEncoder API for creating animations * New WebPAnimDecoder API for decoding animations * other API changes: - libwebp: WebPPictureSmartARGBToYUVA() (-pre 4 in cwebp) WebPConfig::exact (-exact in cwebp; -alpha_cleanup is now the default) WebPConfig::near_lossless (-near_lossless in cwebp) WebPFree() (free'ing webp allocated memory in other languages) WebPConfigLosslessPreset() WebPMemoryWriterClear() - libwebpdemux: removed experimental fragment related fields and functions - libwebpmux: WebPMuxSetCanvasSize() * new libwebpextras library with some uncommon import functions: WebPImportGray/WebPImportRGB565/WebPImportRGB4444 - 10/15/15: version 0.4.4 This is a binary compatible release. * rescaling out-of-bounds read fix (issue #254) * various build fixes and improvements (issues #253, #259, #262, #267, #268) * container documentation update * gif2webp transparency fix (issue #245) - 3/3/15: version 0.4.3 This is a binary compatible release. * Android / gcc / iOS / MSVS build fixes and improvements * lossless decode fix (issue #239 -- since 0.4.0) * documentation / vwebp updates for animation * multi-threading fix (issue #234) - 10/13/14: version 0.4.2 This is a binary compatible release. * Android / gcc build fixes * (Windows) fix reading from stdin and writing to stdout * gif2webp: miscellaneous fixes * fix 'alpha-leak' with lossy compression (issue #220) * the lossless bitstream spec has been amended to reflect the current code - 7/24/14: version 0.4.1 This is a binary compatible release. * AArch64 (arm64) & MIPS support/optimizations * NEON assembly additions: - ~25% faster lossy decode / encode (-m 4) - ~10% faster lossless decode - ~5-10% faster lossless encode (-m 3/4) * dwebp/vwebp can read from stdin * cwebp/gif2webp can write to stdout * cwebp can read webp files; useful if storing sources as webp lossless - 12/19/13: version 0.4.0 * improved gif2webp tool * numerous fixes, compression improvement and speed-up * dither option added to decoder (dwebp -dither 50 ...) * improved multi-threaded modes (-mt option) * improved filtering strength determination * New function: WebPMuxGetCanvasSize * BMP and TIFF format output added to 'dwebp' * Significant memory reduction for decoding lossy images with alpha. * Intertwined decoding of RGB and alpha for a shorter time-to-first-decoded-pixel. * WebPIterator has a new member 'has_alpha' denoting whether the frame contains transparency. * Container spec amended with new 'blending method' for animation. - 6/13/13: version 0.3.1 This is a binary compatible release. * Add incremental decoding support for images containing ALPH and ICCP chunks. * Python bindings via swig for the simple encode/decode interfaces similar to Java. - 3/20/13: version 0.3.0 This is a binary compatible release. * WebPINewRGB/WebPINewYUVA accept being passed a NULL output buffer and will perform auto-allocation. * default filter option is now '-strong -f 60' * encoding speed-up for lossy methods 3 to 6 * alpha encoding can be done in parallel to lossy using 'cwebp -mt ...' * color profile, metadata (XMP/EXIF) and animation support finalized in the container. * various NEON assembly additions Tool updates / additions: * gif2webp added * vwebp given color profile & animation support * cwebp can preserve color profile / metadata with '-metadata' - 10/30/12: version 0.2.1 * Various security related fixes * cwebp.exe: fix import errors on Windows XP * enable DLL builds for mingw targets - 8/3/12: version 0.2.0 * Add support for ARGB -> YUVA conversion for lossless decoder New functions: WebPINewYUVA, WebPIDecGetYUVA * Add stats for lossless and alpha encoding * Security related hardening: allocation and size checks * Add PAM output support to dwebp - 7/19/12: version 0.1.99 * This is a pre-release of 0.2.0, not an rc to allow for further incompatible changes based on user feedback. * Alpha channel encode/decode support. * Lossless encoder/decoder. * Add TIFF input support to cwebp. Incompatible changes: * The encode ABI has been modified to support alpha encoding. * Deprecated function WebPINew() has been removed. * Decode function signatures have changed to consistently use size_t over int/uint32_t. * decode_vp8.h is no longer installed system-wide. * cwebp will encode the alpha channel if present. - 9/19/11: version 0.1.3 * Advanced decoding APIs. * On-the-fly cropping and rescaling of images. * SSE2 instructions for decoding performance optimizations on x86 based platforms. * Support Multi-threaded decoding. * 40% improvement in Decoding performance. * Add support for RGB565, RGBA4444 & ARGB image colorspace. * Better handling of large picture encoding. - 3/25/11: version 0.1.2 * Incremental decoding: picture can be decoded byte-by-byte if needs be. * lot of bug-fixes, consolidation and stabilization - 2/23/11: initial release of version 0.1, with the new encoder - 9/30/10: initial release version with only the lightweight decoder PK1]:doc/alt-libwebp/PATENTSnu[Additional IP Rights Grant (Patents) ------------------------------------ "These implementations" means the copyrightable works that implement the WebM codecs distributed by Google as part of the WebM Project. Google hereby grants to you a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, transfer, and otherwise run, modify and propagate the contents of these implementations of WebM, where such license applies only to those patent claims, both currently owned by Google and acquired in the future, licensable by Google that are necessarily infringed by these implementations of WebM. This grant does not include claims that would be infringed only as a consequence of further modification of these implementations. If you or your agent or exclusive licensee institute or order or agree to the institution of patent litigation or any other patent enforcement activity against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that any of these implementations of WebM or any code incorporated within any of these implementations of WebM constitute direct or contributory patent infringement, or inducement of patent infringement, then any patent rights granted to you under this License for these implementations of WebM shall terminate as of the date such litigation is filed. PK1],licenses/alt-libwebp/COPYINGnu[Copyright (c) 2010, Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PK[1]<)  doc/alt-libxml2/Copyrightnu[Except where otherwise noted in the source code (e.g. the files hash.c, list.c and the trio files, which are covered by a similar licence but with different Copyright notices) all the files are: Copyright (C) 1998-2012 Daniel Veillard. All Rights Reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is fur- nished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT- NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK[1]G놓\,\,doc/alt-libxml2/TODOnu[124907 HTML parse buffer problem when parsing larse in-memory docs 124110 DTD validation && wrong namespace 123564 xmllint --html --format TODO for the XML parser and stuff: ================================== this tend to be outdated :-\ ... DOCS: ===== - use case of using XInclude to load for example a description. order document + product base -(XSLT)-> quote with XIncludes | HTML output with description of parts <---(XSLT)-- TODO: ===== - XInclude at the SAX level (libSRVG) - fix the C code prototype to bring back doc/libxml-undocumented.txt to a reasonable level - Computation of base when HTTP redirect occurs, might affect HTTP interfaces. - Computation of base in XInclude. Relativization of URIs. - listing all attributes in a node. - Better checking of external parsed entities TAG 1234 - Go through erratas and do the cleanup. http://www.w3.org/XML/xml-19980210-errata ... started ... - jamesh suggestion: SAX like functions to save a document ie. call a function to open a new element with given attributes, write character data, close last element, etc + inversted SAX, initial patch in April 2002 archives. - htmlParseDoc has parameter encoding which is not used. Function htmlCreateDocParserCtxt ignore it. - fix realloc() usage. - Stricten the UTF8 conformance (Martin Duerst): http://www.w3.org/2001/06/utf-8-test/. The bad files are in http://www.w3.org/2001/06/utf-8-wrong/. - xml:id normalized value TODO: ===== - move all string manipulation functions (xmlStrdup, xmlStrlen, etc.) to global.c. Bjorn noted that the following files depends on parser.o solely because of these string functions: entities.o, global.o, hash.o, tree.o, xmlIO.o, and xpath.o. - Optimization of tag strings allocation ? - maintain coherency of namespace when doing cut'n paste operations => the functions are coded, but need testing - function to rebuild the ID table - functions to rebuild the DTD hash tables (after DTD changes). EXTENSIONS: =========== - Tools to produce man pages from the SGML docs. - Add Xpointer recognition/API - Add Xlink recognition/API => started adding an xlink.[ch] with a unified API for XML and HTML. it's crap :-( - Implement XSchemas => Really need to be done - datatype are complete, but structure support is very limited. - extend the shell with: - edit - load/save - mv (yum, yum, but it's harder because directories are ordered in our case, mvup and mvdown would be required) Done: ===== - Add HTML validation using the XHTML DTD - problem: do we want to keep and maintain the code for handling DTD/System ID cache directly in libxml ? => not really done that way, but there are new APIs to check elements or attributes. Otherwise XHTML validation directly ... - XML Schemas datatypes except Base64 and BinHex - Relax NG validation - XmlTextReader streaming API + validation - Add a DTD cache prefilled with xhtml DTDs and entities and a program to manage them -> like the /usr/bin/install-catalog from SGML right place seems $datadir/xmldtds Maybe this is better left to user apps => use a catalog instead , and xhtml1-dtd package - Add output to XHTML => XML serializer automatically recognize the DTd and apply the specific rules. - Fix output of - compliance to XML-Namespace checking, see section 6 of http://www.w3.org/TR/REC-xml-names/ - Correct standalone checking/emitting (hard) 2.9 Standalone Document Declaration - Implement OASIS XML Catalog support http://www.oasis-open.org/committees/entity/ - Get OASIS testsuite to a more friendly result, check all the results once stable. the check-xml-test-suite.py script does this - Implement XSLT => libxslt - Finish XPath => attributes addressing troubles => defaulted attributes handling => namespace axis ? done as XSLT got debugged - bug reported by Michael Meallin on validation problems => Actually means I need to add support (and warn) for non-deterministic content model. - Handle undefined namespaces in entity contents better ... at least issue a warning - DOM needs int xmlPruneProp(xmlNodePtr node, xmlAtttrPtr attr); => done it's actually xmlRemoveProp xmlUnsetProp xmlUnsetNsProp - HTML: handling of Script and style data elements, need special code in the parser and saving functions (handling of < > " ' ...): http://www.w3.org/TR/html4/types.html#type-script Attributes are no problems since entities are accepted. - DOM needs xmlAttrPtr xmlNewDocProp(xmlDocPtr doc, const xmlChar *name, const xmlChar *value) - problem when parsing hrefs with & with the HTML parser (IRC ac) - If the internal encoding is not UTF8 saving to a given encoding doesn't work => fix to force UTF8 encoding ... done, added documentation too - Add an ASCII I/O encoder (asciiToUTF8 and UTF8Toascii) - Issue warning when using non-absolute namespaces URI. - the html parser should add and if they don't exist started, not finished. Done, the automatic closing is added and 3 testcases were inserted - Command to force the parser to stop parsing and ignore the rest of the file. xmlStopParser() should allow this, mostly untested - support for HTML empty attributes like
- plugged iconv() in for support of a large set of encodings. - xmlSwitchToEncoding() rewrite done - URI checkings (no fragments) rfc2396.txt - Added a clean mechanism for overload or added input methods: xmlRegisterInputCallbacks() - dynamically adapt the alloc entry point to use g_alloc()/g_free() if the programmer wants it: - use xmlMemSetup() to reset the routines used. - Check attribute normalization especially xmlGetProp() - Validity checking problems for NOTATIONS attributes - Validity checking problems for ENTITY ENTITIES attributes - Parsing of a well balanced chunk xmlParseBalancedChunkMemory() - URI module: validation, base, etc ... see uri.[ch] - turn tester into a generic program xmllint installed with libxml - extend validity checks to go through entities content instead of just labelling them PCDATA - Save Dtds using the children list instead of dumping the tables, order is preserved as well as comments and PIs - Wrote a notice of changes requires to go from 1.x to 2.x - make sure that all SAX callbacks are disabled if a WF error is detected - checking/handling of newline normalization http://localhost/www.xml.com/axml/target.html#sec-line-ends - correct checking of '&' '%' on entities content. - checking of PE/Nesting on entities declaration - checking/handling of xml:space - checking done. - handling done, not well tested - Language identification code, productions [33] to [38] => done, the check has been added and report WFness errors - Conditional sections in DTDs [61] to [65] => should this crap be really implemented ??? => Yep OASIS testsuite uses them - Allow parsed entities defined in the internal subset to override the ones defined in the external subset (DtD customization). => This mean that the entity content should be computed only at use time, i.e. keep the orig string only at parse time and expand only when referenced from the external subset :-( Needed for complete use of most DTD from Eve Maler - Add regression tests for all WFC errors => did some in test/WFC => added OASIS testsuite routines http://xmlsoft.org/conf/result.html - I18N: http://wap.trondheim.com/vaer/index.phtml is not XML and accepted by the XML parser, UTF-8 should be checked when there is no "encoding" declared ! - Support for UTF-8 and UTF-16 encoding => added some conversion routines provided by Martin Durst patched them, got fixes from @@@ I plan to keep everything internally as UTF-8 (or ISO-Latin-X) this is slightly more costly but more compact, and recent processors efficiency is cache related. The key for good performances is keeping the data set small, so will I. => the new progressive reading routines call the detection code is enabled, tested the ISO->UTF-8 stuff - External entities loading: - allow override by client code - make sure it is called for all external entities referenced Done, client code should use xmlSetExternalEntityLoader() to set the default loading routine. It will be called each time an external entity entity resolution is triggered. - maintain ID coherency when removing/changing attributes The function used to deallocate attributes now check for it being an ID and removes it from the table. - push mode parsing i.e. non-blocking state based parser done, both for XML and HTML parsers. Use xmlCreatePushParserCtxt() and xmlParseChunk() and html counterparts. The tester program now has a --push option to select that parser front-end. Douplicated tests to use both and check results are similar. - Most of XPath, still see some troubles and occasionnal memleaks. - an XML shell, allowing to traverse/manipulate an XML document with a shell like interface, and using XPath for the anming syntax - use of readline and history added when available - the shell interface has been cleanly separated and moved to debugXML.c - HTML parser, should be fairly stable now - API to search the lang of an attribute - Collect IDs at parsing and maintain a table. PBM: maintain the table coherency PBM: how to detect ID types in absence of DtD ! - Use it for XPath ID support - Add validity checking Should be finished now ! - Add regression tests with entity substitutions - External Parsed entities, either XML or external Subset [78] and [79] parsing the xmllang DtD now works, so it should be sufficient for most cases ! - progressive reading. The entity support is a first step toward abstraction of an input stream. A large part of the context is still located on the stack, moving to a state machine and putting everything in the parsing context should provide an adequate solution. => Rather than progressive parsing, give more power to the SAX-like interface. Currently the DOM-like representation is built but => it should be possible to define that only as a set of SAX callbacks and remove the tree creation from the parser code. DONE - DOM support, instead of using a proprietary in memory format for the document representation, the parser should call a DOM API to actually build the resulting document. Then the parser becomes independent of the in-memory representation of the document. Even better using RPC's the parser can actually build the document in another program. => Work started, now the internal representation is by default very near a direct DOM implementation. The DOM glue is implemented as a separate module. See the GNOME gdome module. - C++ support : John Ehresman - Updated code to follow more recent specs, added compatibility flag - Better error handling, use a dedicated, overridable error handling function. - Support for CDATA. - Keep track of line numbers for better error reporting. - Support for PI (SAX one). - Support for Comments (bad, should be in ASAP, they are parsed but not stored), should be configurable. - Improve the support of entities on save (+SAX). PK[1]BՈdoc/alt-libxml2/NEWSnu[NEWS file for libxml2 v2.10.2: Aug 29 2022 ### Improvements - Remove set-but-unused variable in xmlXPathScanName - Silence -Warray-bounds warning ### Build system - build: require automake-1.16.3 or later (Xi Ruoyao) - Remove generated files from distribution ### Test suite - Don't create missing.xml when running testapi v2.10.1: Aug 25 2022 ### Regressions - Fix xmlCtxtReadDoc with encoding ### Bug fixes - Fix HTML parser with threads and --without-legacy ### Build system - Fix build with Python 3.10 - cmake: Disable version script on macOS - Remove Makefile rule to build testapi.c ### Documentation - Switch back to HTML output for API documentation - Port doc/examples/index.py to Python 3 - Fix order of exports in libxml2-api.xml - Remove libxml2-refs.xml v2.10.0: Aug 17 2022 ### Security - [CVE-2022-2309] Reset nsNr in xmlCtxtReset - Reserve byte for NUL terminator and report errors consistently in xmlBuf and xmlBuffer (David Kilzer) - Fix missing NUL terminators in xmlBuf and xmlBuffer functions (David Kilzer) - Fix integer overflow in xmlBufferDump() (David Kilzer) - xmlBufAvail() should return length without including a byte for NUL terminator (David Kilzer) - Fix ownership of xmlNodePtr & xmlAttrPtr fields in xmlSetTreeDoc() (David Kilzer) - Use xmlNewDocText in xmlXIncludeCopyRange - Fix use-after-free bugs when calling xmlTextReaderClose() before xmlFreeTextReader() on post-validating parser (David Kilzer) - Use UPDATE_COMPAT() consistently in buf.c (David Kilzer) - fix: xmlXPathParserContext could be double-delete in OOM case. (jinsub ahn) ### Removals and deprecations - Disable XPointer location support by default - Remove outdated xml2Conf.sh - Deprecate module init and cleanup functions - Remove obsolete XML Software Autoupdate (XSA) file - Remove DOCBparser - Remove obsolete Python test framework - Remove broken VxWorks support - Remove broken Mac OS 9 support - Remove broken bakefile support - Remove broken Visual Studio 2010 support - Remove broken Windows CE support - Deprecate IDREF-related functions in valid.h - Deprecate legacy functions - Disable legacy support by default - Deprecate all functions in nanoftp.h - Disable FTP support by default - Add XML_DEPRECATED macro - Remove elfgcchack.h ### Regressions - Skip incorrectly opened HTML comments - Restore behavior of htmlDocContentDumpFormatOutput() (David Kilzer) ### Bug fixes - Fix memory leak with invalid XSD - Make XPath depth check work with recursive invocations - Fix memory leak in xmlLoadEntityContent error path - Avoid double-free if malloc fails in inputPush - Properly fold whitespace around the QName value when validating an XSD schema. (Damjan Jovanovic) - Add whitespace folding for some atomic data types that it's missing on. (Damjan Jovanovic) - Don't add IDs containing unexpanded entity references ### Improvements - Avoid calling xmlSetTreeDoc - Simplify xmlFreeNode - Don't reset nsDef when changing node content - Fix unintended fall-through in xmlNodeAddContentLen - Remove unused xmlBuf functions (David Kilzer) - Implement xpath1() XPointer scheme - Add configuration flag for XPointer locations support - Fix compiler warnings in Python code - Mark more static data as `const` (David Kilzer) - Make xmlStaticCopyNode non-recursive - Clean up encoding switching code - Simplify recursive pthread mutex - Use non-recursive mutex in dict.c - Fix parser progress checks - Avoid arithmetic on freed pointers - Improve buffer allocation scheme - Remove unneeded #includes - Add support for some non-standard escapes in regular expressions. (Damjan Jovanovic) - htmlParseComment: handle abruptly-closed comments (Mike Dalessio) - Add let variable tag support (Oliver Diehl) - Add value-of tag support (Oliver Diehl) - Remove useless call to xmlRelaxNGCleanupTypes - Don't include ICU headers in public headers - Update `xmlStrlen()` to use POSIX / ISO C `strlen()` (Mike Dalessio) - Fix unused variable warnings with disabled features - Only warn on invalid redeclarations of predefined entities - Remove unneeded code in xmlreader.c - Rework validation context flags ### Portability - Use NAN/INFINITY if available to init XPath NaN/Inf (Sergey Kosukhin) - Fix Python tests on macOS - Fix xmlCleanupThreads on Windows - Fix reinitialization of library on Windows - Don't mix declarations and code in runtest.c - Use portable python shebangs (David Seifert) - Use critical sections as mutex on Windows - Don't set HAVE_WIN32_THREADS in win32config.h - Use stdint.h with newer MSVC - Remove cruft from win32config.h - Remove isinf/isnan emulation in win32config.h - Always fopen files with "rb" - Remove __DJGPP__ checks - Remove useless __CYGWIN__ checks ### Build system - Don't autogenerate doc/examples/Makefile.am - cmake: Install libxml.m4 on UNIX-like platforms (Daniel E) - cmake: Use symbol versioning on UNIX-like platforms (Daniel E) - Port genUnicode.py to Python 3 - Port gentest.py to Python 3 - cmake: Fix build without thread support - cmake: Install documentation in CMAKE_INSTALL_DOCDIR - cmake: Remove non needed files in docs dir (Daniel E) - configure: move XML_PRIVATE_LIBS after WIN32_EXTRA_LIBADD is set (Christopher Degawa) - Move local Autoconf macros into m4 directory - Use XML_PRIVATE_LIBS in libxml2_la_LIBADD - Update libxml-2.0-uninstalled.pc.in - Remove LIBS from XML_PRIVATE_LIBS - Add WIN32_EXTRA_LIBADD to XML_PRIVATE_LIBS - Don't overlink executables - cmake: Adjust paths for UNIX or UNIX-like target systems (Daniel Engberg) - build: Make use of variables in libxml's pkg-config file (Daniel Engberg) - Avoid obsolescent `test -a` constructs (David Seifert) - Move AM_MAINTAINER_MODE to AM section - configure.ac: make AM_SILENT_RULES([yes]) unconditional (David Seifert) - Streamline documentation installation - Don't try to recreate COPYING symlink - Detect libm using libtool's macros (David Seifert) - configure.ac: disable static libraries by default (David Seifert) - python/Makefile.am: nest python docs in $(docdir) (David Seifert) - python/Makefile.am: rely on global AM_INIT_AUTOMAKE (David Seifert) - Makefile.am: install examples more idiomatically (David Seifert) - configure.ac: remove useless AC_SUBST (David Seifert) - Respect `--sysconfdir` in source files (David Seifert) - Ignore configure backup file created by recent autoreconf too (Vadim Zeitlin) - Only install *.html and *.c example files - Remove --with-html-dir option - Rework documentation build system - Remove old website - Use AM_PATH_PYTHON/PKG_CHECK_MODULES for python bindings (David Seifert) - Update genChRanges.py - Update build_glob.py - Remove ICONV_CONST test - Remove obsolete AC_HEADER checks - Don't check for standard C89 library functions - Don't check for standard C89 headers - Remove special configuration for certain maintainers ### Test suite, CI - Disable network in API tests - testapi: remove leading slash from "/missing.xml" (Mike Gilbert) - Build Autotools CI tests out of source tree (VPATH) - Add --with-minimum build to CI tests - Fix warnings when testing --with-minimum build - cmake: Run all tests when threads are disabled - Also build CI tests with -Werror - Move doc/examples tests to new test suite - Simplify 'make check' targets - Fix schemas and relaxng tests - Remove unused result files - Allow missing result files in runtest - Move regexp tests to runtest - Move SVG tests to runtest.c - Move testModule to new test suite - Move testThreads to new test suite - Remove major parts of old test suite - Make testchar return an error on failure (Tony Tascioglu) - Add CI job for static build - python/tests: open() relative to test scripts (David Seifert) - Port some test scripts to Python 3 ### Documentation - Improve documentation of tree manipulation API - Update xml2-config man page - Consolidate man pages - Rename xmlcatalog_man.xml - Make examples a standalone HTML page - Fix documentation in entities.c - Add note about optimization flags v2.9.14: May 02 2022: - Security: [CVE-2022-29824] Integer overflow in xmlBuf and xmlBuffer Fix potential double-free in xmlXPtrStringRangeFunction Fix memory leak in xmlFindCharEncodingHandler Normalize XPath strings in-place Prevent integer-overflow in htmlSkipBlankChars() and xmlSkipBlankChars() (David Kilzer) Fix leak of xmlElementContent (David Kilzer) - Bug fixes: Fix parsing of subtracted regex character classes Fix recursion check in xinclude.c Reset last error in xmlCleanupGlobals Fix certain combinations of regex range quantifiers Fix range quantifier on subregex - Improvements: Fix recovery from invalid HTML start tags - Build system, portability: Define LFS macros before including system headers Initialize XPath floating-point globals configure: check for icu DEFS (James Hilliard) configure.ac: produce tar.xz only (GNOME policy) (David Seifert) CMakeLists.txt: Fix LIBXML_VERSION_NUMBER Fix build with older Python versions Fix --without-valid build v2.9.13: Feb 19 2022: - Security: [CVE-2022-23308] Use-after-free of ID and IDREF attributes (Thanks to Shinji Sato for the report) Use-after-free in xmlXIncludeCopyRange (David Kilzer) Fix Null-deref-in-xmlSchemaGetComponentTargetNs (huangduirong) Fix memory leak in xmlXPathCompNodeTest Fix null pointer deref in xmlStringGetNodeList Fix several memory leaks found by Coverity (David King) - Fixed regressions: Fix regression in RelaxNG pattern matching Properly handle nested documents in xmlFreeNode Fix regression with PEs in external DTD Fix random dropping of characters on dumping ASCII encoded XML (Mohammad Razavi) Revert "Make schema validation fail with multiple top-level elements" Fix regression when parsing invalid HTML tags in push mode Fix regression parsing public IDs literals in HTML Fix buffering in xmlOutputBufferWrite Fix whitespace when serializing empty HTML documents Fix XPath recursion limit Fix regression in xmlNodeDumpOutputInternal Work around lxml API abuse - Bug fixes: Fix xmlSetTreeDoc with entity references Fix double counting of CRLF in comments Make sure to grow input buffer in xmlParseMisc Don't ignore xmllint options after "-" Don't normalize namespace URIs in XPointer xmlns() scheme Fix handling of XSD with empty namespace Also register HTML document nodes Make xmllint return an error if arguments are missing Fix handling of ctxt->base in xmlXPtrEvalXPtrPart Fix xmllint --maxmem Fix htmlReadFd, which was using a mix of xml and html context functions (Finn Barber) Move current position before possible calling of ctxt->sax->characters (Yulin Li) Fix parse failure when 4-byte character in UTF-16 BE is split across a chunk (David Kilzer) Patch to forbid epsilon-reduction of final states (Arne Becker) Avoid segfault at exit when using custom memory functions (Mike Dalessio) - Tests, code quality, fuzzing: Remove .travis.yml Make xmlFuzzReadString return a zero size in error case Fix unused function warning in testapi.c Update NewsML DTD in test suite Add more checks for malloc failures in xmllint.c Avoid potential integer overflow in xmlstring.c Run CI tests with UBSan implicit-conversion checks Fix casting of line numbers in SAX2.c Fix integer conversion warnings in hash.c Add explicit casts in runtest.c Fix integer conversion warning in xmlIconvWrapper Add suffix to unsigned constant in xmlmemory.c Add explicit casts in testchar.c Fix integer conversion warnings in xmlstring.c Add explicit cast in xmlURIUnescapeString Remove unused variable in xmlCharEncOutFunc (David King) - Build system, portability: Remove xmlwin32version.h Fix fuzzer test with VPATH build Support custom prefix when installing Python module Remove Makefile.win Remove CVS and SVN-related code Port python 3.x module to Windows and improve distutils (Chun-wei Fan) Correctly install the HTML examples into their subdirectory (Mattia Rizzolo) Refactor the settings of $docdir (Mattia Rizzolo) Remove unused configure checks (Ben Boeckel) python/Makefile.am: use *_LIBADD, not *_LDFLAGS for LIBS (Sam James) Fix check for libtool in autogen.sh Use version in configure.ac for CMake (Timothy Lyanguzov) Add CMake alias targets for embedded projects (Markus Rickert) - Documentation: Remove SVN keyword anchors Rework README Remove README.cvs-commits Remove old ChangeLog Update hyperlinks Remove README.docs Remove MAINTAINERS Remove xmltutorial.pdf Upload documentation to GitLab pages Document how to escape XML_CATALOG_FILES Fix libxml2.doap Update URL for libxml++ C++ binding (Kjell Ahlstedt) Generate devhelp2 index file (Emmanuele Bassi) Mention XML_CATALOG_FILES is space-separated (Jan Tojnar) Add documentaiton for xmllint exit code 10 (Rainer Canavan) Fix some validation errors in the FAQ (David King) Add instructions on how to use CMake to compile libxml (Markus Rickert) v2.9.12: May 13 2021: - Build system: Add fuzz.h and seed/regexp to EXTRA_DIST v2.9.11: May 13 2021: - Security: Patch for security issue CVE-2021-3541 (Daniel Veillard) - Documentation: Clarify xmlNewDocProp documentation (Nick Wellnhofer) - Portability: CMake: Only add postfixes if MSVC (Christopher Degawa), Fix XPath NaN/Inf for older GCC versions (Nick Wellnhofer), Use CMake PROJECT_VERSION (Markus Rickert), Fix warnings in libxml.m4 with autoconf 2.70+. (Simon Josefsson), Add CI for CMake on MSVC (Markus Rickert), Update minimum required CMake version (Markus Rickert), Add variables for configured options to CMake config files (Markus Rickert), Check if variables exist when defining targets (Markus Rickert), Check if target exists when reading target properties (Markus Rickert), Add xmlcatalog target and definition to config files (Markus Rickert), Remove include directories for link-only dependencies (Markus Rickert), Fix ICU build in CMake (Markus Rickert), Configure pkgconfig, xml2-config, and xml2Conf.sh file (Markus Rickert), Update CMake config files (Markus Rickert), Add xmlcatalog and xmllint to CMake export (Markus Rickert), Simplify xmlexports.h (Nick Wellnhofer), Require dependencies based on enabled CMake options (Markus Rickert), Use NAMELINK_COMPONENT in CMake install (Markus Rickert), Add CMake files to EXTRA_DIST (Markus Rickert), Add missing compile definition for static builds to CMake (Markus Rickert), Add CI for CMake on Linux and MinGW (Markus Rickert), Fix variable name in win32/configure.js (Nick Wellnhofer), Fix version parsing in win32/configure.js (Nick Wellnhofer), Fix autotools warnings (Nick Wellnhofer), Update config.h.cmake.in (Markus Rickert), win32: allow passing *FLAGS on command line (Michael Stahl), Configure file xmlwin32version.h.in on MSVC (Markus Rickert), List headers individually (Markus Rickert), Add CMake build files (Markus Rickert), Parenthesize Py_Check() in ifs (Miro Hrončok), Minor fixes to configure.js (Nick Wellnhofer) - Bug Fixes: Fix null deref in legacy SAX1 parser (Nick Wellnhofer), Fix handling of unexpected EOF in xmlParseContent (Nick Wellnhofer), Fix line numbers in error messages for mismatched tags (Nick Wellnhofer), Fix htmlTagLookup (Nick Wellnhofer), Propagate error in xmlParseElementChildrenContentDeclPriv (Nick Wellnhofer), Fix user-after-free with `xmllint --xinclude --dropdtd` (Nick Wellnhofer), Fix dangling pointer with `xmllint --dropdtd` (Nick Wellnhofer), Validate UTF8 in xmlEncodeEntities (Joel Hockey), Fix use-after-free with `xmllint --html --push` (Nick Wellnhofer), Allow FP division by zero in xmlXPathInit (Nick Wellnhofer), Fix xmlGetNodePath with invalid node types (Nick Wellnhofer), Fix exponential behavior with recursive entities (Nick Wellnhofer), Fix quadratic behavior when looking up xml:* attributes (Nick Wellnhofer), Fix slow parsing of HTML with encoding errors (Nick Wellnhofer), Fix null deref introduced with previous commit (Nick Wellnhofer), Check for invalid redeclarations of predefined entities (Nick Wellnhofer), Add the copy of type from original xmlDoc in xmlCopyDoc() (SVGAnimate), parser.c: shrink the input buffer when appropriate (Mike Dalessio), Fix infinite loop in HTML parser introduced with recent commits (Nick Wellnhofer), Fix quadratic runtime when parsing CDATA sections (Nick Wellnhofer), Fix timeout when handling recursive entities (Nick Wellnhofer), Fix memory leak in xmlParseElementMixedContentDecl (Nick Wellnhofer), Fix null deref in xmlStringGetNodeList (Nick Wellnhofer), use new htmlParseLookupCommentEnd to find comment ends (Mike Dalessio), htmlParseComment: treat `--!>` as if it closed the comment (Mike Dalessio), Fix integer overflow in xmlSchemaGetParticleTotalRangeMin (Nick Wellnhofer), encoding: fix memleak in xmlRegisterCharEncodingHandler() (Xiaoming Ni), xmlschemastypes.c: xmlSchemaGetFacetValueAsULong add, check "facet->val" (Xiaoming Ni), Fix null pointer deref in xmlXPtrRangeInsideFunction (Nick Wellnhofer), Fix quadratic runtime in HTML push parser with null bytes (Nick Wellnhofer), Avoid quadratic checking of identity-constraints (Michael Matz), Fix building with ICU 68. (Frederik Seiffert), Convert python/libxml.c to PY_SSIZE_T_CLEAN (Victor Stinner), Fix xmlURIEscape memory leaks. (Elliott Hughes), Avoid call stack overflow with XML reader and recursive XIncludes (Nick Wellnhofer), Fix caret in regexp character group (Nick Wellnhofer), parser.c: xmlParseCharData peek behavior fixed wrt newlines (Mike Dalessio), Fix memory leaks in XPointer string-range function (Nick Wellnhofer), Fix use-after-free when XIncluding text from Reader (Nick Wellnhofer), Fix SEGV in xmlSAXParseFileWithData (yanjinjq), Fix null deref in XPointer expression error path (Nick Wellnhofer), Don't call xmlXPathInit directly (Nick Wellnhofer), Fix cleanup of attributes in XML reader (Nick Wellnhofer), Fix double free in XML reader with XIncludes (Nick Wellnhofer), Fix memory leak in xmlXIncludeAddNode error paths (Nick Wellnhofer), Revert "Fix quadratic runtime in xi:fallback processing" (Nick Wellnhofer), Fix error reporting with xi:fallback (Nick Wellnhofer), Fix quadratic runtime in xi:fallback processing (Nick Wellnhofer), Fix corner case with empty xi:fallback (Nick Wellnhofer), Fix XInclude regression introduced with recent commit (Nick Wellnhofer), Fix memory leak in runtest.c (Nick Wellnhofer), Make "xmllint --push --recovery" work (Nick Wellnhofer), Revert "Do not URI escape in server side includes" (Nick Wellnhofer), Fix column number accounting in xmlParse*NameAndCompare (Nick Wellnhofer), Stop counting nbChars in parser context (Nick Wellnhofer), Fix out-of-bounds read with 'xmllint --htmlout' (Nick Wellnhofer), Fix exponential runtime and memory in xi:fallback processing (Nick Wellnhofer), Don't process siblings of root in xmlXIncludeProcess (Nick Wellnhofer), Don't recurse into xi:include children in xmlXIncludeDoProcess (Nick Wellnhofer), Fix memory leak in xmlXIncludeIncludeNode error paths (Nick Wellnhofer), Check for custom free function in global destructor (Nick Wellnhofer), Fix integer overflow when comparing schema dates (Nick Wellnhofer), Fix exponential runtime in xmlFARecurseDeterminism (Nick Wellnhofer), Don't try to handle namespaces when building HTML documents (Nick Wellnhofer), Fix several quadratic runtime issues in HTML push parser (Nick Wellnhofer), Fix quadratic runtime when push parsing HTML start tags (Nick Wellnhofer), Reset XML parser input before reporting errors (David Kilzer), Fix quadratic runtime when push parsing HTML entity refs (Nick Wellnhofer), Fix HTML push parser lookahead (Nick Wellnhofer), Make htmlCurrentChar always translate U+0000 (Nick Wellnhofer), Fix UTF-8 decoder in HTML parser (Nick Wellnhofer), Fix quadratic runtime when parsing HTML script content (Nick Wellnhofer), Reset HTML parser input before reporting error (Nick Wellnhofer), Fix more quadratic runtime issues in HTML push parser (Nick Wellnhofer), Fix regression introduced with 477c7f6a (Nick Wellnhofer), Fix quadratic runtime in HTML parser (Nick Wellnhofer), Reset HTML parser input before reporting encoding error (Nick Wellnhofer), Fix integer overflow in xmlFAParseQuantExact (Nick Wellnhofer), Fix return value of xmlC14NDocDumpMemory (Nick Wellnhofer), Don't follow next pointer on documents in xmlXPathRunStreamEval (Nick Wellnhofer), Fix integer overflow in _xmlSchemaParseGYear (Nick Wellnhofer), Fix integer overflow when parsing {min,max}Occurs (Nick Wellnhofer), Fix another memory leak in xmlSchemaValAtomicType (Nick Wellnhofer), Fix unsigned integer overflow in htmlParseTryOrFinish (Nick Wellnhofer), Fix integer overflow in htmlParseCharRef (Nick Wellnhofer), Fix undefined behavior in UTF16LEToUTF8 (Nick Wellnhofer), Fix return value of xmlCharEncOutput (Nick Wellnhofer), Never expand parameter entities in text declaration (Nick Wellnhofer), Fix undefined behavior in xmlXPathTryStreamCompile (Nick Wellnhofer), Fix use-after-free with validating reader (Nick Wellnhofer), xmlParseBalancedChunkMemory must not be called with NULL doc (Nick Wellnhofer), Revert "Fix memory leak in xmlParseBalancedChunkMemoryRecover" (Nick Wellnhofer), Fix memory leak in xmlXIncludeLoadDoc error path (Nick Wellnhofer), Make schema validation fail with multiple top-level elements (Nick Wellnhofer), Call xmlCleanupParser on ELF destruction (Samuel Thibault), Fix copying of entities in xmlParseReference (Nick Wellnhofer), Fix memory leak in xmlSchemaValidateStream (Zhipeng Xie), Fix xmlSchemaGetCanonValue formatting for date and dateTime (Kevin Puetz), Fix memory leak when shared libxml.dll is unloaded (Kevin Puetz), Fix potentially-uninitialized critical section in Win32 DLL builds (Kevin Puetz), Fix integer overflow in xmlBufferResize (Nick Wellnhofer), Check for overflow when allocating two-dimensional arrays (Nick Wellnhofer), Remove useless comparisons (Nick Wellnhofer), Fix overflow check in xmlNodeDump (Nick Wellnhofer), Fix infinite loop in xmlStringLenDecodeEntities (Zhipeng Xie), Fix freeing of nested documents (Nick Wellnhofer), Fix more memory leaks in error paths of XPath parser (Nick Wellnhofer), Fix memory leaks of encoding handlers in xmlsave.c (Nick Wellnhofer), Fix xml2-config error code (Nick Wellnhofer), Fix memory leak in error path of XPath expr parser (Nick Wellnhofer), Fix overflow handling in xmlBufBackToBuffer (Nick Wellnhofer), Null pointer handling in catalog.c (raniervf), xml2-config.in: fix regressions introduced by commit 2f2bf4b2c (Dmitry V. Levin) - Improvements: Store per-element parser state in a struct (Nick Wellnhofer), update for xsd:language type check (PaulHiggs), Update INSTALL.libxml2 (Nick Wellnhofer), Fix include order in c14n.h (Nick Wellnhofer), Fix duplicate xmlStrEqual calls in htmlParseEndTag (Nick Wellnhofer), Speed up htmlCheckAutoClose (Nick Wellnhofer), Speed up htmlTagLookup (Nick Wellnhofer), Stop checking attributes for UTF-8 validity (Nick Wellnhofer), Reduce some fuzzer timeouts (Nick Wellnhofer), Only run a few CI tests unless scheduled (Nick Wellnhofer), Improve fuzzer stability (Nick Wellnhofer), Check for feature flags in fuzzer tests (Nick Wellnhofer), Another attempt at improving fuzzer stability (Nick Wellnhofer), Revert "Improve HTML fuzzer stability" (Nick Wellnhofer), Add charset names to fuzzing dictionaries (Nick Wellnhofer), Improve HTML fuzzer stability (Nick Wellnhofer), Add CI for MSVC x86 (Markus Rickert), Add a flag to not output anything when xmllint succeeded (hhb), Speed up HTML fuzzer (Nick Wellnhofer), Remove unused encoding parameter of HTML output functions (Nick Wellnhofer), Handle malloc failures in fuzzing code (Nick Wellnhofer), add test coverage for incorrectly-closed comments (Mike Dalessio), Enforce maximum length of fuzz input (Nick Wellnhofer), Remove temporary members from struct _xmlXPathContext (Nick Wellnhofer), Build the Python extension with PY_SSIZE_T_CLEAN (Victor Stinner), Add CI test for Python 3 (Nick Wellnhofer), Add fuzzing dictionaries to EXTRA_DIST (Nick Wellnhofer), Add 'fuzz' subdirectory to DIST_SUBDIRS (Nick Wellnhofer), Allow port numbers up to INT_MAX (Nick Wellnhofer), Handle dumps of corrupted documents more gracefully (Nick Wellnhofer), Limit size of free lists in XML reader when fuzzing (Nick Wellnhofer), Hardcode maximum XPath recursion depth (Nick Wellnhofer), Pass URL of main entity in XML fuzzer (Nick Wellnhofer), Consolidate seed corpus generation (Nick Wellnhofer), Test fuzz targets with dummy driver (Nick Wellnhofer), Fix regression introduced with commit d88df4b (Nick Wellnhofer), Fix regression introduced with commit 74dcc10b (Nick Wellnhofer), Add TODO comment in xinclude.c (Nick Wellnhofer), Stop using maxParserDepth in xpath.c (Nick Wellnhofer), Remove dead code in xinclude.c (Nick Wellnhofer), Don't add formatting newlines to XInclude nodes (Nick Wellnhofer), Don't use SAX1 if all element handlers are NULL (Nick Wellnhofer), Remove unneeded progress checks in HTML parser (Nick Wellnhofer), Use strcmp when fuzzing (Nick Wellnhofer), Fix XPath fuzzer (Nick Wellnhofer), Fuzz XInclude engine (Nick Wellnhofer), Add XPath and XPointer fuzzer (Nick Wellnhofer), Update fuzzing code (Nick Wellnhofer), More *NodeDumpOutput fixes (Nick Wellnhofer), Fix *NodeDumpOutput functions (Nick Wellnhofer), Make xmlNodeDumpOutputInternal non-recursive (Nick Wellnhofer), Make xhtmlNodeDumpOutput non-recursive (Nick Wellnhofer), Make htmlNodeDumpFormatOutput non-recursive (Nick Wellnhofer), Fix .gitattributes (Nick Wellnhofer), Rework control flow in htmlCurrentChar (Nick Wellnhofer), Make 'xmllint --html --push -' read from stdin (Nick Wellnhofer), Remove misleading comments in xpath.c (Nick Wellnhofer), Update to Devhelp index file format version 2 (Andre Klapper), Set project language to C (Markus Rickert), Add variable for working directory of XML Conformance Test Suite (Markus Rickert), Add additional tests and XML Conformance Test Suite (Markus Rickert), Add command line option for temp directory in runtest (Markus Rickert), Ensure LF line endings for test files (Markus Rickert), Enable runtests and testThreads (Markus Rickert), Limit regexp nesting depth (Nick Wellnhofer), Fix return values and documentation in encoding.c (Nick Wellnhofer), Add regexp regression tests (David Kilzer), Report error for invalid regexp quantifiers (Nick Wellnhofer), Fix rebuilding docs, by hiding __attribute__((...)) behind a macro. (Martin Vidner), Copy xs:duration parser from libexslt (Nick Wellnhofer), Fuzz target for XML Schemas (Nick Wellnhofer), Move entity recorder to fuzz.c (Nick Wellnhofer), Fuzz target for HTML parser (Nick Wellnhofer), Update GitLab CI container (Nick Wellnhofer), Add options file for xml fuzzer (Nick Wellnhofer), Add a couple of libFuzzer targets (Nick Wellnhofer), Guard new calls to xmlValidatePopElement in xml_reader.c (Daniel Cheng), Add LIBXML_VALID_ENABLED to xmlreader (Łukasz Wojniłowicz), Fix typos (Nick Wellnhofer), Disable LeakSanitizer (Nick Wellnhofer), Stop calling SAX getEntity handler from XMLReader (Nick Wellnhofer), Add test case for recursive external parsed entities (Nick Wellnhofer), Enable error tests with entity substitution (Nick Wellnhofer), Don't load external entity from xmlSAX2GetEntity (Nick Wellnhofer), Merge code paths loading external entities (Nick Wellnhofer), Copy some XMLReader option flags to parser context (Nick Wellnhofer), Add xmlPopOutputCallbacks (Nick Wellnhofer), Updated Python test reader2.py (Pieter van Oostrum), Updated python/tests/tstLastError.py (Pieter van Oostrum), Use random seed in xmlDictComputeFastKey (Ranier Vilela), Enable more undefined behavior sanitizers (Nick Wellnhofer) v2.9.10: Oct 30 2019: - Documentation: Fix a few more typos ("fonction") (Nick Wellnhofer), Large batch of typo fixes (Jared Yanovich), Fix typos: tree: move{ -> s}, reconcil{i -> }ed, h{o -> e}ld by... (Jan Pokorný), Fix typo: xpath: simpli{ -> fi}ed (Jan Pokorný), Doc: do not mislead towards "infeasible" scenario wrt. xmlBufNodeDump (Jan Pokorný), Fix comments in test code (zhouzhongyuan), fix comment in testReader.c (zhouzhongyuan) - Portability: Fix some release issues on Fedora 30 (Daniel Veillard), Fix exponent digits when running tests under old MSVC (Daniel Richard G), Work around buggy ceil() function on AIX (Daniel Richard G), Don't call printf with NULL string in runtest.c (Daniel Richard G), Switched from unsigned long to ptrdiff_t in parser.c (Stephen Chenney), timsort.h: support older GCCs (Jérôme Duval), Make configure.ac work with older pkg-config (Nick Wellnhofer), Stop defining _REENTRANT on some Win32 platforms (Nick Wellnhofer), Fix nanohttp.c on MinGW (Nick Wellnhofer), Fix Windows compiler warning in testC14N.c (Nick Wellnhofer), Merge testThreadsWin32.c into testThreads.c (Nick Wellnhofer), Fix Python bindings under Windows (Nick Wellnhofer) - Bug Fixes: Another fix for conditional sections at end of document (Nick Wellnhofer), Fix for conditional sections at end of document (Nick Wellnhofer), Make sure that Python tests exit with error code (Nick Wellnhofer), Audit memory error handling in xpath.c (Nick Wellnhofer), Fix error code in xmlTextWriterStartDocument (Nick Wellnhofer), Fix integer overflow when counting written bytes (Nick Wellnhofer), Fix uninitialized memory access in HTML parser (Nick Wellnhofer), Fix memory leak in xmlSchemaValAtomicType (Nick Wellnhofer), Disallow conditional sections in internal subset (Nick Wellnhofer), Fix use-after-free in xmlTextReaderFreeNodeList (Nick Wellnhofer), Fix Regextests (Nick Wellnhofer), Fix empty branch in regex (Nick Wellnhofer), Fix integer overflow in entity recursion check (Nick Wellnhofer), Don't read external entities or XIncludes from stdin (Nick Wellnhofer), Fix Schema determinism check of ##other namespaces (Nick Wellnhofer), Fix potential null deref in xmlSchemaIDCFillNodeTables (zhouzhongyuan), Fix potential memory leak in xmlBufBackToBuffer (Nick Wellnhofer), Fix error message when processing XIncludes with fallbacks (Nick Wellnhofer), Fix memory leak in xmlRegEpxFromParse (zhouzhongyuan), 14:00 is a valid timezone for xs:dateTime (Nick Wellnhofer), Fix memory leak in xmlParseBalancedChunkMemoryRecover (Zhipeng Xie), Fix potential null deref in xmlRelaxNGParsePatterns (Nick Wellnhofer), Misleading error message with xs:{min|max}Inclusive (bettermanzzy), Fix memory leak in xmlXIncludeLoadTxt (Wang Kirin), Partial fix for comparison of xs:durations (Nick Wellnhofer), Fix null deref in xmlreader buffer (zhouzhongyuan), Fix unability to RelaxNG-validate grammar with choice-based name class (Jan Pokorný), Fix unability to validate ambiguously constructed interleave for RelaxNG (Jan Pokorný), Fix possible null dereference in xmlXPathIdFunction (zhouzhongyuan), fix memory leak in xmlAllocOutputBuffer (zhouzhongyuan), Fix unsigned int overflow (Jens Eggerstedt), dict.h: gcc 2.95 doesn't allow multiple storage classes (Nick Wellnhofer), Fix another code path in xmlParseQName (Nick Wellnhofer), Make sure that xmlParseQName returns NULL in error case (Nick Wellnhofer), Fix build without reader but with pattern (Nick Wellnhofer), Fix memory leak in xmlAllocOutputBufferInternal error path (Nick Wellnhofer), Fix unsigned integer overflow (Nick Wellnhofer), Fix return value of xmlOutputBufferWrite (Nick Wellnhofer), Fix parser termination from "Double hyphen within comment" error (David Warring), Fix call stack overflow in xmlFreePattern (Nick Wellnhofer), Fix null deref in previous commit (Nick Wellnhofer), Fix memory leaks in xmlXPathParseNameComplex error paths (Nick Wellnhofer), Check for integer overflow in xmlXPtrEvalChildSeq (Nick Wellnhofer), Fix xmllint dump of XPath namespace nodes (Nick Wellnhofer), Fix float casts in xmlXPathSubstringFunction (Nick Wellnhofer), Fix null deref in xmlregexp error path (Nick Wellnhofer), Fix null pointer dereference in xmlTextReaderReadOuterXml (Nick Wellnhofer), Fix memory leaks in xmlParseStartTag2 error paths (Nick Wellnhofer), Fix memory leak in xmlSAX2StartElement (Nick Wellnhofer), Fix commit "Memory leak in xmlFreeID (xmlreader.c)" (Nick Wellnhofer), Fix NULL pointer deref in xmlTextReaderValidateEntity (Nick Wellnhofer), Memory leak in xmlFreeTextReader (Nick Wellnhofer), Memory leak in xmlFreeID (xmlreader.c) (Nick Wellnhofer) - Improvements: Run XML conformance tests under CI (Nick Wellnhofer), Update GitLab CI config (Nick Wellnhofer), Propagate memory errors in valuePush (Nick Wellnhofer), Propagate memory errors in xmlXPathCompExprAdd (Nick Wellnhofer), Make xmlFreeDocElementContent non-recursive (Nick Wellnhofer), Enable continuous integration via GitLab CI (Nick Wellnhofer), Avoid ignored attribute warnings under GCC (Nick Wellnhofer), Make xmlDumpElementContent non-recursive (Nick Wellnhofer), Make apibuild.py ignore ATTRIBUTE_NO_SANITIZE (Nick Wellnhofer), Mark xmlExp* symbols as removed (Nick Wellnhofer), Make xmlParseConditionalSections non-recursive (Nick Wellnhofer), Adjust expected error in Python tests (Nick Wellnhofer), Make xmlTextReaderFreeNodeList non-recursive (Nick Wellnhofer), Make xmlFreeNodeList non-recursive (Nick Wellnhofer), Make xmlParseContent and xmlParseElement non-recursive (Nick Wellnhofer), Remove executable bit from non-executable files (Nick Wellnhofer), Fix expected output of test/schemas/any4 (Nick Wellnhofer), Optimize build instructions in README (zhouzhongyuan), xml2-config.in: Output CFLAGS and LIBS on the same line (Hugh McMaster), xml2-config: Add a --dynamic switch to print only shared libraries (Hugh McMaster), Annotate functions with __attribute__((no_sanitize)) (Nick Wellnhofer), Fix warnings when compiling without reader or push parser (Nick Wellnhofer), Remove unused member `doc` in xmlSaveCtxt (Nick Wellnhofer), Limit recursion depth in xmlXPathCompOpEvalPredicate (Nick Wellnhofer), Remove -Wno-array-bounds (Nick Wellnhofer), Remove unreachable code in xmlXPathCountFunction (Nick Wellnhofer), Improve XPath predicate and filter evaluation (Nick Wellnhofer), Limit recursion depth in xmlXPathOptimizeExpression (Nick Wellnhofer), Disable hash randomization when fuzzing (Nick Wellnhofer), Optional recursion limit when parsing XPath expressions (Nick Wellnhofer), Optional recursion limit when evaluating XPath expressions (Nick Wellnhofer), Use break statements in xmlXPathCompOpEval (Nick Wellnhofer), Optional XPath operation limit (Nick Wellnhofer), Fix compilation with --with-minimum (Nick Wellnhofer), Check XPath stack after calling functions (Nick Wellnhofer), Remove debug printf in xmlreader.c (Nick Wellnhofer), Always define LIBXML_THREAD_ENABLED when enabled (Michael Haubenwallner), Regenerate NEWS (Nick Wellnhofer), Change git repo URL (Nick Wellnhofer), Change bug tracker URL (Nick Wellnhofer), Remove outdated HTML file (Nick Wellnhofer), Fix unused function warning in testapi.c (Nick Wellnhofer), Add some generated test files to .gitignore (Nick Wellnhofer), Remove unneeded function pointer casts (Nick Wellnhofer), Fix -Wcast-function-type warnings (GCC 8) (Nick Wellnhofer), Fix -Wformat-truncation warnings (GCC 8) (Nick Wellnhofer) - Cleanups: Rebuild docs (Nick Wellnhofer), Disable xmlExp regex code (Nick Wellnhofer), Remove redundant code in xmlRelaxNGValidateState (Nick Wellnhofer), Remove redundant code in xmlXPathCompRelationalExpr (Nick Wellnhofer) v2.9.9: Jan 03 2019: - Security: CVE-2018-9251 CVE-2018-14567 Fix infinite loop in LZMA decompression (Nick Wellnhofer), CVE-2018-14404 Fix nullptr deref with XPath logic ops (Nick Wellnhofer), - Documentation: reader: Fix documentation comment (Mohammed Sadiq) - Portability: Fix MSVC build with lzma (Nick Wellnhofer), Variables need 'extern' in static lib on Cygwin (Michael Haubenwallner), Really declare dllexport/dllimport for Cygwin (Michael Haubenwallner), Merge branch 'patch-2' into 'master' (Nick Wellnhofer), Change dir to $THEDIR after ACLOCAL_PATH check autoreconf creates aclocal.m4 in $srcdir (Vitaly Buka), Improve error message if pkg.m4 couldn't be found (Nick Wellnhofer), NaN and Inf fixes for pre-C99 compilers (Nick Wellnhofer) - Bug Fixes: Revert "Support xmlTextReaderNextSibling w/o preparsed doc" (Nick Wellnhofer), Fix building relative URIs (Thomas Holder), Problem with data in interleave in RelaxNG validation (Nikolai Weibull), Fix memory leak in xmlSwitchInputEncodingInt error path (Nick Wellnhofer), Set doc on element obtained from freeElems (Nick Wellnhofer), Fix HTML serialization with UTF-8 encoding (Nick Wellnhofer), Use actual doc in xmlTextReaderRead*Xml (Nick Wellnhofer), Unlink node before freeing it in xmlSAX2StartElement (Nick Wellnhofer), Check return value of nodePush in xmlSAX2StartElement (Nick Wellnhofer), Free input buffer in xmlHaltParser (Nick Wellnhofer), Reset HTML parser input pointers on encoding failure (Nick Wellnhofer), Don't run icu_parse_test if EUC-JP is unsupported (Nick Wellnhofer), Fix xmlSchemaValidCtxtPtr reuse memory leak (Greg Hildstrom), Fix xmlTextReaderNext with preparsed document (Felix Bünemann), Remove stray character from comment (Nick Wellnhofer), Remove a misleading line from xmlCharEncOutput (Andrey Bienkowski), HTML noscript should not close p (Daniel Veillard), Don't change context node in xmlXPathRoot (Nick Wellnhofer), Stop using XPATH_OP_RESET (Nick Wellnhofer), Revert "Change calls to xmlCharEncInput to set flush false" (Nick Wellnhofer) - Improvements: Fix "Problem with data in interleave in RelaxNG validation" (Nikolai Weibull), cleanup: remove some unreachable code (Thomas Holder), add --relative to testURI (Thomas Holder), Remove redefined starts and defines inside include elements (Nikolai Weibull), Allow choice within choice in nameClass in RELAX NG (Nikolai Weibull), Look inside divs for starts and defines inside include (Nikolai Weibull), Add compile and libxml2-config.cmake to .gitignore (Nikolai Weibull), Stop using doc->charset outside parser code (Nick Wellnhofer), Add newlines to 'xmllint --xpath' output (Nick Wellnhofer), Don't include SAX.h from globals.h (Nick Wellnhofer), Support xmlTextReaderNextSibling w/o preparsed doc (Felix Bünemann), Don't instruct user to run make when autogen.sh failed (林博仁(Buo-ren Lin)), Run Travis ASan tests with "sudo: required" (Nick Wellnhofer), Improve restoring of context size and position (Nick Wellnhofer), Simplify and harden nodeset filtering (Nick Wellnhofer), Avoid unnecessary backups of the context node (Nick Wellnhofer), Fix inconsistency in xmlXPathIsInf (Nick Wellnhofer) - Cleanups: v2.9.8: Mar 05 2018: - Portability: python: remove single use of _PyVerify_fd (Patrick Welche), Build more test executables on Windows/MSVC (Nick Wellnhofer), Stop including ansidecl.h (Nick Wellnhofer), Fix libz and liblzma detection (Nick Wellnhofer), Revert "Compile testapi with -Wno-unused-function" (Nick Wellnhofer) - Bug Fixes: Fix xmlParserEntityCheck (Nick Wellnhofer), Halt parser in case of encoding error (Nick Wellnhofer), Clear entity content in case of errors (Nick Wellnhofer), Change calls to xmlCharEncInput to set flush false when not final call. Having flush incorrectly set to true causes errors for ICU. (Joel Hockey), Fix buffer over-read in xmlParseNCNameComplex (Nick Wellnhofer), Fix ICU library filenames on Windows/MSVC (Nick Wellnhofer), Fix xmlXPathIsNaN broken by recent commit (Nick Wellnhofer), Fix -Wenum-compare warnings (Nick Wellnhofer), Fix callback signature in testapi.c (Nick Wellnhofer), Fix unused parameter warning without ICU (Nick Wellnhofer), Fix IO callback signatures (Nick Wellnhofer), Fix misc callback signatures (Nick Wellnhofer), Fix list callback signatures (Nick Wellnhofer), Fix hash callback signatures (Nick Wellnhofer), Refactor name and type signature for xmlNop (Vlad Tsyrklevich), Fixed ICU to set flush correctly and provide pivot buffer. (Joel Hockey), Skip EBCDIC tests if EBCDIC isn't supported (Nick Wellnhofer) - Improvements: Disable pointer-overflow UBSan checks under Travis (Nick Wellnhofer), Improve handling of context input_id (Daniel Veillard), Add resource file to Windows DLL (ccpaging), Run Travis tests with -Werror (Nick Wellnhofer), Build with "-Wall -Wextra" (Nick Wellnhofer), Fix -Wtautological-pointer-compare warnings (Nick Wellnhofer), Remove unused AC_CHECKs (Nick Wellnhofer), Update information about contributing (Nick Wellnhofer), Fix -Wmisleading-indentation warnings (Nick Wellnhofer), Don't touch CFLAGS in configure.ac (Nick Wellnhofer), Ignore function pointer cast warnings (Nick Wellnhofer), Simplify XPath NaN, inf and -0 handling (Nick Wellnhofer), Introduce xmlPosixStrdup and update xmlMemStrdup (Nick Wellnhofer), Add test for ICU flush and pivot buffer (Nick Wellnhofer), Compile testapi with -Wno-unused-function (Nick Wellnhofer) 2.9.7: Nov 02 2017: - Documentation: xmlcatalog: refresh man page wrt. querying system catalog easily (Jan Pokorný) - Portability: Fix deprecated Travis compiler flag (Nick Wellnhofer), Add declaration for DllMain (J. Peter Mugaas), Fix preprocessor conditional in threads.h (J. Peter Mugaas), Fix pointer comparison warnings on 64-bit Windows (J. Peter Mugaas), Fix macro redefinition warning (J. Peter Mugaas), Default to native threads on MinGW-w64 (Nick Wellnhofer), Simplify Windows IO functions (Nick Wellnhofer), Fix runtest on Windows (Nick Wellnhofer), socklen_t is always int on Windows (Nick Wellnhofer), Don't redefine socket error codes on Windows (Nick Wellnhofer), Fix pointer/int cast warnings on 64-bit Windows (Nick Wellnhofer), Fix Windows compiler warnings in xmlCanonicPath (Nick Wellnhofer) - Bug Fixes: xmlcatalog: restore ability to query system catalog easily (Jan Pokorný), Fix comparison of nodesets to strings (Nick Wellnhofer) - Improvements: Add Makefile rules to rebuild HTML man pages (Nick Wellnhofer), Fix mixed decls and code in timsort.h (Nick Wellnhofer), Rework handling of return values in thread tests (Nick Wellnhofer), Fix unused variable warnings in testrecurse (Nick Wellnhofer), Fix -Wimplicit-fallthrough warnings (J. Peter Mugaas), Upgrade timsort.h to latest revision (Nick Wellnhofer), Increase warning level to /W3 under MSVC (Nick Wellnhofer), Fix a couple of warnings in dict.c and threads.c (Nick Wellnhofer), Update .gitignore for Windows (Nick Wellnhofer), Fix unused variable warnings in nanohttp.c (Nick Wellnhofer), Fix the Windows header mess (Nick Wellnhofer), Don't include winsock2.h in xmllint.c (Nick Wellnhofer), Remove generated file python/setup.py from version control (Nick Wellnhofer), Use __linux__ macro in generated code (Nick Wellnhofer) v2.9.6: Oct 06 2017: - Portability: Change preprocessor OS tests to __linux__ (Nick Wellnhofer) - Bug Fixes: Fix XPath stack frame logic (Nick Wellnhofer), Report undefined XPath variable error message (Nick Wellnhofer), Fix regression with librsvg (Nick Wellnhofer), Handle more invalid entity values in recovery mode (Nick Wellnhofer), Fix structured validation errors (Nick Wellnhofer), Fix memory leak in LZMA decompressor (Nick Wellnhofer), Set memory limit for LZMA decompression (Nick Wellnhofer), Handle illegal entity values in recovery mode (Nick Wellnhofer), Fix debug dump of streaming XPath expressions (Nick Wellnhofer), Fix memory leak in nanoftp (Nick Wellnhofer), Fix memory leaks in SAX1 parser (Nick Wellnhofer) v2.9.5: Sep 04 2017: - Security: Detect infinite recursion in parameter entities (Nick Wellnhofer), Fix handling of parameter-entity references (Nick Wellnhofer), Disallow namespace nodes in XPointer ranges (Nick Wellnhofer), Fix XPointer paths beginning with range-to (Nick Wellnhofer) - Documentation: Documentation fixes (Nick Wellnhofer), Spelling and grammar fixes (Nick Wellnhofer) - Portability: Adding README.zOS to list of extra files for the release (Daniel Veillard), Description of work needed to compile on zOS (Stéphane Michaut), Porting libxml2 on zOS encoding of code (Stéphane Michaut), small changes for OS/400 (Patrick Monnerat), relaxng.c, xmlschemas.c: Fix build on pre-C99 compilers (Chun-wei Fan) - Bug Fixes: Problem resolving relative URIs (Daniel Veillard), Fix unwanted warnings when switching encodings (Nick Wellnhofer), Fix signature of xmlSchemaAugmentImportedIDC (Daniel Veillard), Heap-buffer-overflow read of size 1 in xmlFAParsePosCharGroup (David Kilzer), Fix NULL pointer deref in xmlFAParseCharClassEsc (Nick Wellnhofer), Fix infinite loops with push parser in recovery mode (Nick Wellnhofer), Send xmllint usage error to stderr (Nick Wellnhofer), Fix NULL deref in xmlParseExternalEntityPrivate (Nick Wellnhofer), Make sure not to call IS_BLANK_CH when parsing the DTD (Nick Wellnhofer), Fix xmlHaltParser (Nick Wellnhofer), Fix pathological performance when outputting charrefs (Nick Wellnhofer), Fix invalid-source-encoding warnings in testWriter.c (Nick Wellnhofer), Fix duplicate SAX callbacks for entity content (David Kilzer), Treat URIs with scheme as absolute in C14N (Nick Wellnhofer), Fix copy-paste errors in error messages (Nick Wellnhofer), Fix sanity check in htmlParseNameComplex (Nick Wellnhofer), Fix potential infinite loop in xmlStringLenDecodeEntities (Nick Wellnhofer), Reset parser input pointers on encoding failure (Nick Wellnhofer), Fix memory leak in xmlParseEntityDecl error path (Nick Wellnhofer), Fix xmlBuildRelativeURI for URIs starting with './' (Nick Wellnhofer), Fix type confusion in xmlValidateOneNamespace (Nick Wellnhofer), Fix memory leak in xmlStringLenGetNodeList (Nick Wellnhofer), Fix NULL pointer deref in xmlDumpElementContent (Daniel Veillard), Fix memory leak in xmlBufAttrSerializeTxtContent (Nick Wellnhofer), Stop parser on unsupported encodings (Nick Wellnhofer), Check for integer overflow in memory debug code (Nick Wellnhofer), Fix buffer size checks in xmlSnprintfElementContent (Nick Wellnhofer), Avoid reparsing in xmlParseStartTag2 (Nick Wellnhofer), Fix undefined behavior in xmlRegExecPushStringInternal (Nick Wellnhofer), Check XPath exponents for overflow (Nick Wellnhofer), Check for overflow in xmlXPathIsPositionalPredicate (Nick Wellnhofer), Fix spurious error message (Nick Wellnhofer), Fix memory leak in xmlCanonicPath (Nick Wellnhofer), Fix memory leak in xmlXPathCompareNodeSetValue (Nick Wellnhofer), Fix memory leak in pattern error path (Nick Wellnhofer), Fix memory leak in parser error path (Nick Wellnhofer), Fix memory leaks in XPointer error paths (Nick Wellnhofer), Fix memory leak in xmlXPathNodeSetMergeAndClear (Nick Wellnhofer), Fix memory leak in XPath filter optimizations (Nick Wellnhofer), Fix memory leaks in XPath error paths (Nick Wellnhofer), Do not leak the new CData node if adding fails (David Tardon), Prevent unwanted external entity reference (Neel Mehta), Increase buffer space for port in HTTP redirect support (Daniel Veillard), Fix more NULL pointer derefs in xpointer.c (Nick Wellnhofer), Avoid function/data pointer conversion in xpath.c (Nick Wellnhofer), Fix format string warnings (Nick Wellnhofer), Disallow namespace nodes in XPointer points (Nick Wellnhofer), Fix comparison with root node in xmlXPathCmpNodes (Nick Wellnhofer), Fix attribute decoding during XML schema validation (Alex Henrie), Fix NULL pointer deref in XPointer range-to (Nick Wellnhofer) - Improvements: Updating the spec file to reflect Fedora 24 (Daniel Veillard), Add const in five places to move 1 KiB to .rdata (Bruce Dawson), Fix missing part of comment for function xmlXPathEvalExpression() (Daniel Veillard), Get rid of "blanks wrapper" for parameter entities (Nick Wellnhofer), Simplify handling of parameter entity references (Nick Wellnhofer), Deduplicate code in encoding.c (Nick Wellnhofer), Make HTML parser functions take const pointers (Nick Wellnhofer), Build test programs only when needed (Nick Wellnhofer), Fix doc/examples/index.py (Nick Wellnhofer), Fix compiler warnings in threads.c (Nick Wellnhofer), Fix empty-body warning in nanohttp.c (Nick Wellnhofer), Fix cast-align warnings (Nick Wellnhofer), Fix unused-parameter warnings (Nick Wellnhofer), Rework entity boundary checks (Nick Wellnhofer), Don't switch encoding for internal parameter entities (Nick Wellnhofer), Merge duplicate code paths handling PE references (Nick Wellnhofer), Test SAX2 callbacks with entity substitution (Nick Wellnhofer), Support catalog and threads tests under --without-sax1 (Nick Wellnhofer), Misc fixes for 'make tests' (Nick Wellnhofer), Initialize keepBlanks in HTML parser (Nick Wellnhofer), Add test cases for bug 758518 (David Kilzer), Fix compiler warning in htmlParseElementInternal (Nick Wellnhofer), Remove useless check in xmlParseAttributeListDecl (Nick Wellnhofer), Allow zero sized memory input buffers (Nick Wellnhofer), Add TODO comment in xmlSwitchEncoding (Nick Wellnhofer), Check for integer overflow in xmlXPathFormatNumber (Nick Wellnhofer), Make Travis print UBSan stacktraces (Nick Wellnhofer), Add .travis.yml (Nick Wellnhofer), Fix expected error output in Python tests (Nick Wellnhofer), Simplify control flow in xmlParseStartTag2 (Nick Wellnhofer), Disable LeakSanitizer when running API tests (Nick Wellnhofer), Avoid out-of-bound array access in API tests (Nick Wellnhofer), Avoid spurious UBSan errors in parser.c (Nick Wellnhofer), Parse small XPath numbers more accurately (Nick Wellnhofer), Rework XPath rounding functions (Nick Wellnhofer), Fix white space in test output (Nick Wellnhofer), Fix axis traversal from attribute and namespace nodes (Nick Wellnhofer), Check for trailing characters in XPath expressions earlier (Nick Wellnhofer), Rework final handling of XPath results (Nick Wellnhofer), Make xmlXPathEvalExpression call xmlXPathEval (Nick Wellnhofer), Remove unused variables (Nick Wellnhofer), Don't print generic error messages in XPath tests (Nick Wellnhofer) - Cleanups: Fix a couple of misleading indentation errors (Daniel Veillard), Remove unnecessary calls to xmlPopInput (Nick Wellnhofer) 2.9.4: May 23 2016: - Security: More format string warnings with possible format string vulnerability (David Kilzer), Avoid building recursive entities (Daniel Veillard), Heap-based buffer overread in htmlCurrentChar (Pranjal Jumde), Heap-based buffer-underreads due to xmlParseName (David Kilzer), Heap use-after-free in xmlSAX2AttributeNs (Pranjal Jumde), Heap use-after-free in htmlParsePubidLiteral and htmlParseSystemiteral (Pranjal Jumde), Fix some format string warnings with possible format string vulnerability (David Kilzer), Detect change of encoding when parsing HTML names (Hugh Davenport), Fix inappropriate fetch of entities content (Daniel Veillard), Bug 759398: Heap use-after-free in xmlDictComputeFastKey (Pranjal Jumde), Bug 758605: Heap-based buffer overread in xmlDictAddString (Pranjal Jumde), Bug 758588: Heap-based buffer overread in xmlParserPrintFileContextInternal (David Kilzer), Bug 757711: heap-buffer-overflow in xmlFAParsePosCharGroup (Pranjal Jumde), Add missing increments of recursion depth counter to XML parser. (Peter Simons) - Documentation: Fix typo: s{ ec -> cr }cipt (Jan Pokorný), Fix typos: dictio{ nn -> n }ar{y,ies} (Jan Pokorný), Fix typos: PATH_{ SEAPARATOR -> SEPARATOR } (Jan Pokorný), Correct a typo. (Shlomi Fish) - Portability: Correct the usage of LDFLAGS (Mattias Hansson), Revert the use of SAVE_LDFLAGS in configure.ac (Mattias Hansson), libxml2 hardcodes -L/lib in zlib/lzma tests which breaks cross-compiles (Mike Frysinger), Fix apibuild for a recently added construct (Daniel Veillard), Use pkg-config to locate zlib when possible (Stewart Brodie), Use pkg-config to locate ICU when possible (Stewart Brodie), Portability to non C99 compliant compilers (Patrick Monnerat), dict.h: Move xmlDictPtr definition before includes to allow direct inclusion. (Patrick Monnerat), os400: tell about xmllint and xmlcatalog in README400. (Patrick Monnerat), os400: properly process SGML add in XMLCATALOG command. (Patrick Monnerat), os400: implement CL command XMLCATALOG. (Patrick Monnerat), os400: compile and install program xmlcatalog (qshell-only). (Patrick Monnerat), os400: expand tabs in sources, strip trailing blanks. (Patrick Monnerat), os400: implement CL command XMLLINT. (Patrick Monnerat), os400: compile and install program xmllint (qshell-only). (Patrick Monnerat), os400: initscript make_module(): Use options instead of positional parameters. (Patrick Monnerat), os400: c14n.rpgle: allow *omit for nullable reference parameters. (Patrick Monnerat), os400: use like() for double type. (Patrick Monnerat), os400: use like() for int type. (Patrick Monnerat), os400: use like() for unsigned int type. (Patrick Monnerat), os400: use like() for enum types. (Patrick Monnerat), Add xz to xml2-config --libs output (Baruch Siach), Bug 760190: configure.ac should be able to build --with-icu without icu-config tool (David Kilzer), win32\VC10\config.h and VS 2015 (Bruce Dawson), Add configure maintainer mode (orzen) - Bug Fixes: Avoid an out of bound access when serializing malformed strings (Daniel Veillard), Unsigned addition may overflow in xmlMallocAtomicLoc() (David Kilzer), Integer signed/unsigned type mismatch in xmlParserInputGrow() (David Kilzer), Bug 763071: heap-buffer-overflow in xmlStrncat (Pranjal Jumde), Integer overflow parsing port number in URI (Michael Paddon), Fix an error with regexp on nullable counted char transition (Daniel Veillard), Fix memory leak with XPath namespace nodes (Nick Wellnhofer), Fix namespace axis traversal (Nick Wellnhofer), Fix null pointer deref in docs with no root element (Hugh Davenport), Fix XSD validation of URIs with ampersands (Alex Henrie), xmlschemastypes.c: accept endOfDayFrag Times set to "24:00:00" mean "end of day" and should not cause an error. (Patrick Monnerat), xmlcatalog: flush stdout before interactive shell input. (Patrick Monnerat), xmllint: flush stdout before interactive shell input. (Patrick Monnerat), Don't recurse into OP_VALUEs in xmlXPathOptimizeExpression (Nick Wellnhofer), Fix namespace::node() XPath expression (Nick Wellnhofer), Fix OOB write in xmlXPathEmptyNodeSet (Nick Wellnhofer), Fix parsing of NCNames in XPath (Nick Wellnhofer), Fix OOB read with invalid UTF-8 in xmlUTF8Strsize (Nick Wellnhofer), Do normalize string-based datatype value in RelaxNG facet checking (Audric Schiltknecht), Bug 760921: REGRESSION (8eb55d78): doc/examples/io1 test fails after fix for "xmlSaveUri() incorrectly recomposes URIs with rootless paths" (David Kilzer), Bug 760861: REGRESSION (bf9c1dad): Missing results for test/schemas/regexp-char-ref_[01].xsd (David Kilzer), error.c: *input->cur == 0 does not mean no error (Pavel Raiskup), Add missing RNG test files (David Kilzer), Bug 760183: REGRESSION (v2.9.3): XML push parser fails with bogus UTF-8 encoding error when multi-byte character in large CDATA section is split across buffer (David Kilzer), Bug 758572: ASAN crash in make check (David Kilzer), Bug 721158: Missing ICU string when doing --version on xmllint (David Kilzer), python 3: libxml2.c wrappers create Unicode str already (Michael Stahl), Add autogen.sh to distrib (orzen), Heap-based buffer overread in xmlNextChar (Daniel Veillard) - Improvements: Add more debugging info to runtest (Daniel Veillard), Implement "runtest -u" mode (David Kilzer), Add a make rule to rebuild for ASAN (Daniel Veillard) v2.9.3: Nov 20 2015: - Security: CVE-2015-8242 Buffer overead with HTML parser in push mode (Hugh Davenport), CVE-2015-7500 Fix memory access error due to incorrect entities boundaries (Daniel Veillard), CVE-2015-7499-2 Detect incoherency on GROW (Daniel Veillard), CVE-2015-7499-1 Add xmlHaltParser() to stop the parser (Daniel Veillard), CVE-2015-5312 Another entity expansion issue (David Drysdale), CVE-2015-7497 Avoid an heap buffer overflow in xmlDictComputeFastQKey (David Drysdale), CVE-2015-7498 Avoid processing entities after encoding conversion failures (Daniel Veillard), CVE-2015-8035 Fix XZ compression support loop (Daniel Veillard), CVE-2015-7942-2 Fix an error in previous Conditional section patch (Daniel Veillard), CVE-2015-7942 Another variation of overflow in Conditional sections (Daniel Veillard), CVE-2015-1819 Enforce the reader to run in constant memory (Daniel Veillard) CVE-2015-7941_2 Cleanup conditional section error handling (Daniel Veillard), CVE-2015-7941_1 Stop parsing on entities boundaries errors (Daniel Veillard), - Documentation: Correct spelling of "calling" (Alex Henrie), Fix a small error in xmllint --format description (Fabien Degomme), Avoid XSS on the search of xmlsoft.org (Daniel Veillard) - Portability: threads: use forward declarations only for glibc (Michael Heimpold), Update Win32 configure.js to search for configure.ac (Daniel Veillard) - Bug Fixes: Bug on creating new stream from entity (Daniel Veillard), Fix some loop issues embedding NEXT (Daniel Veillard), Do not print error context when there is none (Daniel Veillard), Avoid extra processing of MarkupDecl when EOF (Hugh Davenport), Fix parsing short unclosed comment uninitialized access (Daniel Veillard), Add missing Null check in xmlParseExternalEntityPrivate (Gaurav Gupta), Fix a bug in CData error handling in the push parser (Daniel Veillard), Fix a bug on name parsing at the end of current input buffer (Daniel Veillard), Fix the spurious ID already defined error (Daniel Veillard), Fix previous change to node sort order (Nick Wellnhofer), Fix a self assignment issue raised by clang (Scott Graham), Fail parsing early on if encoding conversion failed (Daniel Veillard), Do not process encoding values if the declaration if broken (Daniel Veillard), Silence clang's -Wunknown-attribute (Michael Catanzaro), xmlMemUsed is not thread-safe (Martin von Gagern), Fix support for except in nameclasses (Daniel Veillard), Fix order of root nodes (Nick Wellnhofer), Allow attributes on descendant-or-self axis (Nick Wellnhofer), Fix the fix to Windows locking (Steve Nairn), Fix timsort invariant loop re: Envisage article (Christopher Swenson), Don't add IDs in xmlSetTreeDoc (Nick Wellnhofer), Account for ID attributes in xmlSetTreeDoc (Nick Wellnhofer), Remove various unused value assignments (Philip Withnall), Fix missing entities after CVE-2014-3660 fix (Daniel Veillard), Revert "Missing initialization for the catalog module" (Daniel Veillard) - Improvements: Reuse xmlHaltParser() where it makes sense (Daniel Veillard), xmlStopParser reset errNo (Daniel Veillard), Re-enable xz support by default (Daniel Veillard), Recover unescaped less-than character in HTML recovery parsing (Daniel Veillard), Allow HTML serializer to output HTML5 DOCTYPE (Shaun McCance), Regression test for bug #695699 (Nick Wellnhofer), Add a couple of XPath tests (Nick Wellnhofer), Add Python 3 rpm subpackage (Tomas Radej), libxml2-config.cmake.in: update include directories (Samuel Martin), Adding example from bugs 738805 to regression tests (Daniel Veillard) - Cleanups: 2.9.2: Oct 16 2014: - Security: Fix for CVE-2014-3660 billion laugh variant (Daniel Veillard), CVE-2014-0191 Do not fetch external parameter entities (Daniel Veillard) - Bug Fixes: fix memory leak xml header encoding field with XML_PARSE_IGNORE_ENC (Bart De Schuymer), xmlmemory: handle realloc properly (Yegor Yefremov), Python generator bug raised by the const change (Daniel Veillard), Windows Critical sections not released correctly (Daniel Veillard), Parser error on repeated recursive entity expansion containing < (Daniel Veillard), xpointer : fixing Null Pointers (Gaurav Gupta), Remove Unnecessary Null check in xpointer.c (Gaurav Gupta), parser bug on misformed namespace attributes (Dennis Filder), Pointer dereferenced before null check (Daniel Veillard), Leak of struct addrinfo in xmlNanoFTPConnect() (Gaurav Gupta), Possible overflow in HTMLParser.c (Daniel Veillard), python/tests/sync.py assumes Python dictionaries are ordered (John Beck), Fix Enum check and missing break (Gaurav Gupta), xmlIO: Handle error returns from dup() (Philip Withnall), Fix a problem properly saving URIs (Daniel Veillard), wrong error column in structured error when parsing attribute values (Juergen Keil), wrong error column in structured error when skipping whitespace in xml decl (Juergen Keil), no error column in structured error handler for xml schema validation errors (Juergen Keil), Couple of Missing Null checks (Gaurav Gupta), Add couple of missing Null checks (Daniel Veillard), xmlschemastypes: Fix potential array overflow (Philip Withnall), runtest: Fix a memory leak on parse failure (Philip Withnall), xmlIO: Fix an FD leak on gzdopen() failure (Philip Withnall), xmlcatalog: Fix a memory leak on quit (Philip Withnall), HTMLparser: Correctly initialise a stack allocated structure (Philip Withnall), Check for tmon in _xmlSchemaDateAdd() is incorrect (David Kilzer), Avoid Possible Null Pointer in trio.c (Gaurav Gupta), Fix processing in SAX2 in case of an allocation failure (Daniel Veillard), XML Shell command "cd" does not handle "/" at end of path (Daniel Veillard), Fix various Missing Null checks (Gaurav Gupta), Fix a potential NULL dereference (Daniel Veillard), Add a couple of misisng check in xmlRelaxNGCleanupTree (Gaurav Gupta), Add a missing argument check (Gaurav Gupta), Adding a check in case of allocation error (Gaurav Gupta), xmlSaveUri() incorrectly recomposes URIs with rootless paths (Dennis Filder), Adding some missing NULL checks (Gaurav), Fixes for xmlInitParserCtxt (Daniel Veillard), Fix regressions introduced by CVE-2014-0191 patch (Daniel Veillard), erroneously ignores a validation error if no error callback set (Daniel Veillard), xmllint was not parsing the --c14n11 flag (Sérgio Batista), Avoid Possible null pointer dereference in memory debug mode (Gaurav), Avoid Double Null Check (Gaurav), Restore context size and position after XPATH_OP_ARG (Nick Wellnhofer), Fix xmlParseInNodeContext() if node is not element (Daniel Veillard), Avoid a possible NULL pointer dereference (Gaurav), Fix xmlTextWriterWriteElement when a null content is given (Daniel Veillard), Fix an typo 'onrest' in htmlScriptAttributes (Daniel Veillard), fixing a ptotential uninitialized access (Daniel Veillard), Fix an fd leak in an error case (Daniel Veillard), Missing initialization for the catalog module (Daniel Veillard), Handling of XPath function arguments in error case (Nick Wellnhofer), Fix a couple of missing NULL checks (Gaurav), Avoid a possibility of dangling encoding handler (Gaurav), Fix HTML push parser to accept HTML_PARSE_NODEFDTD (Arnold Hendriks), Fix a bug loading some compressed files (Mike Alexander), Fix XPath node comparison bug (Gaurav), Type mismatch in xmlschemas.c (Gaurav), Type mismatch in xmlschemastypes.c (Gaurav), Avoid a deadcode in catalog.c (Daniel Veillard), run close socket on Solaris, same as we do on other platforms (Denis Pauk), Fix pointer dereferenced before null check (Gaurav), Fix a potential NULL dereference in tree code (Daniel Veillard), Fix potential NULL pointer dereferences in regexp code (Gaurav), xmllint --pretty crashed without following numeric argument (Tim Galeckas), Fix XPath expressions of the form '@ns:*' (Nick Wellnhofer), Fix XPath '//' optimization with predicates (Nick Wellnhofer), Clear up a potential NULL dereference (Daniel Veillard), Fix a possible NULL dereference (Gaurav), Avoid crash if allocation fails (Daniel Veillard), Remove occasional leading space in XPath number formatting (Daniel Veillard), Fix handling of mmap errors (Daniel Veillard), Catch malloc error and exit accordingly (Daniel Veillard), missing else in xlink.c (Ami Fischman), Fix a parsing bug on non-ascii element and CR/LF usage (Daniel Veillard), Fix a regression in xmlGetDocCompressMode() (Daniel Veillard), properly quote the namespace uris written out during c14n (Aleksey Sanin), Remove premature XInclude check on URI being relative (Alexey Neyman), Fix missing break on last() function for attributes (dcb), Do not URI escape in server side includes (Romain Bondue), Fix an error in xmlCleanupParser (Alexander Pastukhov) - Documentation: typo in error messages "colon are forbidden from..." (Daniel Veillard), Fix a link to James SAX documentation old page (Daniel Veillard), Fix typos in relaxng.c (Jan Pokorný), Fix a doc typo (Daniel Veillard), Fix typos in {tree,xpath}.c (errror) (Jan Pokorný), Add limitations about encoding conversion (Daniel Veillard), Fix typos in xmlschemas{,types}.c (Jan Pokorný), Fix incorrect spelling entites->entities (Jan Pokorný), Forgot to document 2.9.1 release, regenerate docs (Daniel Veillard) - Portability: AC_CONFIG_FILES and executable bit (Roumen Petrov), remove HAVE_CONFIG_H dependency in testlimits.c (Roumen Petrov), fix some tabs mixing incompatible with python3 (Roumen Petrov), Visual Studio 14 CTP defines snprintf() (Francis Dupont), OS400: do not try to copy unexisting doc files (Patrick Monnerat), OS400: use either configure.ac or configure.in. (Patrick Monnerat), os400: make-src.sh: create physical file with target CCSID (Patrick Monnerat), OS400: Add some more C macros equivalent procedures. (Patrick Monnerat), OS400: use C macros to implement equivalent RPG support procedures. (Patrick Monnerat), OS400: implement XPath macros as procedures for ILE/RPG support. (Patrick Monnerat), OS400: include in distribution tarball. (Patrick Monnerat), OS400: Add README: compilation directives and OS/400 specific stuff. (Patrick Monnerat), OS400: Add compilation scripts. (Patrick Monnerat), OS400: ILE RPG language header files. (Patrick Monnerat), OS400: implement some macros as functions for ILE/RPG language support (that as no macros). (Patrick Monnerat), OS400: UTF8<-->EBCDIC wrappers for system and external library calls (Patrick Monnerat), OS400: Easy character transcoding support (Patrick Monnerat), OS400: iconv functions compatibility wrappers and table builder. (Patrick Monnerat), OS400: create architecture directory. Implement dlfcn emulation. (Patrick Monnerat), Fix building when configuring without xpath and xptr (Daniel Veillard), configure: Add --with-python-install-dir (Jonas Eriksson), Fix compilation with minimum and xinclude. (Nicolas Le Cam), Compile out use of xmlValidateNCName() when not available. (Nicolas Le Cam), Fix compilation with minimum and schematron. (Nicolas Le Cam), Legacy needs xmlSAX2StartElement() and xmlSAX2EndElement(). (Nicolas Le Cam), Don't use xmlValidateName() when not available. (Nicolas Le Cam), Fix a portability issue on Windows (Longstreth Jon), Various portability patches for OpenVMS (Jacob (Jouk) Jansen), Use specific macros for portability to OS/400 (Patrick Monnerat), Add macros needed for OS/400 portability (Patrick Monnerat), Portability patch for fopen on OS/400 (Patrick Monnerat), Portability fixes for OS/400 (Patrick Monnerat), Improve va_list portability (Patrick Monnerat), Portability fix (Patrick Monnerat), Portability fix (Patrick Monnerat), Generic portability fix (Patrick Monnerat), Shortening lines in headers (Patrick Monnerat), build: Use pkg-config to find liblzma in preference to AC_CHECK_LIB (Philip Withnall), build: Add @LZMA_LIBS@ to libxml’s pkg-config files (Philip Withnall), fix some tabs mixing incompatible with python3 (Daniel Veillard), add additional defines checks for support "./configure --with-minimum" (Denis Pauk), Another round of fixes for older versions of Python (Arfrever Frehtes Taifersar Arahesis), python: fix drv_libxml2.py for python3 compatibility (Alexandre Rostovtsev), python: Fix compiler warnings when building python3 bindings (Armin K), Fix for compilation with python 2.6.8 (Petr Sumbera) - Improvements: win32/libxml2.def.src after rebuild in doc (Roumen Petrov), elfgcchack.h: more legacy needs xmlSAX2StartElement() and xmlSAX2EndElement() (Roumen Petrov), elfgcchack.h: add xmlXPathNodeEval and xmlXPathSetContextNode (Roumen Petrov), Provide cmake module (Samuel Martin), Fix a couple of issues raised by make dist (Daniel Veillard), Fix and add const qualifiers (Kurt Roeckx), Preparing for upcoming release of 2.9.2 (Daniel Veillard), Fix zlib and lzma libraries check via command line (Dmitriy), wrong error column in structured error when parsing end tag (Juergen Keil), doc/news.html: small update to avoid line join while generating NEWS. (Patrick Monnerat), Add methods for python3 iterator (Ron Angeles), Support element node traversal in document fragments. (Kyle VanderBeek), xmlNodeSetName: Allow setting the name to a substring of the currently set name (Tristan Van Berkom), Added macros for argument casts (Eric Zurcher), adding init calls to xml and html Read parsing entry points (Daniel Veillard), Get rid of 'REPLACEMENT CHARACTER' Unicode chars in xmlschemas.c (Jan Pokorný), Implement choice for name classes on attributes (Shaun McCance), Two small namespace tweaks (Daniel Veillard), xmllint --memory should fail on empty files (Daniel Veillard), Cast encoding name to char pointer to match arg type (Nikolay Sivov) - Cleanups: Removal of old configure.in (Daniel Veillard), Unreachable code in tree.c (Gaurav Gupta), Remove a couple of dead conditions (Gaurav Gupta), Avoid some dead code and cleanup in relaxng.c (Gaurav), Drop not needed checks (Denis Pauk), Fix a wrong test (Daniel Veillard) 2.9.1: Apr 19 2013: - Features: Support for Python3 (Daniel Veillard), Add xmlXPathSetContextNode and xmlXPathNodeEval (Alex Bligh) - Documentation: Add documentation for xmllint --xpath (Daniel Veillard), Fix the URL of the SAX documentation from James (Daniel Veillard), Fix spelling of "length". (Michael Wood) - Portability: Fix python bindings with versions older than 2.7 (Daniel Veillard), rebuild docs:Makefile.am (Roumen Petrov), elfgcchack.h after rebuild in doc (Roumen Petrov), elfgcchack for buf module (Roumen Petrov), Fix a uneeded and wrong extra link parameter (Daniel Veillard), Few cleanup patches for Windows (Denis Pauk), Fix rpmbuild --nocheck (Mark Salter), Fix for win32/configure.js and WITH_THREAD_ALLOC (Daniel Richard), Fix Broken multi-arch support in xml2-config (Daniel Veillard), Fix a portability issue for GCC < 3.4.0 (Daniel Veillard), Windows build fixes (Daniel Richard), Fix a thread portability problem (Friedrich Haubensak), Downgrade autoconf requirement to 2.63 (Daniel Veillard) - Bug Fixes: Fix a linking error for python bindings (Daniel Veillard), Fix a couple of return without value (Jüri Aedla), Improve the hashing functions (Daniel Franke), Improve handling of xmlStopParser() (Daniel Veillard), Remove risk of lockup in dictionary initialization (Daniel Veillard), Activate detection of encoding in external subset (Daniel Veillard), Fix an output buffer flushing conversion bug (Mikhail Titov), Fix an old bug in xmlSchemaValidateOneElement (Csaba László), Fix configure cannot remove messages (Gilles Espinasse), fix schema validation in combination with xsi:nil (Daniel Veillard), xmlCtxtReadFile doesn't work with literal IPv6 URLs (Steve Wolf), Fix a few problems with setEntityLoader (Alexey Neyman), Detect excessive entities expansion upon replacement (Daniel Veillard), Fix the flushing out of raw buffers on encoding conversions (Daniel, Veillard), Fix some buffer conversion issues (Daniel Veillard), When calling xmlNodeDump make sure we grow the buffer quickly (Daniel, Veillard), Fix an error in the progressive DTD parsing code (Dan Winship), xmllint should not load DTD by default when using the reader (Daniel, Veillard), Try IBM-037 when looking for EBCDIC handlers (Petr Sumbera), Fix potential out of bound access (Daniel Veillard), Fix large parse of file from memory (Daniel Veillard), Fix a bug in the nsclean option of the parser (Daniel Veillard), Fix a regression in 2.9.0 breaking validation while streaming (Daniel, Veillard), Remove potential calls to exit() (Daniel Veillard) - Improvements: Regenerated API, and testapi, rebuild documentation (Daniel Veillard), Fix tree iterators broken by 2to3 script (Daniel Veillard), update all tests for Python3 and Python2 (Daniel Veillard), A few more fixes for python 3 affecting libxml2.py (Daniel Veillard), Fix compilation on Python3 (Daniel Veillard), Converting apibuild.py to python3 (Daniel Veillard), First pass at starting porting to python3 (Daniel Veillard), updated configure.in for python3 (Daniel Veillard), Add support for xpathRegisterVariable in Python (Shaun McCance), Added a regression tests from bug 694228 data (Daniel Veillard), Cache presence of '<' in entities content (Daniel Veillard), Avoid extra processing on entities (Daniel Veillard), Python binding for xmlRegisterInputCallback (Alexey Neyman), Python bindings: DOM casts everything to xmlNode (Alexey Neyman), Define LIBXML_THREAD_ALLOC_ENABLED via xmlversion.h (Tim Starling), Adding streaming validation to runtest checks (Daniel Veillard), Add a --pushsmall option to xmllint (Daniel Veillard) - Cleanups: Switched comment in file to UTF-8 encoding (Daniel Veillard), Extend gitignore (Daniel Veillard), Silent the new python test on input (Alexey Neyman), Cleanup of a duplicate test (Daniel Veillard), Cleanup on duplicate test expressions (Daniel Veillard), Fix compiler warning after 153cf15905cf4ec080612ada6703757d10caba1e (Patrick, Gansterer), Spec cleanups and a fix for multiarch support (Daniel Veillard), Silence a clang warning (Daniel Veillard), Cleanup the Copyright to be pure MIT Licence wording (Daniel Veillard), rand_seed should be static in dict.c (Wouter Van Rooy), Fix typos in parser comments (Jan Pokorný) 2.9.0: Sep 11 2012: - Features: A few new API entry points, More resilient push parser mode, A lot of portability improvement, Faster XPath evaluation - Documentation: xml2-config.1 markup error (Christian Weisgerber), libxml(3) manpage typo fix (John Bradshaw), More cleanups to the documentation part of libxml2 (Daniel Richard G) - Portability: Bug 676544 - fails to build with --without-sax1 (Akira TAGOH), fix builds not having stdint.h (Rob Richards), GetProcAddressA is available only on WinCE (Daniel Veillard), More updates and cleanups on autotools and Makefiles (Daniel Richard G), More changes for Win32 compilation (Eric Zurcher), Basic changes for Win32 builds of release 2.9.0: compile buf.c (Eric Zurcher), Bundles all generated files for python into the distribution (Daniel Richard G), Fix compiler warnings of wincecompat.c (Patrick Gansterer), Fix non __GNUC__ build (Patrick Gansterer), Fix windows unicode build (Patrick Gansterer), clean redefinition of {v}snprintf in C-source (Roumen Petrov), use xmlBuf... if DEBUG_INPUT is defined (Roumen Petrov), fix runtests to use pthreads support for various Unix platforms (Daniel Richard G), Various "make distcheck" and portability fixups 2nd part (Daniel Richard G), Various "make distcheck" and portability fixups (Daniel Richard G), Fix compilation on older Visual Studio (Daniel Veillard) - Bug Fixes: Change the XPath code to percolate allocation errors (Daniel Veillard), Fix reuse of xmlInitParser (Daniel Veillard), Fix potential crash on entities errors (Daniel Veillard), initialize var (Rob Richards), Fix the XPath arity check to also check the XPath stack limits (Daniel Veillard), Fix problem with specific and generic error handlers (Pietro Cerutti), Avoid a potential infinite recursion (Daniel Veillard), Fix an XSD error when generating internal automata (Daniel Veillard), Patch for xinclude of text using multibyte characters (Vitaly Ostanin), Fix a segfault on XSD validation on pattern error (Daniel Veillard), Fix missing xmlsave.h module which was ignored in recent builds (Daniel Veillard), Add a missing element check (Daniel Veillard), Adding various checks on node type though the API (Daniel Veillard), Namespace nodes can't be unlinked with xmlUnlinkNode (Daniel Veillard), Fix make dist to include new private header files (Daniel Veillard), More fixups on the push parser behaviour (Daniel Veillard), Strengthen behaviour of the push parser in problematic situations (Daniel Veillard), Enforce XML_PARSER_EOF state handling through the parser (Daniel Veillard), Fixup limits parser (Daniel Veillard), Do not fetch external parsed entities (Daniel Veillard), Fix an error in previous commit (Aron Xu), Fix entities local buffers size problems (Daniel Veillard), Fix parser local buffers size problems (Daniel Veillard), Fix a failure to report xmlreader parsing failures (Daniel Veillard) - Improvements: Keep libxml2.syms when running "make distclean" (Daniel Veillard), Allow to set the quoting character of an xmlWriter (Csaba Raduly), Keep non-significant blanks node in HTML parser (Daniel Veillard), Add a forbidden variable error number and message to XPath (Daniel Veillard), Support long path names on WNT (Michael Stahl), Improve HTML escaping of attribute on output (Daniel Veillard), Handle ICU_LIBS as LIBADD, not LDFLAGS to prevent linking errors (Arfrever Frehtes Taifersar Arahesis), Switching XPath node sorting to Timsort (Vojtech Fried), Optimizing '//' in XPath expressions (Nick Wellnhofer), Expose xmlBufShrink in the public tree API (Daniel Veillard), Visible HTML elements close the head tag (Conrad Irwin), Fix file and line report for XSD SAX and reader streaming validation (Daniel Veillard), Fix const qualifyer to definition of xmlBufferDetach (Daniel Veillard), minimize use of HAVE_CONFIG_H (Roumen Petrov), fixup regression in Various "make distcheck" and portability fixups (Roumen Petrov), Add support for big line numbers in error reporting (Daniel Veillard), Avoid using xmlBuffer for serialization (Daniel Veillard), Improve compatibility between xmlBuf and xmlBuffer (Daniel Veillard), Provide new accessors for xmlOutputBuffer (Daniel Veillard), Improvements for old buffer compatibility (Daniel Veillard), Expand the limit test program (Daniel Veillard), Improve error reporting on parser errors (Daniel Veillard), Implement some default limits in the XPath module (Daniel Veillard), Introduce some default parser limits (Daniel Veillard), Cleanups and new limit APIs for dictionaries (Daniel Veillard), Fixup for buf.c (Daniel Veillard), Cleanup URI module memory allocation code (Daniel Veillard), Extend testlimits (Daniel Veillard), More avoid quadratic behaviour (Daniel Veillard), Impose a reasonable limit on PI size (Daniel Veillard), first version of testlimits new test (Daniel Veillard), Avoid quadratic behaviour in some push parsing cases (Daniel Veillard), Impose a reasonable limit on comment size (Daniel Veillard), Impose a reasonable limit on attribute size (Daniel Veillard), Harden the buffer code and make it more compatible (Daniel Veillard), More cleanups for input/buffers code (Daniel Veillard), Cleanup function xmlBufResetInput(), to set input from Buffer (Daniel Veillard) Switch the test program for characters to new input buffers (Daniel Veillard), Convert the HTML tree module to the new buffers (Daniel Veillard), Convert of the HTML parser to new input buffers (Daniel Veillard), Convert the writer to new output buffer and save APIs (Daniel Veillard), Convert XMLReader to the new input buffers (Daniel Veillard), New saving functions using xmlBuf and conversion (Daniel Veillard), Provide new xmlBuf based saving functions (Daniel Veillard), Convert XInclude to the new input buffers (Daniel Veillard), Convert catalog code to the new input buffers (Daniel Veillard), Convert C14N to the new Input buffer (Daniel Veillard), Convert xmlIO.c to the new input and output buffers (Daniel Veillard), Convert XML parser to the new input buffers (Daniel Veillard), Incompatible change to the Input and Output buffers (Daniel Veillard), Adding new encoding function to deal with the new structures (Daniel Veillard), Convert XPath to xmlBuf (Daniel Veillard), Adding a new buf module for buffers (Daniel Veillard), Memory error within SAX2 reuse common framework (Daniel Veillard), Fix xmllint --xpath node initialization (Daniel Veillard) - Cleanups: Various cleanups to avoid compiler warnings (Daniel Veillard), Big space and tab cleanup (Daniel Veillard), Followup to LibXML2 docs/examples cleanup patch (Daniel Veillard), Second round of cleanups for LibXML2 docs/examples (Daniel Richard), Remove all .cvsignore as they are not used anymore (Daniel Veillard), Fix a Timsort function helper comment (Daniel Veillard), Small cleanup for valgrind target (Daniel Veillard), Patch for portability of latin characters in C files (Daniel Veillard), Cleanup some of the parser code (Daniel Veillard), Fix a variable name in comment (Daniel Veillard), Regenerated testapi.c (Daniel Veillard), Regenerating docs and API files (Daniel Veillard), Small cleanup of unused variables in test (Daniel Veillard), Expand .gitignore with more files (Daniel Veillard) 2.8.0: May 23 2012: - Features: add lzma compression support (Anders F Bjorklund) - Documentation: xmlcatalog: Add uri and delegateURI to possible add types in man page. (Ville Skyttä), Update README.tests (Daniel Veillard), URI handling code is not OOM resilient (Daniel Veillard), Fix an error in comment (Daniel Veillard), Fixed bug #617016 (Daniel Mustieles), Fixed two typos in the README document (Daniel Neel), add generated html files (Anders F Bjorklund), Clarify the need to use xmlFreeNode after xmlUnlinkNode (Daniel Veillard), Improve documentation a bit (Daniel Veillard), Updated URL for lxml python bindings (Daniel Veillard) - Portability: Restore code for Windows compilation (Daniel Veillard), Remove git error message during configure (Christian Dywan), xmllint: Build fix for endTimer if !defined(HAVE_GETTIMEOFDAY) (Patrick R. Gansterer), remove a bashism in confgure.in (John Hein), undef ERROR if already defined (Patrick R. Gansterer), Fix library problems with mingw-w64 (Michael Cronenworth), fix windows build. ifdef addition from bug 666491 makes no sense (Rob Richards), prefer native threads on win32 (Sam Thursfield), Allow to compile with Visual Studio 2010 (Thomas Lemm), Fix mingw's snprintf configure check (Andoni Morales), fixed a 64bit big endian issue (Marcus Meissner), Fix portability failure if netdb.h lacks NO_ADDRESS (Daniel Veillard), Fix windows build from lzma addition (Rob Richards), autogen: Only check for libtoolize (Colin Walters), Fix the Windows build files (Patrick von Reth), 634846 Remove a linking option breaking Windows VC10 (Daniel Veillard), 599241 fix an initialization problem on Win64 (Andrew W. Nosenko), fix win build (Rob Richards) - Bug fixes: Part for rand_r checking missing (Daniel Veillard), Cleanup on randomization (Daniel Veillard), Fix undefined reference in python module (Pacho Ramos), Fix a race in xmlNewInputStream (Daniel Veillard), Fix weird streaming RelaxNG errors (Noam), Fix various bugs in new code raised by the API checking (Daniel Veillard), Fix various problems with "make dist" (Daniel Veillard), Fix a memory leak in the xzlib code (Daniel Veillard), HTML parser error with

fo|+ܠt7[MɓFR%v! S<j{p+JK]q!rH'Lʱe47em}skR&Ń\ \JC\ܞ< @ORjuS 6,=昷 1:BȖnZjQ9 MdNPH2q+IbPi/I9H em{)0f{_AJDsdž@;1ヵ23ۣIfÉ(;C9@ 8hRI-:AVo#8/I5;DE^(_MX(bݽ<@JJ g joq]%(NP}]C8½6:'Cj dHO(YP'Kb_[ggQBU=C ᩥyOtj#0̴!;T~`lH-s8*GUt@ڠ^)K}4X~%gwu{ 4mu϶q ΃:^eºc&#l(e źu[ρB #rMEi"3-@hj[zzI_ \$/ ZEf\J(gQ\:;SJVN~͈l.~*۪)74IɟQ^n L0:R"Tuj@ 6L5-\\H|pcoAyF 8ҩ' OOIdN{9yqͼuiϯf^QˍVj*7 }YH&O1Ţd[y*32.t %FfLV 6y%} dg?rNNUݯ/Ui}TJ.}<jb7 }It`0ewڼX$Ί^d]Z" 'oyl@6Iܶu-[[ "Q4~u)MR$h8vp"l}y$[(w.QRcѸ`"kW SӠCuh7A4AtJjL\6h׸LRELZO#ccFe->f)%z;OcG2>.y:4˼Zì:5UU0m8c6Z ?AOwbE0,"mslz`I|B55"i|RO~mgGPKyVSw4hpip/_internal/cli/__init__.py%1! {^"E|b>HigFs~Ǡr(nAbb'H0d<潓UC,x6*Ju),4g!n{Eu^0H>>[yPKyVSp#pip/_internal/cli/autocompletion.pyXM6Wa%ī69u]A"IуaH\ȢJR ?$RӤ"9ÙǙ%INxEjҊg&85\47JӺf;x!e e/$Y,RJwZ8BԊHycOi.Kǚ-;ܷ/V "˫'V#ddT"mv] 6vJ蜂tG5 L;] 7J-HbƊ20SвӚTT[7GV7V@_` 'ȡ Uvu[<R ATųɡqӽ.]*Atmu1èc&(> D'imʜ91",`Ihm]a|;7j$т~|h8kA|q"HNIؙO!/돲cD@0ixu5“x|^$#Tޕ%/phhYǸɕce9Osc!֣,m G1 s J0u5sL]ͺ/ ><<07 (j K; ߃m:K#PyPY :쉋Nc2ss 17>}zd^(k턤9-Y'滭qFYb.6E?7{P3^m9}z5m2ed.XtXkSt[ Qް)Ee&fWRti֬[mg٪yBTE|cI~#>#{N_FBpa_q|8ᔍ<u_f;!I7t 4"=x(ˡ_u*}uX$>$#lG]jppF?׸t5qz{5qwX"憐_ h4o0Mj,1Yyb'* .DM/8pr m.,]J8Y_@>Ɇ Of ,pÊw88jO(^+`3[Z. UCWt.2Ƶ (1X;{"z( D-]cb@\zdW]x \q*,qvblS!A05]OI?~Y+P9neYbpV/8J g5a PKyVSv n!pip/_internal/cli/base_command.pyY[o6~ c/a:zw'Lk4u3] D;ܑ%-)R%N;$y\(.C݊Oeb)Q۲RE.)[e&GTeU^I8ɔlbCJTz(Xu,ļ;SP&\ɵ(cSdM%@TO|,~Z.}/\/>}X\MG%v}"mH6Zڭsrgy!]BL{wZ>6+;[:qx2䶍'|奨x&wM{K]|*őq[?3KOIz[eOl#+^b3e9>SVPjcko+~/T=o?Nu+qZ*/ņ/ߥtULϞsxm<G#Iٌ=FMD_F#$Ѭ4֍]s|2l:7Ʌ@ AyvP5lȟ![!Ȑ,X*׌CCUq>62[_o5X}Lasj΄-\:Y1u M&qKx2:GL,O C5o&ygBY:R\ѯ>FǓد$oQpդ6D'pgN}6_edD+e<-P3rZzmMy/0+@̇a橨cMaU'eoA-[T,xn'Tdx'r p} Dw iۤ{\=Id#$By[{6oW[@6tTI] ; l/=v^7Ɵ):e fǪDbІ۪dEӮE_KddCY)+-5+Gdt~cd; ␙Z l z3&@ȼ:p$9$ ζ7S$6C7>5"*Z(4om!tmŁN.!5|l۽/mׂ(avj(wh2V*S!jTy"#h*Ewzgc'LJntSZӭPɤaܕlO ]R_eшZlIpV36BOgO< egWH$< 30P/z#%\7@8"ɪ08mSHdp|T&QwpPsn$;oWЋ32y]٦eKNK~6h* (+ }0( ١B8c۫ DUEPJ Mt˜hl{=t)  ǢaySx|n d.h2vꇷ\@9Ce]An~QwˈZ(HJ.h:?-?ۅ#ʢ_P$Ȱ[])uh=p$0 +6\6A=uYs-SN(w)'FC[DdcL@>!m)lؙ^IMb%sUp6]") ? i$ #I(É;v:;h3瞽μa( яLلcI eQ:":&G!f~#4fl0َL2N &OyU$`/2mε uZLپ0r4o ;3St v~Mwo)eHo-:i̭;ׂ":?~ Y .="3iS4`;tm@.];O%2$Dʦ2ҿ-mػqsP+W,tgYtr-(}xJ_^;-J3' ɜ`j/s|{F5qWӑ;5zD{+U*[f w97{Ib9:1'a!T8ݩPьָ4[;9u{JpF=qY;|!yLԶo"81jr.c֍Q]6jI&-F;'tevMiJ–/+D'S8\j$T`wUCaGeCn'-ltTak+"}Or*Ni*պ. EvvB3h^6'[vHN2JM D*Zƚo1qds `.a)҂nzYZ'DJPKyVSIDinpip/_internal/cli/cmdoptions.py=sr미! qt8Qg{Iɛ#P2u_ (t2T3HnnwonoEINjPefeѨXU]nzUUgEUVk]k5-B/B>e걚o[li͒Vv7''y[V) 5YIkgZ%)~9$U@IzjO溽׺PUR7UUYt$,9 Lk6_J=u R)̚V-4soreYyyD,t$,r&^&igm]_9?|wV,{bw恶mR㬪]=$}=KIvWחo~5{}vi5-''' Tdvi=,7Z4+m#5N\zz4D 5 deBm3:իA)ef`ɈLas;37n{. ԩZFGn8UGٮ0RQȣ!*h4:q`|'1S71n۱]#{⥃z "pȠ&ӖxJM& v% *<wzx+" y&\n"/eF4\B^,[!8Y,C#d~c!DqJ,_X7Sc`{oaNwY"[ݯFT' ̬HbO[Q))ٸ إV ~Όׁގq:`}f[3nmbpdp4rܺ'|}jڐ V:$m[c G1dDLUdB0޶iQIW/>sl3Ϳ5ZִK̶yL&r "#]`oê,o\)yF(NPmMdx@&ɵamDLECB1Շh FZBL{Ҥcri0$jD(k@gDa{/2Y4bpGFmhp;`l-r[g'iRف4vĀLN`Y*8S8VY&sĨ'-.ܺ2[ kcM>\ #=&ˉK~#A,3VIQN`yB9)?\0a8%6m: wpej !$((|' d}|`OuFے=^r":%Bz%j` lk tmLUwcuBiq; /V < k TD1<\7dc"j'ګ1F4F#gN qZV~&%:䂅>q?'O,< u^ͦ6pDZ , ` ~0o XM֑4A m4HhOuyO!+̷GMgBY8 ڤڶ'CF$ݞ~ͽ-9Hu/ZQe0nWn&[P\=p`ڗu@{-/W+ ʥ](rhDxɚ^|j ;AۂBh @qeAq;q)_Q0XJ [FjmH6hypdm, `hy* 8\Yj_ΡF!#,]\_ŸPmsq[B{nP&0U,F>ՏD,o,\O1fnP7byHDT| `֣4e1WyG!.]]& Tځe=h~r}X3(Z— xx[zÓ;|Xf`cT%H2=fQv3ǸR tv9%GIj =?vHKy22D(Nt˼Ly` m dS4/p R1մ͌}EWO8+qo<kw ɄG^ξ8%ѭu=3גdo",>kX (|^(]\"Y׏e}1>UhFZ_i g bdݲ}_|w`/ƪIRGͯVչW]GX:?2-YYϮS}v FC6O`1 }|}0|u4$GNs_D|IJ϶u~̜#@U*󞈋l2/?vqCOl= IđgDap Y'&yr{B}*$ij~7# $rplɓ@qltHm,-IB=j8w@7}BёA!'q7ꊩd3i͡~t%SR}=| 5jH,d&`IFĹmf3e2X2`b5+X܉Zi.ivdb36BIt .--tУxVA:䦫̶i@l4}f `8#)GSӑm}4 u(f0 d j %x!%([~{uO/%$~Jw!Ty*c4ܮ*k}-J3"ii<@A[닫.ʼ3Xnd{//Cvn0!)sQ#؍t*џ L2lmG[!<({Gc0''ڊA-/tiPli>U:Dŗ# 96hKp3wb`qox:++jV T#v(D0@sgCMre9f k2o9&LIF❤HHFv=39}kd]c s~NT73TG0lfTAز0ζ4qpH3% iG11wOmwD̩ZJn=.Xt8jSkTΊ;zI l$pU ?mM*^>o )nH~[X03llm3^[C3qܲ:y 7I=vM"AzTWGʒ}+m"$97~9+z*{>r*n@h|mcG-!cubC$WKrs+Lklwc>,/9HLexUJ"f^pM3/ZSlp-@W" +{0ZiMSKL&"9zVְUW#˙c8w/.<-Ix3x2ӏ_ DM]aV*߮;ȝO3M'> Pǀ^5tB>[I&!=CTlGz|5Gzƾ L]G;GCt82>2tmyPI9j8ҡ$X&#o> E ԉ* l\/)$2U۱pt] `{Bbs)]bȿ(,o.)7GM;8;<`Yk<&<`9̘+27C9ƞa ^gvƢ9ޖ1Z- GOzA$p̔1VĊLZQ,rY]xY]a*;qtnZtgӻ HD e!;Ew0vWaҤ'zcZ0czgßcc`!x)̬0t'}-Ijӧۦ~JIO%sG1.$#R-HΗeēYv*ׁ6 F61lmjP/8J{LrKRy`Q6\ǭ0,[]8Y2=^xTKt6\"aÈnJ-`9[¼#]Dx\1 ~ Lt>t=j;ɜ1Ex!OVZ4Q.4 ̠+S*X n ~f=8uV.2uH x1s!O:(" -8= L,"6]s]@]y@6^2/^PrV/I[8Zt5]Zrℐ(q0'wt[`Efb4)'hjAH0:eλT8'Z,ʿ'aWCΩ xl3v>mW_S1 Ŧ]40nKJ ʩ?ni2=Z7 1+UnaW &3 ztG!Sv3иQq2{F&L/F`#oȺt Crb3!f k%R~]:xW%A ϴ8$`'_QiNqJ eY$M^'4']Xdxa\%* .S$Nul]]//煌f{Q>]NT*A$p|@0TJ'ͥX]Ѥ̈w*cRأAP$^3.EAK2>, dСU0`/Xl.B=Iƞ0w;\֮?;iS *Xxe{b[2Eѯ#u%\4:r? (]p!W54btt5k3Ըĝ0k`&ܑ pУel@{.4ʩ=gW@|ʱ+\z L!gKc}`1^a3dWg_= 5 2iZ9dI[k;%B!+$'x7f̓w`?^p"`]/wV. `h.aL!  {Qi \%K>9,k߶$0U Q^'И^_=ޯIAD;׼RM ~M%Լv^?y\*ax<+ya'<IGD/vhJW=mP`[!G|P:CTxꞸ9ތPKyVS f)$pip/_internal/cli/command_context.pyAn0E>H)RJYnraH C j(nϓ+5/!I"^skiU~Fc?Q;d[>4.1mJV"_y߳D% ms eZsݿ`0 "!t{j ڮLlҥ\[=~rn!RE("(!9bYsX-8|pfvOn/E+ -E2Rzp}\.hОU .^ 'as uh-R PKyVSjYd| pip/_internal/cli/main.py}Vmo6_)f~5EaOA IbCIц=GJvҦ3`/w=Byũ^IaЪAY#7 VPdyg BJjVvZ^ioy` RgIdPCQ9Vl?h/NgT+KSP95蹢Y>9g[cP,X(QeL^x,Z lUFT,ˮG+ g"L-FO]EGH(/dԑZʎNTn#J/׎DGځ:yT &tmFSx7Rj?eMя<$h>#i9تOxMBnk6'PKyVSñ $*pip/_internal/cli/parser.pyZmoܸ_A(,d%_i ( LY,=U0m RokluD&ހ5/dnqlCJX'WeJlr[2zf|+QLױk%#ۖ䆫ȿs +V=923=-k&_, 0z;Mw oƥ%hdai~qANɟDZ`U2#g4\`+ҪҼ\Mz?/"r#p[2@%쮽YZ=m2 z̒m-5=2$G8Hg՘_wNܺ8me020v  dOs82&-qGX~yׯۿvxSQS W lNg  s* vwlZf; j1xҬW^ˑRYB&QX#qR)@9y"K\afǫݸa*vfv6 ׼-vu J!qz m''A^q)lU?S:?f)h}K[~0˄OA1uS{f{:RZOsrAš|wZQg3i ;i Vv[N''Rv+,i :()TI^D@=56Bǁz8P<`PoaO5Զu0)b{9OuczUZv`O %jTbBZ;rP@Y#FpJMOu bx;1.M9\ $i+݈7 YnPl*5᳾ЙH{;f@>H[nfc36(g[1V}1,u0M_J#;ɟ &W'̌0P R$yAxo"DFY6W%\d 5զ|O7gnuS{~54t˅Ln1ԟ;:W+ 4}aQ9lʣly++@W=uRV@QPISưў.85$[ !oX%%9Zgg[,-$d\21 d,E89==RXPu~>;&1[T:&55bcO̺:4 [J+&ÇA]K%XGjl@*`&> ьYeۋo;oA >׊ã/Tl-kA%V%K0v z'b(e@3b+ȋ'=q ߀O}̀ ,0kRGL5 (`!O|H&AG`kqG.XwKf {8ƎYc*f˵B=1k-kPiDSF:֡=&M#:9+HpGM]Pf?Z{~v% *k VÍm{,$}at?jxMi!~(2WT;Y XHCZ@~vpbey.1Ʒׇr@5f+;f|Mia2^z绑.d!UbI>/PKyVSsO l "pip/_internal/cli/progress_bars.pyYmo_`D) u|hg"56Ҳp IPIU-ܙy})WҖI+U4#o͵Z1#/Y}} +Ĝ7N*J'a'ZVHvQmF~-lz/Bj1ٌ w,A})u^ n]U+QY^/}+!4k,MUm\x=TEO  JU ӭ#\S#՛gW:W|G_5LY!s)Z+mX+[ .0YeSc--e0]9N6-{뾠'n{UAΦF"ӈ4/1Z Eq-8e9Lٳ?з+\9 X+c Ky_1ۚèV.ڈO`ѣ=gZ)p%HWLN P$]vwoY)"Oik! %t&#"8cJ ]EB"Tw9_+}gF*ٜWDʠLȖ$3F)lg6$g%q^98*@awnawrd{+:o?]@ngԉ TPH\7HpѿB$O it@•4qJ~E _0U;)oy߸!aZ*/ n-xP8ƲR{_7渔w-eF$aJ\ g.`q`8FKJ,06'{xDp$fȮ̙yƮPͰ޻^g|:\=fS KaE/)&-Վu.rnD|F[M̗bb X,j8V| [I5bAΫȩ4p1Rf%Z<zS>gʼԂ #J(u:  阜wžr0.rݺtEQ?ғ5A**c1 IAK-n̨-97d7'YLW_b 7iupwmjqК)-F)j5pH>-OѮ`MG=QA2qMbٺ1-k2./P)[}p->IGp_oPKyVS%߇B pip/_internal/cli/req_command.py[m8_S;ճ9.΀v&4.%, KmYҊRw|WU$ER%3 Mb^*RQ.d݁Uf̤ X]RUXl?QAW!ٱ.ؔ1ɛ:nȚ\Uu8/DgE}_uV vٞYToY5V;AE9𑵢<1qlC 1ẲdRU.\1"B+2?kiɓ\v}wu]JYMA߲zdwjpi ٭؇fM{#8U[x簬kl v-˙1n|wƌJ17DQzZߋWUshe]NԽeF")KjDu4ΏkҤ;5zJѼH6- +da;ނ5.kKSw34nBc+-@&9p>ihPp0Hz 7djG^ȫZ= QWk{IB`]-*yDSoLGEs,SoQrVA1#fH] d5u˺q0G|tK+]x~;X4)=iL : 9!ax#a?6i!Z_/nLL6ݜXytLz (g6Vx'ڮJ^ݙ9[iƛzu؊ĸ,-Ӵʎ8闘nHd:Chj'=&s^G0Cz0D~o|2p7p_\pGH{X2l[eUBx+V% и$^FCCW!8[A]\(={Z2=N40h`M ZvR r i3F@|HWRၰGh7VC.y[j&Te* \;\qiv1cfwFT,GX":290'؏^T\U)us{! hQKphA8(.%2 a&|2'c\~576orNL`,Q]7\}Lo~} i׫woH(뷗o//ߥ2DeIgm] )fɴn9R|и}.vǽA}Q%ZZկ0-Y*4UuE5wPB[VtLjK?T3\6) `+ *L3f1n[W\!)eXg``cV!$×N"\@S E/(<[7aw_@<92aDLјqp|)݉'v-V+@F'\w=qZggtJu  (ÑS% W?yFgfI6פ നsWovciq$,տ?M=<&[ahU/ .Q`biDE@{;{Km} Y4r*#D8it[Oi}z.+BV-&fG<*(pORa6`0ۃud-zFƻwi^%/l0$XR*jh6U-┖DcY,kO*뚛0,4_6@i¾{XRL2ybύ[Y08_TL^9I')JE̟6_"@ ~%QBmU-3䙊3"mnTMJ_!^#h1I` kͿ U|, J0 Cqg`mYC :CeAm&lO-%~-izr5)Kt5G4PqԃV@YA_ӿA'z$Qݷ0V.kH~ :@PaÖ :\F E%gimHEU=M;VqY-F&]W9(!pOR}%[œA634GabZ '[: 괯ʹ#ൂ# gzSakχ^ ,|NfٍW؟RIo)S0e[Kv):s?*jx 36\;ȣf@/SUG^oF!BIx,>GLB?XAz\2PJlO3d.z:=p 恍%3޶'7]1_Aݎ -tVsBW8W6: ޭqMIi܋E0TpǃOݧ.ۜ:i~'7%U`ZvAk9j q=qTq8d0dOU~ɽtm~lq쐑/L1d&qt%߳=U=hn>d=U:L,{=Y{EK_Ne+BOJ@:~hzK\ӻ6#=-HŦሏ.Pbn,GmUDxKUO`/i0SL}(񱣿-1E3lLg\|ZNOx \9p|#HFxXQhAVqe`H'ŭ{J3 gł*gϕA͈ nh>[b ɳg=QY(3㇛K{OȖeZ?^q:. k&gh_ؖF tƮvTF將 5霛 vghf4WU=+wXC^u m|x:Eu=GD_|+K3=;J:V _'~)?33/``'$1ߥ=g?_H% ~^,DNcD,ەO ?Vez#4ɹԁFZ˗(bY6Qֱm&EN{\S_Έ{FƲ-^죛ûT|ž7B6%{vf^[sw\syTubԨmN$鲩퀗R8zǡ;GeL7tq]_}*{/UKis@@T#NsM%nZ)ѡ[mDtV\P1t'u@uQ)҉\+Dd~Gި4:%VTڗ8On x齱_>=em0*%ںB߼ PD\}S rnE'mMS靑o$)'d{jk_ePKyVS$%pip/_internal/cli/spinners.pyXYo7~ׯ diZ"48@va.%V㱇֖EQ?xpf8ˋRiKR%-d_xXi0PlZnK>9P 0LLfJ'V+͌_9[|\/ݭjjIERY.Lp5?r?{4_1 IK-Wr0ptimk7~mXHZb< RA%LL9Mɀ/c91 L_s%#i Ú`fٙJ sڋ*:VTj+JSoYT}7aڰg],F^ 9X_|GiR_`arDΊkR!TeF-33.F"yZM 4pu8& qJ$O!3>1)YDS5t{7=׶bpF{(*x x}Fߑe =z6Z4hNʨqS8h ɐ|ӇhL<|Ia_ ڹEbpT#j5LS+ȒJ%KޘVvD [baB&./3U25ȈˏBX;rوqt}tb&⸣8{Jbu*BJG?3ݙ`r )Ovl7J%M3[inâw(ͶPk3',ҕ~#,Nx+yo*m]^SV %~v3jВ%\j4|dYDEW^,ɇMm8r}8={rL?ul?P~Lpړ]md4 mfvUEI'q:1a)hczG7_>l\W3=|ĞOtğ@8d> VpIcP;X(JA5K-zMưJČ.Ic9#u}W=:0! \ĚJ W(',IBR-bٰcvAY2p et4i ݚZ [p9_UWpu㪄ǧBgyμVo ؎ΚNpOL{ _ 厤0=mӛ&EZi5?Վg->W]*3J{G$aJt_+?bqc}9 zdgr{w+7jcd.&}E\ff ?_r35H=rU4-hB.ch&,וpWQmC 難3Z fOӦAd2?y &'BKh9Vqfk^2omiNfs1+%qc*ff}yX}@@܋=k%󝫍 }C} D\6 PKyVSzKXt!pip/_internal/cli/status_codes.py= 0 \2@D$(iʘ6˖RB0Pv3ZjԜF \/V@\ӆqPKyVSACv"pip/_internal/commands/__init__.pyWߏ4~_1 6\ip+qpUMƪci/ wfu,f~~O4MY~`{V %ЈEuTaQ78)vIit6R lj^8c$\Y\6WcLP*)M_xO.Vˇ LhplجW.#%Rb._}/1^Ѫ1J rֽ4齢L{A_Xe0 kRX70H<i.'cӓd]c+ NQ{L ;XMլbGsj0&**~6HlSt!J Kÿ7ņWBzTLJpwVPՙ͕w9"tgľ<-u C~mH]#X/5926 Iw1q|[q!5<~؉W$/!7w~zpbfo74/atO{O|h*mhDjB3e I)Q6 os8xYϑ+>o4({1qC|ݍ` @! ζF =wkE 5"xև z-uN |f#wt _G.?pb/>2O,zvxXŏ*xg>(ltd~6"1Y+ PKyVS8~dpip/_internal/commands/cache.pyY[o6~ 4DjmeXENw i0>"KV$79D]d_>r}-fB Al#ŞZשTeÕǺn8g?JϜ㤨4UZƍ.Jo|RźoFb#+x*dbOi0_O(FSΕc_8׺ svUqۭ-? YIlխIR{$l6T)vf;nu3ZA@Wf N 9SdJgDzݬmҬ,X^%މ;nq $ y-QQm?>Št-9򽤬S.'Ći 2Wy%ߋd铉 IzL Fn;,YFD~:՘S5m)֌%W i݈Ŷ@Ē^=@>odiQȷj3?ok) uadwv0_`{ww7`5Qd-`bw%y"L-޳KH-8; `[O3]T=hL d,LHfE*t, >厗*% p Ultt/풑ZU׉a!LqQomlhݼl*/-BzVFUZQ>_rɥ"{'*,X/ `Xvc:\b^!Rb HM9br*FGM_ qGŹذJhukٓi5ahhq́u<4U$SE9+J% \72Gg-6V‡b"=c}ou:~oUĴ`5X1!YPgpak%N 7; &T" &^|'|lMe%c p NS10}̳e1DVk#gU|2«1s/V+@^PDV;C):O0CBiDE${IԷ7 o.p;,~ณo;輛[hsk&#!GEH\؁X˟Sӛ}(ΛK?*0/eWL/*9'x(rN5WJ{J@Idm^m.j>ev"`XG NGkyY W=˞3(}trZڨiں0(?uSp憇q _C*+{= @˵P< aު`,^n%_,v^CyuUOtE EzEV5kz.($s9T %KyR9kq{C>96k` FhC3-p.@-n 9ێ07ǧ !Y_«71/p&]7t'YBm[)8' 0I 3GsZE-+vccO\ /lp,9K(r}%p`j6'cyd}Ǻh&n`2?Od \h?*?^ II~GUcMu@YZfطcy1xy" V(*6 F, va^ oS8؛^8=tv,x;S C%!-V}mNuT]hL̹sƸGIe2 ƳpQaՓ\u2Q0*cǩ kѠߦ4aе jq,+fVSy~؁ ¡2G yI]srWILglQ$F7$ ٣pa gd~Sm^ 1p:\ڞ޴ۉy l!UlXu!{z/Gݓ{ER`/X-P?RﹽԆ`o&B?9ku e.am;/3}ΧPy5L!;R6}ph.^56 )@>Aywt[ Qo~ñDkЪD; ̾fƬ($w4]4T ;裞$$Sqr:^Ȑczsn~T78&&㶇͉,yvm%@N%&m/7f_P}Je֖MR*i>_F'-Y§L+m/,S$9f k3xTodBރNKzTP.p'g𶒹"&:st)+M.r Ʌo0ST]m.2*wT;J.BH̐ SMk(4_+sxm|P֮-txb|AqAUq ғ pQCv"[a)2~H(Yb$eGf(r#M+UrmF? bW;\f5d`>Qp@CMq8bqq68D0ocJC9 1:bRP ZJ?Ɨbǫ5( =ckhL_c72.Ѓ`.`%VsFb:zN ygWxH?>HP8R0aܐ,*L*A0[5vOMeۡ ȼȚR^]/]]'EA.}G7d|A(~qyם-hZ4>^,Vsiqgڳ9 [N[GZِ ,GGrY&t]BVz) ق"a%UGO Bm$#4 ybvF镋I{4\!Z msDd>'f(ɜ[gdv:]HACU"&AطfJ )FqjA 'oJj9UV㌋3POn);/UsSfa%/ց(\5*C恒/,^ja%dQͩ@ETtI(rRDKI f&#Q^aV o9@碒P&8"{oOYc=М%glK\-`Ahܡs*4L;N^gJh~՜[v)bnlґ ϭ@։1}sBzAM!6}I }<_h!cݡ̏`=fZ GfI~+bD"'A\e3 |+3U|`jnoM͞n,åa;Ni~~jCҽ||ֽߟ 6kU%7>.nպęT :t ҲrȾRSgcjϋjR|bT: ='7 Nـl΅ ]ݛ0Y :heZOu"{!A{mбm =c?36L(|P qHycҼql̪ +9mI30%{נC7|Ot6\7j\fٮ`&%:0yK6z5œkbZ wp+LsWmj/DPKyVSZ]pip/_internal/commands/debug.py]o6}"lu j^|(-/~3(nb6E m/!,XW"|յ)uX^UJDgJsÚ3srh-l@9~kr) &B?{#dǚU}NyWJDyԞ\c_ʓ?r|w0ԟF J4R *zB6Dg)RZq|B-ʀu>ϡLDB˾rjk̠rIUub/ x!ڨ5ь@>ˎoV~\tL7zCdM#BP(U[=z7IfV>$>Rd'9Ax H835 mY轷_gRHRtD-85I[cH&8%=ŝk532@\9BTtFt\F5Q"^J|#R=T@JEW4CT0A:(Y q'B8*Ju&|ݿ{ G0rP}iEE6i>8mLn4b$IZ~4T97rq7 +3>,^}CDIH=0Mc5M+4SUxݎ "R8i{d˖&}AfyM_>^ X{#ePV5́~_k*WglI2QH&?s(%JEiZ%qz_u#w]}v+ț, ҈:${ClӦ ~ oO #|dؘ}x;ijͷ@+n#eplxĤ:E#"vTeryC6R(M# @O<&^poرMyŒ:Nao͋$ Cb{v#B7!ah` P!܅Ny`+!+zB\긢Y^i˵fd<uy+afHdNkn% ԊMһ1ym^ACIINPR`1בtـ8#g`I,/n73!hSޣ_R KX9fr vFۖns;El~s`F6FoS ^>2֩>6~[ E Vy% pas? Υnlq$ Ff`\JJ>2 wyxo,"K2pn| H 8ّ?tz}`iIh~%mn&[n|vfuU͈rICT>AW?rO9?o#9lDwq~O (Ћݾ5y627E )mUvVg47h1jOO6@>`?6v;*j+? EE:!,S2o雓䳌*wdK1)NJAQNPtO{-7#RKt6D;N(Nb$3C,kvɨ1xd7Grּ\]ڿo66ooȂֶQCG.) bM~9C8ps,Br=G'/ռEg*tyn)<-0>Vrwk LWh jkdPf\|eh#c}b'vc rֹ>CyH.u3 Ұ F:CZl 6( ,0Z(Ƣ)+wU`Z*Afr( O@ң$|4ί'4;/XRu`y$`< ]!1=Xh ailI <'&HAh/*Y4[@$$ y>-W'i:@uﻫzv v$rw] u hՏQ/5trHQbqљ/-qPKyVSm("pip/_internal/commands/download.pyX[o6~ <[A H7`@1Kח `ibC*I9U)J"}Ik@b[ssQ[.3UfjGTc -(o&\ZВ|)w*J31P,m:[+yNVgZݎj>\)KmMKL9U2VhwwߟV L֓ra7 ҴhHwL@fktAs Tiymv4oЖ*f.hk1>Gsgl6+3QR(Vc`73|Ҡl0:;of"wEJ!&+fAZr*BJM?ݓFO iU%# r8`PbTK L5;6χX&"mpTg{qfayYItuĪJG*=Jz7$N5"kWxЖ&烜7o{#fQ=Qif,`ge$r(\dO:uߠCXV}dnu %p5O%qa f.i[~cg_~#%Lіn ֯D>eY-M8z37R~NxxhV?,n,i+`6i1D- ziO@,Be0tt3Xt6#c6@@s +df &@KXT܂h7d#MI؏ $׳ 4wxjg7Ci%D긒Q\)ڟQR5̺eprǞ)il ts;5w)0?$K܏\X Ǒ[C.HrKn18<{RmskIIf혞K딁ʙas&,|] ~nca̒Nxl nITn~Q_vAb[)Sг6"D;l,PoBYUq?7Gչ_QFבk_Xj9 @ -b}o,T\9{CS-vpg(3*UW#z%BHx1^SciS͸ѺtvAf;vD'-ts/Cxp2=H>%vl(8#|/nA7K0nۯ5g~f@GԣxfAs]LՒ#W [ȤZ,RNrZʹUh&Q8,n+r{] I0c*/qA 6< =$=h4PKyVSpip/_internal/commands/hash.py}UQk0~ s d렴t}P쳭UeKY.ӫ SFFg؅5?Xg8@"Joq>Z,3_1ˢ ިae&1sAx ]P ~fQf+!91s8%Mk,kc;JQcCiŠ;Pc)v(39'"䝚bo{7eA|GpR{MfC\W@B^v  7V [E݊͟6orWjPz u,܏Mpy6 km+C[pEPȂf8U:%HS5c|Ti\ThK+ :jmŌco>_98jj} cgܴ?#r8X /Ibx8JX3]ǛY4xS:>'exHs4B&PKyVS- gpip/_internal/commands/index.pyXmk8BR_s(iH±,Fؒ+Iѫ]I3g^\(j'xxz,$A@=Svѹ"ߴ$G_T9+nsei_Tw5\=o#Bq}a7s~L%Pn窻; ?wfg:g Cf A{Cgtu*G>]_/rZ{U8)Hn[Ŏ/!jbM"GA7&3XKڛ%T -yn|se`s -_ &]K%,zIQX쉪:l^%^F= umwV*RAZt OTbҪb#UVRڀsKgїW>Is&{0mu!Y~c u/'}ِMSJi y.8# lLBSGNx 2[hoK'RqA*H݁ "4^$,W7aq|)#gqx #8dB[T{ i`9AJ04yDc2Z@sU||]%3nϑS~G D`SH`zL|&k{Jjq&kqTN -JTj3D~DָE($HHw t[ YK0{ =W$K|>MeaS2 !ƕy5{Gv] !WĀ.؛PZfHqxT$%GI?P-I{,&΢ ]u4][z[0f=v{ƫQJc,<ƲlS6l7QgەCEX" pn׏!M74ԛf400mu Ѝ`Zۘd~44M֙ XCscx[pH}m4j|x)jsY tQ^ݒ[ip m1uT4Ose*-0&_XV\F.ya^Y+Sc(Th5SXgt STݖTfˀb:|@LWV= T7\utI&x#|@qc+/.]|͈,sXFO'9IypW49qyڧuxnwp7` JxB?wuu8qfnz+te&43TXbB`Oy69MܺrRfEM/M ~ L^(0ʩNJf61)l֔8Y 1C*=k2F΍MaJHk$YYlAM<1ׇS`8u&Blj&PKyVS`+`l!pip/_internal/commands/install.py=oƒ2LIt|Upi%F|P%~3].eP4=bBں1&][WUSoGzsE٫){Vv\Ȧn[T׺Z|^7۶<6Ioxdk BgU]yV*pkQ0\g@ۚ9> 6- 3fY@baҷ}6Ystku5ַӼlj;G >oǮhWs~JoJUtUV(uƪ(؉ot6me޲ <ٷM!rx%D[lA4hB>6usNU!:n SB$Ӻk];’Xg/`rn mR@6TIVwZuY/r[+eOMr*kvʲ/ \@Wr)H/-QBmrtTfyqqJba#x8tpX`р&1 E ˺aQ41]d\j\(vkx5B"7GGVЪy d|Q؅`ށ 2ܱ9!vUP{.\5, )Ykw-$/^ڧP1+@)jk9fnN6cpB%b?V țbtˢܚ* TǑ*‰rd?]+ ϟ< |g ,> @JsŪ+(y`WtC~Xg%u&HY/ RO4(,Mm]Zq+@m}$(i׌o|ҙ7;S }Ra糏bfbXSs ӋTAp ~`#JD?˫JR:Pw|hn;O<&SS{s餠Oc"32"~ALN lk1, Ia*jn}Z ^P0讅fwfQa@ 'zGQMJ-@Q+AiTtQu<:CIx_T^rWSm$&B b"n=DԌN+E}].vb8=PjcX׷[9s94R>I+ˆbj(uT J8-7|`LB2w~y*=^lcY"KVa)zE[Uaa׮`r1z* 3K9ɂި%E$pIRX2ܰqpg¦='ōƓTQTߘHހ)@`x^Kv~v~e yFrp c,Ƞ=RmƲ:[N-^ajuWf ' *fq Ɋ*D>b>1c"aS9VK)%@ l:fxS FId >/)%P aýpN5WT6xZ̯` %1q7_ d VZy1R -k?10dH|P#rEbVu,7u3q[!D}ґdhb2`{))|okjCK8m$klT˺Qs Ƌ![4$6#Ir' %N}-U{,ݍ/oB5uVhmQڪ= v9$Fi0*lP$:U7jܥS̊ƁЃ0U )#j5Mv{۠Rm<5C%&[ `~+%.K:`;&YzlhƒHSA=Px"#sJ#%#( W.6WNCtvSuI[oFWq,J*_3|: _`]U}KG'8*L y}2 ^Qܺ5")!" Լ;yH?} -`հ~DܿˏamXmaTZ7zRp= aPBgsQK B`Q6qC-e$V:4CMV Җ1CUG1+| 5WNP:ϊք^ D@w٥Qc9>pwɻT\˫h3~CaScX~#iSjAޫb*->Yz ýCP(>t:{pW:AyAd~Sr-$M&&\ ,/~jKҐBΘ7`X [t¦oѬcU}}vIT"RN>c ēH-*$_\Zt2)e^?HoȪvNo"41Ӫm@B@:::b^tyO^c# bhE7dwȇq0n)` iE/SEcO쉋#0{Ձ?i-R8Uo ׁ3hHJtfxܝofFU|Ky+w}w2X)fW~#R,2Nq ~~L.bj*zȾnrdt OevԼW}w2b>菇z!ʢp 1J;13,ɺހ#Oӽw) Nps<`aFZj}G%ͦm8 v '2[pqISkGb(GIJB. 2!r:~́:DN`qӧd6pzGz k,ϵmPv.+1W3RBPUlݶ[qzrr [`df'zkBkiLkEa|H2rǬV]l%x;e KެeAPُ*C$qdMߴ~^_ G46ZvR巬ÔcYsC*@}ʋ`{x\b OՙupUc wH9vA܏Bs>pyEu/?r#YK h۟L˩y)mF8e-U"RSY%rDN=*s'o]RqWhfKø~d[|BFB;YGtNVt,iH  s&3; y'P;C&oD}KlcE]}aceYCCZ3E%o+exB#=H/gnvdޱ`c@<Ѩs5 {=2 'wаr< >C^"8O aStQq c{NhlM6/$;OPsQ ?V \_ĖG>K75WWj0K兹|2 raϻPM&\h[S$P܊GɡG*-Ij\>V_ٛTlf|SC9E=FQ3WcԲʹJ;RjG_\t zK:MgXw`'@zJR%QȾw56*!V/-m6*+*Qk4p Y{A\*:Ũ u'b[Oم.f3hbʵjXL(6SWqwbm Vk יF)LgbJ /#4s Dh--hJ8 oӦ!غDxtHٖA®m<XAxZu}fE[ޠd`֤ i3㶄\U*|S4odB?{o~CL^MJBoT%Jzd |yȐ4r+'th6@Dq4ZK(lJ 9·O)fG!P.*5ݩwϯRZ%73< lYert(HC?{ Letcž'yA0"pXPC{!S;kFXQxyИЗ/)e,GבeK^yKS^h P6@Ex^7ʎxw #q u{3-_F/RGHT#RE=ʐנ*;ϚCxNc(U:3j矢xA0BǃSxf` ^xwi=Y8 ކ3'/kUse?F#H=bblT;Y݆^ 8ZhUwyk>ZG$_WCad(`^Q]-t}0zZN*uJ&QϖZJ, Vjɪ0]'6ʲNN'^l?xH0Q^OSIHcVzNٱ:;|k 7R80xN: ׁ%c>)DƀCRRլOSu>/14)e-}Y*y؍ zn% /pip/_internal/commands/list.py]o8ݿТtp溽mq*m6YҒR>v~3)QNڴr~rf8Vl׿v~푴]Q!1z`Rٝyˋ՚}+ z6^쏁5[&-Jx7Zwt{M|y-9[ڴ Қʆ8Nɷ5wǪU\?m{<ҦHݽ]%\~^1..^Caw[K!ZImmA o/2"+wC~WQ1#iE{j~(f@d57\͑5}Ȥd$ `QHv2UPV\)]wJ IY bȾs4ʑ˭Efiz+xv!zS BA!?Ү<u`Z. :/ߙPWW5o8 ,ͮMjIa?0Ry |J-'iwgu%#[^dHvJZqGZic( @‰'p#v@ȍTnEda"/0R;CPb9j50T-XdKRXujX nUؠÕߨT,3@h=~#, D7kXCe UqjI`| * hFu.`&7`%Dā710=H8,(xe % DRwd_0gO>[߆)þN0IsJo|x:N;!d~5StvCk8ΚT-i^.I4t}6 y=EVgdbvoId~DgVw/3\ m`ri !;B P/ Evxv&6̏inXvXddN~gciQXIpʙ srZq) EI$k@;9ZS>Iv?V&b+NUWzl91HjD]CT-R4IwY  ^t~" W*Ac v.'4Fa}ї,/SKrZiITXm4y&X=)0Q5 =tCcHjyOW15%cE*ڏkKЂfc Zn^We51787 &u#ᵙJXsQAEG=vPtf߂B/?Qj58(?ߪRóMa=C#^ޖfNQȌ.^s//-|E֑}4Ms HhB=X)C!'g|mI =+e7ޏw)w{h KA9([3O;"Eh0pd `qV'Nυ.]d#ܒ5THOJ /2 K"l6f`7qa9I&{]e}Dx(,ţU9r0A6Y%(]-)x((s3H,xC@\ù@lT4ztg*O5 3(8V!Ajǎ Cy) hAƘ0R̶<[(-z l=qtI kް-1 \SjBJ/ "ߕh\Sg|0u7PvU{(FBdZAJec 4ٟbz;Rw7$ P=0Æ۪a)ܑCȑnl4pI^՛D*SXQr {bUyOiD,9~4կ?A1C0loZ@ WT)Xf+:DF$-$k'޷ܩi_PaK' Xʝ`TU PV~>/h$J&'LbjxWpaH|O1D\7c+  /+"/m>xitVq8f;ǯ2*8;@{PmkҖQQ08E\fE@^MT886靛) H#i_@Alck_1+Ղ*t Pmpjo8s s+?\[_SOw£1)?L$ ]T-{oH;:!Yj,-XA+mnMI+.jO"f~V!ٱUWx̣WTu/rml֓%dcH{iG_&Vd2q.EhѺ88n)B+GXƶ!kE37b7\V>|oH1-m7c'M_PKyVSW/bA pip/_internal/commands/search.pyX[o6~ TV[ś ti uup iL$ w/()sw. ߗ$/[.#n]y^:(ك,W*LF{*4/"Jd7|hRT* yŔӇ,;7_f'qvۘ19 >JTB+yfwL tC 8*ݶ.4 }J?Q Z=k/|E/]9S1^.VJ5wy]̳Wod~{rr: a+VwjO,Ǟiz-ٚmh댉;.  f*b01C;hG> 9]:xP+P oϼ!Wbm߭./48I\N$yS3p)z8,6?:c 8| J8VOQ B5.$M6.oڊ3 #.7OLsEbs] .,}x}CW9냛7#-倦A |7)w n"Y`AW6OL&1k~X< <Cy͈k5^ƙ ڪȫCܴ_-iq)Nj<,6-} |OX F5g"npKaEԸ)Ec&N,P c=!G -rH v_)>nIPu&:!?ukZ e?WO;<ցdu?6Z 's%s5L#8M胈0#}^$"TzaOh=‘ďv혴X4}=!NCg)Y VpOQ;(?9w19'ScenX<6 I2PKyVSBI pip/_internal/commands/show.pyYm۸_A8DJe"G pmK7niՕE;3$EJ})Ptq%yyf8*<j˝{D/dՁ%E+k9U@澼o MAn>U!jrU"a01U^(ˍҼgZɋ1]RZ2+cH ueV(7Ƽ^"Ѵ5md'C}|{uͯ+=|$"Np|-! >eI%ͻ>1(GLBl:1,! Sbx_L%h P ({{Zr0@kjS pQ2(ʱ q N {ك0bf| #Z)(xǥ5sY?> a`hxnCrcsjgijm%WHsgX*/^r^E^>C7{GFhK|Q\n`YU%~I Z _~6xs[~!-pcir9K+ &-CAK4rg:U]B$0QUЛag+@nY8&Hs=-<(g< F>)Sq[BIٙvs緒N!-md`^#qlQr)G/B~7gAa~O/ЋIΨqB]Q1۹7;sjF"r.l[(2LR0ZMtd]ѳ]`<= E05:i 6X~]˪awͽjZ+=[╨[?TVg\%vh1;g {l 鞉C?@DiZ5GbZ?Ayϒ6@^6v+NaTf_jJ,vOT}e$@rsrePh쇱A 5ߦ].P'nZ~FqkdE}dJЕS0baqɞQFBۂߕxW2 {ExoY]Dx.vgC 9^O`ЮBA1u/co^0MW^C&tKX4}b]`ԥƏ%ݬ]-ۅeO98{1$pl_g":c0:I~g %t_[< ,ag+=hhV;e@wX (fhFHg@/ytfh5]FF[.C)7REw=A[Ass7v+4e5t"*Qצ@w5[!3we9{#t6M]^(AAg7d\xtFOa3E!KKDq01}̎3T&S.~pCOOf"E7 H%n<UD1~((4<3O'u\TWUSآq@? -vU(jеo8ٱ.B)SZ{fIeG&~,Nቚ}?jGzsZ259}Sz&eVJ7Uٔ] _CK| hI'BHEg& VQ4Hذv&J9lcp9%A/ #XݬanӤ stOQK1?K{xA%ѯ]x?S44ǯxMu~nYlv{P%$NcN'a >FO U^_xX$AmxK>OI. mܜG.V WJZt=q}h-h4ι_}7qQ}M2ʻ݁\Gۻ;2t/tR3r PYkʎ=Iyv=6ظO'tEaz,on?9F*@ 2zaQt Dk&3[+ٚ* $if">R>1mfв6/0yEmk $+gjjܛIO0 l*vLA\{ʒ<2-+Tt)QPiVn~s3ox"sRRM!PW/(Z= R<%S UНfjIT ([j"/^SGc>.1dkO,R98t6щ"iֲ\fSCTyh6|>w==PUm*T[5 jϜeHw1MO7`4,Nנ41Qa,\M˸!kHSvv$LN>d064g>RJ_1pk:F.Kʙqm.YBӵFJ/$^g. HH `a_/8AdFlrm>t y*!Pܑ7B bvϮ cN\b)̛VE5T]L_/&\΂PBz2zg|yrXa@bsuLm02wT)vRUlI[m4# bvI菒m:گPوx&VV2/:-o Y3A>[ m`}y&**/)slǟ#A z\ܰ<ժ:6xCu|:[_[pS]Seg+z?Y8PI*b=Q((0Q:xeNVʰ?0>[b=Ӫ.)Ϸ+mrQT/*X);ݫhcE˩k;[N&eD}N~[Zomʍ]bNI-4s:k _/-΁%6 ;L9DuKs7lٙ^ k8{ÇtXsk^O;)id5 b;l&7f 3`Y'.F aU"Ct@F,UcW6¡MN욳]}"Q,; 8ug*B%hllTpB,6Q:듻\ep>۸t#2'uM'=Lؿz$ācw J*ROG%ڍ#R3tQO nd*'Ol}s4F&-{vX:]*,ak1hh<|lX/k;~.R4%vs tXI|wLiRJ>t%g\$((7?}<{a>*L,{I)XTm]q q{FdȎ_^IGaJБwSsK8Pj|m"zmK'}pT7ncE6j? [YĖ{Օ|q<&0J?pT~<~3}c/ԉh%$;"  CA^)s6pxSH|66OJ_PKyVSu .;Z'pip/_internal/distributions/__init__.pyRj0 )DwX ]`;4;'QZQnego?%i6J 9O5pS V"6>+G8K!]腪o1(>\%GD nrA)Ua >b 9Qc ]<=' bв!nB8 mZUta"w:iCy VQ2!:xl\%jdcuHWgan^Bhůj8|@gLy\`_cPKyVSh #pip/_internal/distributions/base.pyn0 Dnf @zbvoh%UGYSA @s?'փwۮ(׍LUX{>-wM1*d[4n9vd5aQrQЊ vҭL5i>~.վl6L,٠wNh:``݋sID.YOoMƸsg9L0t(nmee!@)Dܡйc󔱳:1J"UR5a>?g'ģ_]D7_sD2u#3H@tzhfL@9Jb40kRId;MzoxYkx EJ غb3'rCF⧹iHߦ.WW#;K)UT#|rO)]990Q/cگ>Z}nf%5'lyRB~ /-6Ƞu.gޣPKyVSr](pip/_internal/distributions/installed.py}Mj0>E)t/8%U}GV8$T+y~>Bj(X4B阂nǤHB3إME*hWxHϟa(9xy . ϼ o6&46qwO]ןE)ZЋw\u%h'V"`t"3ym{;qM T,YDJ,Qz8RkD2o CCPdn {9d2UWɂ݉ȞbIv];K{IpW@i h . \RiV,׏i::3Ιu׾PKyVSJw$pip/_internal/distributions/sdist.pyXQ6~_! ^ظCҲpI>h]l˵,H,پ iЅ[[}37u+;*y8wfLW:`^ް7}[.lE ]/2hnD'Jwbk!~X hvQW/NvKDSǬ5?@3><\\%{?_T*Sdʣ.+ <}=Xu_gi7<_Vh"‘.q]1$I(dF1ΔBBY 3!eJCcYP*C2`ChӒ::5.:+ʴW/^Oe=xqb3+au jfqd@3g!xB3+Ӄ!⮱OYsY^mX!IE2a6Iqzn;-IY+#R'PL˺G%hhH&Xso0'z֐j2uhL0u=Pjp.< Ė^ޔӠxx^lc Xyf0*1-7\bb0 ,Za<]"X܃2X5ϵ D`B)l]UgϾb=r#ܰݷbPrFjĴbJ `V &1yDH=ġx/Ă'O̊E0¸ o𔑴CR&fɁ :e4P3 0^)newqdO[M9m&׫P|Qy5F6F(C re%.5 0<ǨJ\R7Br*fe (CwpswAmX"R+~Ji="+TMh +QPylX-K M"跄K ^8%28}<"&C(Aht]|[Q'mɼ(evmFTlfv\t7b=s+lT-KqL. q>}TEQtcܼg+o)Ιt:}1P$25U^M) H:bxL&Pv%H΅SS$l_oY&aQG+fm=U1/̭!OkapDڇ#I4bl=ISҬ65~'#TSHh쬨Ϛ/T20IB:t.*|NiN\߾0bU`I-vmװNDo_ghc;z(PKyVSC?K pip/_internal/index/__init__.pySRRKIP+I-JL.SHOIRRRPKyVSI~D pip/_internal/index/collector.py[sv_gJL'۹Zfnvf̓aSBu{{> ){O? >A?QnU~v(YKoTdzŒ]|PUkٖͦ΄ݫ uR0n@ T=f &5M]z&./?Ч6F3U#sn4i smO 5Jfi2󺻤ghlfTc7c'OMjearidӚ4!])?0KdxdnNDeNfS1_>Z}V/ʋ\6ʼ?5,ba3qen -RbL3]=,{~kd0tUm`|l7צnNNNr˭l@#PL`s1gndH>h}/@W*sJېGUTL j U^Qi_\'"jjdhi i /̈́N(\rn\<_M|O'--+1bnP37HL )rbTUaK.f+u:fLxj!Ĵ+@@5ҀWUVK jR[ͽqبt]!3LB ѥ1 z#no-`ܮm@g؜#1zp̍A$! Pa5YN ɭuX+вaŞnU18ވ_I|s*[ %+XUj#kʘ#Ŗ3 f8d3aqa(]$TӠ/L23//>Ek\,r*~e#"WyvdL'@.(HKud h֪5"f#fPş*'e zK9:|]tAj߿Z[Hh{{@ qw7[qjh#vzh!Ll > r; =3k}NψތmcG4$Z^_J] O-UPI{Lmi4 l#5reyԺ:kP R6a0ltUz5D7-SptX]–aa'G:\.5պDJ ,3|> lۧ}ī; tozH_rC1̒0-g]6N*8ޑKK +LDR;LixqxD**j-'H1P80PU1tb[>QJ~b(o Jg $GL'9ٶXo{5jQBnSd>ot[–kv S-Ѧflǧ`x`sbCA }Hk8.<W^zeK7ޡӷ #N 0U?|IQR;,v\2_z;2+}ǟ~~stx'\gwZU 7Fj3 ܅p6(s` cD#Uis"GK=*:jYDujtWarPБl~o $)5ZaOmkn0j*j3ZF9777븧O=&p0EE8R:fړ[ dѓNʴ ]+"z($L ūb!"^(Pa5NG$MFK۔x•=xG:(;GXG|x| 3nj b,0Rf r%/^2S|z3  \ZX2O[n@\RN爤E=u[(KvJ9SSc(J5r^L@n~0U#х3')ӷB?-߈"6=K[-)߂fwxl!)8q;?r}MeR]AP]ZP`&Q{$|cLhc|`9[`$W[ѳcwu ٟSYp#x@:h AeH Y[a<,<ݐEu7B!C$x(ƶx؁OE4on`g&+ ݯW\l-bę0%Lf´Qx4rYl&bŮs'JAguq0bGu,@Hwck+cÀ,]$Rn*!;B4t\zmI^^vfb S7o\ŸK)?v^ݑYHw3vW xYeT.ƚL=iVIHEsH]}*$KnTCo&~!/w7BxޖrdHbbwWﶅ"0hA1?L^?ƅnnX]:*=50.BK8d"^pM;'KE b&**[)EXS_*$Os|ڦ%< j1_#]%-NO0s2UUmxͶx1ea Qa^S}i{'zS/1d]GMmzÇW@1~~!z^1vE9 wܽ9 {W u [{W RT8A-Fo~ X!Zm_R<^皇/r-[DT97mm#[&H4_ ͗-`^YVzIDM&>'  *2 {`_msuMгJq\~m>s0PxE[4Ue7!L.7Ƿ0(SIf]o2fƊ*4&5ŒB{ԟTa÷nlz =3 %>Ύlo}ȱgyx^>9a~yZ.B4/=vwwGȽs'ZνsѮo+c#q#b`J]\ lQwvC+: zt,u/]Ù뫝TCwSCWd / &B"@p/B/Du6= _z|kplӋéwR Ҩ /Qo[pz*K㉘A.622vhŘ :?$EGo>Pt:# >; 1o*lo!ÿ{󟿼((z*' ŷ (v. >t@?`>O{ͻ|—iw3H̠#[Lp6J| yOĈ8$Uٴy Tg:w ѐN#?PKyVSW_"%pip/_internal/index/package_finder.py=kFrWQx$;W1YT*K>jOp PJ=7 WRRWl]`_ӘL&?6ܖ\o?^wRM&;Fu6U}%n'NMsخĥ-t/abTEݡ9oBumΛ}W5u]<.JU}vк^vMUA6xm g+mOͯ~!x˭~y㻟jxxƠ>/zմ\)4V?RQ++n8%=|NF^j>ƭ%Z/ D ȞTGydp5Hi=zZ<ϗxóលRm\?_í2͑iqҴ^^07k Uwv`+lEVr- &8,(͜68-lfBꦕ1̹0#8ͩZ㸥3SANpVYtP-.ٕo]Umc݄ț g3F7z3LDN}4ڛV#" 30F-|.?\)Ƈ}a8LyO+yy 0,Mї̞#zӳ(3ijHFB i2{2`H{ꍉcSKɕ;笕ĆRʮ9s-)Kk!# 5xC^H$duߜMKVThk- >fU{r3b}NGn)}_Du>k[Qxԟ[!`lm};z陯'zd\D0FQq[o1X-Æ:{F>ęEY}&$'= 8kIQyv,aA')Lȡ A ,b] gp D;bphWn֓/֓П\6{ğ c>j?9:N)\3 vfQ4ކ? =q/fI$Z/:QPY$!4 \ H3Ĥ m 17 -RG^,I Đ'oe!izw`ؚDM @xUIs' TbH'iN" g)O6$ङ8 >A`of^z0= GLC3`lz~iN^^3 =䢽Ѫ;m`&Lio$s #7G$]5qmaH3M!c%`<!{ݗ N#_`*O? }`Ŗ+7PfI7ubrК[Pe^PЃ]ڃJ^ߤkqLvl[ jIDGCC <HU[pOchb!PdHIVJ`czS Rc=q#,$tX{LN S:` '5l )ḡC&5&CӳqOf(6KVl2:İVtN3?`Jiv8A=i>wdѓ!H1>.Z[p4tBӞ*Zxi%@sEs]^S~FۭCՔVR 2YٍMzcȩ9K)k@gy%jHNg:4pP;5hR& G"WRU^iOFo}הʆa{s ~#eoԲl;#x֔u^N×NI;/6Bs-q]vܬdp3@ѴEJ*\yGd(Źa ݃:*L+ K=C^ꔿ C)p()>^؄$.R9z!l[h/qr!8V%EQV _۫JFu]gmiP@i4y2: l\kz sRZ H9b֛?RIC: /`j(&^{uIH\ۻ.DbN'(8УDstRQ3m8%4bG5AsmO5>-<΋lQ RŹiv-iEk@xM$Y hTzptxk$A ς_T +E$d7%lyoѳsd4R6}-v@fLs+SCKc/n7?x#<9r-KW--l(뇎{#xs׀dMhތ M ܄?DŽs1>Q$^]T|UN"ՃP&5ȈVƣ]^$\!"n{I'N5fV0T|Oِ:ާzNk|g4 Ƚ^d}׸}F=c 4|)H[:៳\ɉEW0)+ɧaѳ2?1!6VKRn Iy;ƓE) }Hًs6lnHLtŸ<-3 e2wGDajR zw@,=닷M6Zl2mcm ƅ~KY Lκ@? 8PDBRGHt#Ԅ(ZŮ_B+ p2Fwz63R*%Szs} T7WE+usEQ pt'Wg_78-@)DfBܔUj6&k˄QKg !LL`hBVFQ:zLIPw6.09Q j=?@ i^%YI>)V qaejFSlzP3>Agh$埱QE)iuVřyӚ7F{NZ\IM=q9-c cí| &Ձ}8” n%Q^cҳ6̹p\V8] -U85ۊҼxVFAxA"J;1"jv%*3HqƑ@s%]xG4) fp(3<. dv.4+ʰǹ$4.y.9bm F^/W}M,RWz44HBY <Oފ>3*|8аʚl~D_=L`Rns"(I|Ӛvq\gh۪l[~[0P}\gJ{&4 a(,csx4"mK]|dzSx[l|Op\UKtfO|ͬ8r L.& M B 9,%TIJr^9Y]_"\փ~}l s?GS*L\zJ[*L|# y,/{΃S5gcY?_:|43$m&Bnq?+5;Kph :j7>#_n .ib$Sw?C[F63 2_v_V_NwNf1L쫂}U'P[9e1{,ѝ3nmx3|=F%_UU~])a!kx2|C@xI:opl#.Sg,])6ߵSH\)mYxlʫ9lb]v_"> tyh~~Dvq"&h(O@R =!1!{7 Bbx.?A^M!v[B9Nj{YBxOK.7uA,Ն‰;0 g`!{|B5GB H \oP<=BW7lyTa6!a A/N뇌pa?c*W+,mE؁JF]K>xс)goY`89'U?rSAX}QWg<"몌>m.ꪍ&p,tOLf|Ņ;J}X<י2 SRqݸ"iꗋ ,FPeDMt۔0D?pE({ j}2G^ɔ bf)+{@̗4q,rq9Ž@Jpbz>I#*:Ob&N j3ڞla2PDgS!P^דX>{?$#sOdV6>%%p$hR~%I៳a.Re40؋?u _zU7q. ,TyeXiLl5%q kR;^UjP#xu:(u<$գ(1è7y`N.:m :'dЙi@N1i!->(-W`Xa«u΂ꤨqz=G"%-<KIto㏚[t'ǟS眹9 pTL(jVUltiWb.^WG;Au,J~T2j8"3uwKH,>kQ!\sF+Npb*!*y6I͜ѵP|/Zb~&9z Vr[}ɮ^3__qP G)=3עS:` R'D=|EԠb.%aMtwS <+1:3N.G  }G~h'cI2'l,@Klqb)L;:9)'Fu޼$k10 ~_{5&:Z#L-%|_xK(E1V|AUf |+"^f+%)XT9SY@\ͯC䀇ud˦ہe(fM-2T 5GS˃@">,EP;̗sBb.'d(ܘ@c:-* Κ̌r",?~6ɘc#w UG#r4'gHcئW~TKFt~t{OeKJ9/ywW"0az7G$)Ұ1w掔Ei{~ ˘O#ϕsvɾ+KuRe Yxmd))##1F-]]adkcyJd$-oFtZ?K#=̯K.٫?d$)ϒF'Yč Ɗ-|4b\ r'*',;U[$}e{z=i2ѻ==|5JHIMХ8k.'1Ck[Ή>{0y܌}Co}{[u,OlcF%c@|LxF5O%H-k+;(kڰ 7~(Xw)CcFdю"e{~ NJZ+xBSk/͉%(EMP!37=ANڛ6CP_5IFf Z+LQ6YZv "h} !b9Q9J d{D >K_e0űC[+"$ +mt&O iGU~pHmuUs-OL-L ={}-n ŗfHU-h92 PKyVS+pip/_internal/index/sources.pyXmo6_Ax*\̀Y/!mE7J8I G"I/LV@Ͻs{,TVv':hg@\J%r~/PE*#vu(%_)8PTW9&"yjy-o^"Q‡zR75އHVAK:%V%01w6a#s\Yx$)=Oq\C76 ]t=hq1,V)r툢/ TCJeL!:ӀSUɵ=W7 .7c6yׄIs mP`v▃ЯW-F3C-kFro1%Ć^H }wv/&$ XM a*B"B=pc|o"]K6w;\ePri3eS&حY Vv NNX&( p#Ī 8j;?_턁T7.2((*6,[XAՔP`A U0f6 (|q#|z_o #dK#lMp1i櫕 ^%Dˁ@! G(B Iq @Yu;<ϸ*sM9#!Єʸkڜ.!XlԀ' *&˜Q?"T<$ y=ßitcT  =ؠ$60)t:Y!ù>a" Kٟ hRAwX0{ .vΡ- a+Jd[z"ml,\^@ )T1Q2``ү> J/.` q7CCVppROׯ묺r/^YbzޣOeekB;o*!O4>6ND kCp{PCBb'OE[Hn-,-{ZزZ21B AîHy I[ۜ0!ĝ\%H>] 2ْTGz &2lZ;Iy z:4,Dڑ k}$7>݆7܄%T[YIlD{ؑ[=|Jޒ*;*(Xw-8 l|FּA-|BIdLyuې1ٲq+?!((%Ddϸ1>%]u gP̆#ͯb#Y4PFӡq4!ȵ_ ٓ^Դi4ttz<]ev7KSnuvό#"LBEDŽ^ZgMFZ/qR4Cw0ި$UېDPKyVSőkl8#pip/_internal/locations/__init__.py;ksƵ+p2{IВ\քn[u+KKQ5H.ID $b e1"qy?Vr兘ȲDN1YD\b+,h;BJ(V`8sHaT) JIEXF4SU@:><<9 ٺ\ M<dS2EvɳR XʕgyW5*?(ϳˢS8 t88BdnDl4Ⳃ9飰\7W]7/b $j.XD[zխS s &o7Eekq/4+LL5:O ٜdV[ݴ CfYiS I^A,\EU#\}D.@)KXK? }_dK ziԴ]:*PK)4Yh-$Je5.$P$CsV?J5aoz7l춓xۧ(FSo{,0s2uq{up͋h:g!`JK 8{F*\z'J3!l%]k~o$KSonHwnn1D77Z?;`4@:@y+^D4 \Bj%i 2G|L!M)G_ &Fbm{)&Š@èL A?kACAt-8,8,p z_.%;Y>Bd9=찆ị^, f1\%(P~,Wf:$G3<~8 Sߐ/5 WZ%x~Y?@ȶgޒ0)rDd] { ^_087+`77i«(Z^8lg b1I)XЕ8sV"=esCt@6#R ?@xi`gw_km# {O0 C ph*V*D zH 9 @.!{,5%AIP>3Op<|XE ;lmÖ/P}% T: .kE.kcr͞fZF'ʉ\hϒhN^ݯlh|aM:h?wQ{'?x9FڜӝY< HbESK,K9T7GQ%i ACy_X p7,螆*'j|()PQRJ䗪#6de(YM(ڌՀj+ }I$W Q˪(7YJVKcvmsV: K$8GkQȇ9@u@ VyكoZ@R4[#>VF@Whц _ DD[&w!=/ <2[^9}}m#LUiBM.?,v pU2aݭ"3^mD4CxRȈWYZ|[ezjp f2S7Zҏk({vmv>MUa ej6>=?uχi) ϜhCt>ȘC-Au~L/lɛ ǿfJ l U7H)yYaN2Ӧ ~o[-5 wބk@!5o!\Z締}-Rػf ܀4kF@ =C]q^@|MUGԴθ媉=+: z5ڼ|W+Vi1R/:L]"DDNT;bYcuhwu,A%&W+$]vMx%82U+|Ȣ)X;WP{6)|B&GVUc 8ڍ%xF̂ڌTϥRpL7l<|5}bw:fÓxZrM*<%0/CtH%c,/r5=f&)^ u\[`+E1zB# +'vQ,kWq暝ƞؙ6QL45alRt3Sϣ>״S8`N}8Ђ>\%1Gww{ ǿYr5#\R?ԉ!Ehk_^Կ׮`fyqjPg2q,>Wmֳ"'F!qhI+$bʦX鍨)g]kX4f!Ᏻ!yèQOo`ۤ7[:{\PKyVSڝ{%pip/_internal/locations/_distutils.pyXkoۼ_A("gb>xͻZ`A mExA!s9^JU-^RgRZɵIeɌmV GQ4\OʲT{YA݊2QMY`ZlNcQmľ%6Z=ԇ9k[U>\K#&j!29J-+$4i-X} x {. ozowzIgvYX VZج>؍R.==@J=D$cpZ>5u) ˹\:VhХ[U:6ĥ5ս#@w??\&Im^jײ親`ATYTL܈VVɄ2AhhS"[[edRxG\#-*!e_ߨ@ Z){**99+rsX0Qd>,92et1$IR-?b/\!QSXT:wwz z \GJBzzzz¯97\.r #яɵKDrJRhn:z `kb܉TʞuiwՇ Mk͘e`Z Frku,Q;mE$!ȿjӁZ?3JS  )аZh77uc],S2i%x'Q*a51:/F>be2T!ӡb^ܐ^huRU9@FGkd&s*"T(]\/u)siſa,_+*%ϟ75YD@݈he=zL6u4Ypb䁤xy. <4R ĽDRwQ 73|snށ9cBAb/Uz=,~ n춼⥻Xq;>ns04t*=H@5Њ݀ k Ӄo*P4(}rHgx<Г񊖼i/^O<>p+A`G;J:/>>g+Cl)jNli%P@~hrGZ@ ]Py{t";Ƶ2WlV 0 L;Ա-=Iw+mAN=H\udjCy+Z, Sm_H$$Kד! Z1ݜq);0˗9{_1ia"4wvy倃ls& ⌢qF&0(ʙv8n"ײ!x^e#B=]C50 O.IE /H"C~M="d E< <Ǭ1zV*p2' "+N&Hs8KRs}ݫ99$~/)$(*E 7ضߣ0gpy {mwj4?6:[ K|sd"_cR> w<?$(ă[Nx*#ˏuoj)=:PKyVS8 z %pip/_internal/locations/_sysconfig.pyY]o8} ԇس@tӝdi7 蘍, (\($٠MlshTݰ\md_ϧ콪YHkd$@ey߽U{7L W5dFZX%$e#xDHUj_M+N f/J0`W*PJ翞&fǣ|,Fږ+sM_UdߏFSEWbш,*jvڙ6l%d4aT#4S%Q8lі]ǟvna,QllFGłEZ;'U-9SxXY{%Rh<ZkhZ6KK84[/e!2Ֆ Nx~T- l,r6߰wכfJI)pβ1 Jłzr%,-`UGZ[l3o,h.ע{$rOҳ b1oz$"a  N)BKREZ 9㿱RtEѻVd ìP˫K &r6;B{˜]ROP%ז=䅃3`H9x!ʩ Ǚ?ɥD#:y;~w3fH;>bz JlQloIL|ҎvQZ>z m,k6s΢ڦjg|efNG. >GY )AZ H31k1B+a[q)56f-D A0mdjѴ5ÈZ_sAoH+Jd0. ,2/ Ҫ5r8T% wGȚV66õ:EXq'=ghtJ6>|a!]tDZ7a"SÙa곉ibBې%9saUIRŰ]1) ]o7T?oZk6i7&-lr8ߋc C nqdmbj%A٢w W*^@ F-Q2'gXCwCv@n *SOFGƝb]A>NqT `Dl}C>Mސ99^ ~@VVΔ^ߩie< MYfሡU,,ͩ3( >Hi^=Cbh&d|rΚuΘchld{W;$ed3aڗ#m0I4e ;U%<\4jFz 3M*D$453YB8Swy\[^A 12y2"};YkbmM< DnD' vJy0s 8%FSIhZlk*4LGQ|jOVYKtا[WfhLA+zuBX1Gƺ~3hݠ̀Zd 8&!@8y_*Lf"DdUvwx Zʹ,da5*fFnBXKl/=Q9 Oe!\huvjSQ̆@澬3ƧF %a[Е8:>6Yԍ}e4 5PwRbjP9\u b}w}|nYrniپʚbaIqN؍\25_.N4fwwz'?dE&H4=L`*/{0[ >$ݞV4~ & }y= Y3vw\2Ihzb<6aײbA-A@7|m4 ѺCgv8` P >9ݎ>퇣 U8=ǪduEz+P6vB7f+|'czӃ(&R03ӧ8o OܐI{.+\Dr]OҘTdPcJM=G.]:uMaF}$Rȩ;{}m-mu!M Wղjtކhy^w|3mcrņ @l.o {1/#cLPzwEl#kUwe2s%V'Bĝ@臲VيgW7ff_Zѵ,#[g1zu4nV "'_/'S3G?u}0fzl;\'PKyVSY+pip/_internal/locations/base.py}Tێ9}(< 3ήa@  X!rnLbėSNUYmխ)'?W?Yj9}2Ijg7ԨFvFj= vi*vb\hf]\k T}\MhUʠ1p}n:/^ݼz;-^W,W\`S,{4ҶXGE+抂 ;aYw׃bDbR}jbacDRqMf#[WlH uB4Zg(:Ή'uwU1+JO2B%X/wJp?Rqq?ŏ,HH Pڞ&]Y4kwU?Z,Mwr֋(nbRA}5FPPu%52Hy,Qm {M=58G#FJnYwxA&=54jh=PA5ܠIŚW T{ ޶$4R@ښ% %{z]`쎞Ʉ+1J8۰{pVofԋ*j4M4]cNL8mQ_ݸŏv}z?wqc!!ea1z2D-GseTPR+Ǭzn<>7hwD^)r}yxvme_~λS~W ;Ous/īo7履gwi'PKyVS sB|"pip/_internal/metadata/__init__.pyɎ0 ~ "&@`=].].zXY2$yKKe0s\S(7tOG]oo-4U5aK~@G0]5#_"wr-w=M/eeJ%][] x"?Lpb5.^˚/V5,?d,X]ouQsJ|K0%ޒԡ16.ZBgQW+gj!1G-"¨GuًuTZ3AZUq}[U[C'xF)yh)PcJ|E|?K 7]r4OŬ7 F8+ޅa1ELx 4yn&K! vFEmJ]:x= Zȑ!o 1W c"P-TƎ ..hoOL4ڼsM<.bH^x!2"}yԍ;WKKn.zݔʽ58\^bjiU Ӆb]dPKyVS. _+pip/_internal/metadata/base.pyms۶~N]Nme+'5Q I)@;j=@E:vnIyR<7L)b#noD~b]fя\򜍖Rlޖԏ|_|6"I$@bBkI \k~TLSQaP Q0 ‹ : F<5A`&-kK|,A҆ &yL <QyJ0M-&Z63yc/Y: ѷ 8 -2+XWce\c~M !t T$K! x⿬e,!6m 3b?ʸZ+MQ> @Bi 5AK~!$d4S4|N1UM Zho ,/>#Ɨ;޺V;bvqM^,RX䁀fKZ_3t5DSL{&% cT!ϷTu eHf#&Z`CAZw$(ĂrcK8`D zOֺT+˜}d>WbjwO&C* G770/"i>+ɟPEW{Ig_ ϩv?;J)F%L'Y# 4U wZj,&cj-<,Ć-hj_3 C?4xK-oYŦϟ[1$a96>Z=ǯ'vy.6CI7.lTdsk4ދNXaw%ڒmTU -C.! j]Y{ u^nyR`d *C~p^36YjwFhRG A|}v:bu+&;vm-D&#{\beuӝwh+E[A f[W_kjB|O›^QjIER)3zO;aA#m]A4uK璃T#0PSդLՒ6DHH{Xj^sg#aʄd$)q4Ս߅ÓGiߢ F0cp)!\ p˽g#38ɫõ[S {.QMB7 zHW 8vp@h 650q7h#OgogXMnHNckBZzd ,ٱׅ-l|!/ 굯r= z`޻c[݀h.Vl<;lS֔HV:lȱ>u)/sbo{w/T *YK-F0z[2Yz׶hgf|jw;Lh<*zdH#St|ny"M!)kXU@ku:%sx +Y"uﵤPT'M&[$ Ogg)7\X!ĢZvе-UTb&[*Q+#q!H7`@!_4S0-?I8aO9`z="qE?J])z\|Yۜ[ZT0 E @@ $[0ۘQv6TV  ;eJn<]aej :k)z{Ǹ҃OYݑTgnf?)@si .cV/jrj=xG/U]Ca){kA] U:e)(?u-=n^<\+,?,0}N`4 HkMkvVJ7OP)+TRn)t ڠA\`+$zS0v>}|qޫ#*!y@Cvcf@ۃ=rFn_G9AK +s*q4?~Wt\}->PtvZI_ =|&jZQNfCrfh;.fyVE͍adJA-Cy.kյv uKv9'kƞsE+J(_Tح_uZ߁&k!6j^~骴LP?ONzJ-4#\|: yg* ߏ?;ښ6 G,F^/s ljj/*& >%R\ lo|PdZk$HP;9PO[jʁC%Aacm9e!GBoxl2֘dfA@=Sԏ l$4֍d},2IxO_&c߻iY}ᆵѣߋ]d Z -jntlkҟh3ؕDgߚ2 ď{PO /a.1^Ͱos55s (J(sBB?;@(q$dҏȎ{`U's\jK¡Y-'F;#Q>~O_ ;gؾD/efC*Tx0:8YPC 3u- 2 7` BfFxCƥ d$;nXS=b$ JjezE6k#ԪuJ9 =nqr,II}urРKf `༂_K_4 3Th%4xD%޽W9xu:>JTbĤqj`(Q{MW`xEC )~8gx)GѸ</#̴PJ]GXLb 3и=-/jQxcmYZ{U| usE: .f yk,z'eO OPKyVS{(;? pip/_internal/models/__init__.pySRRrT(HLNLOU(H,QH+I+VOI)(qfIfjPKyVS0Oy!pip/_internal/models/candidate.pyRn0 &pA>E!1%ARAGT 2m;o[pqq@S[ϝV-?P>> p DͲfLDo歭Q WJQQ0ԾQlKM}+m MRki4e| R2@1$8)RR xZse^m B@Y $}]klBuSBB D_Bn-5[$,OP`M57#żeSfَtvxC9ZN]r"lEgUhՄ(Wɢ~e6hC2<ƽ7?s ]'?[&=mDOΉ?  2]'PKyVSU=*"pip/_internal/models/direct_url.pyn6_ }:h_̀K6h"p0S⑴[/ȿRHɎ,CB|:D~zDQ4-ʮ( ~=ZS!`)꒨ݚW7ٟVL%b^,!+Z$d[3\T ҔE S.X.D%%-xNݙU~NE[.3|.jF( Nߝ:K/ߧfl~~8(S0}Ng/yzۂ\ Eӓ?ɿN~>I?~A_^AVP)!g_3!k-IzP\*p*`u YB|X[;D )؅u19yÍlȎߙ"[ZlA*UNL`#)KNZ^?FTV 6"&$’kR+hFX{l)cqOVTMhO wdwq4hIU魝+q&b Ͽgg:ƠWlahetI "%ҴrQW%Cp@ "2̗!'kW^]y,F2*68?5v뵫eaIr:}:udֱW{Em*#, +D+ j,af}} u5UֆuOhi{Ob6о2Z }a;ͩ3[]2,Iɣ4A #hs0mqG5l@qEӮ`.X,G7؄f.nFެ5 !nd Po|6-۳졤\|T4C՜چ&7ZvJoNNfb<\BhSVO#I˸sQ?PKyVSbch &pip/_internal/models/format_control.pyUKo6ﯘ-U9Ѓa4eM IyR/R8@7JJҤgJi$B[3s= Ѣq[}E?XM1^aԊ.jd.^ přf=zὝz̲1 C?PknX픞z=/%a83+՟ӫ|Ze\kPrNdТ4qFʙ٣rv%c~| GD%It!N@|C#D;Ԑr%4?:`I1 Y6\Y%Hy ř`KP$liQ:"n´QHo  cA#p.K-ãkǝ;R0ZnbĎuO y+ʞ~19cf!J3… k4s*/%P z!h׳5l&2΄?`Cp^4[ܼyqi8K7}_ 3n8oDs{ի]/rV|7n#:j`YXuW2qݣ /> +$|G 5;f9ɪqTT~ayq1E. :^j0ҹ%0tLٛS2#Uʆ1^yg4W/d]d(S'V4ƮDU$oN@C[I„Gd̈SFBgrB(6$U^UC #1؈):,f7"ig<\B؀c0x9w} !Q5j_Jy9{~րW+Dv"ȟ+)׹Y'&=Hm!z""L;J.0:WF:ׇ8[spހ܃0ր6$R-G?igap9B%&7ҧs\@+ȇKRVQ\{B)-E 5g`  _Uv(=f iǨȨj0*Y鑅$Oѧ )ܲ]xI46D="!=|/Gs͈::(ٴԚ%bGC {Ƞ?|F* :zM AMD-[JC|~K)5x`\d^qM"y5$ (YK5 Ns,Ϭ$sK!CXK#BmABŅ9wxV 7 RCx%=nijp0{Uy]}$=NowJ0wKEEI|ۖ.:J#M- [Vaɳf#i^+vI0{=ǭ!1X wkO0UQ6n ?1XSP*yLÛKM!!%'"rm3y 1/?Rfq&C-laa)ߧz}fqtyjĎ(lAK9?|HQ'6af3{C5J%펽,]&܂r$JS;g%Ѷ&oDw2uA{iVjÖ%53}3AGwɢ"@BiHN)/ v7[ݍĩ,~b)}!O7 9]Yӂkb#"m̬%Zu\Wc"_bcH:|ȹ,׶G\ ogBail 9Sei@ :cecCpԟLm32gP)X0F'nŽTN#H׀Ɂx lNBgY/2\܎sjsuSoG>>a*Im-o Ql_ousW3öY@3 U  FCQf62GÈN>XX=!O`LB!+=2"a@+cέsQ{]kfpkѠB ff:LqX=ܰJ[z`ns]}uA !+3+'M PKyVS W7L$pip/_internal/models/search_scope.pyWm6Bu(K7J WG8r)֮%Gn[g-&/ky43zf晑*+SA`+,h{8(sh_m^=T"BV;^ jl±Vv~|X5*UN:gL:([LkT&]FrUiͥ\\>;WdzNia/Zac]C2'sTԡHIA V+L:v"2\Ƶuji=euf+yZ1&e0]N: N:^Ɲuu(KnA{98Џ`9Ő.M&1@B SseEx<['X#~WV73y(o]*?IdG梪e'LOkΈiwL2Rh 7$69*v{;$XCJz؎֓ 1gPfvvP"dH/7 z`IBG`D38zQdN19'$Ȓgח[DyŊ^3E P%],4O'C Q}t.ڎ Ɲ)CSt;Ҵ7oy>R{Ȥe,PC|Me8\tn q?O׾&7Lj%-+FY6݂WKS7OU[L_^k8`_is* zFP{ש )#BzUp)"wl߾o Vz*788u쩾5F' zC&( Х(iek+^(F|d h]!I|,}{-C rN4s5Z癳,O vX׭LsrٴHKgX?Z46cn ;uՁKM\  Ld}$bI~3's$1MXwK CxG"hmM5{[#2eBf>|h"3 [`% S:-\ t``y 49t/FyK+\p0u`‹; ͘?Mx=z÷'H<K\}N ٢֏*m xRxBFOOǛgևUA&k6钏ItJ:4HOFq)v]Rtɡ*S {'vOr:~s#yt#ibL~ 1~-|y4nVPKyVSs'pip/_internal/models/selection_prefs.pyuUKo0 Wa-z>[v( CF,i4~d'}dZg:{+3h Vڲڣ㳲3 **[:h=m:,V~:\9lѡgX{] K  `1;hy . 5yT( <UEx*x쪽[lWs8c y7 lZB>kr{1zxJ0=WSA VËP=7Љ-ww( wO!I!#٠#m`4]c lJ|poP~#<%6891Wd-ށ@R┒!mP1Ec,D'¸@Jj;B{#KXw338'qyc'vf/\tO<%\ˣj[S)NN_>, 'Y `\ЀQ )|L |2JŢdp4s4NPpP~ )e2H  M[@>@bxfgg\pR7wKoMS+Xq6exYFAnߤ<'Idvrr?UZ5imPT'ܥ}B!\o9UQ<"y#Rdc]f&)hΧhRxhj%N5˞.e5ϟgSYh5^*rS˓TҷT,|暚"gaGddƿn.lj$I\q0@[1z$f tk(P[S %4%p 8`* ƢtsH檕ᘞ9\/Y.p^ggpgu%IG ~nQPsȪH|~=#Eˠ%f!YP=v$D<cHyċH1JI D#|5܏pԡg_s˫iw/qm!9F6`{F/0L;n 63o|C{^RRbf3NNT(=:fꪖ,ը IiW.Ə1(clwLM 1XA.#g}la ; 4NV3/(z~ٱ gJ賿O)ܦtz:ȳD<)u| mtyȏEsq_aLYN, ]o~S+R2wâ{'V$[Aؿ0FAE(]Sk;%+7l1Ro+^o ӹq7Q>1-P$>ǎqLd5@˦#%{hbsՄ@!b.1%fpEǪ-^)ޱ@&/v]$׮Er`jBٷȎQ]RxivP$vB [[n# =6 -t=kDdB[F/;ZLɶ5WLkHmó'8?/m0y80z@u&EILWO/pȟ ESEXK|B#%ZI:š3{$yZo݁@ZwL<z{vPS͢^qtIgf&āE.q@yVY /R,Ia6Q_q?ӅPT$+uΚׂ>W2׹\PKyVS'TA pip/_internal/models/wheel.pyWkF_1(ȭ-'bt.ې~݈tmO#kػNsglRaAs}i(ʐY!KUe.*w*'HN &F4K>rCxNĆdU2h66N- ӟT&##MʺQPT;*smJf]:nJY$Q.N*#|h4 |$ޗ?G)I2{9 W?M8Vm~nz ZAm-} [6_&9(r_ѤG2Ch9-E KR\Xgbz.~%OLK<7RYsUS 9 bM]u gdI/ Flמ["Ru[<7^Fkw)"mI55:}EVqĂhBJ34hDӢ-|@7e-o;3+-s|6[).vgվ3L-/ńAԐ= ;ee]x鱉y{wƋq $ aC1n̅ ;DV9BpP c'L5p1VJؙثL̳a"arB GZ]qmGy]ۜ z&a1^),yhn`4ch02q5Og0m!Eq&\vcv 0ơ SZ)6D0WX ہ0 ڹ#D'T?<ǂS~uy%E #C"{2 J=½69^mcrbX1t<삷ҥ1kd$~Ǽv{\^\^g %SGyqMlzA46ˆȩ  eZd̀+8'+wHڍKnL1SpMRyóQei;'Oce҆|qr˝Vyۡ%:5b+IlB«4+jB>+ɀ~ȚJD#Nۮ ws?>V{ ܰ{;^/5̷q|}U{.m0J!a[w{l@5nNtk0/Iƭe- "9"“{<$cU#p`o /rtB;@uA_+Y*V5w^Nu_]@gmAk@FV8>,Vh2'T8*p5$"jJ%~.LW P8jwE8of9#JXAV҇'mylnUsđ`bE=, ypm}ibAf3̽&G1Ð{\Tpx[XqwQP}\`$0q~1_apsNFo?.)FvXge7RU!.=?SK_:w qOF8Ƽ .FnŚ\qQ( YCy d50#t)~JW%qڦr e  X `k. ב]^erl-X!@a~VJfhH,)-TF!LH>$BL"E)kMnѾɏkK=6̫6kX'rzFcNT?06Dl Fህ^xifaGIpTЖTdw*h0-|FtfžYQS':AJm0W[9º۷#Dԣwz&Y/B!<>'uhݲQ:ޏhݮTHQ eX KZ DWh{sXģ4xcM7u jX 4F5a?<*j1Vt܌D !&ӑ@7?V+WcCM?f#Zw5p7VkZ,Yarٱ #U>]Fb[pQ恫y>{{PD G b /`bLAo\M}! omȃ8vjĦRQ9j21`oc[C/H˨@GϜ&S{Qd^\4F762+tebЃNu~;36 okhm<ύ:Ozq֕䁠DZ$H8@<2/Z=%^FE+Q!O@LHIgBXLlgخiMDfj"!z#S4@ \"f0SuDZ֣^DB( W63mAޏpQ:f#',.߉jW3*C0:8QտNm)⥗maB 鈎6w[Ѡ)-cR4/t&gv.ى7F{6w-{eiPnqF̯$tx)<Wpsoҹt-`|cႶZR6ǣӊ,d";tZsg[ b/9"K!jÝo @ j4 Z f~ 736i4y%/oq0yA CÑTn/ڇm{oגgof߷,(cxBG:}!J o8I_/I ,_!B1R[U%g~zwƃ}4;54wyAENcRZrxLOɪ^KH%W-J&@>6d^#]W6C"fՁGIMI3_Kֈf)EC 4+Ӷ~3=9^:NGv"'4H4]8vJXdXpNb-PҠ>4=긫|tJ|*ӈ"j#,kAکñ:]ǰiܲM`F0SI1QQWaJ'`grN9`=4qڰ 6Bu$a#* ?z @FBz^B&w5Ej_xHcrJ/Y$@t#6RƔyP$7C̦ W3\sXF@dݒ5]Ukx-Zhir?7 Y&._h_hMZd Hh{@_\f@h;\=}ՙvX598 U[‹Pbx tz%3$l~ₐw?\Ń W̱B^PKyVSG\A84pip/_internal/network/cache.pyUMo0WR߸TćVULmlgoT3o{v<|uZb4GT{<2ZM@玍kjA+5SiXJMKTyq׭K^k\f~m˔[*ZH&HzeShh٦Ydw͋[TBaJ|-*V+|V~9P\qLQ-y+TzM.G{+&ǔ9wBa%~9RZs)wۋGּq[?\1f1)^ڕDŽ68=ˌH+BJSQ(WF45W TPCEK]Ѝ^Y6ښkv$:K?(fEFQT)SҺ-YxF&:)yփ ̼D-FBmuj5KS#bACha ż{9[Z*YQuhM"14>3k~BYdtEx5jBӺCX %œEIS $]kʛ?" JaVmjFwk$6p@k= *1$75?i$$_qn_I+ԞIH`!BN4*RY`H:-T'3c% '-nY䭯CLB|l9F+8@nʷ$ysD~$9:pBْAрHÌ 4rǀQPI‚ȚOHK.k6\| <Ľ7%ee'L4 cw8r#u~ T9UC;c7&#kM;Oe6U &NX2W[1(C_A3Sn'jk3|ޗԡƒhalJ4Jr ݍBuhڑF<&f3/Xij/)^^N+ L~eLhkTv ,ِS?k9-o Yf3@.hm)WCH1MhzTB>7jsàŦLCG'K%}u&,7zXT"fI?uA>ݔףuȸ(M  Cq6ym3k4ŏ; qYwMts{O1`v\bb7OF\t a8YDuzutP̘kKgL_ Ìأ; ع;tf!.weMoɺ6>åH^E82#㲉 NᓁҼp&PG3 ,.bch8Cõu?c_^wxͷ"ёqx 29 qkm@\q/ÄBIdpc#XXI&Rq@G!jfz\ܢ h:@]: b2Cmh7:ʾI\JB:B~ݿgd#x5#Z$ybF¦3Isdٹ?@~;<Nͽ١2OP]g\0$YM:?m-,0PCP#LfYSI'| !zFn'ݏ吡,ğTl3˘=eL=\>[:4PKyVSw #pip/_internal/network/lazy_wheel.pyYo8_AhVޓd}\) Zl6#&bR%iڇV7C6\d7ػAfI‹"I؊|y˭k6KuȂ&UOwBI~67l#H {$wB&j(KVOoTF\"[,-Eyk#7{]}K.} ]W&.{ax xA/#W'f*b[a\2hJp)cb-VGYo^bDśOS* +hMs-IwMgYZp ^=8_3 1KF ~'sgEjQ%6_mJ.#xFw;Y*-3S,,&ݑsEH%x}maAdř7&$:gU\ VVu!^?(N$(͎!@|θfϹ +قM)4_<ZeQZt7VҪ;n&scD`@ mY)᩽ȡ[bx1$x 6T~dO90SlMwkh{eFú(CW){^"Â]2.2Y&q'+HV+in\eJRj*- 5&E)>4 $I$I.1Zw`hH u $0CU!vPv> WM}P5D3Ɨ%HR9lb ݺ`ާ: HZ!- @VΉupikbH*p51%@[r#B_XKk0w m%'id΂ȒЧap\ZPi\;E0S%  ;eطt'҇ }`\ )?te+Ň 궷 vU'/vS6)mD'}PشM +={M*e +9!M-yy}I;}<-zKA@琕r2k9{?c2}'wihŹՋu? ,`TSc ߂P)t۽" ,KR)TI SZ2,AyY4p">⽉{~YңZ!iFb9ˢY {16zi5@ VhGUh#;cp>l/ː jt;;"+x1dH1tv; edˆ JүޖUpZa ڂ>#yج;\*Q`wMaP ]?=Kv(C׹ WؒI>ӯKv~:-GC2AihcUW4NG~|lWyzwvPRic⦕=Y*-xF0Uӎuwq%J]l`2<&BmVa?*i^o9 Tv胿G !E'rHdb8 D Oۜ"%^.tbP@;GwRJCP{ɴʺ?:*>AWV)*l㦨RweZāUwUgz0/FKA"(jwՎ! l;v@gg Aa4V̩Z&U (کqԞO8w4u> wnRI m=NH܆'S7]ͻ.x7`o') nC*ӆÖtݻra?lwSi5 hõ_Aa:+@oTVkLr{Hysd҅n`n%%X%_[U邨O O]@{ ܬZ6^+3h'U_ia1ZPDwwL.=ª>7ֹay p 9zKDp}B\*(/p+xM; PȂR_.$Akn֔Rw$yۓV+Vzy]X^/=2{@4ǡ2vpf`;hLF b$mE5z<Q |azU^&b59/M;FS8 0;#u/^,Oj}a1Mș PPbbvȽ<SsgnSig#.2/=LIo=\1PKyVSaݞYA pip/_internal/network/session.py[s6=7ZvfzF7:Iϥq&v>K"m5~HEOڛ{rT@`X. xvRkU"Sݮ(+ERrϼUNqLJ%\VEy/JZԺ.ʁ[M2A0-r,+i7.N(uY^sd3hZگU/wex}.L-]\jh' I\RI5r.*?;9W;I5+O@ڛzO&0F2OAs7a'IU[vAcf,SbOL`;0aj1LljwvrTUO_ŌMzq+1g/ O鿓=?yTnw`jRӰ^ۃvuQ4wyZ Hi*^fP;A-oSM*j d*աo`4$ĵIUX}If4!4T*<ˌi@Vn3\\,zѯ]t{fg2;{ !KC&aEUs=TcY| ?FV<:T" }2^Y/p5R BnyQv+,σ$|[eܯKukUm%鱓~@{(!L'/e/M߾L-ALBëǏo_Ey*pwCJ)'?B %O⥠k ܔp4F ̋]l /G"(u{%j:)3! asbsAI-3fV2@8Jat7EJ|A=%EUT*Lc Mg3 i)&x_ߐkfY‚Ԅ[ q/y}a ӧ4"gNX~J-!rW3Qř{QmV")A K}m5v 0كO'M+ ħ0ƗWKNvnRsчf͞μ0̙$d~3~n7.!ZL>Oݭ/ 9- C`PPq{oc(}CVb9l`Z΄ v0.;)t|<Ȍ[Ǚcg%2a^|ًXo4cx"yl]¨⧚Kx/5n乬#t4Yȡ@6y-I3F<5+…a!g+nfR{N~—G4'1bvL<>vS{Wwsͺ0f7fXG΄J0y_ NrәwH苿s]̐yp\eKQ]f6Ǻ=+^o`ukU ;YwPpy da]zPN4$gǍJ'`ɯCi7^Mb!P:*[ Sh1]MkӜ!PZ|! -4x,WWoR[ h5b2jlbĞǠ_, ·9-a)r55yC*=33yE\LX&'߾X.ŋϏO_0o#}'I%~4ͤx3J.U9}vWCPa*9<s2AH.0$}}tT&WڄPuŴj)96B۸", 'Ed5E`l ŚLsH !.yaGz Y#i2tjRJ@֤IIKD19oJSpigh!gj^E:f(⯯o[%Ӯ'꥙x5&~|MtwK`ܲgj$0>ZSO3v* J sN3JUt/zA4Io)^&E!# ?  3v*~tqe ,#մ@@Fڟ:-mdrsډ,[MmҐtW4: ?yP>Oi9V3Tk-RR ¯Zq)_mI&[ʈm NDtk*ᚂFno/t*طxp8b\7t5> F_pa:p}4Mg[ 44Cƺ:ْke?ܚCMmC1(r̆sВ,42I:yΔT&kEaibWJSﻧIEFTH=NۧPg\/iNOuϫEdtA\_fa,@ـV#Σ]-2mvt6Y啳m-oGe£!5%VBRD`YY@ +_A/ㇷdr-SβX/_tp`/M' ̑y{m8hg-jʒ'=c6~P;_I֌ގ,26=d fC0mz3D-jj&SuxAV(gYiA44G'gס2Hι~:b(C/az|gJsNZeCt{y +LjVꩣB:N]q_ߘ<Т580C(f{J#0`ܗVhxR!=\f}|3OR)rNF K;1lth; S7br>2 Mxy]|~6@' N} 'tw\v̶1͙!$=]R8ǯ bu:VPf/COonk-?Eg]h9rK6oL0 bhZv IJ* woBPR`{!O=Jt/vٱɞrtY% =eR{Y޳!f֓y}N m̻W oԑ$p?8 ~} m}Uޛ{ e Ϳ1"3Z±z~ŅݮfvFbE;}j<$YLI cN;mOm22uYmvHo:K]}*|r&vRh JqjBU0$MeT/@oX`{g6PP2Hp5H.6nƦNe}Ӑdv!k!* _I"f7$&5=*< {NdQp$#MY!?b֞u (w`6N0ڤk+O6:nJzH鴬գX_WX&/"T N^RzGbigRQ_!gw%Twhs\>=_NOPKyVSӏV1pip/_internal/network/utils.pyWnF}W ۀ$(v|FhKr(mErݥ%wfwqgDZV)M.4W• n-ja b~%dYD?k&01ww˛wo{4 QdFЙH&9J: Ю^],ÐOf a$Qkf4Y7E5r!d"0`CWEpo8 3XHax|r\קӣs|s6=;=ã(: G+B0B?BȈHI!6"PkU X }@.5CDu/3!@X&+QhbX!sC j dP?2UE1"0{V($)+C$3am,$l}-$@E&(ʰN^pI?BN.gbF8U0?CnZǞOB 8Ɠq}[S̉]A"KUYi2~wu~Gc' `Xeb4 A ipN< [}nY= `QT6#yjtp螓KȌ McaSh9ܘrCgTM eZ=вJD"_\H JQunInQ%BmpŘҨ9).}UfR?zӸȟ1ÃoI>ro V 6lUL kAQ;~v7ҏP#bF1wF^/UKV{R=lZs|nhS/vhJ5}tɳC x#M Af&ò; s H8+Ci~0X6TLXSV(i7coZTtr 5ڳ_ UMTNǏ,6_`Fθ M=FkߒÕ)ҞaNt?R/mfrݬ°~aϰkXR_=S/m7}ö2*ܲ*o_[`ôѴ,YIe[ooIJ(NxAv*7a#CDaU*q_ں1 VUTmikBJ15vmDMA DxgI"0'i4$Y)7-A=9s>Xู.ܤy֛PKyVS-{_pip/_internal/network/xmlrpc.py}o0WX&Rf+ic*B\[cZ!wЇ-9}}|.T)[JXnvȪVPKJi`,,)s8H}_Bi->*2DdoME"%_w-ikY3.bp*- ?P{kVL9FWx=XH~~uBfq>?א QRbp%+;c)ZT*IB B_T:ntpvmy%A8|a5y"@ܬMC {‘1>>#Rp8kHz?# b-/+X1 A<V-y2֣xS#JvwJPq: _C:tθ+&* ^fv7r/ oG5\ɒХrߙܸvgld`lhFcG8`Njs]@B#i:E6p?іU_?h6y`eVQUxBaМK%kY3eZ{Ntм2l dxiBLʼ H~W3+d*8eéSvdXMF{Rt?T1'Ƥd:N5thel]sTQ$)cd=@gSכis')1h`<vJ= D恍퓏\)v$|d>'p&3-&ԞG(E+_yȁTO[b jVؚP( .QU隼~dۘgYlNr>(;.Jkbf2 ȭtm@`ReL*mBtekdDyMזVQ0e-r M ";Y6EId&ߤ2#f> UxȽ^k U']c}J™ӪJӑv|T ]${i%/+ '3g%9\APh'/t-x뜺]!u䈔ڝ]H^Cc9:TH9-sh[$Y2nv*-q9J<$5b*Fr?'Wh Mh ݙ|7z,Tq#4{|8rՁ}(#]Vfe8<,oU+Dx4 Hݑ0߮r=K\8io rzpvW_K!I='CMzϿ&mCCEMDu֣mc/nvk* i ~MeyK|0P$lƌ'3 N cA{#E -TFL13j)L9C gD5 (]8чf|/~NTH%> 7FޟE`l22u{7*~Lk(!3K#i׷M~bz@EA!z3B29BKPKyVSľ ' *&"pip/_internal/operations/freeze.pymo۸OA |k^v݇,hNtEA/)Kn77\(b*y-Y]Y߹\.v:4J-k&2WKV1A7J o=+> h$ZM٤ .҆_(;-5˜VWՀonq{?ae $){ʙ;o= `VEBp1b-=~Emq1޳Z!_iT( xA) @e9cE_W":HÕTo~)~=C!-JVYհD YÚ1`u 4*&YW RyLk4˒bWTJ w<'Yk@a L/5Xkmu7| #;Woux `qÚl9ZILLlO(%b ͪ-X1lֽ/"N@dKڷm@!:/]7eu/ л _-ǚ$-bfEVx/Cyn52AB'MɼL;.4]o5=6@@1-8#T:h0 Ird[). Z ='|K `OB@?n%JD3Vڰ;E_dECV;F!j$>Nq4*z~Ցa|y̱T8a_dmP]BSfxMIZb1KXU{ytuJB S@$Ǧ+30%TDR_WdA|-)H|}:æ GA~?^'P ,+˲!dXj#}؎2ADDh;S:%Җv.di%-.i+gois%>wKӼu̸Wo4|_$tO [ˀ)UŠkG^t.)XECxVp=S ^0uBUa3m[NCAH@3x ?bn pNjg먟Hff Z D$_aSiM=p6qiG"ti"|]YV)5:gz/='s8"YC O|l/b2ti2C0g64F9V)(/qsR 3::!rjf: SHBMcf%)ƥ] -ЭĿ$zC\=W=Wox[d/~-&>d[<@no⎌nx@tMEpάaV h]GP10MZ?չ %%\t= `w>4778L.~y`͉& Jϖڗ$i4eauhXi I U0{ZRH% \ҍ^D.4 Ӓز''qmbۉ}"'8^P 3]H3{J[Q\dAa b-jBVz.eNZRnԹՅct*^TX=97VOJ*2PVB$ z{ #U$zXAp kct]IJ7.KšOCM {ϼaLZ\toƏ #']@R.UDIVz" ˶O`%l>`ÿk4bA&b氞1׵p[koM?p+Þs] _~LKSFgr$Fi ٸ,F.CS0(uQK:w X0&pX4аqFL`Gvv R0|~Gmwq A7^PKyVS:e]#pip/_internal/operations/prepare.pyfݭwU<4{oCk%:oVHxhޗq?t*0c{ަ_6- sYiuqQۦE"}WU>ho6]]f/`̼yE:B3.f Җm^ܫzty+7wp6nr#gUrpBǠwOX|bn&z 3o\ QmB3X{7}W}'ZZ| _z/} H|uMǏWC}MlCY;?7zyLeU T?;{9=LM^[K;kcH7[U*;;%~bíR#\3mPW{?3UTc̴ݻod})ݏhV `]jv)Fma^oIj:߲~oN w@فެw&5D9ߕGݫ=8NOfTNldB?'fg#;}OK"m%E+ۅ-"Jmj 0G;¹[iaOO;(aALͺ8\ʪF/YV&-VD 6P@,K A$C֨R7Ǯźi#pM3fz>f'e6=okNnP.d`8YŮ#xH&;=$`e&e> DBMx8aǪ2O7zs !9Z+f9Ԣnz7\ٽ,C_xMs`w#q*Ш"Ӫ-'g*D=kh|wNq6q/] taKo@kz!?}t6q-xeuY *5DZF"8Ŷ uZZk1g qS5kc5v;/v(BjSJyU膃?:s"-DnAn' Q4bEia|k֙8,I,21؍Ý'dKQJmou1X;ϜϒdKaYؒ7u)- rjC>&6 ; Mt` Y ԙ6FUoQ3I[5a{e/$7 x(V(t508pCxlc!JBbPMNtʰ[C+o0 ,r E!. L(;L|#ǃ|3ƠRl{ Tvkvt\X8x, 8jD`"zG$0% phf)ꉾN]5Xrdo>[&25}I 0o@4ߍC~SaTRIs$s3t$Q Yh3Llk5yes Ə|6Q1>xʽrK=tC"Eadl{=XpJ9Dv};9u+;w$DFwB,nf0hнRs历:O;d h`:irȡƕ1'(ǭy!-@7oq q;M] z6%4^ch26 [m0L-O=cr@CgԟYtClvKYKM_UScJы6t@2:V8kð NAq&>Fas 2'hp⸭VSp4#CG:mS)YY& xG-h &À m!w?<![Ղ?!v5B&ES?%=7da,-Cϊ٘ 4gr휉'#V{1mWi$OuX$2o0f*1]26HcMoZv`x)2&6'ꢇb s-U4g`B21Pn!,q`KiY!O~Aͣ$TvAymDf0}CD5e*Hz4FW$"?>q ;ɷ|<78V-D񿚲Bv…1!q,u@=iNߋ]"`hf6h_`59![ =[OĒJNޡjvԉJp3 QK\{ee57P2^DѬKV̈́\`.eC>~Sd0 :2UT: wHcd'7ha~ _;{8įį| Ȏ޻z0Qh}QYPLt%vk`׃4&ZI+{T`|dB_~Mn uoN#ɛ0YDNuº s3/ 1o\YhHh=2W}U\%v$B&N~4AŠ/Ԃc6$"t pqḸ:9(t>,?ێ 3{k~@8[Va L|dŸ7X=NDc\,嘑?F^ХEm{"+2.l@3&>W>47r&0V4;Mƛr-;I (B'hͧ@LU.h'dm قHMa/]s@uAj_B`ZJ4){sT*9In?Ce6169jժaUC>} O' P wY nSQLRA) kW/oU^{Q .зQJJ1ؠ/(h1;5y6ЛIe =K_Sw*X2x0"s#{D{ݸ!NaN̵4E.1a$10ݦ%)g٤m48vj5L$j?MUM6{vd{n)Q#M%f餺G-(}v5!tR5^Daa\>{aS_ta-Y;N.Ff4;1suo+]1z) ^ha?at$nf91*0 \Ft++yMNq=L5`Q/`F?BWӕR1e}~FOi3 `vj?Џq@m&*& =ZVE /#(&9xY+׷iZj@|Aq(!܋+"T K4 قD& DLE6mMc+p%jي 7^;-c?~8m8~4'!K;e3vGN TRիqHbN܇*rPJQDhQϳpleo +MP5JEFe9s9_qE: 2^ l'r^>lm޶Mb,t/s랣l靘RV+J9}믁ơ ;*J4.JkZ?/NU^C3mMgx7Wq>.#X5zUN8ꪌt(Kjk*xQwwCPMQ^vw>^s)o: G:ƹql-J [=*Ybkr3n{~7 =SaD+5Ҍ* O{<}gY)&Y)ߧ z6Lئ8c T /=641? ~~ Lg*i fӃ, nB IRWG=}ƴLF=cBLn&w15AX.Yq"wItV"s@$Thb cCR|t2FHS=_]2o`bQU2PQH#FI u`RarzB`FiW7p~űfP݅~˯ l0Ku `&`)<adӿ4shh/fQ :Vg 2el^R++&%MTG-4l ڽ{g_$L.673=IilbPn-5CRE!ޅ. O^{ICI:l`@Fhsc<=g{OIܙ2xSJ]=0 l G!2_y{y<|&w) 30 얏m,>ز,א#OLCRJSȹܸu}{u;r#WE40?.5)u,3W/aJbNoJHR7=1;ᘆԩ{de fj#(p,{z. |]OYv}No#dz#5d(RG:1fſD&`Y&'O".~eer9q5yTa^ݸs)Ρpݎ?}*F$EݓVC2l<yk:4_ILy`+pWՑb[GsD\'G^#Me]R +Tea'! X_A-gj}+H w^ sH2{^64 C2}{1-ΌJD*7y;7ȰVx&6ygv쵭SI̮ͣ1'%{~Y!v4AaΣST"wO=yyj@ GU jORq>34+Yѣ/ۑ`{xp''FD2Skt%&*WE+OfgφWTL܊g8OU^PKyVS 1pip/_internal/operations/build/metadata_legacy.pyUQk0~܇n 1JJQh%#Ieڼw  Ҁ+2Q%-4g[yCHr,fzc}4J7ӫqYV[ӑ^+jѲZA?1s\'e@sUJ#=7LsSAiNyѶW֚0:RHʧS_hZC{~9aB/He}GR:0 3؏Ơ'EG"d _X"n'G}F(Dkt+NGD- ϚA<)j &{yxlW$[IRぬ?/k/v7fG׏m.1k@1c tD= 2( g9M'd!0RX7j}H }'2jXʐe9?)-Ǿ1225 ބjSi،,sπ9:()vß4PsT9VdT3_4ŢB.WY%ՈL'bKYޮ]װ<+ >I-r-Q?*Eكי^lx(lK# PKyVSA}0pip/_internal/operations/build/wheel_editable.py}SMo0W"UJłEZ(E zE(rIֱtY!;H&gfɲCD"kpJc \pwsS0BIDG7iA3׃Sނ8LG֣3}O^܂T.)#`5vy㒅ᄠ 9Y2f|yN +VVr}$=ԩAْ\,_ȼ<]ټ PTR('Dv5Wk:8W `v5aI *h14Emh&)ymSݎl'Y)7\7}b\`FZ,84\eV>(.PKyVSȇe  .pip/_internal/operations/build/wheel_legacy.pyVMo8WpX@Ba{5Ihѽ K#-E $;)g޼74z!5am)o#^RFS&M; ^ȹ 5H\*F3{8H5n=/>gДLz-S~wwW((VU?8{)*PS&ID{W\͇]JƊ-6Bv.*u%Q("He-]KzE54!$i2{|F1~[<\1%[}t&7)= ֠*Kj7is?MC\cOˈ<.e 4E|)X係p0po*Majy/ľG{ q ϦՑCc=߀qTsgn=?-ypJd6gBR0h`DeH&/j*=F8>"WIIRS*pip/_internal/operations/install/legacy.pyW66 BI?hcH!s$=YRu.:\EqeRY'F8.8l&<8diXZz|6nY)l,lgjȍ.k# ێ́=ɎR90H^6eqգ4Z-!x*ԟ XK@WlqF{p|.UFxcVbe{y)oZ}n֍}8FV<@ ˅3ӐQ_;3E-Eڎ|?is" 57,s%Z4hG琣B6~Lbz~:D*v1.y%9 ڢMx;o{htDq_laQ~ci.23Ё*=Ng#++`08 qizt `"e:GME%0S:(rL"&o4 (g Ko&f[BPs;*o=lu&EJd@X; |rl kKjlX5_b5``g&"2aپ2)l#}J*$b8>~4SPNDKVɝa$&3\,td; 4`0]rX%M׎r;hDJhVO"=8!d3v&(uS&,C%?*M%theÕd-5p׮QՍ>y?κ%nz 9ܢ7Gv¶:\ vaabgTk][@r*Ge-^aG^.[?76{M, wڎp=yo6eѿ=iU iϬtZUF0{,lfaywN#jQm哯yMIOoo }̖܇s] r'6 ,6W^©SbJ[;M/{YcGeNb:rSԯV9pL06]X}"pP蒰ȵ5wagޒsOarm A!+햣A|-j6CaWJRnF: XO/BK nn>PKyVSX* k)pip/_internal/operations/install/wheel.py=ks6+IFYoj6ʞc+Yuɮb8CĈC2|H~ޭ5CFw/ AvUU֭ZʊM<+.TRjey?KK@-"JVWɅn 6jU^YY4MOVmli4+q`b~MT%Yk̎l-7I]fo]LKFuu$k/~Ujn7IG48Qiru[ycޮ.uÛu7z@{ *=s{?ѣb_%2Ij ?_mYY#뛳m%^Tȧ$_H/qUUg<߲ fm~I.UUQ|GͪΪ֒~>Kt2tmXmbYSYIQ*ɳt\$"t@H]iw«$u]SrxMKYǛu&i&| 2_gˮL@*zO~crSlr4ְfcX f''ů_==zQѳ9H}]kVzcq㿟΁j/'Z9IK$Xrx&kV=u7mZv\颁@z.RVIf0خ@q-UW׈`I+B7Y{1Ӷe17$mZ@ G2N:r_\]mk k,3$[[)̮,׾YJe]%=g4 I]h^̸+ͬ^0ղNc _/G`[i=H} ʳB-K3^&aL|OH.kudSzƴxף-E.ٞs>>۫<[R9s<~0AkډUf[m 﩯VҬ8C( gL(۾q6R(20f"|f}z6m^+F"@Nb㣼_ Y]+4HU=׋3cp7;']ojPg6uYMTˁ6I0@i$@N EVv^ހ>#-dVܚMjqT]Q͕[9 M Իlt!B!&$p# it^EuU4؁/. 7:;Gn:3 ᄷ|.4a:q#ɼae|tP- {CL5Ĺ{=@ .-oRlYRkR29Lo-rT_?V'[CwS M惯/E9˚(D~Ol2#- _ 6n4ڌ7M#b{1BC{dtĎV1 9`R fFQj,37CA@Ԃ/+ eW#4Y|RpѐIc2SDi7(Г=&kSB:=1bg *  r' d!&J M;"AX7ɖ5ȽQNN1-AXjGF%E?{1FM4ͫ.o&X fG6$iK?y`=7c8<m41>/-ўoHg -&ܒ֑zP%.DmR$WLځѬfs.gd`/52,CAsRGx iU "TeR"QBwJ9Ro?u7HNG#m xB]AZ!2G14pTh_zb2pڭ Q@ &U[ w$P9|5}"$ ]t9ö֪83aY_g~1XK،d-\VMf"6-R[D:X$Z#P!@)`7{5di.Afs"V+ E-ux'-hI Pnp'cgN-oE 4l۪Y^VaUYt9Wr5?VONX&(Y$2^7-} ? ` 7[k x^c~;ZQAfq:ŏj!+R!%55*t&mK4[E\^^` !W}_5q@nMkt4řd0|Oaiz[B{49)P;;쨇~6m c(L[ Sv<ŠVIB)4yFo/tjQM-s^Ldbv3=IDA iʌDO hBC|׊v§3z x"1f9Y+xgnO ;+?;013yPt󏪲 @Q(nO"ެ) u797ߣ* s/$9LqGAD@cBAfEC~=^,fCCMšsxe|&¼qSU /fK'MW7m2(72;a];G"6] \&(S%B92ꨶ83&Z `u?A3ȸ+&bɨ`- 7 (tB U]l&:`3tr0FՖ(ZkD#xLw u0G`G#W{=)5W]EێU%x9 ,2|֣L& < &U!m Р+J>7FMp$oʼn`ހ7!-j `.`3b.@ymt'yihi-:{vՒ!4>"@݅$  ҰdVF8Y?=2]g7Ypd0fl+^ؙ,tE<`f. L'[tՄ],Ʒz`YV(#P:4_Y,T%8fq\gu%]/ˤEVpi9'TYY]IXq0^g8$ݍSHEFhGWbSRl"BgiNfڌƨ1t)`15-+*P@P 󲼢e???}˳OYIDmzI#]u#M86&g?f9([i+M(6Nn~5m<Bz'#g~HKrIWeV2kzD'=z̙PBTAdh4m3S4v9En!t;ٔ -ohIAb <I1p 'kK}`zR(߀8{OE }fDH\ q4Ύ2pF#wW@Zk5)O(H$${`i$9+KFG3]]5"?E??@YGѹz~}\a!n NtwJ;c?U-ʡpḑۭByRz!Y!?(S.৔EB (+bbsNҚHmr2 [YI@$8\lb.O˜0嬍^,v>.u#W~10z"h ~ MWO=W G=Q?RNiÿ@yIsg'F@5Y>9']ؐiviQ!|t `>~nℑl[f`W*Pvmյ;OQH b2J] Ӈ8,JO &ڱ+f=6.nXN{arwvt 6T䳬9wڭS8:6IgL?00am'ug :#֗4.*Zg?*!ޤC-}6չ (hPI1Zx%XMFKԛRSde" ={/;Yi!C>0Mg$*c|wODpsKPY ȸA?@K[H"bcYf mYѸ& U>O1BNj+Ӫ;+$b &g,=hh9t9fԐ$d{y@TJi$d 1RJv@q;t"JEKMt6tÓ|VgRYP`1+&)זt ;>|If#ÆsDJ9vX#߶@^8I@Ӊ@vWWqN _PLoKM)F+J":K=KϏTF7 T"sR`t R"yu56Cr ͕Sɟ}dcHDEҖհ7k3E]u'##6Oo ! \+o61WY,>-oXyEzh.s`)W>x>z|jeap`uڈq/gc١uX65}\xU^Hg}z鸚L»z .Ls4ln )2RN5&|qeoP);~P|*#N. Rg2yhN &T n vٽ;џg1|VniNl͍Iٽj!E2r4@]M+QDܐVeKTAob j{Kj;0@e5(N&pNqiA܂g.S>b; IU :i2׺˴[KsK\( 6 ]R~Hӣ1GoH=3ue>ʷW%`6Dml")|5TY;n3 ><_:Zgp?.= $ ӑ@ ږT{![sqE6н,6}XB373C!nʨek'dq lpGh״s0` \MS֐iц4{#&զx4݊*t #^%@կ][>FDH&`") gJgppa;gpX7bg?lCNJ YްU8slx̒ *(-mRgN!Y$\4XnwYҩsWwD\. +~Ֆ*#4XG'G ɩ!s+m޾*؂ݲLtB32kJTBˍ<8t&`9Rཙ#41 9*;{(n\IOa|k@eg}*_sۑR6. dt*gkf@V+RSaxA\<tnT C*XgϜTaBouBy (LP$Mc9v|/w)HnP[cҦz$|k|tf=̀*>مkͧ&1ƎBfZuKT@gFa_;eq_ Z@6ykk%z!|jiDfՁ"&@.cBw 8Zpރ2JыRҐy5ne+2ZBz7-,y;gpZAdW̲~v0wI‚+]:SJG[&Ȱ8pϷB O|07&x:3E6Q.+=V(&X]Ϩ9qR+鱶ֻ`J rJz8C84RoAt >ȡ&p =BXb-@R:vib(_$;m_vM/Hj[õov,9)%& C-wUW#MO)aOi70ww$dL-ROe\8EN@X%C.]:"b_f'|<}GKt`{[Wӗ>\ q5*txa{ȟq돊b٧w[傌jƁw^$Ӹ(\1,ܯ6u 'AtM.rk02F?+*7 ? %fBfQl칹6jWoGr:s::0^͉j?-o3_ir4m7A}ՀiVo?O3?mK ڑƛp_q6t!#vh[{]qjKlgB;@>rEQQø_M"t/f\ʹ;>)|?5PKyVSecu˔;!pip/_internal/req/constructors.py]۶]5ULI3(돉g8Y@$$G AY@ iz3b]QgWEež.^T-o YlJ,ߊ_ECsSŮySȃb搵Qɻ`E.8ۉ~SJ g{fCuf*~%l/,@gʙ:ԵlZ7,yRCK[g%Wje 6`i-V4P墁h4 X#T+18l/xk 7t曢#V-7yIE OVl?5bi䞵H GU9{QdPx9g? |wQO NR +IE&AwJ6Xs nꚗEkΜsT-bS8$hGF]mS<4Ol7JlY.. Tؕ 9! e{PR%Gk<(vfUYEg@ADiacpJnRtq)  Y|h Q7ٮDBe"C_"͋Qu[Ѻ>Д5owi+S9$M4eKv9aS?I/xh~Mj-u&t$ph,NӊE& G p$i7\g$.T|lb<Ќ=FK~V "q4D&9N?__͉3;e%hذƄ?J>6POf`KL80F ĮkѴ%bSXT5zb̎J%!8K,!D;%nDJ#{HpC9r B)CDVGz ڝEs'+C3`ڿԳ -DM1Ѯm矯K?Oq}!+)׼\_WR5q rTA;\ɥ`+Dʲ@+3(P=~^oFQ{0*ά2AAHYE>:ηAMJItj.) z\wRkW:Tg_y  9Qk x`ώ0oتii#Wh%zgA 6`O@ ]VR ӟf>ayɡڣ1T ,h&V1P\aYc2D;O"Ə@>^"]i(UkzAM=:pfP 2DYT1@$^u / \lV,q(f [vPQ@{(n8bXꖋ] Jeql,`QT9}ШPPnD!V/" Z7I.\\E $Ѹ:| 8a?:» dD7P75>-J` ^7yol*4V&-̾ul[7BQC8ݵf;I<[PikrI{8uWoryVɰ}rhf]w|z cՑ$xŜ}_J SݪV4u6bB2AWb:lŒ]iQvs24$*L{J)U`޼|þ j ڈ?[Z?D5}7#! nKoT?aAd-^y%ZfJdB)xS! R}'=v4^iƝ8U7Շ)F%6T>LeA1g ŊI(<t%t\߅;5|<@<7}Xk{Q d'ĺVOduJ9!rWc< "hW`S51vZ771,xCP\"%(?uL wD瑮vn0#n _ikV,YZ/:4Zf0DFJm+6]mcxOb˿wyn# >p] cܸK #ws5D 1A$w,q7zJ> i*c-];`Dx#ͽa5ܓnNXs-ZGh )cwrx PݻoA=EW"ߢ}p=*wU ]>Öﻃ >?پǕ5XG^7-xt3P75ئHױ0[P;%"$ޛ*KH|BeM"`A@ر͛OVZk=An^:qѕ^_x13gͥF0~pEq](tSb'&L#‡pIQ連clʤ{mOxdc.3IeU ;ۄjA9+(GYyZFbS޽ V8 |ک]}O쏝 PKyVS' Dpip/_internal/req/req_file.pyUq-)` :?3Ku*ʢ݊]\HWV*MtۮjZGnpua/1ɯ+8ePCq1&ɔC/뢒Y 5Bzb8]\`딥O`3 P2E,R#Ym d6bɛ7Go#^;KmOA¶eu[Bm ؾnni3HG|z({]IUr@ܑ(@2bMH5A$X(=䑕G0OAă)9IXe}yB,$PH 'S'߉E  w˿ o_OOOޝNNϏOޞH]8Ri:uj6e-wyC)0@iE~lUnjn4 a(0<;\l#+f8vqUTZֻ%Rj5V |kFV5-^wRZ}Gѐ4P,Qf;.|w3"d!u g|ˣsMd )E&:K ]l&[9mr/Cl㣋dT@HM"m!PQ 8\Yv{b9V)6X G:HCP.85 g;@r0ء5~'x)S8<%\W̼(`/)ƺy_qB=]hx?'hI4΁{b 6 tbb$:(%͓-0<Ά!!ZBnu?궧azZUQ5pN:$~(ଁY?k]q< ơQax- r$a x%X?Z҂j,H&=|2#u#  \Vovf60>pqy.W+C"oğ)%o% $9!`SN"i鳀c2Nauv?ޟco@n'zy0#0Z)ntOH 3q78\Mg,y,zܩۀ",އiuسa~V=M{:)+"0/ MȾ[IY/ nnXMlӦpZhHq\BGg2hVv*oE4vzȺ0. ng4G!` qLlS>~6Z &6%/^"ԒDz]=Nղ5 tNz\[urM=urLbӟwbz\ѽ5ڒ':h2yu7G X7`T)@끢&#`ڼ%[}&TY - #Ӡ;e-wk:>Xm6LT3˛f o [%>[c& /R3 eϕ燗vG,4&oEp{\f0ܮ9 N;ŭJ[lWMǯ)YOԦT6o OlUrٙ %:T('v0_&SppGae{m-є04U{L IǓ-71*0n2U~cPu[|{5kS6Z\-]bm3w[=4x9 .DȴC_?)|EX:|A?z\N%EnZ>6Ў O;s_ڕ|tTy=S'y? xil^,T;nM c 5C]tK? B- 16|) _%ZM#7xAddFLp>pnԛ3$y4:ڱ(?D B&pyy2{6䃠#gw9(EApĄ˕;:KSSF6%]\بޭ `H4ZMcRJ߶T$_<wMt1t 妠KR:h4z2O6@KȌDeh&KٝLqpdc33wڸmړ#{e |X({l=[;{E t&Jϕ,#D9312/ 7|ς8 &vn($;\kyh 51TtV[tw GtrXH_/T+w*o4½nJMihVT'FVc{eF;koOΏfbmB|xŝ\ef?tevpp B# ]9 tGdS2zЖ[ E.>CammL&(t):w[0R(rVhs0IJda rxbkV"ksb0kOSq˘ |sUkDM7;oX>LS?938}`-MY: ş91OUr vɋ7:iZl6W:A\zӤ> ={=;Ź0XZ9E/k֩ ćCxK<<2tH18:_QSþBP,X`l#]NjTr,[?;iSͻ{%~x];P-ݹKȰl5` O@5/PKEtVS"J pip/_internal/req/req_install.py=k۶WXh8m6Lq7s=+BɒԮ<]{nf888oɛHVv^:Yhdشm\JTI ۝HIFlNNݡ;'ƺmmum?Bf9vPz .6_]וkݭ8Y._#XHc"y6؊E 9EZk/%m .+DS]^ _!u9e~kj6_zek:tbYjkq%Y7ZMr[*܉c-7j -DR o)ˮfm/1K=F/@ԢR.*DsZ4Wu6E?(LFm}ǚmeZ,k+m\{j dPd~G5LGSV2.gܣs%iYo~ "W'[XAcC׷M4k/B?-v\vW^ aW07j[%t9nL-D Fz$#̀؈nU5_A;r M9l g;r$#pGqv05ۖP 5l!fbpQN9SeqT~+h_J@K^Zİ1͆z~;(kyf5 f;ž~?m{P*J˺,ȅq_n-19QW0 Sqs'''>ɕ”FӳYA('''X%Zf=gG *iJ2$dv]{RWX)vy*|7kaE,ua,7 J Ts @=;'<Z$q:ӶgtN$p`b֣~,`wu#I˶I:ؖs@}U0ꕆ2 Lguj9H#r ?>Gqjf->SS3P$`39_\,1KCtiz%y\{6rps-YzCP £4n0v)t-4'X)׮)/RJ QV/;2 Zf,#dD2ue7{7-2`IXخ 6YHyoE)bVU25,4Dijdyr# ٔUP綱ו1caMo[Ve9Z-a552%_A#}tm/OlzDڼc6l %@.<`#%64itG+>9j‘POe l,ly^  BWRJ[I&]B4ncV֋r nDeAnwŁc^te/ 7`qa} XSG6 NbсfrG=YU]f[+%+ [):8 k]hCr؛T(xݠ + RnYH6֙TvŦ) JƺPpY,ɽCP\lvV(ُ4q\ gb\Onzk2{M9T\qxuԐl x%ٴۊy;Hj0.BQ&$)')zniޕHnW@8?7Y j˥(XGz@ƏR6a m k'\eѝݨ/@Dm- FCDy8]@uycGZ87bj_""V C%E2J I*8Te!4p'$޻\5y$ @8 }ڑ0fklyXrWx;\VǞ2AcP$#sRP%3sT9Q]m׵ۅMd `fYc]ɠQoG80L}⑌4"VWG"01<) ?- ѵPUbmf@ RxJ:4/D:>I~KDͰ' Dcf`gYɼNļ_QqG S䨏b\۲Q? ,u <P%I`X~d%]ޤՋioʠ\\\l?21j$_tY_FĆ<-$1 r[W?7aybHY=Za(}FbUcjqLihŽ+*3~?=V6;ۏ󪆄ն\[!+ #ʊ$`~$ʏq{m3{ PG"ش:J7;6b⇟FCRr83U:wJm:b %_I.4c, }y'c0@Hb?flQĚ2hք,yЩn9c =uJpچhs3Z n@/SS[ߘs =ؤ ?M۲Aͭ4&au(]^P* rHE"@ % ׍[pQzbi؀p6QbkPZ="HRp#[Ki7뛙C1%h`;O$/!SG1c77)v0B!<]$iK[73mLu G)2fBc2_A8Ύ2VeOt/?Y}RB5<ꌯl2QϪPS3qa9'Stb(7 |LQgYeGt%.)HZM>u`, ҝ$/xx3jxҞ@.S@~PHH:aͬIM\Q]Mц t_K/ XF w"32ÉWuB3׸̟ddf08'6ϓ)Xк%(I05 D9EQn>n;I٫'Y 3@m@3rLpi2 ˚ݶ\ o8 ^XvNu&$y6R: vY6VJ+m,`h ١6Kdd@5zզ\T*}cs!V=H wm~_L'!""J%J{ӯJ`֤붸cf<ۈR{_r<#|" Hq Zf%< $ e@%,EsM*L1wؕ>RV&ŐZ\ʀv-1MـjT ̹5Ȃ>(*LP3gP 9NcU(a+\cN5.23Ҭ-2"9PZZk# Xq}BК;ǔ d->ʵ\*,ŕ X+,(DpeNNDNw Nܜ6P Z%mVDhYxD9gpߥpc~P=vsPRuTk.y}Ⱥw ҌgؒEjşOM?ybmSAi K풢K<:svOW%8{ ]儙M`H PRΒ-(| GIK'#0]VE\_wv[i19󛟊:rMΆ,Jຏb~VpYdE EߤwnS+u?xZc8Yp$T@Tr4;*~Br*Nfh[/1ȜoeG?Ҵ.Gg0;m~}k5vdmxEDId f\c8tԬgdDY(:L^P(m -δuN)#_) សy{pLX;O;R5rou]5 05"p@3&JLK =(;OΜ sgLE" wƽ)*V6ɩO9x hC?1;ck -sLu|)DhvըS|_'OqS?P 0YLEw1f0Us+Ob5O8ZVZk1ESVaK0iZ>sVrrZP65/zjrr47lGn8Ix١fbr2r.9e&Z IB d°&ZR,]ݔ$ B1\/۲'kMCReu*ug, }i /Y&/i0bo\D^O[ۘ,uMqV*qS: چB=Ƒ*~2l h\d&ͣU~>dF_NbTc+5cj±4dS&P6xՔ&sWtELdIwN&׋&;K6򸈨i8_i5I,u$QOYՑE geEkX6Ȁ`9j1k)6~$1Kd7Gi>&/;:XUOJ߸z?t,l7L)~& կc!Q$甆d| y{lsezw NN;#+ j҄J ($2 ~g;3'8~-}aRmLIlNdY ]J8 j9X EY9g`{Ξ{ (Oʋjy)4>؍R=l+^ぅWDJ^跍>[!xқDgJyWx/_"jNնV\-~Q"{ JCw7Gu,0<޾ҹ=m`:tqZkM& %h:\fzc65ݵU9o;}WM9GȽXڙ2nA;stRr6W۠5%HyRGAe3[y!̄()PNi̼Gi4OL__3&R)P w?@eЃqB|cdjLn+"r%[ ۛ:CCI:"Е|TUUꆀiU׏Pw(ܞf :$nN\vnp;`cb@㨽!D4XzH,g*eɬAyͮu'b!tv1*@r~oq1gi L^SM֥NCjaxU^icyvBKy`+1Ș*1t)˼Dr?KYQar'z[D܅2ETɀCwVq.,n*|VuCG ;4g/O> nE#ih<ƛ+Hq1O7ս\^JR,>|8h}oKȮ/g pO\tɀZ˟@ z[S0@gzNr,%zUwwW/{0.- ~wZodhf8^GkQ`jXu7jsi Ke}zъz]$Q&]G -GqD*9 2ϝV@p &c/}<1xcwWsw?΢[B J?>)?)`>>>̐VuIu<9t*%%TFF3w5tP(*Kez:-2gVQ-Ghj-8 .z#$jDť ?A1ȵom^T#;.Nr$b; y!ҋИqh:^Ha.GِסcO]ZZ2l6Z3OG0 O V@a2 9NP=oI*ԥSo^)>u=d߱wDŽ>I7BPKi 1^.x3JR'BFq]&PoEuCw<8gw,P[-i>)a^&gG.{iā5tI$?kN y}a(3I(ኮ6" cV9$ͮ޽όţ M {yc;|}Vv간yd_Mǵ4֪lՖ0 砭|_׾&4fF-k?9X80F'y PC߅ Cu1 zPq°4K#p6rPnrJ>)OjhbI$9{*xY3 ݓ@Xoٓ0'L *hKOA; ZTorƖu&Tb#/hh;e(@" Q NB0/6t2,Q[Q Gs\Ѓ@M[!GѩF̻'a aB ($8`G,3 qr-BbL`a`0\hBao]r&ZYuU'Dc:p$μqG3KZe|mv-0*{Vѷ$8l}PnВ]TK%1H 7e }DyY;t]t2g2$:4Ǔe;:&2; Xlϵ-Xbzt 8\$? /R+2qAB{`&e!Tp۹Q""Yam'y~J~Nx \m E W^ݖf~~" ) %R^H=K9ruEw%9p&pu}!Lۢb?G:1:Fo#6 .#ln>Yك_6nt(vi<`i~  >\էoK1%_qB ݘwkE' a/"0F`*+{0Z6ч \-R x`FsQ1`ަ/&7%9cpr_ jɏ߇#ఄP"Kn7Kv<D vO^mLw& V8ApDHIcr/V0Tg"~@Up P0;,åNQqp ]=,94D\DnT6j0yfjDvևK ^N&'v:Nvu8$~m-.+Iȵ2WoٟD&_MPKyVS2 pip/_internal/req/req_tracker.pyWo6~_A(&= 2k!huid-6TI*[w~r@K;}wY*m˵rbJfvXFMlmC%,k^n;V+Z.aWNIߕZ2aH-a*JVi&ᚁTӅ(mZJuZ|SF|?e+~ZM)N'vUVHEa6#@a-2Fw]eDg=iuجkVWw">kN8~,rv3s{bqfm 3xt}FB6`>ݥ^EisԫO:{ E.|B2&-T~JkmA0%2ߦPx1Cp|,Ix>com~Dн#+H> hiT=*ZwE<; (ݡ:8ynfX㎉=$VW뗯^^G2KncDxۀv ^s+p-l?L|'Uq!M\|VmGNQ:E[u'scr Q+F)B/Y_̢%Pa 6 Gy NԜj̴0A!M6E`=ߡоj~[42x : so\J J罗cE'@ڣD3gw1kQ b KmMV>-@^d|ϡ܀!jq݈J+)/M|;Zpx.=Ͷ[jis2PKyVS\"pip/_internal/req/req_uninstall.pyH {޳(wm bߟ|i^?h]toM)g ~Zt?)Vf }x8eՍ }^U \ bDz7Cy$|o+yp۲M{YMw۬SYTS$Oqߛnm鳡Q5Z+)XҼmAg_L#utG qɳMA)Hq@U.٦4:MSA/jx~kNzzw ӧw%D;^J=u0TBVUM'.X,͋bMRV9sS~z&Rq1=7@yYX<_^>[K`ussCc )uTP%Yi 4"`ޯE^x`#|#CZ<1aIs1wΨ,xdB!@(@h-7;.D8$h/W4 !30苟_٪n//.|}tutۋk!6]m Y,/Ee>Ԓ໥ P)j͝"r4`w2*s3TU`{@#c21wFMk*GXhTNF8^u0))j~!#,   11K/fbe#n;-RՇi5^$hAڟ+%LΝU)㣐OA`oCޛeAgFyy":A<)& 怺}Pbhx.t8uڣҘ%dLfXorOQlS__.Y*0EHH4$v(Y>؍ ('=DYFuȕ ؕu  /SZB턼ʆI[/ |~fCw XU QŦ_QBȅ:m)h"xnr# \0N*Y;Uw%h%Sn`ޣaC;K: /=h>A/JӰ6 =Oղ3_:dX(o>T2?u*nHw -E-D۵:(#] 뽔Wn%/k鳵[ [McK=` r0v~)Z+\ SSc`~ 9C,d=GiT17=❑yS$L~4SOg= *+xͤ.]DA.sW~sEL%Ox*nzcGX[ y\VI = v;z0C^t8} N `$g>F> jà6a o?8H'I_|(*McBY| "( zH:g(rɋE.yf#;p!iF/pAQ9{A%rH4w3FsO 4M!lCh?U) {yra3fl5ᶄXф*'NPfzw5M BVWJ ks&@EPwظ/+P#**I8z]@Mr[#[=+k-g:Рg}R*h6[YhW%_aZ0B+澐9jsRn~" ;päJ"ܱ|wprp<<}<סl4t,\n=%6>TM.'m^bQˬG D!VAگ0WQ/ 4cyͿ~I2m̥ ohqc+/Zɳ`9;\T.APm*rCk 1#['5mIs/Kle(U/2R#ĝ \JP5ځ#\4[A攃q9^J#w Ekp{u YegbOIj{%T|W`N^LQFIyDt /?[J] (/ i]E4; PκhZ! #Q?ά6`6B( FՆL vr&6èB 8\ɂ RȎ/v iփy9=kU@`֨@~́l91L)\*`@ЃهaG@A /=33hy Vyr/0b+/;|M f&źiKJ|Y[ۊb0ǩ_$Lt2H\JE!(]YWam'8)Pm̴ө^4eUl:^/f B_!0%_TȒzs[-!j)2$-|BUdx KvRܼ D0gW# knFV@;bo1Az<:w| s'soG-ˁRڸ1gŨ7U"ɌU+Mu\u43{P-Ѣ! BC\~4zb$mR஄ V" ?Ǎ&e5uM5CށSO0H:[C~j$*P|l21Ch_G8*щ(,3c .H&҃<Ș!4Jxlj#"smTz8Mvdt6G9l醞UNU4zԠJۨE|@n. ~a b]9LyC16qߋrR8tmOFQ)RyHו}; >d =G3ͮU;1_'yf)h ޤ +~@>ߪmd!`!Pd 25BԾa ogKVwFAhhX e(V/hv<s'@>&"4礢җ'I*i7!/+G1"<7 O5Q[p!4V:7卶$៹Q?285)ϕ ԯ$؃A|p"$RH/I ʏ5kё%p0 PEHWrD3 &"w?)k1i9U̷8DU'8wLõ~,9ZtwwTHup6mM_Ev'`GîKgiG5`Ja#cim!Лr#¸`J6 .ۏϲQ` PBf:5k|7ط"Թ- c`x?#c*h+Tuͽ;;=p\%;<rYv01֖x X寷~3?BѨj6JGfZwA=Q_݉h4Oc :[XYdtq8If!ejPGKvYXmy;xT!;sgt3NhI-0X,)b/8#Bp?Fsx5FGMW=՞hq.XgMTb.5({L*5pw_)bqs֐N_;~LTƝ6a> [6Hx_˩ Cc Mр}B0!GG ?ht&Q!?I.0v'jt{y7sZ!FMr wt'AmErI&8N9.8K"ٳXy9&ȅv\y?Es0NŎjy<r;??_&pD?RǴ=vtj=^%Vr)\vb#zھ6(hTx{j]h  >k;/D%&÷jZ= =ru|Oc(04 VGqz7?֏V#+ u+>_e1y6\?RHeOZI2b.٠u^!ᙍn@IF}= YS]`gCĜ /˼?fI!֤Rt%ghP>wV+2oP{qui)"6_Lwo?Z BԟM: ҦUM/06[>)s:|G,M뽀'+e^Aie=rTIpo-f7 +3h\s ǥ.$_,9!7 ljĵW{p/ G,xr:3"0Ŕ=x5@Of]0xU2 | l*WMs>z:{0{M;|˽bL+^(&?59b|H˯>kv. TrgۮٷWI6[ s\qUAW][V\#0'ݗI;NNi71_6*>=)3ȗѰj4TK j򼚘r#-AI r30i[@Ϻˁ5`-:*RBNC$%0wywW\; iޮ΁=(7 ̶j_^Q">wkrH޾[%oۼI* !绖>eA :m"5M)(U?:a ˉrcExMq[e$)d} zhAUmWvJ/;impO"_;ą=o?B%våt@1a`C3:. itVI4pxIUm>W;~s^_c6SW#KM@*A  X'z}% m4|>_ /iOȸk_˯nl$aA@_ޞue[ҵ}:ub,0pݾoM<|uU v>Ov>`8&8xЁQac9UcV3ZxM砣0 `.ĪDŽP.٠0PqD ,PKyVS$pip/_internal/resolution/__init__.pyPKyVS˩G pip/_internal/resolution/base.pyPAj0{L 顥@iKr4(:&lciifgfj@?k@h{ <;^:j7>Vk9b+Sw" yfntv YPG՘UȰfd63fZN8 wd Uvx$I0L@:ӥl; ҺjWJf6]ϪpMWuQ}ל`ӪX)|9]488rY"f^R"2(W_zI۠&pi_-lGIZ×iҳE$|90D33dA> 8el6r&BF#l&E'FƏp?F_3`ܖN#=6ꪑ&Tꯀ$K٨K] -@,/)$+n6<Ɂ%Do$UB;X=Ȭ& hXRoZAfCF@~ HЍt"K@ϊPC R]6#bԶx c -iv쵬u91FXdԿm!)6Rfl[Xrl/nZp,eo9BT8\μ!Rsa!G<iĆ=Wg+(BPZhN@Mr-gwʼn 2>9 /hZSb7g~KQcrRL~;ROR 𼛇 bi&kvnd?u_Md _3xq$1IgE!]r#2O!Y-XVl w3'̣ 1$Հi„z 7YV%7hAa/:<0$1{]1%>雵 _繣ޱD hMQ&r͘d}t̽zb *[qz9{:pF'ʔWƓaG+p㥰 GK##p+˲{74bu w 2>KHQa#N5;qt>nr?.d"bS@ѓhwh`IOFAOa-eB!Ѯ`pzBaIir3iۉ^$Lc@D_eQ7E5JJb l&X$뚆R)z-Zlt91@N\iH Own$H%# t Z-z'M"3:FDv'g2:\W (j\5?Xhİ(ND.0VY~-s.0zbA ;ր#DNy(RѰj!F, E l1<d#d9yW*l#N/M?vLAr"vRPbGG0~n֙z,Β̽gz{Q%}tF5amՊdv)qˌ@*>zfk A ģ%IĐxxiKco$QFy mvbid^υDOj1^KW4p_VE=.+u`%qp4)Ov3vީItӅs? aSCI:1`X T'B[@]:!|% pٶl/S.QBp֫q\YBl%ӭwF_`{Jth)Bv`jH .Hm7PȄ) oz2 HCmJgky/8 _j4ᰜL O Iq4BįGcT#^XbdK7, E#GVJ]vCbG1l3\QW_SjRc d%hkRN;%.~zo[=IC0G1迅*pRXHH֏{bp_4Bwjģ 2å_W5Duذ; )>*L!hfgx{g':䡳ACRB&񜜀ͺ-1WC+&5Fyub. !%\Wv#m(+s/4ѕ00x9ICVCgMUEK@;ͤqR, h(bD@NTo"f^D>&*mΌ3STnHlN$r A;(Bp9*Bviwx[`ZM 1 { l'^Ӕi+~c6h\,S @]S8h˜BtxŊx7~\xc\݀%@(ԯ ]ys^ 9H~>:NhnLFuXhhamɒ~Z )?ׯ$V1@!ۮف^$Fܼv16.faM(te8U.zڇ5 nJ";glc<.ǓNOrS&G}I*Ο5j}buu o5B0ֈI-?畢9|L.c(L3|B\b $VVHLRn&ڛ$Gk=2K&&~Ⱥv8RH M ^Di8z֥ 4u<tA,HAdcǼtB~hƨ_5( dkK{e>ʹ)4KxE`Ӽ92+E@d19͝R GÎ 2ur78VEeh1@|`x0Щ^[”. PtA؏K5;ZX;BzZS߯5h:?_?s!-wj+NPez_$)Dr r%!4Ho\OX$O038a>9A&sD{𝥲:gPԷāo4|]cTwl7qCؿ`}*2"vXhlS&4p~xNx"1MSo"hKMQmA(U`oLvݎiX }ŊK >g'E 'ةnLa{qFAl8ty :*k3$Y1Y(=4(Yf*}+׼8JA$چG'hGc5HǕ0jFXF)(xEDa S2Z t#5Tݏ(r7O2Tq08n遘uhٿPKyVS/pip/_internal/resolution/resolvelib/__init__.pyPKyVS"Hd+pip/_internal/resolution/resolvelib/base.pyXn6 T`M` C鐦Ð*f# I%s.?DQ%aAؔ9Rj#l ɹ߀ ޶Of6'7}gp$XWk!V5 <V1^6] -`rYeJK<=btsu ws?U0%cFЪe>X眘UCi \2`pAiڶ K eNO- ZrF~\'XCívHнˁ݌)lOLZ,ܦ,X}H;'i_n Z  ^{o+܀^:NoU6t,?ݣkfj>5FegGšP8L蜘O;HRCcb GW{̎Tf.3teߺ)yv}N.B(C|߃,fSge=;pc`9zm*$&e60^Af䇼VQ%WB_l1;uKx\žcaO#ZqΑY2/Eo:co^L$u#*L]uylUCO;r LN@[a {\|$UJzqG`\?2Wi.d!Es݄aIXN(@POtLDSi*$ږ:>g$zkfS2D^H+XۺDH#n֢^&]dJUf+j-pMP6Ů.$,'y\ٍa=FsՁ۠#? ezj IH1V) wU$k4%׶QXVYV?QfZi3*e^^NrUldTknkhfǓɳ'8~=.{To#v&(x^m^iM KE*ډ\u\2NW)oڼzUN=OgU/))ikXS./0 N bu4U66=.0 -/!"k5N6;=8x!"/sİE$M~c 7+ =24?lmA :ҢH`T-p7]ӱp9__H _DsJ:O)z䓴r8QzĜu#DTIzFKjWx*N&h,asm5%o0!I2LP '0LV ̸@xpׁ6O&ug wY &oW]qv=9f*8 N*x__JN~<}yrG/SҰk|d7jCd|R$ ch>L_'qԹ< NfS3mՒycGEE #5F7=2CznSiK RrH4 o7M̐*uzœ!᧙r-_Ѯ%C\k @L7Z~3⡗䮁⽡(Ii IB۠ 6 C"6u]Xm.VPRӈ hE V,ʷ|ZjPcp%{O͛X pPk03h,58pZަ 4.8bTPZ^^MUa%`mSؒ1Cx cڎeħ1w[W i\,T_[.MAuTu+e.xg 0@/:mҵ!jllOBZ)r NEKd]ƤSL:)-9X7to dlʜ^~oبQ3vDsM{[LK^(iP %lR*B0XŊmZ';BTN}{|oV0A'DzezXw.40hGt|f~zc>e 96N8b:tapA~ΖZPy4x8uO𵗵BL&26;KXpKNG4:":Ib]}R#G6u+E*\-WʑS"I/bj3ê)Ѿ4TՋ8nБR-7!}a-1=# ?YZ*GHUR Q;^7a[ae9qX`_! R&fu׬/)l=;G=v}d\2G}d/j^c`t@U%eX- 8M^db05cß=;﹃D߹{FM"[}ZԻqIE;;r'-}4\ə]u+ѭ^̀}~nz *Ȉfc WZA-Zl)TLX&x˼Knv13 oQ* ӱc 9%M}٤"=ݥ#D@]4ZƂaU(v;Sy3#. \eKunx Tj|w`@() L=evԄw%|Xem>oSb1|b#'|l}E MȚB 4S΀fKE;KN{SG=U0-oq)Tg9M<`[2 *[h 1ܭQ2*b*̈Bj+UuFnjdlyزt7q쟴_LlK9tx[-7@ÉUzM{Qiea^g&q` >=7S9EKUO;UQT7>݂N1Z6e3%!޸k"aBo Mjfx1[ebŔ G&Mzt֒/nR6qDnxQhrA8YWv##C/UŌ|ɱ_B8Ff %E6zQkqH]ˇY v)}8!2e@ -O\+ V\qaY!bEZ IMam-("fϤᴫeL+ġ!5iP+x%+.2xYk+IwHyp gOAlJ3Ӵ3绳: CzW+*L^$?hI,mNpݹ[(rK[S'Bij~)AִO鈶M+[ l[B7z^e2;Azos4\ s(VZh)Z9f^5-B_w;61auq')utehc{^lYr T+!怅qJ|B8 dke>{wRcyiC·'`J>lK1 `}bm| BÏƟ| yygwpuvCurjy]#Ԩ?U;F^|u闾ߍMU]<_*5G]3]+{ޭU]D( |˻Q蕾Y` vn ؛iKa s@뻦[ebH}{a5In7z[u79qyK7R5>W| 9474~|ﻕNUV0wo솾B8jU8Rxϝpnz7} =Rp ik˻of嶯+|,l$V-EC%K9jߎ%oE6ݝ}dA%;N\T)#$Q@ ܪq؃ȝ.yzYu} VoAJw In)d JO0`=N~ f !ow@mC9V>ohĆFz X,9Q;.bCRo?ԯH%ϕ f^}H݂RmU;juPWv zzjFD0n8jBa%J\oYۊۊ52^GF́@ou SpߵM݌$Fq)=ڬ}ӿWn}{&Ųm^j/xG$ 9\L/ G%# ЫMKe xg:Nn=05ӈ ?Ki[GE-ݩ7W גaa@D[#iFM Jf,Jv=7W-|g+?,R*Vwt´o*Xzv(,$f7:k臚ô qsA|6K;p.(iǯH_Eq} &˅/jX9W8o`ʃHKˍ. zý0n";kg7]i0h!߫E]}| %ŐVs\)JB1l:b:#8όş&!5)Y׏z76 :C'F&"H`Azq)m` |SYj'C]?!(|<FHrYcbP6k؍gθR9 Mm()/SbX%i[7;v\40"-j@B<|6;:VܢYX#h 3),AxtqՀ9L1sHL2:3tK@Ҭ ήķXjެr>,G78ז)+RӀ 7+'QutXpdP<&|vgmGE)mTS*枹QjI#YҜ$5tT=I15pBK˝mǙUSXk2Q2Φt)wŃ-=_̟Jn>3[gh/tX3R n;дmu Xo Uȼm~~#>Ա(7Բ Z\rZsxƬ-D]Eyx, }dȽNy}zӀpN̓z!{r+ӣ!n6͢JRU%h?=([y꾮g21_+.5GሷdL󩼙bVEFϓy0G,̴Nn9օī;fT^IIJ{50Б4T  ͊01v.&i=)3`ZkŜmK3ƞ6d+P1+7|쁳_,כA` pI"U}0ӓ+-@ܻ޷큠_x=2*ggu? xm#@Fa܂buU`[,yFlq z(~ *5] ψ!*Mv`Re&6]Q*K䧝[eYG>kXIwVÅx/|fP)K`ArPur"BGЎ!pmLra X}%WvC既 Hxbk2ՓH%BNJT]#14oWk605p 퀪vv7G DX^{>{:|ݥSHYď2ZY@KOd+vkP8׌yUnH{K'v:yҴ hE!rkNKסrJ:-ӿ4VWt xt5nk$2a]>C>s4ZRTuw gmzXmv$&fY?>x:p[^Lh(Ԧrc#>B'/r|йh '"`cbPdWCfPCHFk}Nr9gBJ㬁 ꩦ^<]5Mզi54ᓥA4=& m'6>Q[*rՎ4̡MЫqZ8L0(p>"CH+QS &IHMgbwלi|v}Dyڦ6o(3.ʡ@숻xplZBT\дA;\^x(XNov9\ {ھ'5^7^9$(bȾʀ=Q/PDbTS=oZ]SǺ்2V~ZA{pLl:smȝ>GfKj:В ]v.*oZ"pF? TCmD#mh2bH@݅ۖ'JY;kpq蕥 y5 1naO,XbA d=[ނ=4Cs@I&=cѓҨv1d+iT:<>5H/b_h+{zsԣȦ˛wV*CuR( wFi 9IPkձ{}~E3=R` [54jۥ'"ӝ^cRiZLhaյ'p\ٽ ;S))0T1w%ZT+Gd68B%ȼ۵簫e:ljR0C04ZK/,mw4=lD:I<><?A\:)˹㪂 SxK*o ްc);g_cL*UBlW鍌-')h_*^"XEs`zc+cUY7&?k%;wI| ,hMdNC?pس\cN0 DT`2a[[L(>ʠH+C熬Ri8iC-}=Yc g|0Cp1)8H>T-ԈWΨS5w`m ضCtķdo:3Ds1w,XF+@ @<V(hR=P<N;_UD)-MF=u$dDk`HŐډ>1uF;] gRk&(H; T ?Y# ؕ@FcFxs\& /s^ΉfȜ.NGu5%>LV fa2+Еy1{pk` ";LIr,:,R;H>Ӥ "#1F갗$mzNj&|ɧ(tPJS{׾]Q*d:ӽ$XT&]AY]؟kPtŦS8C}[եS(Y5,S)$U)d$`-W)\+VK=G_iMׯy!6I7F<@hqs"<%8-1Ðg*4C8GDp0,ud%!y8ᜃr=CHtLX?3D9v~9{~pz aKslbݍsBxUKIuOe4ux> FMć;F~񻾿c=ZePpJ>M)s ?* =, y[*t<ձɁůqQ}r.[ Vz)/'4UMLе}'7:z`fdÕM4{tÖU# a#MbSbV{Q "M&f;`$ցY{_J6͠ ? 9űhDcLkq=><8Uvwƅ, `D"dߛd7SY-^,#6Ywaz/; ,U!y=(ͷkAkՉf1Ӯ7Œ9UH_[m#ܼ|<bRrӯ93{Fo:V:Q7FsQRhl#uKݶM2H!uZP|&u ,(E hnX5:hA]Pc"%')b)0oa^JJbYX4!k ;f}@RRn\Ǐ/;wĘ=Zn0N-\&6 =y?~بI]~9;G̩3BZWj}̢@z,:fuJݸhYsk3"Y8b_gPu  ǡz(1:uK'XH`UEUTR1ˉq0teާ2+L~Hs.mCX$dC7/'^_쟂].'}4CXKrȫ/ݸfLٝ=ZkjvdV7bYjO:h0xG9dG[e9ȼeg"4*쉐[zJn:Y)O\TJv>+Ťӛ)9.oGEk_͏ Y 3iJu*Jc1ϫO%YYL}֋%t Sv^'N9#\{tf%HĂSTڹ5gD c^VX餽œ Եai朚V%zhFL ͟<]zUxE>f$KMGnjo\Kŗ [b4s?٧dyr~d^vGiDKm]+sY6a:m ù>0Q"(SL9CQOIU6Xy'1`>,@])i}-Xƕ+O3XES_tSCbai7/vG?vS>CuJR-Rq4Y )0zʩ9 g.-5 ow3RzaVu9O.2םm}%5kp3 ;Xߟ∌i1r u B0A=.ufвT6DORpL8i?: _&:2un!#z {5mߺ,yʏnqz-, K_@jիLJGoC Ůo@$8Z9gQvkb(}(薰{0a0یN,^saWMw/W/re LpPKyVS)ZI7pip/_internal/resolution/resolvelib/found_candidates.pyXMoFW,C@! MZhnhZ[RR.?%ѩH]$͌&oN*$YPGv"3:i҉ e3+ ]yEg,#˛^ElKfLd:(wk*;,OYvH dErSiDQZ!L+E*ԯWM3]6]mr۳0\tsf..*V9+EQPX*NTUݦ7lgk|sO&{8kW8j?kCEeN$\x5yu \.T1vq ڈt=(HY O<Lo*y[ \\t@N+Wm9WK$jJi8*xg:`ٜ_ht( e(-@pƥn%;HL^Yr;̅ߋ9U w*Um7g?Ҟ frW63rc_,/[+a_Ze"7l2 bٜEQS! M J.:_j9O{>!27O;1{} NdKE\[TiP(U|ZdP^Ͼd{pLC I%-%);x^pfYrY nrb2[BBu\ipwYSw Rڛ[m;I*&iO!.;IeaQ>ZT,T#6ѵ?l>ǜ;KnuucҜbC tmҫI 1d!u G쁕}FhVVc A<`1׈UAk5pـv"ZRa6%ġXn㙟smЂ0F뛵/-\+48 *tYD=hyh us(hǼ*OMU ЕyZ7a tG nCOyɠB[bq!NOZ#Ʒ?NٗDD/ {O ZB !OCr?QeӃp1veTFo%(c t~L5da su6I6)6Z3qmw5BmZ9 Nn͆}02IX˫AL2s_y 3p놝NȄ #5^ep?"]L$ZRżvC.iZhy{,=uk:D2_aK*j79Em (dQ2[pbyOd,فޱ^hmoQ}1dƶ-`B-vlXݶ KX}6hjDֵ x(tD[FT1-^ ,aSI%Н$Sd6%O>!+!{tYo7ǜn&R do3߆Y:^o7 ~NmÈwн9̟OYcz.#l,"~+S'Y ʏtb6S0N}4/}rYZѿPKyVSU- #/pip/_internal/resolution/resolvelib/provider.pyYr}W`x"M9qOqx<$Bj`:wm$Jr/H`/ko}t֪ĺ6#ymڳzǟoܾߦ+n*?JOw{SAk&/^E]*kaT**^e)ݬL/|ōμu)Kނ{T`,؋?~{?w_~x_ـҧh$7)#ߤڽ4x w#p- tWlqSxLhݿ${/qHTF1 Ee3;ٰhBwϔ3=uō&ñLjqVܺ% Wxy:g829B0*bb%G6'>6? Y^/(:1-IJH*fM̭TL)/AKmGECGԏ\l^fjo҈q-F֢dҹsluhG0Γ6WĮs(ES%ٽf ׭\*(XmY Pbi\JSxl(iID VE)F!9}Y0VdL'=yk8YaZ5F ÒNqiz'>4j̐.L))#\ Le^F GI>ٽl<'`CeL4I4K ,ۯ` 3=IY`lߊ.W\J,5HcV=ՍJ$j;1.Yf8o˔ԡj^m4!Fg?ĺC8\P| (";Z +P̡i*HV/Ki{/D޹yϊBֲ-q#ʹfu&'|;s8{+pܘD.*0 &lQ{WL^tYn+68wA tpo.  o$K5 _l*gRsxĩq-je@>oNcq .. ;w.,#f]_S)7=.3p-:_ Z/zEaۋEx;;n@ڥ{OETVHy/ :@~vё,؞ V3CxEl%N@$T 8QїLS4zqoZCjuhPޢҽ=;e2JH-tD,#lJ)pS>k3$֏AlDYl@uOZLJ A^M؊iQ[LĀzbX.!;tQ)3ėV垾fd@۾\Wni#icA'yŷjt-?a/4F %Dch/e-.a#\~LDUZV.1l5uiunJV=M8ϕR/XTZRWCGSVPG5)Ty-`lƟzF`g:yw&yMG !kL=DV=}\Eg琷~[DS6%CU4m"LUcBg աEB@ImኌtvGwK:`OQ|iJ t#^Sl%od3y}hg埝#8M.8qH3YHCgӄ!#,"_>Zr;J<CDﺁOj;ƫ,.9Hb]7p]݉hQ̢)vąO>@+ʶS*YZSliJLHbk$J76j@ܼH{hZ$v&0P0^<̑nG2`F,`kMcKj8vSiuIVsqBnS0&Gk*E<6N6wD#aN c^wsH-bَ;ND{q"W!y0=csçj:%Ǩz F#! ӭ2mSM$q+Hv¸%E+hGȍ*1ВU؂c݉~6ឳTJP3ȼX0硶K+Ւ"23Y\݃fIp-%G3D G䜹RMmC$SVƭevJ#M1x00Ki+8$49 >=Zs{ϟQw{N5`y4l^¢"tpp?0gxV>1l!zrMxjvndDGSҊҜ}y芨[DHIJ2_ `ILٺgn5': _y,Jf%V0eHN ŊV㊓5qMY֤GXDv!,s4;^_[ؽԵ}FI7q ab3_{D6iE%.ݶlL J=5,8~i7𿌒kTsruu66hH%hmND5Mnu7ӯNk* M-hNDS52_ :O8-TJk i1nY5ְ "md C%D;\zƜW2C713b^h&FPKyVS\O3pip/_internal/resolution/resolvelib/requirements.pyWmo6_j_$LV`(>@K E)$5{d,*0` $pΒe/7󐕖=Ha!X#Hp^]$wB'd>՝JOf5M=rhj3ZV5HqWϐ1LF?9ѕcsBW:WġiOnOoe. e%I!ԁ8נSw2jzQ9=(c3*B 決YU?UrΕg0]a#u0D™1 >a4_JFLDi0@ |ђ*C Bv=a5z})aCQi{ME2'z2axw!YUt+'< Ԃ71-&I`AIo@YsX +IYI )ќ81gz]5ÑLPbٚkl@4Hh8߼bW<g &[iSc*AW:+!tAt$YA8DLnw Ge13 .W6><'Dݰ9Voy$EQj||SfML3Y#P)uZw0}c{6Qs[u|8)D113(7 1*ң^],D&x{axr^m$|*fL?Sd9_#n$Y%4!?CX%b߭O153 xAf ρ'*{y ̢4MPo)]i1x%t~ߓu`q틠vr;Z:Ecɑ ^ ol;ڨF?:v _##٠+ݏґ?u]̬Lkje ]gM2>EidXoOdm#a#e2z)Yx=މ)Aߋ:s̾PKyVSWc l%/pip/_internal/resolution/resolvelib/resolver.pyZm6_:8jڏƩ@Isb\q[(DۄeQ!Df`/3 )c'&۾qЈݎ;(l+őSĵ꯯~MF^Jg-Wﯝ梥MF1x|w HEYI򁵵yG^FymE+Gv>S2%*v+UnCRnm$T۷驙Ҳ+.YYFnX&hg~o{ƚ+lI m>;3rϼ?d1I*$d.?摵$H Ai4~} <5Y1Xݖoo/7R< VDN܌H7/d1#Qئ7a4 KhWyM5șy-P'?'8mkSu_ `7z=|!P]l)<;ߚEiB,a㳪J a;H2gy9\iŷآJWmI ,X͆'r q uzc#ri{=#=0W% TzJq| ʚuoV` Cܺ/Nz/騭rb}fq5b#0Dm@ erG`y~ENu0|prT1T[p30Fo0^J mpZv5](XGCW]B q/)YTv n5FyԌ\Sd2@d38qOPh ?pʄTm+#L`&{8[޲:_P35Kʥ$s#ߟv4,X!@ XǶ˺' ݲ|A\|#_ܗQM-.ZkNDKX;~=v:kTzN,ŤښB;Ȏ?DĝG*-i]:*0fQLĥl⿘؈QN,egջ؝ۘ ONc87 d; ^*f0\DcR2 n܊1asL=k:a쎿F" :y 06ٜ1^}g` ײ^5F[k nDN55MDv:&l@DS0cQ܍G`kTn8y' ߘrx1FkLO6̊%а8 HeOӁyft@6u7d~ȋrD*=ks8d3k>`,fϝdQVp EE;-ɟwɻe\#*cIJ #7񁝊}g9m`){Y$]V`2O923O2s61$sƸk2^, 7xcn d0R| jOP7Td/K.l ?LbHOX9;f BDmG$8 `l8𳃺 jb /93{"ʒs(Q0av~ɂ,w԰t3Lz/EBc~eb "j|y3duqZ_%Ʌ sE_nU8[[鶛@Y8'(V3kk 6Ȧ3^ϵ!s{+A;{yUV6: bDKCw qZ0 Cgǁx}&1x;h̵@ 78ڄN|-w3hjn{)2v9бI9`{ " HzPs8tb6'kHߌu{rQC$CYzvxݿ !vJ0TbjK7`Ugy22b:]pDet>#C֭HK. N$x=wqhɾ|ˮ^M jE؋z ڵ+:O%ذ?A~^.3KTg nFDU#UTiNKQ/}N0V9q +KNÏ/Tˏs>~bETYwPwM츱ԁj83X22J+5^Q?]㽍b۾1D EŜc(@ݽ_&}KPKyVSpip/_internal/utils/__init__.pyPKyVSJx9?pip/_internal/utils/_log.pymN0~QT%\*J ZiLSk;Pyqh?e`'m+TB +ܣ6xŜs^lCw gL(Q0/#%!k)  t$etڅG b&e w6nb=t0N%iX؏@;].G-;t 73{[<'\]3Ƣ o1: 5ZYru ws0fXˀBL%DLI:'$[:ۮ:ʶMkWьt,` 6Zaˠ/ )9ܨ%R >Ԙ\AzE;K PP`㴑_op> )/g&Ll lu7 5g%zԃl?ZU!Jpy%UUI[3nff.+?PKyVSrB—pip/_internal/utils/appdirs.pyTMo0 WR[.;TcӶVDI.b#Ez5Cn Ckg Ў5ExlGw;^ bu<B;+&J L8; +Y.PmR9+O\(4=s ].#˂8 d"R{oyu)[ O5ۻAӳl|y1í? PK00(`d_ߟW(ׇ>= $[RyXz[j#o1Ԫiv@Y(vZȻcmynWlv}],6wkb A!J%:Re9mb>}f-y 1 i Gݾ ˱ iqe's'-r:uVS2taw,Ԫ h;ROvdf7c U]4P|6!grba/#2.^9Z(Cdj3/eAT&U-J6f h9=qb4٤,BtgAd‰r`g`dpX W8tBf{.g@J#:Pp+=7FuAy8p)79X:4(rȍ|;kv@x\_!~ã!/XkJkG5WWIsq_7q!< CL]Me`?o(U'H :nXX -` ZwHrM I (M*s7%qhQ7\ў "WnbB!lo/^h&S /aفiLV?&iP"ӧ5C Qč.pS@"^ZU8k,ݒjo0?rG3#J>!澁V2lၡ.3vz89%1nr$W\{$͹?PKyVSs)pip/_internal/utils/compatibility_tags.pyXmo6_qS>LB%N۴50 ؊} h$ "ewGꅴuGȇw=w<:_yk92a+> ^]eQ1-D.>GAFde- лJ+hGJc,Y>MY*N6dW,f+\kDV#O;_V26C}H[rv2+&RgDU3)YG7K*gz)(&auN1#rAlāW29R&Z&̤ѩ%j!EAzj9eFqh*RJJ( b3i *E g1^,κ5$19/iKNGFlFȲ3W, "M¨_H J4LP0™~(S[kA[S T\^ )Ű29Y@0W-3uz9]cR?Zq36`ktR!ǁ]?cFɂ5%`.ygX(-$F wpa%eFҩy!7 LcZde LHM5(0{]r{ӡ 7c2lJ[H C'V sX4t u+)nW]v\ln*6cBnIfIJoXk-ef3vkUпaMB*  ҼPch܁B@-[ǻv]rDK,3l/Yg7 x]GP҉6u>G['  07SJΰ{SOR{Q/%be@ұ׷|P}xp̎3ފ<ӏ)=[kA65Ӈ5wڼs쩇l`J |Xrnrv=g [!>oѰ8ۭ1e ~' =UѯWBqT!@ܶE`vo#ZJ֮T4{sG\"EUMߞF!!W3`:eFzgΏFX 'a5;KzX /vElA^f ~߰Fgаf}oȦ(wkcFT_{(vha qYvre AZKy<:ʾ&ҁ[,77_?\үR9x/O1I9g@ BiT*$"|%_#(r co|r]E4t̆gC.xMIT^:.C'bO@zCPj:DA OI$5O2`VԄIWAYXѿ gӬoً]yܑI^%ˉu$}f -6JTaԟ2ޯ)hjw+XX_trz"is}3$M:ɮZ]E3,Mj@NKuh6Gbne=mq$ҹQ'PqcZU92N#PWX@EH>vG/EW@K/w#$=JjqT)ށ)q&i@o͎Ƒ #EҌwܐ5~4d/Nܛ/^6pl4x_uÏ(Ns8{;LPδؙ=n?eNELjPZ_ B蕄DMׄZu)kv&EzInY7nqx EcPn:c.^C PbrѷI'c0\aI7*=D}=Ola~`֜0f #|qcЁa'ގ,̜ࠗ:qvYP;T`]?^)-yz %-"~jq @8%LVOߟ 4M{^y&H2QO~$]tg 1֎AlqDoPKyVS&] )pip/_internal/utils/direct_url_helpers.pyVM6) A9%VmHQrH ئ;VT0U vQUZu~|f땶 %y}ViA,T*Š]6OV)}]n9>8m|[qͬsVQ. nfK7E7PC d +=3V'컷>QW/*bJLßJ; M(;VTB0D̈́tF`%fvi5wGwl\kL ~ىȧX11%cQ9ΗeB 4VT]'l!**q+hmu3^GZʣٯfAVɜŘCZh$ uf8;,RB<#cxDDJ$SWAzy b]2Y~O/] Βt9mD:ozI)"IA4Ƣ1S„gAYIHv! 2QqjڢeqPEewPğl3YN7Ł/xh,qp AuI_ ]dʊ1l.Qp۬߅&l17fy`f꬐%N$@EY ,ag,;Rz`O5',Uk0W{:e²4g3\đVtZm_L}I mŤG88)=I -᪔vܗgᒦQf+;`5A鐢rTY-usgjPxG}2پ E#ʆK.1qVFIN'Z!6ۙ ) {FqJl`<̀~ +-oWJׇYޮtY!wn9 U OJV| B@M.ٻDHVLrPKyVS#/ %pip/_internal/utils/distutils_args.pySM0W Ρ 8@ C"m$Fݸ#;$!,ͼy34;6mȑQ>N;?EOR]+eu_9ھ ν7>ԜWI$`ְI@<'afY M߸=^uXrm@X4JpӚMo_Mu.7T" Hm$# P: Goq9neGs)ryR<4O-_]#%Vh"Xŏ>PKyVS[w]>pip/_internal/utils/egg_link.pyUM0Wm{b% -VK&[ǎlzf8mPI7oe^5TmQ{pӫV[-3ώ-p [Y4Zr$`\|%5-CGddWt.ZZ+ohDzV-whK^;{\BE,^k uMGE=~ڔkeVB-yfɢa~?E=ύ0QGeP{ ?4 YؽtTH;+pjaqYr`cu@:X|S FϧPp|x,aosq('cYm7492ig *YxCv|>c2ixO԰J}|Sc w8)c.N$ ylլXF~*kf3fFOxxLa7%F [9;C~PKyVSapip/_internal/utils/encoding.pyk0Wꋵ%I C. ] r"HF w7a1LO~}u8J[UsR9MOdRS-:J;Ƙ؛oIf4$ ۂ?# -n'㲇V\5ͦ4gȭ.b˗?_~lߖ(QxiP$^%Lƛc~APXck``E0t}=?^qV*&l{ך:A->sಭ<Ϭ2yo#q[k^r-p*A\a3>+èB_vGfeښ#'҃IquSt+n ^ 7`)Q8 3r=6˙fJ0#IhOӫv\d\od8>t?C͌A€T^WT#VMmBfr!7>,ʊ3oPKEtV_^j"pip/_internal/utils/entrypoints.pyeTj@+_"rЦPP =!5t̬;5Λ75sYُ@TIb ?B$ʑ@y&ִѐ{iIUUuMȗ}yL >wXóGn {V+gZ}FX8Bm w:3zbIa0gL0oZ@îOؚI|$2d,$I,N5ġqExX$iZ3KxgF3'XdAL}k& DQAްBor/k|g2u gFlH,b>(cdFQk=s>W36pDPOZg&qQ2΀vb3h^LϿ+r|IO2Ab͑)"s`%-xI vI#!*`?ʼnQ^vg Ia1v9P}q`6ڷ)Q\_ܔTw3 |N v(̞S Z]p n"GZg<h ]M^'F|?PKyVS;[!pip/_internal/utils/filesystem.pyXm۸_:"9f\ ];d :ZlfeQ%>3^v?$53<3PzEYo~ +l'n'/}{&5ګ;_饈FrljӔR[Q{EM5^uR?ջYKůPkSE.F7ivpzU\]g*oaydPVjNc+RKsA:[+3Z~vyQdN&IJU~mlB?/'Kc#qY{JH8#¯Per> SbQ;X+$RGk[y!k7 /q4?V-lKYEcBL~~>ƊڐSNzoYpmqzZj_\:,oզub!ޚZvM"!E/1l7;{BA" l AZ;֪ڋ{|<\2(kY!&uQ3a 6?=QFjwZY-j6O!J#m?nD.;rUkk=m1:/]=!cQ 19aݛc5@w3:}z,溺F45gs۹(C rgM)+BKoeEulL~2w*ꔜ`ѫ" 5s*Xy߸O G&ujƱ/ΫP\¡B!$d$Q3 a!)q- >¨QV(X%09sAm'C S `#P\BjCJ3)%CUwr^FLc#B`7X"iի_ӊVBygS(1`MdU藙4|a+wA&n]pF2Th\@ǔ -qMGbP)`@U;N&U\RqQB0p#P![80R1 ݎ,g6ZS.<: 5xS'8Tjm>j8N4^jsJ̵%MSc:,w+3jRsblF~'vZUE T@2Ȥeպu>pPZ0]]_~˵Zg?j\ nW]{h3}Ί*Mъ$C$)vGϨx >̗8 L?Fnz8js+Q8NR#G"+]&ŇW 8}7=r893*9}735f &=q?FKH̄o߰p%:"8 >aB(T[#.qǒdլRy\jT4ηۻgΟ/K"n|gg#DE?zӯIƧڠn١fQSeց̈́4ZtY5T Ya`\eww?uc÷obEߔۡP@Wo%!TlC϶٨^(tTSjkZ5jw\D<=b~`M3\m\ vw0d*pm)&[n-OoՀ`Оs^fRGӘX0Xfl Iݸ᠁q$uw/"Uh?}!{% TxG_ҙh%L%PZةӷ|`vOC|-'OOUtE*Lh' 󗾝Lj#͍}+É*?sN8f6̯AV6-ge1Z߻GeÁLث<Ϲp5maY%k wc: ޙ0}+`3,?NNHYzvcXs3$bXɓ8kޯf(=zB/PKyVSO-X pip/_internal/utils/filetypes.pyRQk0~ϯ8d Ga7* -PFM5&%IZlKr(CHr/fRJHbM 'd!3%?X,Vsr/ct2%8zB>Ga_Q8&y+ %;ynٺ.VuA5UEOUz,hox#*L9&2ߖX h0]Qon`2 ,hˁ;hhM Hso'STQjNϭ# J h'Š-p59d(ކS`d' 0u<"4!WN_PKyVSR=U/& pip/_internal/utils/glibc.pyVo6~_qPXdGV͐kdhھAIM5JHю8,D}w<~OӎSTR7Nu浺%1,Ssly69os2VTkjI'QJ[Rfdz3 o_JVqRRk ~=_# ^hJIiV 'QQ,uEYw?sS#bM  ;CErwِ:LFNFK$ ˉQ.,UXq!zѕ‘)>YؐkuZ8 m"ut 15%%rMV2[)]SN󰼳5V]#zvyR󪨖bqW?WbQ)oV˪:-|\l-k?Y/^ק>8f\..(B!gqvf}r۫( j/*E;WllzЙis ZwZnn&a1yaG"FaLHKN1"VҽL,{Z=lM神Ȝw) 3XKx?JAjDn=T[TP)U˛zF f|W P͚m*7)EWUBAkL9uNC? кt@CZm5М8R4wPR\Wb 9'ـŎV;7w>@r{f|#Cg9uTi[VB<)pvᶓLSJLX !d> k 쮳'' 'F*;YA {_;4r,8I!Ӕ&OaOWЮVwvB%f.pn%~s>e-^܌n s`ڄAY4dXaW wW{jEKeqp57 ,;0"n윢 V*Yܽ9 24'b&#汸]Epǂ Q'Ɓ}kKlh`C1C; } P2y~o=tn\ Rw]ۘ&g5HwZx oݧnh@ 80^p_GdPKyVSPgpip/_internal/utils/hashes.pyXmo6_A*k1xȰ,kRYdZl6T\ݑ%K h!Q;-Ji˶lsdZ+Yntnw_^_Oz?+ڈͭ*_P*Nd Js**+UiK46j l x<9οZ1ą4i_'.d">لxCe =Oؕ%}ZשX#{}W*=˔F? nEzV?Y lY\=Ie*_ *=m1*cvh~(  QZxQecCҰV6 h+`6+RkZv| ʨax97ۋ%;eg/^D k|4x0^ibH%k\ptZk0&G0_$+h*anY.`}zEx5x ?[gؑ0NGAUE~W׆une h2d hrK+O6Jka&a-2MЈ<⺂؈k>VNc zur ?w7l^BrXvG[=czGA>I̛q>ipZK \9\IB}Rox(q} jz4xĥD۠`-ߪsj\w;Xߜx m/>G.H#\hK@JH9aEGhAr0Nf~UHKGſ+'76Kɣ3_x%F/0=\]}+Zp>ONTͬ_(KxYL3Tͨ{gbCKHj񁽚Qp>TGv7įk\byZz}f N?6ʟ:-\]BNTo0u^{59+)>mh,_~!7>R9;/~o PKyVSɓ] -pip/_internal/utils/logging.pyowFɔt:.$J*kVA'HZ}ik7Tb:mɫCՄZU7_r~wNJhYN+aֹ}{1YW|k@^VU/7󫐜I*^uf$K N +xE'9-xe@8VZj+ pjEYǻ*)YȀ$tQ%@EpOI3ecq 9%l mq]F&> :e>̦7fײ Q56vV!孌 Az Ih*TDMrOێEjq Z, J3aK,&YRv6va֣6cŽ֬! A8YGǶ$YU|JcR6.ulubwX#VS3%U t$䛍b-Ϛ@¡rF 4mDͷ#+i9;ϒ/=\dŚrgٕU8.r U@6H}7bE:@9\hSuBX 焲%MY;^Q\J.mp&w̻DQ3c;+;h eVU=;XSa6'ƘRe~d H;kPtb൜GNs Sm_-ϋcvzCT+zslu&X%"f\ j|[1SZ?$W\r~;UeHYupwfoM/}fy=U#>C1v^ /aB ~_FyARp n6UCO>N@`Hӕ$֞e=pe i{~yBUJ+9\)'MAHoxfw`B;ΘA^V㊊GM(y(=@GPXbzUe]B,c{Gnf7?Fog^wCPaݭ Goumc[Cۿ>b~3s~1_R BK4\H܂8=cJ(]ABz+ u(DKn1k*›>ӶPuZW=8 GC֍V.-ܧV C"K-uUI w|vK/~*bSrIkZ$ޤM˔ dY_ `mRxTN$C/GI{L/}1"#F&Ovx;B: q닉@ 0:$p}f0 ܹ=8D͂7AjX&jg]KV02\L/|/MȾ0]V8ic&orȋH5ʨ5MyO?1[zC/A /93C.X \7pPu~oKh\ rzVG1zWT[e* GX: 4#hre#hhG#"Pp\sle hI!W}nN<^8h?PKEtV7#݆*Qpip/_internal/utils/misc.pyu@(D\t P (gzVEAM.~rYqcDbC~$YݴI.[;-mQmq "b*H:zRz,+^ 揺7P*N+Y{h`:Ca>^YX4Awk\*$ *F@DU?!\,T?̄on /< ~[`%ƒhBx 3|նY\^:Uڇ% ht"gg0@C~LEĿbzg4:|r5yv[ h} Ć}h4+\7K䆕CɍP%6HAۈx~FpA>kDKQ$%uY*q?_bYH|x/ʈI) 򖎼3H;"صV ]oW3Pפzy{ur4<8Ͼ@E .{D&m=#ܞ\_7I ~G;= U/ާJԢP<ӏ@_O5 l+Iw\)( j "Oѯ%AM/V8iC'ы )fQZx :],h1o˜"0 O{w!*er~ j(em+jb]+0!`E͵NJ~W%rX$FI^Ex)6+Y @7t: dP 5~3G.,heRɦF{ReHuUVrCƬhU< W=3ƈ+FԄy F(2^I1a1ruTf6dj—w( ؒ\sm>B8tbp^W N0&W"2mUw?!VW$}ݢ@׺E"<60 c T7a#*ٷs@️8WȺQ(U AEdSԓy7Wh\̨.q D9tEHgɍø,@GYA(>rqكI3=hR$MAHgs3  TK=D@NmV:c=! ,ã=*1B#_.4 "Uw [sKM>HXA \jGa|\^^{3 `$=C?v6jd Qϒ0m;F:6ܷ⋷t 66{[xoM/oe&J(&^NnV3 ZݐxƂ[ װ#FID CY|1`Z{C@yH6+S?XU;,"|A!N[L ~~Oi[4\`-Cht2ds _xYd$v| gIrmuIӗM ^D<Itx.nnvLщpHM4Eq7 Ƹ@d8JBת ']bK}Ж\wLS(yB4*^˻d ]M&˗WW%5,3`qurB0rq3"v .gy˳x!6;= s, "r*(毓' [E%W}W";jNn# 8\ӚBJ's)8R3&E忶!BB2Z[q´JF~ AkB'1L2"_yԔu)8mUۿ ^,0bX h _Ғb#E^ddX\.EmEbuE E6lq̄ 4_,L [*A2hg$¤&*X9^bl{#A 1 ZkܻBh2JWki?6:V~0Qsa>껢1U[K]d~ȼ? p9p6Sg~-?QQRmя˙ΑZ< GtIBT2a-ShT)'E:fgc%(UrA_zU_~ļFǴXcͲqPK[g?a;0RVMBκSwcghkK⸧;X{Eú&Ň S  ^ք]e0v\%o~82j@4QDШʘ# E m ȊVs}T $Ā8Rtݠa6m2)ʍFDҽ\Q&Fa.ͦ)lIwBLb ZֈN+ tPݗ jG:(n4cXYVW C>_FTyspѰ[&ꤪ V1RvybK3$5*Y(cF<ڎ],Ds"\EN#ӻ!c׮RW ( WLAR+c h@h;ƳjJM 3 q8J |[`>,eW2_ĉ]3BC$<Dre$FW#16|$;TTWV৬kHBM Kd's$d>u|ʋ2' 򆐮`u&Bu~* 3[]x ԲtCP۰<"Weņy.p{Ӛ`0%\?o\BbƥBe=%f-Zޚ=\,Y lf/lkj1 /{2i|z\j. VU- 1ݤ ?@3rWFhӫSw`|Z`LB3dG$i K޳ 튝uOC"l1n#,{w=ZS'iL> ͋.|y !lKM;w 9-{Hx8]@}OF}0|YA `˅&u >\!e0G3Ӄ}]5v[Csa.X(ƀs!%YTXm F>ob>5ޣCUe#I զ0[/c?S>9VR1_HbŸG{ 2RaGs$ϰvZm \Ksno=64 L.l)B?WCiSSsat-mT o4F ]NV'<&cdzx\hďp*{VAztz0XoH}D8haN 66](t=]uNo*.:ڙÞ$'﬩=gknZ~ЌnbФ uPN% ÊB3{yUr%yTU`ZeAyq8!]s%9~3#:é,_ВE(Q=tq2YS59#Ćxpr+$ӱ:+@E@  ;ȣQY(Uʘ:ec(aet5e'0p1ﬗ]]liͅ龪Uufb)Xca-rn{ƦqcVI]ӵ>dߝQz ޣzhVE_8v _}%Nw/hoGu>l@e;n?9Ur gJ@yX춐y!`kħv|+~;Ӹ*Ń%;7cw2mܺ^F+BH-MUDzۅ`ۂ(OD~~bS=A Iwͫ[hr3}9FMbGJL%X(?RW'0 sz;*Љݛ6EjMj%D8acA&(.ЀM?P$Ry yD=L_|nH e=ӓJOęx.`DJx!"i(3g~>q yrPKyVS; wpip/_internal/utils/models.pyMN0>ȫV =@%BT,`EL l$i ޓƜO dZeB1d=PVzԁw=!gkc1Vj$VSf @^rd]VzP* +"p!b-pq#^C5 bPYdnmʒk!xؑ1O<½EBaj=LrEc ĞՅz[)mq[7m7,7,M7; t[ߙ9\E+.T Syiʓk>MȿtNJ-.`~PKyVSf=O~ pip/_internal/utils/packaging.pyVMo6WZvы v"h#d&TwHJ2e;{YμyfHRR4R:~MEJv:[ځ֬2Z\Eϔ5\Twnۘmp)X"o!6=ih9,_Y8=7F)߁+@=߭9N(<؜T00K %#7R5V1Ε1tjq]N*mM2!Shʭ%y.XyFQTAM HD?{\Gia2zD+B)}Rr'yFdYw #{ιܑ #ȸ>&4r2o'Ƣ!&PA^nVl O^:pߥ3#=?$PR警aSNXA!MZ P4G טv&w;װ"1).X1RW3IWl2*gMjg( ;FvfT_>J AI,'[>)*ˉR5(syI}ӉY?lErtOPKi|q9αinl[dVwRS]؁r?99&铽 (Lؖ;k~sV y)pәְC2EseBaPX*l48+ 4a&| 4w+7 p/,KRtzݍ2B!n+^נsO``{ ܸCc+J)`6"±iaÌKq{t/!v &ЏK?(>TɵEaMePKyVS>S| pip/_internal/utils/parallel.pyVo6~_qp f"s,f--6TR)۲}؀awwh4 BNjZ}AYv T[SτXTCmV#4>=g f(;g* I5Nɀ&++Y`A2mX`mݙrlQS!ΠM^:(2UãP9%{'=<[Z="n|ʳN4SFҚW6-f" {:;o%jb[tr1KR&V{(*(2֫dZH{K2Qm weߢn$uQ:!O$zc ]AaF#enRHYr18Qĵ${Hu&FyNIsxqF%j ٚV+Pu,Fnȅ(5DcWz[t?_oN +ۺa=j|O`3?&CýJmTԍu-3Y&p._-ۛ3!?oҒcdN>; GwS,@F@@]m`6h&,5Ҧ16*,3\7JmNR1sκq]~7ongE㗯/%]qPsCj惪 6 , {O?$NL,׳{&BrTTPhid~e}<C ua=]|.El]<)2*ڱ˟NN##"ܟ%u9)o& 6V3d Xtx2.:e%O-ezCz0's/Ol`ay"l78C;emxfS0}.y$EF*c6q׏qLv=3a{%nRGWCdV z^2EE,wحt圊Ź Q\%UkP6$N@^pcvu׉vzX]*05S 1Mb-U;. {k\ٶ4 >0l~;3_7cƧi@uLf[,QZ9$pip/_internal/utils/pkg_resources.pyϊ1 bz؞KO!gɊ:CvӦK}Zҿ@!yP|Z:.F3VfXPKyVS90Y'pip/_internal/utils/setuptools_build.pyWKo6W6*{vE$hqdIE{gDIdfQlL~AJKmGF;Rmu*Ԋ[ w=^2 *Ȥ:=HNg(; Kn>ܑ VrD7`#9)euʂg0ޚ[rnF'Y-̻xl}I[Q;[,`Qv'R*Iz'-)EMI4Q-{.ָAI+HʂI3ruA3#SQM`@reƠ־q>ޟ=(g>nV-Ax/ç~MZݲa_:jMҤ/=&!!kRc.svm*L69`Y Z<2=MvʸS1euiOSq`nnۨ*G{!ƽV[ * FKY:I͞ ֈCVjD{1a,6tLlh8YxoyȦx!pJUW@*iOtu}djŋ ݘeL=f=4#;cT6ШVhTce j$uұ N{>@\1d '2^|tĺF%/2n J]$"}e'e5VڪVtXQ=0Hk.+WT|XonD(teP*,ewJ|\vȞNarUyu YD51:Edv/' X FMGYnW@i1( A;~'gC;5xa5otOn,ئd: MӶ[_^ݦR A?QKӕ}/f$ǭ'9DD>DE6m+N0Z4 }.n~2HK5sGS;ԃqx|U%7xk* >U]сQtoA:z}P,q>˾oD_`4]!N6M>?Aʼnq"$r?PKyVSɘ J'!pip/_internal/utils/subprocess.pyko8O"#=Mnils#ZR73$%RVhmÙgiXHme7=BX;YeC _N|wy|ٛ{;WՃW_U=7O?J۸_?ѺZ=}P0&~-|)dna޻sYB̙Zz1,s!f\Ǘ; Glp|<<Ayp(a,DS!h^Qs`]5Fm!J.Vrw\_^+;> ~bP4Z9xv&7z|m,[oH71dNjgW'X-6Jl"ng? ',˜x WQ4y /7NP _ ^̙̹D_&$byX YP㠭Hc@@.B\:;X96r%|8S]8aGG\,0Ґ[.<ϳ$P9 ",Tw9iFgp&*?zLi%q"[Vn|4z0ot]­ fx<0P!Cuj ?[~U,Ym3كmذ~ H,=aۍW"mdZ40'4_X'ZBtdu l?Y'T'L_/0-a/*anE)!T>P$#}> )zcAsC,iǾGPϋ<݋Aw~ ?픓:;W;^]Yt̗,@-pmѿ߇({SM%F7y6k4o/[6x.@RM?e ޴v GsGb#ϵ^eXXo4\zxrT.(HioJҊ*(oF3ֱRk(ZOF,3CՉ{PKyVSE pip/_internal/utils/temp_dir.pyY۶'jJuqSer)Mmk_\<%(S=ߞH]嗣b}V2ufVhTi‡Rmچ$5ӊ}]R ,SҾfzo*#4[/[ߋ4]f5PӢyz=F5U_R D;́tMVa [(D ̏#f |>tL9HQBV!1”A|[iڃKaJ-l#vN* 3"E;{i42,ȺhN@f)⌢DD=c)igx6qHvY2V0݁1Pa `[y'*̜p^ˇ7V-=uPSg{RUh𭰘 m4kOV|K˽C egZ7bZ -l)]0#E(gL1\< ,49`3ٳfuϬ1@h`;/! lx3Y :Ao`KÄCA|2xɉq( uhz rS|3`bE ppk:Qy*_sW^W.U'X"H1lEJ٣'`P!u#RfvUU޵nTd ~OF&`Y)0 %؅ MSA3J,N5\i_6V>"š5\Z)!H H:nc4:K Ǟ/.PE­yAnr+!mV[##ZhEoȐal=4>ڸ&'Q㿮5wmm] Db;ĬCV3+<˄iO:~<*{KHwx)4M¬џl?d5gt@z=}mWClggn3 LѲ`d6i1ԣn D l]4]r6' Ҳ24IJ|MuByFT06 {'$*J(q(C``M %8þխ7x@Y#=PgiY[ө4`*H ^+j%/R{>cK$kd{=[!VżzrP.RxfyL6_gs3q 9`8 ~{#9bD`iq·b<8PZB)T}s?9+[Wv}v\ nCŊ*s4P=L "$~"$[D+D2klw R5h~?s/r@)26ŎPvV, H]kJr@RVeី8 MVPd[7ؽX_\6}[˗y?O>u7Na(hGpeԅW U.N?0ШwܟPi~ފ /J&JU&ǝK&.N]v eҕQ/q.yg 2i9̹ 3+q=1$Gɰ6<p2B^>AM NCzIv/ĐD5"zzU'-/RB]e1;Y$ottz`{@y48.gdOT{r=Ӝ I~TZ)nv,4g+>n)%7G-v9Wxo,}Ǣr]80ő#{oM˙5jORe^e`wcUG z$C09C )]ѰGQ[vvMJx=xQV0t'Opa#!=G%Yqz3TˌS=X=|6h IS_ss@Lg~~_hPKyVSXiUJ t e0;6:[rEХz{Xè^ Y=J@0G5xl$hJH= E^XU9N78m8 CTMײ(KpGL%goOшi+\ڪ!IOR`u xí;yRB gO",HdA;Ob^K{@ZK- fkP̻CM9H`@|e^,4]g.qeL3DzH扜/CB 8G^X1h 'wy55/V`]}s8T؈{jjX% D%RC@Q #穁֣b%cT0O+ͪj*4q0N9~׬F`aDz%1YG,`z4 A%kVk+>^'%FJEԣ&bxCc={#+F8'8_A[p];^3LR5Ҍ/km1D65hDsiĸ !bێo.6WE#-XA9$ C0;V{1(ݧ>C+o|=*! ,ȯ(t~B i]rt]\ 1dlla9'¿PtY\C`Cݗ|]rҩO!l=",8͕x>>^1rc#f]!X>_w[X;-1z_/~ ӓ7Q R0$xu4Z@4G06ٻ9/&9-nߋUfdSq=yzy7{k R`&,6C3፦k=`ޯuiF3Ʉ1x\ߟE3]>ڍ>!tD2wA)^1 V̻ Kgߋ 1'*ޡ{߈xl}hwcoEi$M]f챵9:8XѪSx@7kJO=dZWĉ?r;brU(=@М.SUi0PZeUBBST.e&M.K3 FZ'8uM>0n2;3gç@ӓE)J |l $t*'*9aǛzӇ[ 9{a ((_ >mBvcvkOsK}ڦO[k[]{WfܺVB>|S]V9UԁF%Wm篐C?N蝁w;nO[;tۮw4IxaTHͥ`(P|ZQ?|6hj yN7\X5 NhZp'[6C@NmBs{qҁ<:y̛Ik@,{ĄПH7|Ɔ~1VŨ@~b<}PĆd @n!mgL͈&wF? z~2@Av6!2?tå?5T .ǭV_&h~PKyVS~9pip/_internal/utils/urls.py}Uo6 ~_/6r:)0\7 @]`肃km:ѕ|?`~I#Q1ہ| V!z̻[kA<~1Ģ󴅸 !ravq>}}kQ 5#CK^6¡wlxl t ,8z8: ĒC_VבG)#(3r:9A$^ub?zNvZxB ~ d ^?Oq3] PJknM-ڑ*l`д?U\88mZ͛?ɸR%\%(x5'E&qRȳ.O h]!$:D)0tW 4B{H XEORD7|T@ p-Df! %Ëe'h/WנW۞BTG-ږFI}eo_qqր!A'o>x+c9ſ`IK\Ug8 L,1z))Ȗ/Li~vğ'l|r.S9(qyg؛ձc7s[.9kji_9x0nCc"sXZ=k0,;"UgO¶#0r଺ѵR0AK_NIog8V#1ͿC"eQ'C nK9D|;nu}v`dxw ߟ߿ۥiWͣӿZ֘tޛgIH /8_5?$ VRU"ڸQ,u|!y|;<\&~cqrPKyVSZ'" !pip/_internal/utils/virtualenv.pyWmoF _" s sbY4)+ԳDٷwɮ^(id!#76*ZIdxԶdd?mT^_IvR+QE{E}Bw%Yle^_^eys-yuȮ/.x.^. \ojYaXj Q,73oVX~;럶j3 [$&QXBf(QljL`ZW5q_1dAu`)?Pwxȟ]K k2~x#pV׈ Fiڠr6m/ kfT>IA(t`yOzWM%(p$kPTmE7X2Y[CYgHvǹ>!ԆeX ;H|V&{C9 >o A*+ 1v /8NZLO@6_@A{>*Bbo=w{̇TDa')9 !V΃#,T t ۃkpʕ4UJsPR+8po-UIQ|ؙ}*rkݬ]qsY>e#+7 ltн ҧΐ6@w/f)$q?36Fd3%z 0qy<2}B9E2I-kHVK!+f]konh;>8eXZ(*6t{JhO8Ojv-nj`5u-ɀӰ V. %?>ĘxL0Mc =tUO+{uzh*#E!CgU&PN%Cv:,z|ގ2ؐ{Qb+ƞդ욘f r$d%u8A tjB:}""h$܄;\ښBcWKʊ-p@S2;' sq9d3nJ#@~{5/#Mb+hkʱfÈA:g5tAѹmcl 9ck "2; b@M[&V1N,؆lFBR=BרMں0e0ڰ`O,5{߅X-` ~X5wb-hj|4B5x :pxQ2ȭDr]~VxPv TVrJd@.>rxL$ 0VKߣ8L cMm8|%\'Ft٩_ ic:6&Z*|T0W2@SH1JhrΣ؈F(87R}gbg.th@S:mYqu8P %iGM+ d%_VQUICgRVT2F2 xȘCkge\~4<݃Ф،0"5ϜY"I|-? Q 8ܬvB$jp;MQ:i;Ԋ5PZC4` !ޯxq#E0RMcrӴJ lZpx '~rK'Cl`Nβl@CXHԫ΄((, فn@xtC=ùk}J3Yr%3Bqkńg,Jy[b vJur8ˆ|X0IzX ^ԓlIbT{XC]lV0|[61ِIp 8Ţ"9DWM56Yg2ڍj\Bz1|u@Rmj#3'0 ( .(XC Pe:"ޘ@ _] ~%)r R~1jx@ u;*DcQ1v'J9Wnt^fH̥#y,Y#̼V'Q:GB&^ 3eª$S0 \4[ @7{C5AF!)BKԑ,:ՍOJ0Ԧ]3|3#ld/ՇpV;7)aU{8r[ cZϖ nן<+]/e)jOD^#+1\8{۱`wӇ";N*~3<W:ډ?0>7EqK6Bb5`\/PKyVSA@\) pip/_internal/vcs/bazaar.pyVK8XE (W^D(+EFLc؎mgL.WEgD^#7qN8Z1HF 幅B-ꚫ{jd0^?Gcuŝmt:z+zMAvP|*r {|uyQuܷX9N-?Pf.8o3rW dr[EvɭMrX6Sec9” ΢C\ ~֊=p@wv"i6GBhvDC˫o~{aD/7mbiOY}oȞ$ߓom#F+hKPg%@%>Am^f6FSOh'M*U8tC( 0˕?< _b}^Ϋ,Eu^S~ќTz3=~,%eQy^8~0̑%;ms`c*ļ9::B41B,D=0+0ћwW>/ߜ/(D]Thfާf<Ho#zOk^7A,dּMc[\m'Q*aLfQZ(<۱u!&%f\B+XDV*@1;.Y <YۡE{$y9ylByÐJbP>(6Vj5O4Z"DFbigJmCkXwX@W,KVlʒJy*L GU42RE԰7; 4}2_Ǵ/ApyNt?B$.Zjgb&`Tj}v% jȰyJ cDE /$bdi&ߠ}`zTѼfi#~nͧd*4=9[% zw}W瞞(l'2+޾,e2m{V/5QtvV3H5=Hժ*D v+W"Mh*87Cp̰on)h+:Ej2!rZ<xHZu2q>Nz%;j}D֯@/ H>#NRFK֌Ӡr)!dh<(wQ1%M@#`~G58+- ggÆ!xI"sJ/4$H2ʫ4:K6M*XQ*a^ ZR`UmOViϾ]?uwK&4/kXAਉEF*x̿Kvq2U9}G+̳twzUT+[ C%=}mi'"$43cas~%2ш% 8n&M"XyV> |ƀQ2zdvR59Ga@Fǫ_99iQ߃A.GZAcQz 7Q Z\"4eۣʡߡQ-HTb2Viҭ1Y*8gs1J4ȜZ\,Fl[Pi5,5h0VB [^,KՔ! e^oӒ8ћ>e)>YKRV`]%DԨ"KvR ̩' o|&gDhS Mo;oKPӟ*doa{ , J+fDJw8 #.5nh)phڊ$}&&?Bn F޴ɬe鋉h`3;R_tJPfOQ΀\B|.L.0YyI)d9 #1 GoU_G}m'^kPEK+ww/cJ gԵ- UM+m)BmmAQӼ Mf~@Fu+Dn0-ԅ2 ZwV#}͜F=;RԹ0EQ~;P0S6~#`][FKtghK$+P ̶ ~91a`@ؼFlO|6\M13_+8!0kψ})3MO?!'Yw] ԳTg- I okOLvi>GcQ Iwl^0 7+=< a EqbPF@q]lqw"ęY T>I) lQ[/3ԛ|mtNAZL0~ᴖˬ9~S6.(1R 0BKQD ^Wc$`r柴Q‰p9쯠5-CL'P즠\PY2%M HRrGBG^i|6DsQij) %ʩĆv́L's+);;~ڂlkRx^l3(mJf]:!K;CײGn^ j'\_G0șْg юWbL 4'FWovzϯކk{|׮F͚s?C0\J/Qh8Xȍ7*9%}Hiw\* MPeKt SR7NOiMDGo-3-j[[liIm0/152Kr;|+ySZe:kzqmnnvwqcvAPa1ebF"R$ypNO3Qъ?q<ΝNk!bŪ5w,d+LL$4eZ n~?mgB_;;6r__2TXةIWK{uՆ9kol^49m`mFb1CwwRqm/%G^}2bD϶hZ %0r7OC[ejWS3#.b\Nv)ugX}_N 讞pҒN4:D%vΰt[4C<3O'd?Ά^ﱆ7ЛqkKi{+그=y>ORߗcĖo7t̴k7":}Fn4oGyV*T4a-ZǼaڬ=-v_g\3)@+cӌx@(9k|pLC@>ߔϏkY((J2s%2&vO@x2fA%bS6X*B]s0 5 {)azT!p|$d \d(WLr/sed4+<sF@(9 Uud&YWxL MA P3ä,*LiS.Jm>,: ACu(!s6F0MϣRi@<[r,ŝuƘN&CB,57AldzTB%#:ASdu^ rF g 00UwY,T un@K(YG#ѦByvmXPw VB/- >ml [uQ[<.x- [ MEl- ;h$LRB:ΩYmk.36p%Gt}+h F=Gg.ޒm yU_-]w>Hш\Y1|СlYMj_5Y_3m5NJNQm`5%b X.boD@Eߧ׊j|o cn k KXOW Nb!# P=S }\EOO왋|pcXv EFc@؍!tTߔ1R!@d\Gd&*%ũ8M:FjNA'ډLl&<(9EQ#'77hȝH mnN+ QY}7ZS75}oE|ӥ1mvǰA.pwC̯/0Joo9GE6s ,]O 4W'qQ1] ) teqQ`kϸf,;wV܃ L̴eXCe6vN=wQ/l+u^.R9]n_S$E#)ʲiqj#0BZ[ë s-ٛID,ls. vQ7i*WekA ?͜捴ŠY>=^J[᪇mn.f4E0aԣH6߁ ]v!Rv776R| FkiCn5pEXV`ZRΕhsZ@StF:NbGMIM{)@7cV趨1=蛍!YNHs ɞЬN)5yVp.a 0t;.֦gŬ3'-ܭM (v|wQ\QqB뼚%tXM2U5+kŠEr9/pΥc}`nj^9QA;hB\R)y׏P rөҸ>/,RkKć,0{DĤu'0gԧb_^ӎ'#v/gk$4VVۜR.}u-)M1Jƽ9'7RÙj 2l,4!,"pa*[Pf'ʩ6E[tF‰7~=q8.~(0ݧ0TirdJc0Y9Y z#j{Mє>/Ήt/Ǽ5l=36i}xfK~wv@rZ(W6G_ mb+yGȇ¸vb֕s? LeµLmi7jM9Z,ۨ[̥^cP6Mb4x#Ηwϥ$ Oo$wa'WATiWVTBm{35kLƢdEu<׀:vRl (`Ñ#SG.B񹓘ȿ刂ľF- HᄑB6# tG܋ .It 'UJIe0wYӱN~'PQÁ /Z}MvД<ˣxpw!-$l888mw6XN%y0bKs@%٩ >/ɹ5@\(A-zޱfsHS8ulL${PRJטM>з[i1xPKyVSjb6L-pip/_internal/vcs/subversion.pyZ{o_bK#0y% ]-^+g%% E{gA.v^h>fgfYyEE2^z\OMVڗ0OTVScYٔ\e&zea0U0OelKW쾚I*ˌV=(h\[̥h[ORǁFZE6x<&RIi|S;|1"X[&$(Ϻr^.}d9z"y'vb)5ɷP ^Zf5GQAsE*"ϳY$,,A4c! {_; jO%3MgK S~_D@dI^j [uD4I"R%)* ,N(5| _LxvH^\ gߖyjӼΉEV\l581Jgg :s 'C![9R|Ҍ")hv[6V:VZ+xpwJyX¡$Tpki2_Hi'mt1 2Pn o O _Kl6//hw{0)鷔ş0^@v((yPh281`U2>8J[C f vc (~Դʧ{tn2ka'Lum`cXTrVo23Jq#AA Sޥ%I %}uЏͲ^8J~CKE=_ERD!k5?ZBXEr<\mF+H1RD0(\8eA0) ٬L{:_qp bFC8 Üӽ}1-+dFAѐP䎱r\ i1dٽckDwm%iʆ: 1X@ ^]Sxq5g3< O/@Dx?Q@A/x(0~hԃV 6XܢIkzDꗭ:xNZ uaOf7yzxq oli k[Y|d!rQK= :H^a.&᳸b?Qm~GjC C8@Ń[V)J4K/@N%ϹHQ|vg hzZ[`apHK=Cȟ!jLP,# F@}M6S=Bu0B ?aMa-D<*H:U7ԥis$>Daeo @@c.@e7]ڹ`0"h htCaJާ~h'k (1`1_7.s`_ 0BqPz> `E|jpҊc2 DS(|I4lX(|hN:TN58ȵ`nv |=0~4'P!P҇;4҂Qh*8{OSAYZ%G@6$og.TOϖ~Lm {pI@^3qs.jLyϳ>hQ(G"74?Ӭf-M=ȅ$owRHׂץ4e%\lQN+TW 7ԝ셠J2 F`kLBQ(1e8ʫ,jj<Ö$лe1Cn4A~[o5 5Ŭ Iom&LX7L;{hz iq5Kr1pQ W^e!!ًL5x=12xo57٤ Ќku }M )7|*M`1,S+"w_b;ZFkbБȣzֵnp)re+i27-ܢmC8^9CG zx&5>3]iyޛlGu4DT &-+"_l d6jTt. w+ ?p_oݧY~ ^ l )3P ڨkl\ Ļ 77)Leb(X CMCwBլesaWo3ј2%a. M#G(ېkޱ6!Do_6.VjHB\)-qK7zU77~Wv7C0 nQja]:! bl6rAw@ٵ5m< s⋣ӓӠ`Vt%=%_ٔ,gɌp,bG/y8ԗ ç~O9>SOd~89[=wN-@~.G<,c(y . qCnL /zc=(9|;ZukVw9 Fm酶 O{Tbi*Ŧv.-}L1ߛyV/(V;LWUD*f9ŏ-f{^yJb5Fe [HV{7 ;b:sUb.2ے Ou.oҪM" ,+ŸՖ=aEU)engm~=* W aAU +*m.Ɵ>r9Nk" /Q;8hv+{FSy)Ԙc|UFW7*/Ǥ6 tg)b+TǗ77kPP@Ь^kCx(،aAwF6zMo.gΧb(a`x_/*MB-i M}6+x1sݣww{xg+HɧXǂ~Nnh0ΪQƬs-zn9l겨$-}=(>ӕC2q JKe0@n$?0>(hS!ev6YoV`!K̪"mvmS]Z9B`T=wxTey2,\w><{ &%y9m@P"`_6ԃ1NZJu uK gIu!7.0\|E$&' h[AouqW]X،4% vEojg[3UlgepW`cH+ٚ}a#M3zoO| 6* {Lwu:i'zcr#ٯ&B%`ȹ+|FZcLڷr924ASL'40ᷙ-PKyVSs1!AW#pip/_internal/vcs/versioncontrol.pya~JD={Cݟ}wϗhlؽu`'Z) WbFHf% R[< rEf!P )PK[ߑv PW$rՀUKʤ]AgXz^,FT3e( @:Xҗn sd576 r]媛3 I"YR aޮY܍gRńLQE!JK1|ڒQ9 ⣐b;\NL) mSr/uqcrIm܄&ҶyAt-=1AZ:t',}S x% z/ZBwp:㟃äHSǢbzI!l/B ޺`o% au Ҵ5nD mkRiҢ$_>X"g-j?}mDX~& ޺!#y C Ё23x.H{'qQ  LP3c-p(ZBa:3G PS IeX`⪉&Ɩ76{HyJ aG^'ĕ^g:ekXT^?x=c,r\axR_v9zI~MהjٜɢI%tu% ǣ͖L:;ww.q3z5#Fp-$x_νb{:x3z<4MQ >vI)PߊV#xFTn+Ap&C fhjFjTdglK EUA[1-^}<Љ!dM[2;+)Dm0FYlt, W9t]ԃsuJ{бh#Mԡc"#GjltlB$fH0DX[\GjD r{"lU>`a(URʻy@ &QCI=Gs6ɡ;B\$4q +@_;Q孕΍`NQ9%1lS[3ջ:1̵z^i^Y1Yk'n5Hv /P,zwvs"P ?IvtiijDYܣH`f G!jdCtytaueis}zvzyXjj 7FV+hXK-(<CzG;MPhÁ!@BvgFCWxZ % x:5Nel"ڈ6@̫IB)[>{$;!ߺ z̐ag c׳`=pxgh?g`,_+Y#NMr޸Pҟh;hZ]-ʪ8?ӻB\T^]j*+“)wR=@ VS.TvREQ6|u'ӏ߂1KYAv~e)aB U7}@D6R@yi˾HsZQ2}rQ^BoQN“VhyvhqAh^ nEN`X:Ecx'.V88le5p`_a2MYE7Q}fՄ1l+^ +4q@,8,,VN`<ٞZ t7Dz?|[@7<xxaNqKRq"@;8f{Wl׺HOk+R]aI I2l8E:0dQ?%R tUbS(@9^_ W*:Yӓ׫XW/ VBYuON?o 3*IR"tmbOO61ktܶ0};r;K4A;/3n H<){. 9E5㻦;dg Vcp(c@Aj>3FOsPT֤\\lI8nIp(|S VFU w\y)\DCش\?T/+Qjkz{qS MHT:2OQp`-sڗטͶ(jP W`<ᱭs>7@ga)ŨC;(Bdt+36 ǖeCkFQ_4}I r q;.omC>@?Q s6aJ#.l.P7tNZ }׊fLfkhjp.@\aݝeNt5P&^Rl68)6%VG_}U)lxՓCqِ(@4N=eqO+JdjJ)mkLO@<;sBzRܕjӠFk'GnC/g0-x$ahoMC)aW-D}c:K(bzO*ILV@"^7 E9. 5IVSyTmy̯k;+8&=ڞUh\Jޝ,م,nkC Vj-v C-^;.GKSyAM 51dQOsˬA3dv'99"x`dh_rl}dvK: XAK6RI*@o3zDj >$ddP.5oNSqd![1}8zj PQqߧl=xᔶF~|Di9yeS"yQND+2(@ f~Tp'FS i?^)~?7B _"ǰFP*ǝ<&ŒLWw y)z?{2gz(Y%9Q+K!([-EKK:s~fcʛ{i?Jiy/xZ5Ir~/z50_;i*do'BO߳!-qL!>AWzk)pFcʉ<)?,W ,Ks>vHAd+c? y5M\ ].^ԁ=cI+E8~z]rPFChd;X13ӕ !T346gt50}8&LNGeMg=Seh" "s:n!Qe^+Ffc&T# (ktc WF?W~4t# l6BU~{'ߍv'<=@7!;{9w@|~NRj.B1"wƅSՏvF\-/= .Pǰю͢kᣰSmxVe+eQ/'Am⮼36 OE ͏䍑\~e().o<`~-QE{9E$N}3`(Rm>2ieiP&(3MΚ BQ{ͷJ|˾uJ] -[:37elݘLoս)6Ǐ` tYQlJ-U]NMĒt>} ܉/g], YadpOzQw+8C*O"$|fN|F9> < Vd%޶L_wxfGͨ9"?T UEZ_Mnc'yz*^[i WM%(:@BëOnAז7M<è}!o5ZrԜ'. 0@=*U@g\l1G-@k`M[ ,,>j!CX# ؜GUu@WP&#Z@6z7 H*agו\8tNK9-*=X,$o9ǪQ%P>ly|P"6&O< }7;7U]l/$` \cUR|Qg^6NQ \xw1Be=V4M[L7G3D2>}gyw]k%>nWWK<1k5W֣vEk5 jt˚Ғ*o2 ;>Xy/Nsg ~qTۊ[KԳ]bu5sDGk ,.E/P⊮=>\h1PKyVS['dpip/_vendor/__init__.pyWM6Wʡvhi94  !4D}CJ{hQv-rf8|捜iҩ.o-ʉ j7ߩ-9a*c,ƋdDkaZLC-d[$y4֩`]mz]5KPM{$$&;%}WJ#sK)ʚFd%)}o)3 vF$Fݸ%#qim<@ Gj{c֪E-o HR;h8>ʉ[{o{_UZYm6da,Ry\W ~ZݐHKV*t߿_C|;[^ܠ^1RY*p] 2GmEjRH*Yiږ@*!#<|,Q w8 'a`SNKZ+!L"E}\ gHYX}McTR P^N.yջ@q`Lv Z<Ny\&JT)G H&ݡB8' Tc^lW\@'(T)/">8^^PQj;b|ݯ4mϖ?=9.^닟"CWc1Ce"wr^L_Ͼpn.+1M?Юy>9d], _=]g+mCG=T,;t9 zH *b#+#IP+}[@\;ʋ$ҋ!D> $zD^y=gO6MCqb*˹p#v`WsHƆcc:B~^K;ܑ!a e{{<6`RR$$A& gASP(,c)cA")4pglAt6b!_SWƑ8UX>#X=ҳ4,d$#q@/4Q\!t62a =9 $ O^2X2, 2+>RM:Q.;EZȢ&_oRFc6dΜ]d~~Ξ_pά+ d;2щY';R Q`wjDg/No69jVZmٴfӍq߻/[Ib@rHS)@6z=ȩ3gMG֛^7u;{9;砩3pϡÂ'c^!Gft{ܜ׸+'J_'_M IhfV#[^,ׁSs轚C\6XSWt/\'f\2{ጋ3?^d鍼i?PKyVS,t+pip/_vendor/distro.py}w6+pwO$Wftun>Mi$+"$3H-Ics@%9q=W1I`0 xZ,ntvQ'>|8JKx![ijt"J&b'{x/C,œS=7+@-bߊŲ"4ͤ7EI1_diON jF⼎t 4ˉ&/z1z: Ѩ(g2.T=zv`/LV(?i <pa_.2|6U1R$2=_4f_*EoDw{''C/Oů{/^<}/.~:< df͢DI(׉ӂrN +-J9tG,d9O+ KJ:MSV:7ggԻL/lzW"FkZXUj+͡ȜyUsX%p*rBĀ1 0=N)1@5JqQK 8^dDSM?/!;ym2Πd 1 , ^9v`^2qַ*d5 x%it &йs)r9я[zQ,\$xc#3H+ Ab&"qXW[!6t{&q$2$zR >MJ$N4ˆ8%LiobܥwN. 9L[4!T3K)(,*Y-; *E9,ܿR@rJ ;+f*3XT.2yc? 'k5+FŮ=>@S}U i_i"琀%t0d6FIY,( V-Xg#"ߟx@A': F&!g K`G E=@nx%,!W[~Ӳ+lN>o/q h짓y:λߏ<7'>Hͩqz՗9NEzl?jb0JӫU7zmQF bˡz:_Y2Ηs^>+hH53Q4Q-S6RWPW2K/eĤHd! ]QE2JK@kH{$Io^xv]w5*.+_<;;9j~pɢ5( pbiUƲ %K"%K|L~t-bFbP%-X[e^49sj#-~@8՛&>a3k?Yq r VA0TU65;GP D[G/~{v@>^X2df`뷻P(ѥr)etV5-P#5냩m5FLlAg'94-C2dW\uÁ)ttdyEҨs@\Ԏ 藩y]>XW~3P|)~ma4pMܞ }BaDߏ8h0!^.x u v:=;cwMơF 8vXG(4 fĒA<)*4iR8R%A-a8=8:_^BNO^:9e?o_u؆w'Ix_P"'YŀH){ɠ5Q?7X ^?+0V 9͒}Z BCۭ V?6ȝVL^"8zR=z #2^r -0hu|tyf{(`M]2{Z.`D~ ?/l va%)homMQdmF#Azs @jpFwq(2'`K4ydy[O(yn&]@"ϙP8Ұ?jh~BҞ}"l7,gA8 쬟&4pCm2ggL8ª"YWmjT<0WlLq?3TFj( Hc_!D >zUUw{m]SPUƲ).x",g2˾gl(3mGQS<1+iș֕= (UI\d="ƩURē[ɈA%%e@bYNP$^Ĺkj-I:JLƆQF-SFEUkOʢBQVLGc %0hj@DE=h -%"Wq%lSA} aYy" PaYDjGj+x'ܵ(,.}HےTJ"ڮkE%2{ uSL`(I "oYqWڲ﫾@_7Д'<y&긛La J[h(rkm72$3 {[|.TĚƉXjqEtI1YEYBOuR\ 3fa5s^Qڈ9VƌH'jS{0:?yŏj,,|oyeOsKzU@iƏz02>FLx|Ÿ2==*MtB'jSjboL[U 񿊦+{h#<:}ȊeBӏ =GFwEހ>/4~a̐A槅S =yC~_~18!պRz:BraFgxs紡? Cǟգ'.Y&35>:c+ ?.ey6Z\.E*K߷2lRZҲy=ܠ|dWz^YL<;%@ldʈA^;rDrʘ} /2Z726PhK ;' b*޸}r_cWXoИ)iw{/X5DRtULtNx;:|~{H4xѼyOM|ɐJX.Ԣ#eM[,EQ5;lJ!\ K=frdVXnT\GinmY}Ҷ-_7mPZT''e.]Y7k|G䠕_Ųn-T(]q@Ku}K'F=:,*%Gr06-ڰ8Sllqe4D:^l{`3uTh`( 54PbAŗ~&Oa tSmaoխJ`#CiSN@ftc!bF~T$;EA䈕Tx &eJ$% xcs 2L'i sTAT(*gwĖda 3Uu`7#a~_ g\B"<ѧAj#LBV)xٵڔ,ѡݗs.iK\Cle&ZR$Lsclk> 8oEa]lNJm<t=a{Vh#a TJ$AH2a )P}`*zBjZ `XmdLRD7EJ(v15g.` }g \iY- pbh2Z~Ⱦo7B.ߞȂ҇-xY%2j y+NݒgJk} (oUr<;V:wY[Uz`Ϋ^औ ѓq0䤔j=[R$: U] )+$ 7i _߇O\ C4w84ڄ;V@nk=,`n(@u;J m\1p"X')J"N~>͆:B!q5B'35z9#D#Ǐ9@Nj|@)-{qOkKyd"Hhܷ\p(>dE%[\ȵ}uq >=aPg@9 Q>//2{e֜[X 1;lr@j8j:Nvb`&]=4iS";aHp'GON,WV]˺XP{^|#D# cVR|wwvR0f }Y3ߙ9lGYn=;*j٘܍rnn׺p ̡96j^LeN,1 O:h #ȴgڧvY5XtW(]̩zönM;bdSZE1m`\ kk?ӱ@1%E\r|S]3R.[,oi.\țTÈ"eFY7|a<S 1z0eiWVʡ VHսꃦz:E~AɆاGU3 ^7'gr ̠9J҈W= Lsn-ҙevCh;0:ggN ګ2 ]ދ8E>pkۆa{՛_yn-fT><RmGVFOjw8oZbӵc*&PhLc:2lVanL ㉢m]ޢfO5j6cE@+ UlLJg"f A".feẖm:*?#C>G{c m(Hv_rUч}b5wpIMDmX1Ba)3B0^RrHM_lޯfT\+s˚UȒ8|-SyOqW!fO~. ţ/{1f-xM_nWnh~w㋵k{ v( d, `g ؒ,oT7+ksk,*@8+'`԰{atU6T01EB1&\qgYe9,`a(G+Y[мq627t- 4ˈ֔ =iW8WƠ{_l:S5Oի! tTҩ즻P8T d :e^ 2NYtb8E7;c1Y`/Iѷ:2pc6WV7M="g ϙ3̝:w'O`A=膕,33ˊ837Zh^AvI'Q"WŵvVkyE&tƜke~`q@?SW P9n mR@XZmK#!80'(АU2_^TT ܁X\cBz)^SDv4)5^ Fw,NTҐ N+{(}d璝ͤ Ch4*.[& R^f0Oi>ɖ4J|vnϊptw uiRs4ʢA&$b%ӐƳu8<*BYкXm\Uq@jnFvianKQ8 ڮMRŠ "n3M`z@T\E|]@YXZ0jXt<5Oy"ѼB9k?t4NPUP9 ):,'M40C.jTsE.a᨟{ؚcm1+itf;wIy4Jua ֌Ah|>6#Gz4 apDҝ$%݆%6©DbnAZYo\3>A+7,A{7-DcrqM ,%$Mzn){pB҆Y .x; CesKKP Vt9ºyTf*TYgk"\E,Yf2:;<&و÷Th9 C&Ff$ZdtzEXTy!zBPwuWjFegn178\MP%b즨*BO-51- ^Tv!z[y_e(ƙf` Hj9vA.r lld3h>6 өIs-@W:1U$*!Vl9!] &-Op[N@ @|cY$6'(lLØG 9iʮ3*bHi15 CDݩ?ԽA"ƽAKXș?-nt"q4MӦ(p* oy{H~ Va3g?B}BEs 歨FD/NT;LZjW)?bzqg4{W9iE3{Ŝp^u450fݽji=S٦&ڪ lBVt$FYh?U$3Y4ߺBD bg5q (jԤ 酴|7$JfM c᫶ʎd@+i{FXEϛwF$ kxh0Uo+zA62E#s ֪I%DE\yа&d|2' O3yϜ.> 3tmc76XlN7KN]A4(׵ Ը %Б d7Jc_ͨ]|m5dK_+r)&z1}{l/ff(A/U{27Ԕʆvgν[-P_>fBS!8D f}Pu-^0=}jnm)ZѬ,V@x@:jN " ' 8nJ~limB>}cɛrtp+cN&ê1}/j}zmw2z2 5w]v&&cgto =79)]¢CM'9Gi#MF~֐Tzo4)M3|ۇLGSyazn 毚\UYE$o*^5VWwi"?x7$.I7lCk3!,qn_v XT}<˨vKՏ!rl*ʷbWG4\_5nbW @?h'|t-=*l } dr~&%)du40i&dTk(my zާTh %AJ/~hC O$80_|bC|ЅlaG1 * ,}w}$J?]AԄ=3+jb:Φw٭ ˉ^~uG@!'03@u,nA.GB䐄 6ÅeY޴+ >rCN%[UC'' c'P5 詼75 µ>.*!|-p}QمXyS:9&=ب%;kEɾ<~+Zas/xM*7d<.ைσ6[=GePW,Šy,e 3*4|+qv] Wg NV -XR%{M$Uq?+z-Tb6Dx}ATO/9qw6+xVW_}w?vp)]BBA?">b*`_ QWb$G!MdP?kfGuޠx]` Z?"q*G`x:qJYZu[1>}O8b M!Ѥ$W{%D~T077ٰ0=Mv*Cja.44ê e;dFj# 1\Ji bRxppcT sYP zWY\]YMabpD'Vu}9`́YX$CryM؃ X`IYO^|~-d>Q&YN]uRU*u;bZ6DL$qU#q[ݞv9W05"iԇ Cm%R;^ir@nPLHeN$!N06dv}[߀ 2xi[\$V 8vS_^1{В>N"`pkɍJֵNtLF>ƪKݘL27ҡk57a>oY~'_WxIo&|Fߞ,y<㭂&<ɧ~?(?Sv cuG_Uy04dןн7 u8!!~wm C265צuZ@o>t)~Kl}Pm޺5t{TƒM.Cɿ6jM)ٯ@kk!}y5@_'+t2&ys?j?f{R/]qfe;8GkH˽KA‡_Y.P0%>bJlr+vJo[+3'7 @!zoBv֚W΀BƺamkidH1ou۟~R^!Q֥BΪ7ټqhIr\Z`s _aqZ^JV4έD@31"HGh1~e&kʂC_oVe=m븚ii yqL>"VݲzkcjB-5/lS1:{8+ #pqx3Fz(iC;ϮpMOs˖c-> S]YPIY\~!Q^6E BK:Ied$/8{skm@Qsz"j byɏ# =ɪɱ A+$=.sFSJHqbcwЄӃSx+u:gv[g ΑgfD@m"8?S[VԓuO+H'x]Ι_kv~Pfb[[jAvVI}*|ȿ@VJf}]~໗?8E$x:A[ eT$)X#oeif+q A ړ o켭P]/d`* m< dGry䈈 ۹J(Bwݔꥹ7ìqum9 5\tq#_d9_T_+<\ː :c}zCM{GKgDKUW9oj+w9 2x5m0;Z}&(6S=ԒB& ,%xd FQqw*l⢄ Z8tq5*Iu1QY$8ͧqVE8IҸ DVQ$*K|^E\VE2B(](4Jc,&D%0kLoL͇iR^w8AyK|<3"/2NS@iĺT 3Dm%Em]S{<ɼȠxLuxT104!B8q{461 =+1gc'Y*#؃ב1{QV@ L,/Qw=wGg;z}:hV7(_N^#Yp>8zu78:yu룓oP<8>z{t`OI o߿<:>:G7xst~P؃W}!t5>9:y9|{xr#ip7}wp|L|cxO}|u%a]S),аظ?b8 Vv %M&:ObZ(PrLBEI w $QZ?D v L,AOjP?sďX܍03ʧ 66+Y:t?KʄءbIX\Ǟz逛aT6e3!K$- R6*YpBc>H *=I#:Lp~T8x.#Cb,e@"<~:QQ:Em q@ "Zf|ĕ ;iNnoNoOWm,M*R8ICWO]#S]5(t(5<@~NFqՄs1lLjGGN#)Jя~}48۬5VG1sPFO T@#qpАm$0u$XYg׬qƍ;5_F:U3'!_G*>Pzx:7yoK@ݖmR#UTBU”]O(7HE~T\>.ɪ IQ^Ūd#(?j·ZǰбK<׶PvU/T^?%Kөv ׼X믳}TZL+gVm5kUh2g.)-3;iDDDsN@uw4Vrc2苢ؘ$ăsP{5TYc;E&"?Y.ơu}icqΌo2$#_G@'yu/!W///i Uxů 4V2~"^nwШ!Lb?.G+T1X0C7x ^tZkV3ztMz` 3WT r1~ËmօƛkfL3WUVd+DT3 ҿYh52GMDC a,,DQ$'.PH/lK/6PDU_}?!dpxL^#ֆ:@ 3*x >NgCKp}ȓ,"DtKC>B{[㥶v!Bg8EW>^5uui(%5ރn@3x)ղ喼WL10@\Y9G0BW(F=B D*TPy Uw#DoE)@hL (oP}KLqb5ZSJ^pěnU[9#mx\FwaOӼz*lИ̃8ц-9oSl$*VfT: &UZ7AuH>"AGj +$Lkw/VaIz7cpx6°vJD>`R_hR^>= @WrM?*1e83vk 0b#1aB蹔N+6vU+R&DI1 %gIzL(+E@]z|oyfh<z"FPiF0m9Gҩ -Fb~)̾8!i ۞tCbhXcU/ pS'Мq<ォ%!@P/d) v,K[u L]WKuelW'S]dnZkmA|ʦF̹O{*& BJDf;(,gH)ͱsWk;aq/HUj&WGӺxp#2srHܐKÁ`cd1BメIBF4F)vYps0#,nh@잩f2].iiݠv& X=+Y<^O֎q@>U1Ky&F \x1.ǡhb`9]S8\l]n_QzŴ74m| RsP6tEfʥ2l=I*]4 Ru"7@`^4vRD2di_a.{@@?hT;(h?S}Ptޗ֚`??U:z#5q7EDdb-j_Q If27Fq&x c&_$G4[ſF7t:7:9+Qʄ߯p'v|VZd:.NɎ-F3 xef9zؘ[ C B.>Π"뫟/@Al H$q̂7 yW7sJ@M2{aZ8[agkơ )f` IMnYG֡ g sP hzRkCy:W8j  {h2sp&#y1ncXʗ6GB)Rl=8 P@Ls8 c N9UQK`8k2Hh oReZA1tah껐qpya1'BZ74èM?39+kuVj;{eK&%r23N&(²͒h7moY5=4duzRp\UƗu[!3{jTB֦!bVtǍBhϚi X:炵[~Z-;AG2hKR4}r0!-tM4*` )Өu}Cݨ^Pt E-:~a_ȘgAS)ןP-Z,$':m`{p8_ jpÀj(b*8(TPP -e/w6Uh1qiu|%a…(B i/9kӛ6!Ǩ_:dhE<%@G3%ed,촺fҁ9F7ݾt& MWqs=^\6~,|DmX&b/6P. x =)H Glv(x_w}F.J}Ml9.Z5/E suWWW)DŸ=~gh.%~#&?Em@깘 (&x4v>=?-BCD=+BG[#k@qq׶G*S[Lto{6T@L8'5n-Ohi2BDrƇ5+,AMZZ2잝xUلT0Dh!CE.^%B7F%| }˪ov AIMVPi/.]::T3rRkռ ؼʾM gF q4cM-YeIMQM u i,\׺CMx!aGxNb<ڄt!UH5(Wv.רr0hbsşj".nQD&qkCClt۵ePV; [R0gtt<'aa!Ժ5U7KՙPĭ%4[\A6.efޔj(N[hR S8}u2]tMl#5A{d!q$46dMvXZY6xWęV 6Uv.;fz\:| s&=Q6v;*R6:I>g9 a*OQhRH'yqt[i KQSj_>^VekvpfuM&FoUߤn [cٿ"H! =aespG,t/JuTלnВuL-,5v뻝廎 9wZ&t\p^! vVoqeIj)C/3D16hC{Gxdi&dH}$iaϻc(Uɏdpթx\̭ZF-iY? u~=,hmi֋A꬜dwE{YݬU<{!G .+A-%O}]gL@,o^Ѧ 5y~þl r{Conj(G~[, )z7w=PD@DХ4J4*6خUXSs; ?xirF; 0H8$kOD7kqJU٠.@Kf$kI~M3 T6УLP[SY>fBI" Zi;Y_lAY Y@r6l?mggF}pj#uvy- L//:;jTAcKI6l5MU}4㟤 ]u,7Ə5]#F{:Ze?G`vSյ%q:%z#D kܲP 1̪K@*T]MBVe Cߥh'KfQ+t|!HK\seu>"$cI>ݨ;)L;?贍¤5g#őݵpvB({΃:AJ'S(~R;C}O BrDƘt:mG( klz e ^`gYFxW-S?oc6u韵ͫ@+"UI7W̻'įu8pW Rmu{d05eq1ߢFO NWsDkv!YNڴYG` M\y.˾bOuɆR`3̇K ):4FkhV圵0*6q Lu\L=1!\n "T4f~vQy ?i]5s9zE6׊ ++㔈^\GC"fX1Г4 N^`۷}p@ȫ up̆H0^Ftsɸǟ ^Y6#2th:b _gVRG$)T?ND.\cx{-=G Ax씘c NVZ݀>š _cX=JY.B7zfhBJMPFİv[ޞdh1OjbJ:7u$NS}l%cu%ӗgA `ch y%JQj]1"r`*0f j7ԲcW[ g։fxD]ѾtM.NL+6 \\RuOfUI(qxR`z*EːE`?!{%5-[owLH,l#5.5C5qM'ĈdSz}7}BρĊ45m$Rz8Kp Eg>pt %.?K<.U }q \Ý~f.qf0/v.Wq/jlMcIRAuKofsiS]·rߒiچ},o7 UDsW٤,0EaNAbA 5+D#`8Lb+#czd4GeQ2@k8mB Ne۲QI* #?ED52R뱝7R >/ud(?s*fjAH֌>O/o/}$~8Ps)&pA`Å"-L[,d+ Vl^ZKp6gX֑1CZ||_E^GjGk(iO#b4eI G/G׼%|"ƷSYt/wZlRŘc~!:sWآdf_Qٛ//"MET,jNA[< 9Kv*N뛉+_t^=*cא͆a/Ԩg'8&Py5vN03XIA;Dj@i{>.>N$X]S$ab3RBGPaEAvI6 Fqwgkj@w՗[k@YBڣ==hcS00?E4DnodQ-!œU2Ic^<54Wq) MEMlKqQ a5p5)Ԯ˘`Tp@s~PTtu#:AQf>qb2f#;3j^Pm> vanr1~0)GhfYT'A*ŊMNN鲜bh ia.NgWkKGL8G Q\ܺ6)"q*EcFLNH yFwL84.m2D6ZA,6bHvX/a朲xg P7%^bZbZί w%;&T kd%M;cU]=B@+l ,ERN^ ͏W)R0j9_CGLygqlF6WXOV7`WrAuz3S^? 5#>?O~kކvdz+ Sّ >IaGk5 - ǀn@?bWŚ<;4](J^Xe:.D[=.5Tdq!e+AGu7RzK-uAE6rD1f.yX;HGbuNV.gcu/p|#|A!V4i(k8!|H6Ԅɵ_ "T^ { ~s%>5ٹU@C~1 FlGLzg䗙G/-cCҤEiZb 8 C(TǴ ҌZHLT"D#bbW밌,x8bAq*)((QOpMU -!݉J(Y#:(Z#^+ȮcNV%s1 6߻n׭@u ik'}97aMEl=E$bxP $4n"),71lVgXe(% EmR=fRTquGR8uu,_q@0 AaPEn{;e+c%!Љ`,nJL1PdSLL 摿gހpxۥ[0.NU,[[ذҌZj0IA:.ЪNFzւH ̗_K;LW2yF;R80YEi+E]n_yZ4s&88Ga껃oϺO9<,8}<>8+"/8R'?굀_O^G'gG޽;|5:zy=G?AA'v߻6w:fQGO4hQP t p>}0<`dFdNvj,"d^hrc#t_2e#fZ5>Rs%!^?%țD -eQEq0`џ"/OZMB".l|nO)9Z'!me((# `v_YXj %8頃)*Qׇo?>ax3Doccce@e\Y4AWYȹJ 57Nw̠ޫkK8N"ǥźS IcUȤ݀tKҘ%s4aS?fWI&mQC]7xH=d)On.Q*K&(lv_,݀qʹ̘K?%.{ni6`788%2oiJo0TTZ0L+I1ϸLIR,HVAW?wMJC7Ө--phLT$M(i4J6;L>TBK,h?LxRq+bDm@miʀR3<&eyeu 2j 47^ =@q1'LjJ:Q\HxIPh=-')UBPsXBy H+txP,%>=Nǹn-xV4w=WkOi[w_u>;:Wko[kxT}j믾|/w yrH82m`7ZDm~ зbF3\fع^\[gWv7j3L/yʻ#v+EJå*rdyv%.] Ym^yN# Pu$|cy{^RPfa/z lG`km's0ֺgIv-r/_Z"j*mft m4)<#Ƹo8{ K Ú=ʽrA@ M Ȫiq[+uBp^q4ԷAi=^0x 'v)}}cli =us'2)iG JFrrJi+B3*b KT<0S`#S'F E|Pl,ԵFFN0iθy;* E"Ϫ:ATnj̃i"QKkխ1$ Q}Z,emi?|Mgn.RK$۰Ml0=n=ceā6l^)F gߤѕ'b[edhx(J4F pbir! mH6ai{:pPb ]qLr8)?LiOC.f֩E :L\DqX"bOCj"8YAzI$.'ԓ p ^ЄAa rygք_ߟt)ޘvvs ZYoJyڅ7$[(dF tEl߇ -?XLqEwU$d8Tdp6wgdGJX 25i,3'NCa84cWݷJjcJ9c@@J򀙹j8а@jOgw6Xht]1BIc:bP`,'izɂmxGFW1qi~ sw"̉41 ْ|oeFN3.2cvюE0SݳFHtI)!GfRivI&"(Q,1NteLN웆&334m"Aadb۾ʤfIQ e]јìF)ꅰ`"#:k& `6HNy4O |Ctn+YWZu.L"RL(Sej4~*~:s,z`X4@3ewF6+eaC(l+|2s wa6tGSt ܮpe"3N޵\B(9!/a @KZ e5G {#ȡ  HV! N|cX-^v7*hEd%|dю8a .4(m=Na`7 2_ ykj~"⹛wJR=$/-oOJ*` rjlowu_᭽fv&")?(IsīY[pI[2ᅦkaQ)mnJqׂ<ËI dL#Z3l NnFp!ux^wEgpq7nJfmc:b@wxPx:[ [ K1GF [u5\u]wV֝snD\aZQ^@hm>:0b ##RF,cp`Y6MGK#VY/U0XC+g|vͼ{{[0g:䭋^?t78[$cu 77oȊ]fȈ8&Ԟ+fC^{7"אʰw m@fS9rMe4@rmqUW, (&')#D,TäRBFY^V3#;9?M2+I"lZ䯢uC 8nwa\;N`W$N!)pj.\`-tA=x{]<բL{RC& ֫;ܰbW״83F-U#K^i,5NW456cP\K[nbVZۼJ[?4וki}|uGǤ@90J;lڹj^bεPq{.o׌1,[I:TRu>kS=GN| Bq+ /JDVJI%w TڊnKF^RrIF]=(.-K5K%~PWx-?P'S[_7|庳W.MY{OǏk:RM^Xx[>~~f}_>'G+#x ?m7췵#t<և-xuէbR u.M+GEդZvMWmy[VM)ajXR#э*`ޮ{*4+@B]Q*da>4 m:͏Z`EWc5 ]h(p{R1D2Pk-M]{`S (!nPٔ5KB=N챥)ZzwxN,**4_%zX=^QZWsZ" H͂\$R%]_y~09Fd۾r1&Q>ۄ+&tO8f C΍VP)*U0VTXyUjU\Uޫj~ٛ?I(M(OXqxq)ي^՚UuqD.[Wv GYK}ת{?9xy]k"1ȷd(nݰIyϯ4dt(tYBWQ@' [k+oT1nb6! 7 dc WГ]_dBxU[o-$RlU})LE=[]Mxɣ-v2i"U727hQزw&սZ2M{ą1` ++ЫC;,4`xbQn6de-z%&`ʰSc&c խxEkv[5e!=mI!,St?R}Nv [$ROD` Q D16E?yZZeqPn,OI"S_l|4N4CD+nk gUUD>lDLFˋʱ)aiGUsi`hT<]piI:nF$M%G1D'H DYA$3'CfWp=HSE5g Ce)eAg̉RR3 l$$)s6+H:8J(!D!+wEV o\e`;Uh1BDAHWǰ)aӤ 2lÇ_?͇r2زrpe$AlPP \=χigA!ۤm  O#s!(=^Q~ ՇLoPaNNrJOW b7+"{'JTv՟[=\@C:^֘ꕥou`#tl]r*ܸ4\np$$Vgt4@1\Bc-د[@!L3t`[[4؅r e#ܨ0WQa(2 SDR/'ǨDiOSi:"K mI*yLu LJ6@RZU93))D ]fEӋDeyz ux 1UP**O$@)QQ bq3J7P Y:dXY2 OKRQ*8t@ (D!&vP5NJTqp* ~``+rN!jyq[#A wTRdIp q{/|7pA`{Յ e)-y fEB Uqv)@h/8`+6PRջ#i޸`QhΦtEOP&nz$(xrI, i6Jz}-[SЌb"DIfI сtWBDKFXT[#( ͒*JWCo0%\2 Ą9&q_dgml} .o@f0 %( xh;|Q[ܡs;3zu\Czaܢ3TDU^,mTLZaFֽfm@ R%y85rpmȦ$ cRݒwڀM_R=7%c\FT8bƑ4jC3N(Miubr ķҥRL?k`jA/OO<-Pi?IF(Z%89+ -.I} Muo]_F~? V 8TggтT:)dAC'F* _-&%L1$:|P"Vj"*b2u V?Ԯc6;3ϵ[^s AmZ%z5m[ =B?|f& z0c\y5}Tg7<F2=}&AX_xm ,a(lQY<~J䚚q ?0Qr񛖃 e T2I%h|Y#NCD6&6S 0A^ }1(ŬWɍԬ-X i.*nᗰ*(Q*S^-b3 XoEE9?p]6Nw:>dZ3`\u5sQ%k.?8"?ьXjD-JÇSУRD-N9>t6t:YǼ\~O[DL- קgn2-VxuY[ ,ä%mS ~&;(hJ)PWs,hD{U!q[<"&6ŘPdha>14.S P_q YDzOX?%bxtIå I)ł+hly ?U Oƭl/q GAde(%XW>YGS3d-kbMJ7뷿8{6Χk) h4/) ErazW$k =PT< Ǝ^ JCzr}B ClBX{\\+!8rEmZ$hwaNH$cW#ڻ5v)mӅ~. T:W?bИ%=Z0X`4d.f~`wǦTlyavGYE4lP<--GWof-\\t}6c]z7՛N=9{W?XĴf٪  UY$c^F yRb$J0 SU>QI02ʆ9gibZ &4nôs -)xC(cKZZ*k%W+o.%b nE<stpzB x0^V ֺQ]ibfFJ4o1  wE>N @ }K: b魾QZ !@T\ef o`X+_2 GO)I60P`'\`g龖g"#(~#yןz(/|hֿOM/˶lKeC>6{vT?a[i"\ҵepا[CGsvPNQq^guteB4QGEF9Nql;&Ukc*$.Uq6)))Bc¾ 7[e>*Q;Z ʍjtN?%|>Lr<ek|>eS &s!bi/jQ:G5ri\nou+#1pp ܶlh9rga`Du Qm`VhGG ғf1h)qjY[زv=9 2j҃,d,s?jIgwtWR>DUـS!3{|Z^TQ0kN-f5l]FezrJs0 ~A*BSОʉJgCS%h(0* Ygjrb#Vx> }H1q $A8Ë@49j<{\|tvQEoN-c -( (¼tjE 4|TuAZ|;**1O$YHW m~z2H{V,]O輿*b~2NMq4FY rQW?թ4EH3NF -UV`˥FzR\cp+0-8b}HLX'ـn-&NL$ܜP)9ؠ"c]ɱ P?(5LУޑe}-১g _dځ4<<ݍ@bcv|V4;ſ?y-K`nw;x|Re9@/mڃS祖׺v W`tC@mN}_?BZk$F@eg0ۻkf+lOiZ_TrOJu~q"#67^f'2f0.:9C2<74f1 !mHC^3/1/:-C;~߈(~J= Rq 5UYSΚ٘[SCZQ%凎\ϧQabtNh ,&ez%(qT&(""F&噮I%ŨW~#!#5P%HIIHa/ *SMnRYs (!HN =&krgMݼ猡y& ZPBpl?&O_9Y|*LD`gN ž`HrJP'uIMtya^vsV_V#ZX+zlQB#\$@pjg?*  C-(Q2bT Q,wQfŁ㹨W,U:OVM7$"r05 ccO&*Ʉo^p +/-XMQ`ғV?{A.3,QCˁ- e$WYi㤱#~`C̮d\(L$[~bw%B3ul[rZܴ4Op=Ĝx,+o\Ԥ,%_2*r@|hms|ucqyb_0̛H6⏜&{Jw+惽U& !'e'aN8nf|+[酋9 yaQQ!Z[[ _5>_c,ciu s{lд xׯ,?NKDyQ>aL&M0:KK*P,/9z2c>@DUR J [ i" "]3 Ƃ3oZt Ep-L-@7Z2*OI(ôs@yuVKemH-p$'#N$ȋimM8cIZzwf$57`/U aje>f?i*lbIM)55=jx/ުq>@K%Iܫle,H X- Wq6`xG-R,oUVT:(L2}+-j_ Tʶ*>#_E)W$f! Dc`4gJs*dV)4C N EY4{0P+Ց14{Ƥ2"Ӥ 5:ē :`` XHիxn_9s섾أɡ9zڲf zjk۞4)0 GL_URIBWoi矎*GqVm>9;g~:3+ݔUx>3 5ByQV}C[ +mw]q!$ w 1,Zj?h6)V.7eЌK*!ѝJ~GWD4,%嵋vѪF]$P (L6{ ;c }IRW[G-t]GHo-pL/=^ %%s+ۍ:XO4ܒ.?fNT3ːHz!H03''ӷsq>'zNP8 5MĢJe=?WJ[tƊ%'-,$ I6>5B]C!pmDYJz K-9oFD퉑wl5v©|^L$H5g#h͎k^ {\ US>U+MKf$`A+|sp~pi9w ,uӪ4H~M⇮%gqy7]A;xEF-69e_}b3Uj-048*gg {oHML7o|]~U(Nv~U.h>tw'>DvPM}7 ٵeq){/F;& X5FYy['u\ I'D<kQaOw1z=ᡔM8t<0"eMVG̦$t,8F:In#`rM?9&2ѥKr~]sq {8Uh -2 ٷW]t %M ɒ#sm mّsO;*vBiOBG)XRݾq֓O՛3Uv/vӭM or\#:VR8 r I녭K,OsX; c'd o'֗YBU"N,e?+sf}-}| ꢨ'3z$U h5@/tdC7ir,^CƸ6FUL/G̊ ^pvEqwѢݷO*f*8a ;~hW6k# EП ҔINõΤaf1 y(ESZE⢘h>-ռ,` ^^eNe!j.ƙs t7TVjm84L!5~^_uAv&ZGJbjޠF(*M@rԕ|KV꒽b)(_! rQP %pXэԏ㙏VFq#dAl bV,.r!yKEuo8ڧfֈ D` S'%E"hP-K1.kkx/[j>/[H۱3";MFӰHT8\ó,$xt#C51{2Yzlhf' c-bS^az]Ul=<ٚ3QCI&g鬘 ʴR S6QljQ1Z򛮲"D8#9 Q7.,6lrO;@`C9ʘ#vvRW{ti-۔[Z> xOaxiөM)kk%繕LZZF{I`N&m &Oi.]OCq Z;lnwp,--F8ePiV Kh3s"<9&1ԣ0l9ֈF`eX!8Q+AWD:ʠE2X5 ?Z[#cD0yDbY\f(qjMW\P a>[m.IEٹ)ȶo&*"Wc%-+q߹`$%MS RŮzY{~ǝG|F&%:}]A!=@Ak+:HLF|ul%,rEΗ)1K Wz̳,WКJ|+g{:^MPޯ4ጎL-#mL2li.,u'N¦kB(-׶Nf^^Dٺ"y,<;^_l\~u3p8WU_8 ]g9c]sF{@ץ (Ͷ\ P_!N;1;cEŃMqHg jyJ erBXшdLҳ6sزk1JrbXi->{~P(!8vB6[mt*$a5S1X Sw<XDQ Ң J*H _Z?*wŞEr:O+Rw&ȏ媱*ajdr Wc?% Q\yÚmNsBqn$"H\5-'Rm#݃oU ؐT I=5_n#xZ5GUo1l8v@Mj'ml3%9(*Q!Bd;/܁^})#Z<0\4:=oO;5zlU3읞^ț iD-\.U >#ucgLuVUhg[BB^bIpֶz"\> ڃ) QHq~O|%11>vv3,Fش|Bk:p5fA ˠrc2e}yrZK5qG҆CTZk(j#:**R"5pUb?\"vrZ(]L(yo G) :4u[ޮE!wojYNJBskq~697w"JݣMx">5:vR8CAhgaJ-x( tWSQ! GwW9y `hq^Fc˿~Re+.@%QVɽ\>Za2dKa@a⍸`=eC%^UlϹnu zE׆3G"zi[ ^1fyCR;C湘fgA% w M8B(hػ؞E]y(ѕ6mny+y 3h=bn}KnՌŮs({ık]q᡽E.9ޕȿ8ώ}`Kv_=ߗuPiRνeH|8H$u׳kHh-J5iO٨×Ó(ȱiԽ~ _j Kۆ [Hds Tm|_|Y!`zʄ ^6i=Ҷxn:P/l:FHh O)܍/dю6Mk~2 E$ t t3^@.evKD Z2;0FEW$p~e?>yiG3, g]SH> 7nz3R3nQ{nW,=56 &b%5P+k3Eas\Gaor`i{qS"o_s7ƷwρG#󻤘1M0= wҐ4f/ 6RMi#jD]+{uH }p mpB?ppJ2#C@MsT9IN lJA"u8M:| 5TI-ó-ycg-_!뱤Ǯ; *{eke !Kvо|Ɗ.5b˒pXk2V=Y[ۑKL3{Z92pf/iDj#RFB؋қf-H s.z#T4@ w#9F=V]>i-?uѸR!e-%Mz ,J$4 @MTǘ+y1 %\(׳E̤ Vzg\W]{0VVXH]4l9.MZkSE{t0qfgΧ !H NU `y0WΪmkqikvM7Hp^i {| z79^"4CD` %#s9I 4 qw^}_!w槳o,b7`p P(XOT/D =cO@wO9r&{@\`;@/n; 7eOZ072眿/Csx5?襗Sֹ n&fm6=/pCya7MZMm^I&b6eM7ݚ.hxx[5[?>y_o_;=#kD-!{Bq@9y{yn-nOGֆ8oiPym'IKƈZR57X#X7`fȩGtCpw~t"*ߪuZDz(h2|x7QT/ZEi%ˉ`:ĴQ$sD%448K-\J3r^`W>0!쯰S|+T qQ^&̧E9? Ъ7ݙHFxi$oQlo$m =LYK5 q찑6f#Z̀(M]%̬΅n gHexj>^]i)1)~N$XP9`O߭G:-qEMҭ./u_Hm&' KG:yFFF )"1o2@IX:M|D\&mF݇/&PR ~k$:e$;6wڥ)It~F{4{%SKaEo#F*Of}eQI|,Zu;M*ⰒOUu=u" kv ;NEoŎ?b`doW+sR1[R0srV GGee JHwL`ȊŖ?# ɦͻ+1T4.Rp6㽒8E.`P30hSa_^. ;ȶwV+HWBɁ650OqsܐG22WBsT? Q喪P6nWKE\WtK艑,_(%rt=OC%Jf?^3mw_ 8ﷃ}.=KomJ,𤨈~'(g'ObCV䵯7y'U#fnjU W]32,iT?SU%+"xԶޮKvBKrkI{+x76nAkLe!culCy =J덤٦5z58-0w'WIfPdj%s"V&u=_^dnsfZRAwr"!zw?+2Sjkޞzc1ҟ# ׂ &z3T}/ı&SjavX6f ҵlQg[#_S~Y쨝2F+n-}c_K7 [(&e]X ..4BӒBEs㋼KGHFW~i43mzbSORu&R(~4rlSKYA"n9nJfzHҾ+/} % Xr'O8y) 8o Izl<]+:ҏ}-]`R#Z 5\G?ơAn}g7Y#x `2y!Q1@Ђ6lƬI;&bO^Y)NPP@A6(H本;l m o ޛNy{#>h߯,ЊV+7Du>H( ux!/MQ9th2}k+/nQp_QJף KvX(q54(K"0į3F\I ͭW/v)C1ɸ[O񟒼Dݭo:zo_?_>ݧW/vo_Qϟ}?j݃/ zY>oe7L}W}w//^6J~F%_ r_ ٣wK~y]*"|awe7U[6ۧO/ۗ/Tl2"߆}<]|ybYp(IS);U:X⅖yGC~. _akAqé ;aAr;%]$T`rZu]D`؁FGp)BcdWH{J^\yfQN tG#1GNɌZv4n8^PZ}mҸ .oɜMUC[)x$-Co4_AS|WMfqH+g tr-z79J[ف,jI@*7DhC16>>UKJO{4A*c7Po`sugl*g̼ 杏 +h VRW#IҠR+h>v0Ω[ͤgead`\k1}AV whɀ|֟|䊲~cI$-b5,-,KzKoq+Y+зrRta!)P JfPѼ2)+sЮkz5ݬ%0QB"Ǟwq\,Yf'P$;O1i$:%16l> Jj@IH03!hPLMsLr\(cTѩTȵHvhdXL[h 緿K3snI1n1U^h0~~ _DqjV$K6eY9L.o5dO當-X,nϥŭ[K6X&yt-Ws[koՍNtٿL#ey,c^ [6]MSЖ=+JE3V{|~aT;eQݳd:Tn5KJfﰰpAϸ3V:g,*+-Gi@WjOγ$؀#I֘|FLl-ї#@V~unr\ cl DhNؘJpļtC#-1|*>1#N frPRdi˃&.CzKjt0x1X *I6ZsKP1 dF<2qͅw` B;,AKBe1!ch{+fvZw%޻y ܂K(^Y/FFj>A5[Q8f!!~fe<8|6G5;RVTte$Xz˝-XVrIPƺz'kigJРo]e+F eF;UsçS,7xR3p`Z GaaCx`,a)!i? c{728*C&%/0 6|0VLHYwUƮ!*,|ii.bV {T)iɯOo:'Tp} NT7Mg[<|v)1&ꎝXmy+a%*\AZȪ'o\'@N/E_bqy xLjLTڛ 9ů?ⲉZg0B埦յ*ʞV.*$.l"bhz  6L%/Rl'GSiT6rD Z߫N_-#Sd,_Lѕsl ymShA_rWF'"tzßo.[X h9c(LoeU{z5{@'99.%SYãYo8'61e 綽Ƀ\zpҌ%8{ӟ@43&}䰏GF8Qs㚃\Uaja%a֔8],bb-C8X.w#@2[Eĸ#rs1OT.%zQ?O&Bi ׍m,▊ᕌa>݊nxGg$,E^^OF5H]4 hBZ[FUBkM㒩L9 K.;7d#F-鶼Fn`FE^&$+ѝn ~\['vbᓛG]Pc]jpӬi:@x>%k U4sty|= /aZ" c ˄6moO 3#1ՠc>T,Œ^jYaY ]]5؄> [?)n>uPB&2rה۽T]n:ظ) .ڊF6pXh@-GY;ʍ?o˒4Fvm:! W+kKI zi|"tx'P֒aaž ŃGgEqVO$Yz祲1D_/~Ej%bVfQu EZ$͇SXX]WxġbTgY]Jmv1ַlF`)ys3??2I&;TϪX9UFiO U]Q.ODY %4Ga+9Ug_|(aV/!Gf;x,E,]O(s}۠ކ_Xx\Qq5y[tĹ-PoA1Fd$K}+lGOtMgQ=4,g`R˰$ɸC֋R*t6(ȁh@(+)R uDfa|f0v"bv&xp) oypHsD xm-{ahz9Ʒa р[>6'qr:Rhyoy $ofre}Ʀ[ٶY㙽*"^aD!VX{ ̂C(hP͇aۅ%Obdk@.Ƿm#hR߅lH30[d/بW*+(#Iˉ3X@ W&YPĚZHv(%f$GqW@%?nބ$d;Qw8> R0}tox@^DhlO,7rʤ?ϛT(}tnb>[jQس31 []a~|cp|t[ :M,Ugupz>.tq.|6]ۙoR!cG>Q_1"bHSbⲊQdlp8g>&m UZz rx0Z1mXeN2B=G>&ߤ'IsWI #Ho=Sq`1TGRE@=VdFq,{eՅ}.LDb;΀9(6QY7DH.AQWB-@wU1 A QsJ`+7W$_I"L${,vUvJQ eǂ+9Pe_W`cQc nOk{/w}ޓ'H},y/_<كﻏP@?UPZw.PmT%H=5,~60C# j( AASI+e3HL(<GyRIpjs6^;=@Dm/ZțS*BN&WuXD: UP [cX#Jdu+ Ac ؆ո2i  BԹV|a+ɓH3\"Sː9]5}&&OMFpN ]ڹ'Fk7kE O)4ً;D6Tb~+N[ɫZyihjnβѬ: G@1%4b*0 pIfMh3yHU9߰!Atj+פÕTO>tx$$H'&Ɠ^dȤ6&wdHVjAiƶ"Wr(G7GZOb6Ǯ}o5\f(xi[Ú&~##j jM_Ok VUzz7SE]17k]pB Jc6cۛ6.mH(WV'saTl-a.>Bc4[yY`d᥿'/cpMz=Tf$0c||c1waw][IE0Kٴ7ǹs9w\K& ŴRtp^BЮ&H0MóMLp`c]7 2]$B_dwn ւ ғl`#Dgl2$Mw ;0`s.Ql \Y?!ߞs5'PyBZ!g6.@pTI mQһT)tVlџ{Vh&!x|XpF;lGVJm\:lnhRA636&bB9:JU +i%iafvnY QO ң%~s[ƞd@Jd]⹲iIbC { e&#V̠0g׷PeO`_23KNh(Le(z46Ye,F>[7贠 K uMHK1Ds:qTdpZeRjƇ܋-d+ ΞnRU<yYmdD̓a'~r'BFJ4Mde-qgGme;:|5؇i|v9Ȁ+10$pߩ0(HTnMRv-a{_peiԖPv60jZ'VXSEU'W1)§umF6q^bI-[aHנa=F(K38 򲼱l;KEлU1yҎX*3pKWVܴĠZFA>n=' \Iv2~KbuJ t-uKT+q#y 4=9X  g)2K@/8Bstm)Aװ{lnBƬdb',zx8F[76!|%3"ITToUn"ZިC_E,Z1W&b4H9 (+ y,e[ptL 0QdݛXyǘr_Yn)5>߷m;xrWԘWu q#:m>8kL]Axj :yFQ $ u/0~6JVjB#;\h4Yh۾3Uvo|&M# JWNa3 j\F`aƂ |x%l;:ڶ+ b5l"P\,.+Z;e3{V$Kr6MD>N"~J%MG(1]i٫^(ɶmb<Y@L1 %w^ k^Dbd}1yX06YgthR ,2yY>aWV|}AK-gj~QV=n"sk]<DO'}-+lD%qYiнeM6?o:tgO{[ӅfGl hcN]IWsio܆:.g2cukrJ768 O*¤2| }RY+$Z/*|8%yuҘJ:2> D1< 뤌s6v7l@!9} SHz<&d?1=M5G}]ei(gԢrϲaҝyY0/Pnwdbu Vޏ_ۧ}.\w.GssRRȉ'*W@|2/jpUm2=Ynhԇe.g$'D*([ZRnd$ 9~E `: XF"‡IMB`WIHhVXeJ"|Q6dhWAIU-T=ROI pMfM.]] Mu`qAck<{{ 5lz7FFlސqxӉVmw*qY*r|YU޴TۼIk`Zv60dk[|oZңꮐ^YJ?*$AY)1_ 1>//TK?Q*RqJ(|ePbU|e82ͤ吳ڒBAv Xh%O?V"˜zd$$+9>\ ./"pM-KX@3|fC K`| &h '|G 5J h\Z襙UcRteѮFM/z ^R]]EH- 0Luam'm,;EQ \ڢn`)qBFKYx-N=׳Vɼ? :i *d9:}bg MȰ$BQ/Qюs)EQU+BL/RuPIDyŻP"Œq R2_ Ctj>Y tI<VA]N|QC5Zn*h|a pބH%u WD[[Og sm*b63 B-:@TA^Jd>A29P3u):ʴZP9u''ߒje`N2 &dQ \R= ni6 d_"=. U$_F9w3f74 VV6D f.xS1s_⶛hkĘ" "J:?aHC3QYUvMhG%+KARF4)q@E⽒jaOgP9u!M4*8614.xb!Cpb,fhKŃ^/UGE`,Y n6.[l^1>$ 0 EJ>m( W zI(0a=#ʊQxKppy[/>7~n't< O53U膓4^&7|u Jss4ep |B'r:SZN=_tVO 4xnW)P*ؐ33x4|hk7IcǶb$:-.Pgn 8-;?CH LoP :Q'Ó#\beuWu ӾZLEyߗݠzH}Vq4  1A'Kcpf",t ģhilqky 1%'aB. 5=>; n17؀"o41T[yJIX4o? ƧE ;n QXL9>-[M5`_u*h Y)A2:Y8ίTUPZ:`'y sT5ḗYՀ__$+3NRI7ORhLj%)~m+Q*ugƒ5j6t>kUdw+4֢Gި' u?J\=s|Ե? DSdcU?(I40nNQSrK:8iaO>+a_:z0i#wGf?WYuJ{]%UGrcReS|T 5N0X)"c wALz^,=/;M'WMB8vIw<G5F$7]d{BNJPq>*' T}Q%N(p@`1lA05wP}" 6+FMjgʅhx̉"x$?gwepr 7SWQƓ4Xؒ;p6[K#cZj^DwԻp?W~x<_MPWff?3M; z~s7uCa!*GiFbWwtI5e5 DVR?G\9O+<\2t(n-V?^SBf af V)3^LjՑ!E2ԈA4wĴHފT'R(d)2u @FZS÷ p^҉.U`dTKڮ`Y$E1ŁRu%]CnREϏZmbwsy>>stW0{Q  UJɠ.$pld0r@!`مp;Aa7=KPְ}o=(k^}$Γ#ʻTX+qjpItӔ}\&oPtQ>Jn\_nfq ^]jB<8n_$@͝U3ZHۓQq&m8fY+wγ G0[Xz2-I}ulgNggL,kZ-6+H %~|\A) #D&lI$o>i$[Zd'Py;1Ί;FJ;Cу];^-9ȔRm|B4 hI}9SjRm>8Nlqd\ thWcI=\NH(Nm$'?)ieS2vZA sBBHjRAO_(+8ty[[ƺ2ރ2Nz3ȕzޞkހJ#ITjA3ҽ'nH 6c2 ~1[ee$o&vRl)4/uc^9&;R-'f6cE3SAD%$Ԩ0h.]UCkI &&NfL X#>.Rda 0 i&?`&, #:{."rdG*ҭMrNn$I%i][M{a0 KFt>ޚO`hˆSmWMy-ְ7 ԸУ1)tu]-peXgrPGq)rN[ ^_5Mhhۢ j,z#ϴFсEG~|;q%wyҍ/Ef%pRL N }s䛶1 0` }"=&uO*LW6ͬրǮ''[oRQsQR$j~RgТg\Kl}5oQHvS|/xK6ΚK{i?;ͺEf B: D@"辗[4@:εXn2wNGMS :ңؠ00gfǕf| rzGHY7e>s&Iu6ΎRQmp[_NPc%6e9FtbT(*= J%vDL9[qnf{*oKҸ_ҌfuХ񫲲K(l,d)mL K ;h6^HO(#5g6hD3֔# +^ + m_PGQKߚ@t)HWX+ҳMT ֔F>&Pf`s&&N #|W$.;s2@^3*ڔXrpf[mݵ]TYYj.nb1Ӎ?tN ;{9}W .˜rfϦ(PǴV ^봠wI$`lD%*G1fzƯgo,' 0O1;t&v.' Y/(>'B&9 :e2 c@C3s-X.UF3giҗfK_]fpi JH;[P?s^a)DA+ۼcm1dIo$744K6. vY`d[LҀJ)b]?"!^A wn}Qͷ7KSMn0!t3I4BEMpt)-HT5hwƤX#30[ܳ&AZtx*p9z.* wyza//Tv"@?vls0N#hG,گUGx-o*I.+*ՠ iphw lΊzyRTa"q8޲-$l" 0%q@0lEZ Rip┢i1d 6m#V>LiyE<ltR;4G|]$(11ƆF`# 6c]38phD[|]n90QdƗNߠAGȎe`4&TMs(ĭ:#.k͟buH *]d<"egG6҇QilHWoUAvA%@q0"q{P[25zK"}T7Xy6O8.oNigWp[-P6QuDڴ&'9;:ߌb-ci @Jdd(aհ Cܑ l*1 <}¡@J\izA=AZ.I,P %rF@X1!^؜2uL^㍍M*“1DŽ&c$ ֐Kl,c'eJTj\)ok.}gEANOkj_tބO̯dZ&Ҙΰ*^MGufU)W,];ܻ "N.n9*l`Xtg21f/R), 8(h hCS04f`P${3^X^ي|z˴yh|Okl5w:P"2>ΊB_&_xfI8b}Bg{~T5mlItsJ/SiNPo4{Mf _%1VFP!*(Ƭ੺kSVl\rbEk+33Eib[cޝ Kf׉χt|!xY\OQՃtt6|xwK_:XwqͿt*r搜 ;Fĸ"6qaPiRJR^Ȅ.S#3= E6',ێ}3S)iRk&;ALM1>:>GgAW_:H5nydRT5PnvUMFc,w3AFPg?utܝAW,瀞Zs4Q-d I1}4{ͺ-Kq Q-DK]!6V/F4CǮJ+L6Sw0 r!ݺ]'o*W8BSޭp$"N=b;OpBcXveScwh< . rEL໇i?UKJkWD$e陡Dٴz?F$-ba9ŃL(-l;= y|O 3af SJanEP'vlpI4 IC\Ѫ'(^[<9Ni++ˆeN0Cㄡm=(uh&v,F&yk8;վp|%}`o<A_ Qx F͗/L~M;Y>2 C[hPp j.LLr1gEM'.HSB9ʞ.I{  :9 7eO]XRU{.s҄:\R nhA<zǼBJt lˊO)TELj{{<3Ɂa /}-IᩛzFJb}:6);(vjFD]J)50t0U //Y(3 -$fR#r0!E;oDS-8(q$}WK'c#xC]ah|t峼VVp}=i$MoIdSL](FD]*YpN/4>\cA뵰slE; co˻;r;=aO2<2%|>B1 D^oJYT-HԹ?o$x?~A3E|oϾ})~K2{՞v(8$jl*iܣS>d+ U/(Svx#-542q2x*VY6taLЫ&Muq2B- RTUx:ԋc$uk>AHedug h=dac'1&09fblrŧTw.[n;OIN?&z*G'0v5Wpz4wAjYSmU louJq&_˹e3.c>=}1OŋKD Y.l-ԃ(#Ƈr` )zG/è_47ݭ*4{!F5_)+1er4S3k2ZV"dУⰢUXxIGSx`ڽWicnb=0WUKR66"A r {iͷqav'q{%pR2MШ qL*ٝtza6w)u'AՎn6)|Ž@Ŏo57F޳ $O^QqZ^o1'H6t pRp7є' =0˾{1ĚΨEyu$JU^vw (8}˥k%ID'WfpcJAyZE2ADWT#ZLL[9GD,ŷџfەlo޻wO` YE}Wv{N`Ke[ 'n cCB@79 ԖIa׋7ٕ]Ewcs:4X[7DzcA0p| 2UϷ{kwVIm%.F ԭ=W1-;"JORMOs'ªi!fS+nI-sEikL1j86riJ.A MB$bɵ9NB+o*9>:M?4>H /r=S~`ںXvΒ/{٭;w?v0(6U>J/ vCl9BQWB>лֵ!嚸:h7{ ^Fە)WpK%\8{G=(w/0ח̬]q);vH1{ 7@)GxƝ\ 7UTlɶrDkQ慄,dF&ÇW@c#hE4>Sfsc X1g.R4]3"?e= V &Fe:&tPËa)r3k`,΋ ?{scZ0.:}zZnIB5Vv{ &xB9]9{v YOֽp:9ҍnKP2ocX9~dey 2 {@ك/T2#-v f-JYpd1m 1طn<gi}-<ɏxbg4{R&0m25"tAPX tH+ĖCm*Hŗ(u5F|@G7/6[ŧ&R%i +yyX{H޲&Q:|wѩN?\wSs$Ѫ$IR)fעk}(@GXVZ7]09&*T/Jja,b$U6mq)QC(ܮVS ](3}v#ұo8PB#DY8O/z~/N$t;GRbԋC[J__f_ÑJ.=x94[&$eYQܡM L,U 0d hFdM~wP _HبWS{<< YSYZ_lglɁ1N#H|b`q=ٕûvyKul1:$zaqOA[ J6Q6*k^w8$ ,s7{H-`F,eI$s!3-ͬ X0$fzSdjBP*#sNy2X2 iazWO~8 z,ì{h@p2EK-I[1C4#OxU:ykS*@C LYkfu<2?|aTTWAOhپ͇tӪ}kvdf\gSl9†1+JhW,슥声 iaIfx_ +yoq: pc> ^8&Md#%#Dsc~o`BޯZH6 ̋jl_ChÕWRx-i./|T ӵw?GYslGU ~:@Ég?PF?m$5~z|vtl"\ W!IGSmf+P4̽q&)0Ɓ' P>ÒO:<9J`@ۇO3u| 󡜮4%QW=HD(֤ 3~|T%OjD֨.-i둎?&@dnHuZZ1̈́ yI~y1,g 6O spГ-8Yd9boo/c8'|"ߩ2E(b-o0C,Ŷcm '9>'0ms#s-eP N1òќ1aEi`Zbi<+gf. Tx3CubDyR_uV<璎~I>/Zm pClnu-obH&XFl1ZNSFA/_WW%脿Ҟ2KJOCEj K˺vfV.גJehW}bS[4{CԒ{SD*?lsrb-iDtDϴJZa{k+ڒ/ Kqݝ nռ*QL]n+"4p*@*8F$,q(nĤ: =?|e,eכ~}~U*u5ywv]O[T zU2E,?[җJZTA[}/?<ё kdx CL{Yaĝ'ꜺyPI OE.=XK8\vV1+uJ~Nu,y۱vW macS%AH]K+vr EٴM(W}iW:dQnb̡3vWm~ # D' 6δR)wt%_45V ԋh]$^{FӐe^f ؽ*S@$'"F^x!س^ t4^纯ɤJRg hqjp!* d&1)HEczɦD-#CJR7jȻ {>%H.643X#DBFV39>/ݝr.LA^8?"59I[[IdAG1=SUN${nwL\v9FvA. ts[u@}H^¿d2NdRkIVglWtl"1yۅvф`b! {|cZ@i PΥnY#lulB{8wNW3qnZK”~G7$99A\ր LYHXL8,_AQaJ> 1N)]ܐU"9R|rIieg#6F!5ǪuxQ@usx2w1a";0\ͧhƖ_QDl<qLg4>e[95Gҫ,ﲋ)&.Dc1X?]H ٭1ڨ2v'“ ]qxa-m4^S궸n- NNv`#|}d=:[wX{׸ /(Y`A>xM%Llr_DV~q.%0Ě`EQ^ptQˌ͊ߌQш$۷S}}]sԸ&Ho@yC\8dmm%5 F{ UrLS/iԟlU{U9 bdw0swuX5aeƢ􋰦RЍ?>gއ8B:QÇna=cj]fTJX)wIFH7S}d$Hbcy\2 {$aƀL0|ɅݎTd ǥ4jwϳY6S>?MF?p>#DITRO7GY\?<|뜥s00@KwHz5?)JHX24NոrhшI|<ߏeʡٚ>I}PFLJWbZOG,N. ka6DVUz&{&٦|*ɥ/.ӋImdF|oYϼ~(ЛHR ٓR}`ᩊ ,h;H}a}B%m$' 7ަRpb̽;'<5M163_kIw& Szzjm,sr5?ҟc[Q ʈUfY˧$_ݟdi8IF'9'SH8pl5"$/ Φ2T^꽤o.©Կ6O"XSNf8QT\S'Ծ BvHeh{=ksGr+`TC=LQR]VLr2/\ `IٹU%\8s%uW*ݗMUN\T2= 9"~ttKpuwh\!g6)C]瞌\*8 8'>6Oʪ.v8T9} p;l8ح,R"|ˊK+?.#+Id\@ +KyaaaA!H/R⃽ҕJޡSDF<c**\n@l=4++w?so@ )(++34 R[&8]$5~> &z-/ T Cb Ϛ ]&i ,yɨD`V0CiL/zF{]q1%!xi͞HùXNON9g:d2v &pkdhD41w.$Lȗ7 \(IB:Q(.^kĬoj m=)+3iQ"-,@=g|K>*z ?\pŦ-Kp5 W`#zpGlvy@9YQ-ye0z1I6Rħ,V7@3 ӚIoX5a-]O7Unn;EaY,p_U"=0nEՂen_de>U/Ync ŰeD~8&Z"Ԩ~cgb/h_׵:z( ՚4 1{YxPʭGШ !QΝ.N&Q;eTdM/ʣuSW(7{-yਘÜxMP3Hϑ|HyϘ}|#g\-|[-P޼e1O0ɂdW[mlejfB+50Lls6,W_y|rr\ <" ,$\VV܋ybTBdԃ`ل̄[Am!c?$WK&e>8QB:C`<s^LWhUVǂ5~u1/x$&xaz4maO6 Pi=Ե^ꆺ+UoZ$b2^Ka(\V]/gy誣$4a] otsH׃uv0_FjO'ZE:FqrP*ݑHY-eJKZT:i(\@ -1FP,!RuS^J$$@m2KC a+ݩ1ԦC\i\9h/wZ Ϥ%b 2q: $]32ذp[G+:{Z[ɤ( Uf;vokjfT6Vpa܁tkm_F~)V!8mnf%*sco( +I!2ɴOzdtt ?ݢA*gia ij$6À'#RKr;Ѱ%^oE &/*WM[)n&6΢#7Fybxv{W|_ۋa0Ğ«$.ҎXpG!3^$#9xR?:s P3`4nJR>7HZph4%C ʪ'x뺊`mJkFZh_ %8]ߏjn^}Xg`S/sJ,\Owxr}Hc2֥@;` Q_70씊02Yr6uqb Kp9eLRq_l1f ^9u]vA1eJ5M0LrEx6ҘYEȧDc6:j21A󬅉,qS̩L5pf>q^\.ӕ2&:ʡmRghR}۫4D! kfԣ7vviEÇ&"CΕE@Wh w(b7>femYN58rJd=aJWil$&#*Q29XB+ZĽ]~Dp $kcTTgvxf&^\ztRn3qcMgC!dEn$\U {۩ec#aq(l":u7a\x\Vf+jX6[,CX~(5OSnЩr=șLfL]aM䓈K;Ay61S2Mͩ"A@_c{7lK!Rۚ8jZ =|NXKDR;zȤIXҘ0ss2m,jQMuR,Cv=mAh-npYޢRj3r&vJ܁'wU\frPbWoX#4tRQ/'tpgCu6 XLo0a ` NcJ<|h}wN+WNJ0%~ԏƾtL :p雅|?5PU4GBȂ8y,\\`(y rBzVv_7]!6eB 1D8l GZK mN}5 |^n)n$=G*ӣP ,LLDuRCIƶwHl%٪-P&n?YB)i`3};'GR÷ȎH4) {W]b+E*x* *OBhS0[}XVބ\XN-7L±rޕ#q80Ľ $ba[J XoÀboOFEj\q_E Z2YJSZ^B+9髫Kb8glE%@oPݦNe;ě΋bVֶv+vH'=OSeI֨Eh_Z:M[Ml7f dH*^ʶ笔4UK **mD Xle*U6^/x_C(^e}B @ ֮+We6K E;Q37E*mZ__ I0 {Yc+vcR65 (uqjچ(]%mQ:z; J7xMQc,6׶ڡW6% Hs]4SIk݆şD(iʒ6*t$jXn=Zk֞疜u.묻L >/ouUdIPB=4+M~Qu67`<ߍk(8 qpŭQ+ 2H/Lo8K]wx4<ԉw^5&^DΖѼ5' g,=-f >qjRkkDb/ cIKڛMguEtj,orPwzKbE#k8R%Pw-p` n$k*h&hg4gk#+طJD7V7y͔ZNf%jMHǵ>L F3wzcn2x+H.hJMO*!Wԝy9%L si/`/>O 3)|}_|w&yw:xW?Ffϟ;ϟ>%O3g?xK?=g=ųX;?}~?"W~Տ~_?k+! Syj`f\p$iIh?~_|9Sj6C~?~3+D>MxዧY4 'xO>y__<œ;Vjt- Hpi.m|v`;2,jP).~؇K7.=tG  ?8m(yA2sMGWaVCވGz.Lczl5'pnUke/*:0l﷣8}nLѾpY&2J3{91=8X!l6"G}8Ǻ MvR/C>D5•^8_2|X.ylݢ")Sc~^,E}/ X<nIq)xBޅoGq<.!o(hV"$Y ]]_ӲQ܁ 8|H25:(燆ԣlSGA7TO:'9~(dα xDRSr4-]D`oQz?zkMzO B4C>w1 !>fa ږfyTcf(vxϩ`Ĉ`ٖGrO4gφ<4[ 吱EL5v10I ێzճ!Ec0qZi֦8U!aՔv*\Yw-q$yZ+/Eb䪱kbP0N#uXH4Є2,xmƴU8`7ъ("6M3`C@r^#Ħz<вAw)VV3)wVV\Ӊm?׹0$qJ0 j&lҳ[3]8x„v˜k<ɮ,@7 *[0;¶טDu-tLᠯ5ԙ.3Y@HW Ѷ8@޼\بm&%vN..(f _'YuYrc؄ RRAbYfL~9t˓?E$AD002kR(=Dǡbh6.v]ޓr'_bޭIo\˜]-8P)#$2B-#-IҁY2EPsϤU? xUG}>ww1 td0eǓa7e&4Ί#8xߛK=Aڡd#hE1 ?ì5Fԇ͠2ݔ}0|jple_n2Sɤgnrlw/ջD V:d0F6\-^9fxIF9c cr|J%K)*Ah GGhǧ7.S 5&ѣr),R R{,LMv>%@J|ٳ?.s[ɭoZ "z9kt3G*ojYP!57a3['RK)`pC'K2M!gPk{ƪ߆7`߫Ḷ14We-IB_8rL.&S2f.$:S-LfbN}٢5/EHgyp ;^}BM-(I^=.xmA_>hIc0<yjv \di #gxS Ӓ$[)@7F7LKLcUelKU" :X^r`׏VK5Leeku!ra 1ESVOL{֠;(ɒp|~M@)&؄*#4.sKť"np~d, ~d%?$ZqSz96.F uM @ٹZv}Éq٤ǩ s@a)knH/]wBZa$O Pۈ nON6cy+R \>=oRdѣE#W ^) PKyVS!pip/_vendor/six.py=s6)1O%vڻԝ:>Nvyh,a[@OrnΓH`w],/Q\%<7{߼~zx/?E $;/%YiđR1mFGbH)nr+G"ĒE< [ JŕPϲ7 7Mc/pc/_(s3,q2V6bp0C*Ɨn]ՙ!qDYxHe@^ȇE@tG pB=ۑX~0_I[0H#H|gb'#ĂkT{q!Aa9KlDb<^Tk O19<]g>oKq>bӋ1ώN>"89/~3|"j|p".>O./@l|q]P._E\~:<9€gGOƿ|O>Cc1U;:9ć_ ` y_>c"ygX x$'K)V 8D0ώ ϗI]"2WT;;s`q`30} BK,Ebnwvf ,O`v!4.wvT,,T'* 1ʼn~OWEVZtgqH1hV&EsG.3Q2SiJNWJ3Nbv>_rGljꦒڙ;Gd%Guyepg2t3d)v {! U}G*tT) e?( +9,D.x\4.8p_f9o4;hN %d@9+ u4qfA-(_1asNܿVd%5ch0SF7$GH1 ?&ޭ,р. ^f؈qx/AKBR;hbYvcPQs6A菄{~5B F%Qf:/VC:{=ǨDf*6mizK6Fd h p\/DW7.8"LAO]p) (C_z$jC+7~,Z퍱(x%IGvPmY;]H׹HAP-_`ꢚФN+T@tIZWVNg!U+~has}QS.Sp9  P,l-F" mlD)!9wLb.Bv,0"Ct`&;Ia6Q(TB4Uc.`B]jD<5*]?D*?( LCm4m5/aP"(ݞNGWj̯%{k\bCUҠ>i%e^5=4/b0s̡}ra*Nf߈8* VϩZnJhX]ZB R)P5L~ řB[2oU\4_BŐLk]+Y٫~Y#4p6Փ5_#bΙ,.6TSD[wij5Tx:K׻so{4ǁK%"e8jş!TD8LsHoB%z5s4V_jk ZopVjs*G08 %v'0ШyCTO#o?~CC+XӠtpp-Cr{֐E>붙z_ARH'Ҽ>]\o5f> qӆ)n:b \WEJOǧ+)dBG(IDZ&Yi3hkFqA.5ZrU%P!Kܳ 4SH)8e\Eh$pI*N[::Eq`<. \_el[aػYZN݌T=~' cnoP<<[&Gxk1`H^Rk#*p.JNUfcW1+#p :wڠ"+Fثgnk@A'<0H4f " WjR00{W5YF:#e'E 49нLce=>X_ugwUZ{ϓw@ZOHМ6-i5@I8$5&$p8+ګbLmg@:~ ~Z~;=\"Tvu׸CCJsZ,DT${7๪$,PKSU]wvuMH (gxRnN3&yb6Wc&sE)NKUuAuauN;_ vZN5r&, -5jJ҈`BpTX dۣ3 AH%A% TŽ%n=<ںbjꛣ90OG[WmFunEAFAjKܬh,O+=PiHb^"5:@\z:v[7v">k^ko^ \16lWJ<<99F{8 ZU_bqk/m{m7[n nz*c۪m1Pk#n );*zUU[/K.si&a akHrV_P{62[&cmj~NBச.rP'7-&'vK}.x|˺9\3+r!NoBK͕& [K#KU ~d蹤{Dc6 e:tp73RiڼZDy "ʠ"w1./iTN'Q)m!БqiU/sO(Z5@Z7'#]2 h0?Iw0czrFyyI|R3~Ty$|V *z* zldcW/]U$t|.׿]%lG g蟛M 32Xu, CwJǞ5~,؍V*w/VNRw;Et i .a[VfఅVuԫZ!g>S12HZN-Glad-gkEY N%Ii"3>R 'Krf lwAj_ҏ5`hZh }5G|={& (Cg 7/BĶ~sKBw0$;N5;3wCiT|͵@1v`3o[X4F N̛˴|uJzȆmXw S %N܄C<:s# !SBVGZezJvݑxҞQqdK=OR4~?&uPnM[8ҿ6}~a4DЁϾÆ_8<*>/7#ͣFR6 Xm4= &4[Ο`0~ƺnHqS>~NWWACL^m4>)ScІІVR! 3h+ylPօh嘝 5csX2_"iʖKlZ?ث1=E g>q D~"o|LV6m9f B*lZ%n 3t5{1AEU9ULx áQS:a쩗Hǀgǻx<(i%4=`q^ޭ7k  n.f[w*u2#Pv9#!]Usҳ@+X´h`Z4px@*;FRځm N> ߵ0Ev*㸹56L.Y0qY  \B_zŢP*:9* ë4[1pS+ҩ{3[L^K: WQUiyV9[M}ou |fU"8yQc>;p`D74 LYG3p$6D5!:|7likyHiT\kX -5@$^kI$> %*t!}8b`K:@QsܰkN$79rA͡vۂA.>d(9W$UȻYZ$C4Jo `@4h7 2#Ն 1;$Z)47nbMx6r@"օ@f?2`*N*$`c|RK f7 >y: KPf9f1$԰k8Iph*vnoҨgd.VA]AGutLwYߕs].era ,o*KJ=>[fqQ|ǹԊOHFuV3ITVRd3E 1]g>sԑ W)R5 )j`.Lϩ R#P8(erxqqN/6 ZHv+YlQɜj5tK{tu8 nqvvz56m"AVqiz,3IWxk;sq{]=bm jlmpYn&q/ݑݛMOFk&K+6,Zob^tDJwi92x5. cެ)S(:&M5~J>-QmǗ&g~ aSe["7`Y (TQO›}wk-~]J)=[i5 P#,b{g#<)tTiտɘ]53xf˗˗zqL1EEJܨx=n 5!MZhPwgdSБyMgrogCql N!3 0Ko4\觥/:?(҈xq Bw1]睾b,)I) (V3.s=KjK0& CEg' ]oʗ:I}`JD(7aH5>S@D! ~0$>Jy"X&f8whbءkډf4:C'niQ0x?cD#ux!_*nN OJ-4F@kiDc DS /UV^F"jCM.4_B6UCx!MA =2JaՂ^ʒAr'ݴ(!Q%(8X,l_6nA c;>2RAp_衽R-!ڪ7ĠWЋC"x0k b)?+Ԓ"W0eWTf, ?,\Yv{֠ua,GmDE;xM} crϖJ[7F8*'ɘGQyN@ ٷA; PKyVS=< pip/_vendor/vendor.txt-PKv s Ot*`Ŧ䶹}cV4̇k3\q}Ν_7+h2 6+M-B QIɝ̘XrUػ~޷3˒}ۺ,<43Nw:1^ s]D 5D,;VOtO:kc>AWლU{F𧻓UuGO} [Ĭ luXCL^ڿdz/`8j|zPKyVSZ.$pip/_vendor/cachecontrol/__init__.pymA 0E9ŐuNP AP8m&LR7)?Z mK$܃KO$ Z2J=#p J#=:nr/$5o)w|S_b(%7&ڡ3'z`KQq+Cq{Tar͖~{>,Y8 (D-lQdnr_·XfLA̞,4 dƊ2I蝙̿`Fo ۳0f𭝧n%tu>E@N t'^@]Ka:;4o3- 'N܎9ЗǶ~*)DmF'D]EҮZMxьlGN|o޷7q 3Bf']и&]ǰ렧o4؉FXq~nqs7ErqLL[*1ӽzs|z’{XyI(B{9[+UebqlUBsɪVO~x Cj%e^uKrq BW<; #/(M-B78`y%.HS]W󂉂*xd߱r>bg<: * % S7jLaE2p 7:^Pߊ ]eSIۉX%k\sPUB̻9._v׷($dPuer+OT'WȺ2N8S%l5 (U2C(\Ba++ɳTϻ,s纁'fTj<)S.3Ti^vVNpb%Égyhdpa`slhʧ/2>wA-OONlD }t:+:LNYgF +h?~|zLjL^K]ZS3  );u%ˎc+%" >ن YfBUk{NSI18ofA߃T]>n_Y@9;dn l\!~ Xb0_R]/`2vF鬄VkHz$Ui0# ULv {ؐ(~ j]_L/ԛ[OX՛ԦR02)M{y?M]Q* WC06<}uL\`v=ffyLa nьCkLۨPE!GFEh;OI}($ nwO' zjI}V-`}?"V:WޞA 7s{0zl4>j2H^CuK͝(돚Rl?䭪zME(W+^?uZ@`E#]YޑW(Ѵ#OʰKZBfe}ue؍;K:v3`X: Cls7zWmA^Pcܴ!\/̞Q9][;d.}=Z ae\I[> @:3&#` G=T;Ֆ "`X1{{Xstt$.0B@ehYY."8Le! A.nYk<3,`&{ޅ(svp.[ 8R(eG]KqƢ08Ntԫ/0 ꜌8l9pnËv8^:áLBo'0Vs+uJYla{ע"%;dh B g6`L"(PnGF| nt*Ռ2ޗpJ1DPXRs{/),M&7ma,C#ΐyGN;VLI&̌Ou&ٳ0`nYnG1msy8i1|N ^/kba R/Vxj׽ubeFű݇+Gq=#TcɁBTKT3Ё sCLމ 8'ʃ]>iO^ ~b͈۶pU2pӧr BVQd0Y6\2 HﴱoLI _eo1?vcJW+|gp?O@>x4om0:ƏV0:Out78ޱ :3t{giMvJ뎞j-6 QuZXP[{v/em?b?Űg#5Y:U!HjKdV&dMIrJ3A݁˻AD4RJ<ԍ%Y=>+fk$o-A݇U#V`_=h+>m8KC -<ʀ3GZJE;+0EW>i#]\l@Aݫ趡qkZ M>;WEGqG`|}Ծ\Hhχ=3i9.CIw:XA JR}@v-~n[g9ʪ?>ha=ڀai jf8j!z9swsuTؒ$HUSTU)5#H{Iw|,3߳EFEQDUn]\%)\?5ne{G+b3GQ`ڻ.E͈3*`*`Df3H: gf0Vs:G PCC(PD૲\EnE3I*M ΨHU?@ b>4Τ\E=T8I^(K(@sWh_On'y'!8QpY;hv s7v*Tj萵xc.dLD1""2 9QetU%۟ {A?a8erf0Ba*lY{C3M쑘k0q3V9( iYy9Z3W &"%h.)aE9a'4o }E:{4'&;D./ Υļ=W8S6V ˇK]DzNj)4馉8={/ҌC;ub"iVM{ẆǗzklNl#(Vge#qJ}Uo8T TҕyꜮIcg* µz%y*TJqM3!-ŒwWMFyuce*D瑐Z,yh6 o*8:h& A1)4)l޶"אE%eѽZ=N'h{>iZ|~Uڅ[cŠZ8PFo!l"8p5[h,*#H Bj?E/9ͷ06X%j[Q(q;V@\#[Mq%>Wk恅4QFqͻV^%aGiRr}Lq!ŲI-2g^{88Ƚf|5i&ߗJW T.Yd&yI{r2p>wjzC0aWK`^_E󉝴YU'Ӂc?áxuж8u,a e۠Tp-&ZgNim0C_EV1qnx$' T'+px+!J=}