����JFIFXX�����    $.' ",#(7),01444'9=82<.342  2!!22222222222222222222222222222222222222222222222222����"��4�� ���,�PG"Z_�4�˷����kjز�Z�,F+��_z�,�© �����zh6�٨�ic�fu���#ډb���_�N�?��wQ���5-�~�I���8����TK<5o�Iv-�����k�_U_�����~b�M��d����Ӝ�U�Hh��?]��E�w��Q���k�{��_}qFW7HTՑ��Y��F�?_�'ϔ��_�Ջt��=||I ��6�έ"�����D���/[�k�9���Y�8ds|\���Ҿp6�Ҵ���]��.����6�z<�v��@]�i%��$j��~�g��J>��no����pM[me�i$[����s�o�ᘨ�˸ nɜG-�ĨU�ycP�3.DB�li�;��hj���x7Z^�N�h������N3u{�:j�x�힞��#M&��jL P@_���� P��&��o8������9�����@Sz6�t7#O�ߋ �s}Yf�T���lmr����Z)'N��k�۞p����w\�Tȯ?�8`�O��i{wﭹW�[�r�� ��Q4F�׊���3m&L�=��h3����z~��#�\�l :�F,j@�� ʱ�wQT����8�"kJO���6�֚l����}���R�>ډK���]��y����&����p�}b��;N�1�m�r$�|��7�>e�@B�TM*-iH��g�D�)� E�m�|�ؘbҗ�a��Ҿ����t4���o���G��*oCN�rP���Q��@z,|?W[0�����:�n,jWiE��W��$~/�hp\��?��{(�0���+�Y8rΟ�+����>S-S����VN;�}�s?.����� w�9��˟<���Mq4�Wv'��{)0�1mB��V����W[�����8�/<� �%���wT^�5���b��)iM� pg�N�&ݝ��VO~�q���u���9� ����!��J27����$O-���! �:�%H��� ـ����y�ΠM=t{!S�� oK8������t<����è:a������[�����ա�H���~��w��Qz`�po�^ ����Q��n� �,uu�C�$ ^���,������8�#��:�6��e�|~���!�3�3.�\0��q��o�4`.|� ����y�Q�`~;�d�ׯ,��O�Zw�������`73�v�܋�<���Ȏ�� ـ4k��5�K�a�u�=9Yd��$>x�A�&�� j0� ���vF��� Y�|�y��� ~�6�@c��1vOp�Ig����4��l�OD���L����� R���c���j�_�uX6��3?nk��Wy�f;^*B� ��@�~a�`��Eu������+���6�L��.ü>��}y���}_�O�6�͐�:�YrG�X��kG�����l^w���~㒶sy��Iu�!� W ��X��N�7BV��O��!X�2����wvG�R�f�T#�����t�/?���%8�^�W�aT��G�cL�M���I��(J����1~�8�?aT ���]����AS�E��(��*E}� 2��#I/�׍qz��^t�̔���b�Yz4x���t�){ OH��+(E��A&�N�������XT��o��"�XC��'���)}�J�z�p� ��~5�}�^����+�6����w��c��Q�|Lp�d�H��}�(�.|����k��c4^�"�����Z?ȕ ��a<�L�!039C� �Eu�C�F�Ew�ç ;�n?�*o���B�8�bʝ���'#Rqf���M}7����]����s2tcS{�\icTx;�\��7K���P���ʇ Z O-��~��c>"��?�������P��E��O�8��@�8��G��Q�g�a�Վ���󁶠�䧘��_%#r�>�1�z�a��eb��qcPѵ��n���#L��� =��׀t� L�7�`��V���A{�C:�g���e@�w1 Xp3�c3�ġ����p��M"'-�@n4���fG��B3�DJ�8[Jo�ߐ���gK)ƛ��$���� ���8�3�����+���� �����6�ʻ���� ���S�kI�*KZlT _`���?��K����QK�d����B`�s}�>���`��*�>��,*@J�d�oF*����弝��O}�k��s��]��y�ߘ��c1G�V���<=�7��7����6�q�PT��tXԀ�!9*4�4Tހ3XΛex�46���Y��D ����� �BdemDa����\�_l,��G�/���֌7���Y�](�xTt^%�GE�����4�}bT���ڹ�����;Y)���B�Q��u��>J/J �⮶.�XԄ��j�ݳ�+E��d ��r�5�_D�1 ��o�� �B�x�΢�#���<��W�����8���R6�@g�M�.��� dr�D��>(otU��@x=��~v���2� ӣ�d�oBd��3�eO�6�㣷�����ݜ6��6Y��Qz`��S��{���\P�~z m5{J/L��1������<�e�ͅPu�b�]�ϔ���'������f�b� Zpw��c`"��i���BD@:)ִ�:�]��hv�E�w���T�l��P���"Ju�}��وV J��G6��. J/�Qgl߭�e�����@�z�Zev2u�)]կ�����7x���s�M�-<ɯ�c��r�v�����@��$�ޮ}lk���a���'����>x��O\�ZFu>�����ck#��&:��`�$�ai�>2Δ����l���oF[h��lE�ܺ�Πk:)���`�� $[6�����9�����kOw�\|���8}������ބ:��񶐕��I�A1/�=�2[�,�!��.}gN#�u����b��� ~��݊��}34q����d�E��Lc��$��"�[q�U�硬g^��%B �z���r�pJ�ru%v\h1Y�ne`ǥ:g���pQM~�^�Xi� ��`S�:V29.�P���V�?B�k�� AEvw%�_�9C�Q����wKekPؠ�\�;Io d�{ ߞo�c1eP����\� `����E=���@K<�Y���eڼ�J���w����{av�F�'�M�@/J��+9p���|]�����Iw &`��8���&M�hg��[�{��Xj��%��Ӓ�$��(����ʹN���<>�I���RY���K2�NPlL�ɀ)��&e����B+ь����( � �JTx���_?EZ� }@ 6�U���뙢ط�z��dWI�n` D����噥�[��uV��"�G&Ú����2g�}&m��?ċ�"����Om#��������� ��{�ON��"S�X��Ne��ysQ���@Fn��Vg���dX�~nj�]J�<�K]:��FW��b�������62�=��5f����JKw��bf�X�55��~J �%^����:�-�QIE��P��v�nZum� z � ~ə ���� ���ة����;�f��\v���g�8�1��f24;�V���ǔ�)����9���1\��c��v�/'Ƞ�w�������$�4�R-��t���� e�6�/�ġ �̕Ecy�J���u�B���<�W�ַ~�w[B1L۲�-JS΂�{���΃������A��20�c#��@ 0!1@AP"#2Q`$3V�%45a6�FRUq��� ����^7ׅ,$n�������+��F�`��2X'��0vM��p�L=������5��8������u�p~���.�`r�����\���O��,ư�0oS ��_�M�����l���4�kv\JSd���x���SW�<��Ae�IX����������$I���w�:S���y���›R��9�Q[���,�5�;�@]�%���u�@ *ro�lbI �� ��+���%m:�͇ZV�����u�̉����θau<�fc�.����{�4Ա� �Q����*�Sm��8\ujqs]{kN���)qO�y�_*dJ�b�7���yQqI&9�ԌK!�M}�R�;������S�T���1���i[U�ɵz�]��U)V�S6���3$K{�ߊ<�(� E]Զ[ǼENg�����'�\?#)Dkf��J���o��v���'�%ƞ�&K�u�!��b�35LX�Ϸ��63$K�a�;�9>,R��W��3�3� d�JeTYE.Mϧ��-�o�j3+y��y^�c�������VO�9NV\nd�1 ��!͕_)a�v;����թ�M�lWR1��)El��P;��yوÏ�u 3�k�5Pr6<�⒲l�!˞*��u־�n�!�l:����UNW ��%��Chx8vL'��X�@��*��)���̮��ˍ��� ���D-M�+J�U�kvK����+�x8��cY������?�Ԡ��~3mo��|�u@[XeY�C�\Kp�x8�oC�C�&����N�~3-H���� ��MX�s�u<`���~"WL��$8ξ��3���a�)|:@�m�\���^�`�@ҷ)�5p+��6���p�%i)P M���ngc�����#0Aruz���RL+xSS?���ʮ}()#�t��mˇ!��0}}y����<�e� �-ή�Ԩ��X������ MF���ԙ~l L.3���}�V뽺�v�����멬��Nl�)�2����^�Iq��a��M��qG��T�����c3#������3U�Ǎ���}��לS�|qa��ڃ�+���-��2�f����/��bz��ڐ�� �ݼ[2�ç����k�X�2�* �Z�d���J�G����M*9W���s{��w���T��x��y,�in�O�v��]���n����P�$�JB@=4�OTI�n��e�22a\����q�d���%�$��(���:���: /*�K[PR�fr\nڙdN���F�n�$�4�[�� U�zƶ����� �mʋ���,�ao�u 3�z� �x��Kn����\[��VFmbE;�_U��&V�Gg�]L�۪&#n%�$ɯ�dG���D�TI=�%+AB�Ru#��b4�1�»x�cs�YzڙJG��f��Il��d�eF'T� iA��T���uC�$����Y��H?����[!G`}���ͪ� �纤Hv\������j�Ex�K���!���OiƸ�Yj�+u-<���'q����uN�*�r\��+�]���<�wOZ.fp�ێ��,-*)V?j-kÊ#�`�r��dV����(�ݽBk�����G�ƛk�QmUڗe��Z���f}|����8�8��a���i��3'J�����~G_�^���d�8w������ R�`(�~�.��u���l�s+g�bv���W���lGc}��u���afE~1�Ue������Z�0�8�=e�� f@/�jqEKQQ�J��oN��J���W5~M>$6�Lt�;$ʳ{���^��6�{����v6���ķܰg�V�cnn �~z�x�«�,2�u�?cE+Ș�H؎�%�Za�)���X>uW�Tz�Nyo����s���FQƤ��$��*�&�LLXL)�1�" L��eO��ɟ�9=���:t��Z���c��Ž���Y?�ӭV�wv�~,Y��r�ۗ�|�y��GaF�����C�����.�+� ���v1���fήJ�����]�S��T��B��n5sW}y�$��~z�'�c ��8 ��� ,! �p��VN�S��N�N�q��y8z˱�A��4��*��'������2n<�s���^ǧ˭P�Jޮɏ�U�G�L�J�*#��<�V��t7�8����TĜ>��i}K%,���)[��z�21z ?�N�i�n1?T�I�R#��m-�����������������1����lA�`��fT5+��ܐ�c�q՝��ʐ��,���3�f2U�եmab��#ŠdQ�y>\��)�SLY����w#��.���ʑ�f��� ,"+�w�~�N�'�c�O�3F�������N<���)j��&��,-� �љ���֊�_�zS���TǦ����w�>��?�������n��U仆�V���e�����0���$�C�d���rP �m�׈e�Xm�Vu� �L��.�bֹ��� �[Դaզ���*��\y�8�Է:�Ez\�0�Kq�C b��̘��cө���Q��=0Y��s�N��S.���3.���O�o:���#���v7�[#߫ ��5�܎�L���Er4���9n��COWlG�^��0k�%<���ZB���aB_���������'=��{i�v�l�$�uC���mƎҝ{�c㱼�y]���W�i ��ߧc��m�H� m�"�"�����;Y�ߝ�Z�Ǔ�����:S#��|}�y�,/k�Ld� TA�(�AI$+I3��;Y*���Z��}|��ӧO��d�v��..#:n��f>�>���ȶI�TX��� 8��y����"d�R�|�)0���=���n4��6ⲑ�+��r<�O�܂~zh�z����7ܓ�HH�Ga롏���nCo�>������a ���~]���R���̲c?�6(�q�;5%� |�uj�~z8R=X��I�V=�|{v�Gj\gc��q����z�؋%M�ߍ����1y��#��@f^���^�>N�����#x#۹��6�Y~�?�dfPO��{��P�4��V��u1E1J �*|���%���JN��`eWu�zk M6���q t[�� ��g�G���v��WIG��u_ft����5�j�"�Y�:T��ɐ���*�;� e5���4����q$C��2d�}���� _S�L#m�Yp��O�.�C�;��c����Hi#֩%+) �Ӎ��ƲV���SYź��g |���tj��3�8���r|���V��1#;.SQ�A[���S������#���`n�+���$��$I �P\[�@�s��(�ED�z���P��])8�G#��0B��[ى��X�II�q<��9�~[Z멜�Z�⊔IWU&A>�P~�#��dp<�?����7���c��'~���5 ��+$���lx@�M�dm��n<=e�dyX��?{�|Aef ,|n3�<~z�ƃ�uۧ�����P��Y,�ӥQ�*g�#먙R�\���;T��i,��[9Qi歉����c>]9�� ��"�c��P�� �Md?٥��If�ت�u��k��/����F��9�c*9��Ǎ:�ØF���z�n*�@|I�ށ9����N3{'��[�'ͬ�Ҳ4��#}��!�V� Fu��,�,mTIk���v C�7v���B�6k�T9��1�*l� '~��ƞF��lU��'�M ����][ΩũJ_�{�i�I�n��$���L�� j��O�dx�����kza۪��#�E��Cl����x˘�o�����V���ɞ�ljr��)�/,�߬h�L��#��^��L�ф�,íMƁe�̩�NB�L�����iL����q�}��(��q��6IçJ$�W�E$��:������=#����(�K�B����zђ <��K(�N�۫K�w��^O{!����)�H���>x�������lx�?>Պ�+�>�W���,Ly!_�D���Ō�l���Q�!�[ �S����J��1��Ɛ�Y}��b,+�Lo�x�ɓ)����=�y�oh�@�꥟/��I��ѭ=��P�y9��� �ۍYӘ�e+�p�Jnϱ?V\SO%�(�t� ���=?MR�[Ș�����d�/ ��n�l��B�7j� ��!�;ӥ�/�[-���A�>�dN�sLj ��,ɪv��=1c�.SQ�O3�U���ƀ�ܽ�E����������̻��9G�ϷD�7(�}��Ävӌ\�y�_0[w ���<΍>����a_��[0+�L��F.�޺��f�>oN�T����q;���y\��bՃ��y�jH�<|q-eɏ�_?_9+P���Hp$�����[ux�K w�Mw��N�ی'$Y2�=��q���KB��P��~������Yul:�[<����F1�2�O���5=d����]Y�sw:���Ϯ���E��j,_Q��X��z`H1,#II ��d�wr��P˂@�ZJV����y$�\y�{}��^~���[:N����ߌ�U�������O��d�����ؾe��${p>G��3c���Ė�lʌ�� ת��[��`ϱ�-W����dg�I��ig2��� ��}s ��ؤ(%#sS@���~���3�X�nRG�~\jc3�v��ӍL��M[JB�T��s3}��j�Nʖ��W����;7��ç?=X�F=-�=����q�ߚ���#���='�c��7���ڑW�I(O+=:uxq�������������e2�zi+�kuG�R��������0�&e�n���iT^J����~\jy���p'dtG��s����O��3����9* �b#Ɋ�� p������[Bws�T�>d4�ۧs���nv�n���U���_�~,�v����ƜJ1��s�� �QIz��)�(lv8M���U=�;����56��G���s#�K���MP�=��LvyGd��}�VwWBF�'�à �?MH�U�g2�� ����!�p�7Q��j��ڴ����=��j�u��� Jn�A s���uM������e��Ɔ�Ҕ�!)'��8Ϣ�ٔ��ޝ(��Vp���צ֖d=�IC�J�Ǡ{q������kԭ�߸���i��@K����u�|�p=..�*+����x�����z[Aqġ#s2a�Ɗ���RR�)*HRsi�~�a &f��M��P����-K�L@��Z��Xy�'x�{}��Zm+���:�)�) IJ�-i�u���� ���ܒH��'�L(7�y�GӜq���� j��� 6ߌg1�g�o���,kر���tY�?W,���p���e���f�OQS��!K�۟cҒA�|ս�j�>��=⬒��˧L[�� �߿2JaB~R��u�:��Q�] �0H~���]�7��Ƽ�I���(}��cq '�ήET���q�?f�ab���ӥvr� �)o��-Q��_'����ᴎo��K������;��V���o��%���~OK ����*��b�f:���-ťIR��`B�5!RB@���ï�� �u �̯e\�_U�_������� g�ES��3�������QT��a����x����U<~�c?�*�#]�MW,[8O�a�x��]�1bC|踤�P��lw5V%�)�{t�<��d��5���0i�XSU��m:��Z�┵�i�"��1�^B�-��P�hJ��&)O��*�D��c�W��vM��)����}���P��ܗ-q����\mmζZ-l@�}��a��E�6��F�@��&Sg@���ݚ�M����� ȹ 4����#p�\H����dYDo�H���"��\��..R�B�H�z_�/5˘����6��KhJR��P�mƶi�m���3�,#c�co��q�a)*Pt����R�m�k�7x�D�E�\Y�閣_X�<���~�)���c[[�BP����6�Yq���S��0����%_����;��Àv�~�| VS؇ ��'O0��F0��\���U�-�d@�����7�SJ*z��3n��y��P����O���������m�~�P�3|Y��ʉr#�C�<�G~�.,! ���bqx���h~0=��!ǫ�jy����l�O,�[B��~��|9��ٱ����Xly�#�i�B��g%�S��������tˋ���e���ې��\[d�t)��.+u�|1 ������#�~Oj����hS�%��i.�~X���I�H�m��0n���c�1uE�q��cF�RF�o���7� �O�ꮧ� ���ۛ{��ʛi5�rw?׌#Qn�TW��~?y$��m\�\o����%W� ?=>S�N@�� �Ʈ���R����N�)�r"C�:��:����� �����#��qb��Y�. �6[��2K����2u�Ǧ�HYR��Q�MV��� �G�$��Q+.>�����nNH��q�^��� ����q��mM��V��D�+�-�#*�U�̒ ���p욳��u:�������IB���m���PV@O���r[b= �� ��1U�E��_Nm�yKbN�O���U�}�the�`�|6֮P>�\2�P�V���I�D�i�P�O;�9�r�mAHG�W�S]��J*�_�G��+kP�2����Ka�Z���H�'K�x�W�MZ%�O�YD�Rc+o��?�q��Ghm��d�S�oh�\�D�|:W������UA�Qc yT�q������~^�H��/��#p�CZ���T�I�1�ӏT����4��"�ČZ�����}��`w�#�*,ʹ�� ��0�i��課�Om�*�da��^gJ݅{���l�e9uF#T�ֲ��̲�ٞC"�q���ߍ ոޑ�o#�XZTp����@ o�8��(jd��xw�]�,f���`~�|,s��^����f�1���t��|��m�򸄭/ctr��5s��7�9Q�4�H1꠲BB@l9@���C�����+�wp�xu�£Yc�9��?`@#�o�mH�s2��)�=��2�.�l����jg�9$�Y�S�%*L������R�Y������7Z���,*=�䷘$�������arm�o�ϰ���UW.|�r�uf����IGw�t����Zwo��~5 ��YյhO+=8fF�)�W�7�L9lM�̘·Y���֘YLf�큹�pRF���99.A �"wz��=E\Z���'a� 2��Ǚ�#;�'}�G���*��l��^"q��+2FQ� hj��kŦ��${���ޮ-�T�٭cf�|�3#~�RJ����t��$b�(R��(����r���dx� >U b�&9,>���%E\� Ά�e�$��'�q't��*�א���ެ�b��-|d���SB�O�O��$�R+�H�)�܎�K��1m`;�J�2�Y~9��O�g8=vqD`K[�F)k�[���1m޼c��n���]s�k�z$@��)!I �x՝"v��9=�ZA=`Ɠi �:�E��)`7��vI��}d�YI�_ �o�:ob���o ���3Q��&D&�2=�� �Ά��;>�h����y.*ⅥS������Ӭ�+q&����j|UƧ����}���J0��WW< ۋS�)jQR�j���Ư��rN)�Gű�4Ѷ(�S)Ǣ�8��i��W52���No˓� ۍ%�5brOn�L�;�n��\G����=�^U�dI���8$�&���h��'���+�(������cȁ߫k�l��S^���cƗjԌE�ꭔ��gF���Ȓ��@���}O���*;e�v�WV���YJ\�]X'5��ղ�k�F��b 6R�o՜m��i N�i����>J����?��lPm�U��}>_Z&�KK��q�r��I�D�Չ~�q�3fL�:S�e>���E���-G���{L�6p�e,8��������QI��h��a�Xa��U�A'���ʂ���s�+טIjP�-��y�8ۈZ?J$��W�P� ��R�s�]��|�l(�ԓ��sƊi��o(��S0��Y� 8�T97.�����WiL��c�~�dxc�E|�2!�X�K�Ƙਫ਼�$((�6�~|d9u+�qd�^3�89��Y�6L�.I�����?���iI�q���9�)O/뚅����O���X��X�V��ZF[�یgQ�L��K1���RҖr@v�#��X�l��F���Нy�S�8�7�kF!A��sM���^rkp�jP�DyS$N���q��nxҍ!U�f�!eh�i�2�m���`�Y�I�9r�6� �TF���C}/�y�^���Η���5d�'��9A-��J��>{�_l+�`��A���[�'��յ�ϛ#w:݅�%��X�}�&�PSt�Q�"�-��\縵�/����$Ɨh�Xb�*�y��BS����;W�ջ_mc�����vt?2}1�;qS�d�d~u:2k5�2�R�~�z+|HE!)�Ǟl��7`��0�<�,�2*���Hl-��x�^����'_TV�gZA�'j� ^�2Ϊ��N7t�����?w�� �x1��f��Iz�C-Ȗ��K�^q�;���-W�DvT�7��8�Z�������� hK�(P:��Q- �8�n�Z���܃e貾�<�1�YT<�,�����"�6{/ �?�͟��|1�:�#g��W�>$����d��J��d�B��=��jf[��%rE^��il:��B���x���Sּ�1հ��,�=��*�7 fcG��#q� �eh?��2�7�����,�!7x��6�n�LC�4x��},Geǝ�tC.��vS �F�43��zz\��;QYC,6����~;RYS/6���|2���5���v��T��i����������mlv��������&� �nRh^ejR�LG�f���? �ۉҬܦƩ��|��Ȱ����>3����!v��i�ʯ�>�v��オ�X3e���_1z�Kȗ\<������!�8���V��]��?b�k41�Re��T�q��mz��TiOʦ�Z��Xq���L������q"+���2ۨ��8}�&N7XU7Ap�d�X��~�׿��&4e�o�F��� �H����O���č�c�� 懴�6���͉��+)��v;j��ݷ�� �UV�� i��� j���Y9GdÒJ1��詞�����V?h��l����l�cGs�ځ�������y�Ac�����\V3�? �� ܙg�>qH�S,�E�W�[�㺨�uch�⍸�O�}���a��>�q�6�n6����N6�q������N ! 1AQaq�0@����"2BRb�#Pr���3C`��Scst���$4D���%Td�� ?���N����a��3��m���C���w��������xA�m�q�m���m������$����4n淿t'��C"w��zU=D�\R+w�p+Y�T�&�պ@��ƃ��3ޯ?�Aﶂ��aŘ���@-�����Q�=���9D��ռ�ѻ@��M�V��P��܅�G5�f�Y<�u=,EC)�<�Fy'�"�&�չ�X~f��l�KԆV��?�� �W�N����=(� �;���{�r����ٌ�Y���h{�١������jW����P���Tc�����X�K�r��}���w�R��%��?���E��m�� �Y�q|����\lEE4���r���}�lsI�Y������f�$�=�d�yO����p�����yBj8jU�o�/�S��?�U��*������ˍ�0������u�q�m [�?f����a�� )Q�>����6#������� ?����0UQ����,IX���(6ڵ[�DI�MNލ�c&���υ�j\��X�R|,4��� j������T�hA�e��^���d���b<����n�� �즇�=!���3�^�`j�h�ȓr��jẕ�c�,ٞX����-����a�ﶔ���#�$��]w�O��Ӫ�1y%��L�Y<�wg#�ǝ�̗`�x�xa�t�w��»1���o7o5��>�m뭛C���Uƃߜ}�C���y1Xνm�F8�jI���]����H���ۺиE@I�i;r�8ӭ����V�F�Շ| ��&?�3|x�B�MuS�Ge�=Ӕ�#BE5G�����Y!z��_e��q�р/W>|-�Ci߇�t�1ޯќd�R3�u��g�=0 5��[?�#͏��q�cf���H��{ ?u�=?�?ǯ���}Z��z���hmΔ�BFTW�����<�q�(v� ��!��z���iW]*�J�V�z��gX֧A�q�&��/w���u�gYӘa���; �i=����g:��?2�dž6�ى�k�4�>�Pxs����}������G�9��3 ���)gG�R<>r h�$��'nc�h�P��Bj��J�ҧH� -��N1���N��?��~��}-q!=��_2hc�M��l�vY%UE�@|�v����M2�.Y[|y�"Eï��K�ZF,�ɯ?,q�?v�M 80jx�"�;�9vk�����+ ֧�� �ȺU��?�%�vcV��mA�6��Qg^M����A}�3�nl� QRN�l8�kkn�'�����(��M�7m9و�q���%ޟ���*h$Zk"��$�9��: �?U8�Sl��,,|ɒ��xH(ѷ����Gn�/Q�4�P��G�%��Ա8�N��!� �&�7�;���eKM7�4��9R/%����l�c>�x;������>��C�:�����t��h?aKX�bhe�ᜋ^�$�Iհ �hr7%F$�E��Fd���t��5���+�(M6�t����Ü�UU|zW�=a�Ts�Tg������dqP�Q����b'�m���1{|Y����X�N��b �P~��F^F:����k6�"�j!�� �I�r�`��1&�-$�Bevk:y���#yw��I0��x��=D�4��tU���P�ZH��ڠ底taP��6����b>�xa����Q�#� WeF��ŮNj�p�J* mQ�N����*I�-*�ȩ�F�g�3 �5��V�ʊ�ɮ�a��5F���O@{���NX��?����H�]3��1�Ri_u��������ѕ�� ����0��� F��~��:60�p�͈�S��qX#a�5>���`�o&+�<2�D����: �������ڝ�$�nP���*)�N�|y�Ej�F�5ټ�e���ihy�Z �>���k�bH�a�v��h�-#���!�Po=@k̆IEN��@��}Ll?j�O������߭�ʞ���Q|A07x���wt!xf���I2?Z��<ץ�T���cU�j��]��陎Ltl �}5�ϓ��$�,��O�mˊ�;�@O��jE��j(�ا,��LX���LO���Ц�90�O �.����a��nA���7������j4 ��W��_ٓ���zW�jcB������y՗+EM�)d���N�g6�y1_x��p�$Lv:��9�"z��p���ʙ$��^��JԼ*�ϭ����o���=x�Lj�6�J��u82�A�H�3$�ٕ@�=Vv�]�'�qEz�;I˼��)��=��ɯ���x �/�W(V���p�����$ �m�������u�����񶤑Oqˎ�T����r��㠚x�sr�GC��byp�G��1ߠ�w e�8�$⿄����/�M{*}��W�]˷.�CK\�ުx���/$�WPw���r� |i���&�}�{�X� �>��$-��l���?-z���g����lΆ���(F���h�vS*���b���߲ڡn,|)mrH[���a�3�ר�[1��3o_�U�3�TC�$��(�=�)0�kgP���� ��u�^=��4 �WYCҸ:��vQ�ר�X�à��tk�m,�t*��^�,�}D*� �"(�I��9R����>`�`��[~Q]�#af��i6l��8���6�:,s�s�N6�j"�A4���IuQ��6E,�GnH��zS�HO�uk�5$�I�4��ؤ�Q9�@��C����wp�BGv[]�u�Ov���0I4���\��y�����Q�Ѹ��~>Z��8�T��a��q�ޣ;z��a���/��S��I:�ܫ_�|������>=Z����8:�S��U�I�J��"IY���8%b8���H��:�QO�6�;7�I�S��J��ҌAά3��>c���E+&jf$eC+�z�;��V����� �r���ʺ������my�e���aQ�f&��6�ND��.:��NT�vm�<- u���ǝ\MvZY�N�NT��-A�>jr!S��n�O 1�3�Ns�%�3D@���`������ܟ 1�^c<���� �a�ɽ�̲�Xë#�w�|y�cW�=�9I*H8�p�^(4���՗�k��arOcW�tO�\�ƍR��8����'�K���I�Q�����?5�>[�}��yU�ײ -h��=��% q�ThG�2�)���"ו3]�!kB��*p�FDl�A���,�eEi�H�f�Ps�����5�H:�Փ~�H�0Dت�D�I����h�F3�������c��2���E��9�H��5�zԑ�ʚ�i�X�=:m�xg�hd(�v����׊�9iS��O��d@0ڽ���:�p�5�h-��t�&���X�q�ӕ,��ie�|���7A�2���O%P��E��htj��Y1��w�Ѓ!����  ���� ࢽ��My�7�\�a�@�ţ�J �4�Ȼ�F�@o�̒?4�wx��)��]�P��~�����u�����5�����7X ��9��^ܩ�U;Iꭆ 5 �������eK2�7(�{|��Y׎ �V��\"���Z�1� Z�����}��(�Ǝ"�1S���_�vE30>���p;� ΝD��%x�W�?W?v����o�^V�i�d��r[��/&>�~`�9Wh��y�;���R��� ;;ɮT��?����r$�g1�K����A��C��c��K��l:�'��3 c�ﳯ*"t8�~l��)���m��+U,z��`(�>yJ�?����h>��]��v��ЍG*�{`��;y]��I�T� ;c��NU�fo¾h���/$���|NS���1�S�"�H��V���T���4��uhǜ�]�v;���5�͠x��'C\�SBpl���h}�N����� A�Bx���%��ޭ�l��/����T��w�ʽ]D�=����K���ž�r㻠l4�S�O?=�k �M:� ��c�C�a�#ha���)�ѐxc�s���gP�iG��{+���x���Q���I= �� z��ԫ+ �8"�k�ñ�j=|����c ��y��CF��/��*9ж�h{ �?4�o� ��k�m�Q�N�x��;�Y��4膚�a�w?�6�>e]�����Q�r�:����g�,i"�����ԩA�*M�<�G��b�if��l^M��5� �Ҩ�{����6J��ZJ�����P�*�����Y���ݛu�_4�9�I8�7���������,^ToR���m4�H��?�N�S�ѕw��/S��甍�@�9H�S�T��t�ƻ���ʒU��*{Xs�@����f�����֒Li�K{H�w^���������Ϥm�tq���s� ���ք��f:��o~s��g�r��ט� �S�ѱC�e]�x���a��) ���(b-$(�j>�7q�B?ӕ�F��hV25r[7 Y� }L�R��}����*sg+��x�r�2�U=�*'WS��ZDW]�WǞ�<��叓���{�$�9Ou4��y�90-�1�'*D`�c�^o?(�9��u���ݐ��'PI&� f�Jݮ�������:wS����jfP1F:X �H�9dԯ���˝[�_54 �}*;@�ܨ�� ð�yn�T���?�ןd�#���4rG�ͨ��H�1�|-#���Mr�S3��G�3�����)�.᧏3v�z֑��r����$G"�`j �1t��x0<Ɔ�Wh6�y�6��,œ�Ga��gA����y��b��)��h�D��ß�_�m��ü �gG;��e�v��ݝ�nQ� ��C����-�*��o���y�a��M��I�>�<���]obD��"�:���G�A��-\%LT�8���c�)��+y76���o�Q�#*{�(F�⽕�y����=���rW�\p���۩�c���A���^e6��K������ʐ�cVf5$�'->���ՉN"���F�"�UQ@�f��Gb~��#�&�M=��8�ט�JNu9��D��[̤�s�o�~������ G��9T�tW^g5y$b��Y'��س�Ǵ�=��U-2 #�MC�t(�i� �lj�@Q 5�̣i�*�O����s�x�K�f��}\��M{E�V�{�υ��Ƈ�����);�H����I��fe�Lȣr�2��>��W�I�Ȃ6������i��k�� �5�YOxȺ����>��Y�f5'��|��H+��98pj�n�.O�y�������jY��~��i�w'������l�;�s�2��Y��:'lg�ꥴ)o#'Sa�a�K��Z� �m��}�`169�n���"���x��I ��*+� }F<��cГ���F�P�������ֹ*�PqX�x۩��,� ��N�� �4<-����%����:��7����W���u�`����� $�?�I��&����o��o��`v�>��P��"��l���4��5'�Z�gE���8���?��[�X�7(��.Q�-��*���ތL@̲����v��.5���[��=�t\+�CNܛ��,g�SQnH����}*F�G16���&:�t��4ُ"A��̣��$�b �|����#rs��a�����T�� ]�<�j��BS�('$�ɻ� �wP;�/�n��?�ݜ��x�F��yUn�~mL*-�������Xf�wd^�a�}��f�,=t�׵i�.2/wpN�Ep8�OР���•��R�FJ� 55TZ��T �ɭ�<��]��/�0�r�@�f��V��V����Nz�G��^���7hZi����k��3�,kN�e|�vg�1{9]_i��X5y7� 8e]�U����'�-2,���e"����]ot�I��Y_��n�(JҼ��1�O ]bXc���Nu�No��pS���Q_���_�?i�~�x h5d'�(qw52] ��'ޤ�q��o1�R!���`ywy�A4u���h<קy���\[~�4�\ X�Wt/� 6�����n�F�a8��f���z �3$�t(���q��q�x��^�XWeN'p<-v�!�{�(>ӽDP7��ո0�y)�e$ٕv�Ih'Q�EA�m*�H��RI��=:��� ���4牢) �%_iN�ݧ�l]� �Nt���G��H�L��� ɱ�g<���1V�,�J~�ٹ�"K��Q�� 9�HS�9�?@��k����r�;we݁�]I�!{ �@�G�[�"��`���J:�n]�{�cA�E����V��ʆ���#��U9�6����j�#Y�m\��q�e4h�B�7��C�������d<�?J����1g:ٳ���=Y���D�p�ц� ׈ǔ��1�]26؜oS�'��9�V�FVu�P�h�9�xc�oq�X��p�o�5��Ա5$�9W�V(�[Ak�aY錎qf;�'�[�|���b�6�Ck��)��#a#a˙��8���=äh�4��2��C��4tm^ �n'c���]GQ$[Wҿ��i���vN�{Fu ��1�gx��1┷���N�m��{j-,��x�� Ūm�ЧS�[�s���Gna���䑴�� x�p 8<������97�Q���ϴ�v�aϚG��Rt�Һ׈�f^\r��WH�JU�7Z���y)�vg=����n��4�_)y��D'y�6�]�c�5̪�\� �PF�k����&�c;��cq�$~T�7j ���nç]�<�g ":�to�t}�159�<�/�8������m�b�K#g'I'.W�����6��I/��>v��\�MN��g���m�A�yQL�4u�Lj�j9��#44�t��l^�}L����n��R��!��t��±]��r��h6ٍ>�yҏ�N��fU�� ���� Fm@�8}�/u��jb9������he:A�y�ծw��GpΧh�5����l}�3p468��)U��d��c����;Us/�֔�YX�1�O2��uq�s��`hwg�r~�{ R��mhN��؎*q 42�*th��>�#���E����#��Hv�O����q�}�����6�e��\�,Wk�#���X��b>��p}�դ��3���T5��†��6��[��@�P�y*n��|'f�֧>�lư΂�̺����SU�'*�q�p�_S�����M�� '��c�6�����m�� ySʨ;M��r���Ƌ�m�Kxo,���Gm�P��A�G�:��i��w�9�}M(�^�V��$ǒ�ѽ�9���|���� �a����J�SQ�a���r�B;����}���ٻ֢�2�%U���c�#�g���N�a�ݕ�'�v�[�OY'��3L�3�;,p�]@�S��{ls��X�'���c�jw�k'a�.��}�}&�� �dP�*�bK=ɍ!����;3n�gΊU�ߴmt�'*{,=SzfD� A��ko~�G�aoq�_mi}#�m�������P�Xhύ����mxǍ�΂���巿zf��Q���c���|kc�����?���W��Y�$���_Lv����l߶��c���`?����l�j�ݲˏ!V��6����U�Ђ(A���4y)H���p�Z_�x��>���e��R��$�/�`^'3qˏ�-&Q�=?��CFVR �D�fV�9��{�8g�������n�h�(P"��6�[�D���< E�����~0<@�`�G�6����Hг�cc�� �c�K.5��D��d�B���`?�XQ��2��ٿyqo&+�1^� DW�0�ꊩ���G�#��Q�nL3��c���������/��x ��1�1[y�x�პCW��C�c�UĨ80�m�e�4.{�m��u���I=��f�����0QRls9���f���������9���~f�����Ǩ��a�"@�8���ȁ�Q����#c�ic������G��$���G���r/$W�(��W���V�"��m�7�[m�A�m����bo��D� j����۳� l���^�k�h׽����� ��#� iXn�v��eT�k�a�^Y�4�BN��ĕ��0 !01@Q"2AaPq3BR������?���@4�Q�����T3,���㺠�W�[=JK�Ϟ���2�r^7��vc�:�9 �E�ߴ�w�S#d���Ix��u��:��Hp��9E!�� V 2;73|F��9Y���*ʬ�F��D����u&���y؟��^EA��A��(ɩ���^��GV:ݜDy�`��Jr29ܾ�㝉��[���E;Fzx��YG��U�e�Y�C���� ����v-tx����I�sם�Ę�q��Eb�+P\ :>�i�C'�;�����k|z�رn�y]�#ǿb��Q��������w�����(�r|ӹs��[�D��2v-%��@;�8<a���[\o[ϧw��I!��*0�krs)�[�J9^��ʜ��p1)� "��/_>��o��<1����A�E�y^�C��`�x1'ܣn�p��s`l���fQ��):�l����b>�Me�jH^?�kl3(�z:���1ŠK&?Q�~�{�ٺ�h�y���/�[��V�|6��}�KbX����mn[-��7�5q�94�������dm���c^���h� X��5��<�eޘ>G���-�}�دB�ޟ� ��|�rt�M��V+�]�c?�-#ڛ��^ǂ}���Lkr���O��u�>�-D�ry� D?:ޞ�U��ǜ�7�V��?瓮�"�#���r��չģVR;�n���/_� ؉v�ݶe5d�b9��/O��009�G���5n�W����JpA�*�r9�>�1��.[t���s�F���nQ� V 77R�]�ɫ8����_0<՜�IF�u(v��4��F�k�3��E)��N:��yڮe��P�`�1}�$WS��J�SQ�N�j�ٺ��޵�#l���ј(�5=��5�lǏmoW�v-�1����v,W�mn��߀$x�<����v�j(����c]��@#��1������Ǔ���o'��u+����;G�#�޸��v-lη��/(`i⣍Pm^���ԯ̾9Z��F��������n��1��� ��]�[��)�'������:�֪�W��FC����� �B9،!?���]��V��A�Վ�M��b�w��G F>_DȬ0¤�#�QR�[V��kz���m�w�"��9ZG�7'[��=�Q����j8R?�zf�\a�=��O�U����*oB�A�|G���2�54 �p��.w7� �� ��&������ξxGHp� B%��$g�����t�Џ򤵍z���HN�u�Я�-�'4��0��;_��3 !01"@AQa2Pq#3BR������?��ʩca��en��^��8���<�u#��m*08r��y�N"�<�Ѳ0��@\�p��� �����Kv�D��J8�Fҽ� �f�Y��-m�ybX�NP����}�!*8t(�OqѢ��Q�wW�K��ZD��Δ^e��!� ��B�K��p~�����e*l}z#9ң�k���q#�Ft�o��S�R����-�w�!�S���Ӥß|M�l޶V��!eˈ�8Y���c�ЮM2��tk���� ������J�fS����Ö*i/2�����n]�k�\���|4yX�8��U�P.���Ы[���l��@"�t�<������5�lF���vU�����W��W��;�b�cД^6[#7@vU�xgZv��F�6��Q,K�v��� �+Ъ��n��Ǣ��Ft���8��0��c�@�!�Zq s�v�t�;#](B��-�nῃ~���3g������5�J�%���O������n�kB�ĺ�.r��+���#�N$?�q�/�s�6��p��a����a��J/��M�8��6�ܰ"�*������ɗud"\w���aT(����[��F��U՛����RT�b���n�*��6���O��SJ�.�ij<�v�MT��R\c��5l�sZB>F��<7�;EA��{��E���Ö��1U/�#��d1�a�n.1ě����0�ʾR�h��|�R��Ao�3�m3 ��%�� ���28Q� ��y��φ���H�To�7�lW>����#i`�q���c����a��� �m,B�-j����݋�'mR1Ήt�>��V��p���s�0IbI�C.���1R�ea�����]H�6����������4B>��o��](��$B���m�����a�!=��?�B� K�Ǿ+�Ծ"�n���K��*��+��[T#�{E�J�S����Q�����s�5�:�U�\wĐ�f�3����܆&�)����I���Ԇw��E T�lrTf6Q|R�h:��[K�� �z��c֧�G�C��%\��_�a�84��HcO�bi��ؖV��7H �)*ģK~Xhչ0��4?�0��� �E<���}3���#���u�?�� ��|g�S�6ꊤ�|�I#Hڛ� �ա��w�X��9��7���Ŀ%�SL��y6č��|�F�a 8���b��$�sק�h���b9RAu7�˨p�Č�_\*w��묦��F ����4D~�f����|(�"m���NK��i�S�>�$d7SlA��/�²����SL��|6N�}���S�˯���g��]6��; �#�.��<���q'Q�1|KQ$�����񛩶"�$r�b:���N8�w@��8$�� �AjfG|~�9F ���Y��ʺ��Bwؒ������M:I岎�G��`s�YV5����6��A �b:�W���G�q%l�����F��H���7�������Fsv7��k�� 403WebShell
403Webshell
Server IP : 51.161.54.47  /  Your IP : 216.73.216.98
Web Server : Apache/2.4.68 (Unix) OpenSSL/1.1.1k
System : Linux host.ditinformatica.ar 4.18.0-553.153.1.el8_10.x86_64 #1 SMP Thu Aug 6 00:53:12 EDT 2026 x86_64
User : kalaycom ( 1021)
PHP Version : 7.4.33
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : ON
Directory :  /usr/local/bin/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /usr/local/bin/n
#!/usr/bin/env bash
# shellcheck disable=SC2155
# Disabled "Declare and assign separately to avoid masking return values": https://github.com/koalaman/shellcheck/wiki/SC2155

#
# log <type> <msg>
#

log() {
  printf "  ${SGR_CYAN}%10s${SGR_RESET} : ${SGR_FAINT}%s${SGR_RESET}\n" "$1" "$2"
}

#
# verbose_log <type> <msg>
# Can suppress with --quiet.
# Like log but to stderr rather than stdout, so can also be used from "display" routines.
#

verbose_log() {
  if [[ "${SHOW_VERBOSE_LOG}" == "true" ]]; then
    >&2 printf "  ${SGR_CYAN}%10s${SGR_RESET} : ${SGR_FAINT}%s${SGR_RESET}\n" "$1" "$2"
  fi
}

#
# Exit with the given <msg ...>
#

abort() {
  >&2 printf "\n  ${SGR_RED}Error: %s${SGR_RESET}\n\n" "$*" && exit 1
}

#
# Synopsis: trace message ...
# Debugging output to stderr, not used in production code.
#

function trace() {
  >&2 printf "trace: %s\n" "$*"
}

#
# Synopsis: echo_red message ...
# Highlight message in colour (on stdout).
#

function echo_red() {
  printf "${SGR_RED}%s${SGR_RESET}\n" "$*"
}

#
# Synopsis: n_grep <args...>
# grep wrapper to ensure consistent grep options and circumvent aliases.
#

function n_grep() {
  GREP_OPTIONS='' command grep "$@"
}

#
# Setup and state
#

VERSION="10.2.0"

N_PREFIX="${N_PREFIX-/usr/local}"
N_PREFIX=${N_PREFIX%/}
readonly N_PREFIX

N_CACHE_PREFIX="${N_CACHE_PREFIX-${N_PREFIX}}"
N_CACHE_PREFIX=${N_CACHE_PREFIX%/}
CACHE_DIR="${N_CACHE_PREFIX}/n/versions"
readonly N_CACHE_PREFIX CACHE_DIR

N_NODE_MIRROR=${N_NODE_MIRROR:-${NODE_MIRROR:-https://nodejs.org/dist}}
N_NODE_MIRROR=${N_NODE_MIRROR%/}
readonly N_NODE_MIRROR

N_NODE_DOWNLOAD_MIRROR=${N_NODE_DOWNLOAD_MIRROR:-https://nodejs.org/download}
N_NODE_DOWNLOAD_MIRROR=${N_NODE_DOWNLOAD_MIRROR%/}
readonly N_NODE_DOWNLOAD_MIRROR

# Using xz instead of gzip is enabled by default, if xz compatibility checks pass.
# User may set N_USE_XZ to 0 to disable, or set to anything else to enable.
# May also be overridden by command line flags.

# Normalise external values to true/false
if [[ "${N_USE_XZ}" = "0" ]]; then
  N_USE_XZ="false"
elif [[ -n "${N_USE_XZ+defined}" ]]; then
  N_USE_XZ="true"
fi
# Not setting to readonly. Overriden by CLI flags, and update_xz_settings_for_version.

N_MAX_REMOTE_MATCHES=${N_MAX_REMOTE_MATCHES:-20}
# modified by update_mirror_settings_for_version
g_mirror_url=${N_NODE_MIRROR}
g_mirror_folder_name="node"

# Options for curl and wget.
# Defining commands in variables is fraught (https://mywiki.wooledge.org/BashFAQ/050)
# but we can follow the simple case and store arguments in an array.

GET_SHOWS_PROGRESS="false"
# --location to follow redirects
# --fail to avoid happily downloading error page from web server for 404 et al
# --show-error to show why failed (on stderr)
CURL_OPTIONS=( "--location" "--fail" "--show-error" )
if [[ -t 1 ]]; then
  CURL_OPTIONS+=( "--progress-bar" )
  command -v curl &> /dev/null && GET_SHOWS_PROGRESS="true"
else
  CURL_OPTIONS+=( "--silent" )
fi
WGET_OPTIONS=( "-q" "-O-" )

# Legacy support using unprefixed env. No longer documented in README.
if [ -n "$HTTP_USER" ];then
  if [ -z "$HTTP_PASSWORD" ]; then
    abort "Must specify HTTP_PASSWORD when supplying HTTP_USER"
  fi
  CURL_OPTIONS+=( "-u $HTTP_USER:$HTTP_PASSWORD" )
  WGET_OPTIONS+=( "--http-password=$HTTP_PASSWORD"
                "--http-user=$HTTP_USER" )
elif [ -n "$HTTP_PASSWORD" ]; then
  abort "Must specify HTTP_USER when supplying HTTP_PASSWORD"
fi

# Set by set_active_node
g_active_node=

# set by various lookups to allow mixed logging and return value from function, especially for engine and node
g_target_node=

DOWNLOAD=false # set to opt-out of activate (install), and opt-in to download (run, exec)
CLEANUP=false # remove cached download after install
ARCH="${N_ARCH-}"
SHOW_VERBOSE_LOG="true"
OFFLINE=false

# ANSI escape codes
# https://en.wikipedia.org/wiki/ANSI_escape_code
# https://no-color.org
# https://bixense.com/clicolors

USE_COLOR="true"
if [[ -n "${CLICOLOR_FORCE+defined}" && "${CLICOLOR_FORCE}" != "0" ]]; then
  USE_COLOR="true"
elif [[ -n "${NO_COLOR+defined}" || "${CLICOLOR}" = "0" || ! -t 1 ]]; then
  USE_COLOR="false"
fi
readonly USE_COLOR
# Select Graphic Rendition codes
if [[ "${USE_COLOR}" = "true" ]]; then
  # KISS and use codes rather than tput, avoid dealing with missing tput or TERM.
  readonly SGR_RESET="\033[0m"
  readonly SGR_FAINT="\033[2m"
  readonly SGR_RED="\033[31m"
  readonly SGR_CYAN="\033[36m"
else
  readonly SGR_RESET=
  readonly SGR_FAINT=
  readonly SGR_RED=
  readonly SGR_CYAN=
fi

#
# set_arch <arch> to override $(uname -a)
#

set_arch() {
  if test -n "$1"; then
    ARCH="$1"
  else
    abort "missing -a|--arch value"
  fi
}

#
# Synopsis: set_insecure
# Globals modified:
# - CURL_OPTIONS
# - WGET_OPTIONS
#

function set_insecure() {
  CURL_OPTIONS+=( "--insecure" )
  WGET_OPTIONS+=( "--no-check-certificate" )
}

#
# Synposis: display_major_version numeric-version
#
display_major_version() {
    local version=$1
    version="${version#v}"
    version="${version%%.*}"
    echo "${version}"
}

display_masked_url() {
  echo "$1" | sed -r 's/(https?:\/\/[^:]+):([^@]+)@/\1:****@/'
}

#
# Synopsis: update_mirror_settings_for_version version
# e.g. <nightly/latest> means using download mirror and folder is nightly
# Globals modified:
# - g_mirror_url
# - g_mirror_folder_name
#

function update_mirror_settings_for_version() {
  if is_download_folder "$1" ; then
    g_mirror_folder_name="$1"
    g_mirror_url="${N_NODE_DOWNLOAD_MIRROR}/${g_mirror_folder_name}"
  elif is_download_version "$1"; then
    [[ "$1" =~ ^([^/]+)/(.*) ]]
    local remote_folder="${BASH_REMATCH[1]}"
    g_mirror_folder_name="${remote_folder}"
    g_mirror_url="${N_NODE_DOWNLOAD_MIRROR}/${g_mirror_folder_name}"
  fi
}

#
# Synopsis: update_xz_settings_for_version numeric-version
# Globals modified:
# - N_USE_XZ
#

function update_xz_settings_for_version() {
  # tarballs in xz format were available in later version of iojs, but KISS and only use xz from v4.
  if [[ "${N_USE_XZ}" = "true" ]]; then
    local major_version="$(display_major_version "$1")"
    if [[ "${major_version}" -lt 4 ]]; then
      N_USE_XZ="false"
    fi
  fi
}

#
# Synopsis: update_arch_settings_for_version numeric-version
# Globals modified:
# - ARCH
#

function update_arch_settings_for_version() {
  local tarball_platform="$(display_tarball_platform)"
  if [[ -z "${ARCH}" && "${tarball_platform}" = "darwin-arm64" ]]; then
    # First native builds were for v16, but can use x64 in rosetta for older versions.
    local major_version="$(display_major_version "$1")"
    if [[ "${major_version}" -lt 16 ]]; then
      ARCH=x64
    fi
  fi
}

#
# Synopsis: is_lts_codename version
#

function is_lts_codename() {
  # https://github.com/nodejs/Release/blob/master/CODENAMES.md
  # e.g. argon, Boron
  [[ "$1" =~ ^([Aa]rgon|[Bb]oron|[Cc]arbon|[Dd]ubnium|[Ee]rbium|[Ff]ermium|[Gg]allium|[Hh]ydrogen|[Ii]ron|[Jj]od|[Kk]rypton|[Ll]ithium)$ ]]
}

#
# Synopsis: is_download_folder version
#

function is_download_folder() {
  # e.g. nightly
  [[ "$1" =~ ^(next-nightly|nightly|rc|release|test|v8-canary)$ ]]
}

#
# Synopsis: is_download_version version
#

function is_download_version() {
  # e.g. nightly/, nightly/latest, nightly/v11
  if [[ "$1" =~ ^([^/]+)/(.*) ]]; then
    local remote_folder="${BASH_REMATCH[1]}"
    is_download_folder "${remote_folder}"
    return
  fi
  return 2
}

#
# Synopsis: is_numeric_version version
#

function is_numeric_version() {
  # e.g. 6, v7.1, 8.11.3
  [[ "$1" =~ ^[v]{0,1}[0-9]+(\.[0-9]+){0,2}$ ]]
}

#
# Synopsis: is_exact_numeric_version version
#

function is_exact_numeric_version() {
  # e.g. 6, v7.1, 8.11.3
  [[ "$1" =~ ^[v]{0,1}[0-9]+\.[0-9]+\.[0-9]+$ ]]
}

#
# Synopsis: is_node_support_version version
# Reference: https://github.com/nodejs/package-maintenance/issues/236#issue-474783582
#

function is_node_support_version() {
  [[ "$1" =~ ^(active|lts_active|lts_latest|lts|current|supported)$ ]]
}

#
# Synopsis: display_latest_node_support_alias version
# Map aliases onto existing n aliases, current and lts
#

function display_latest_node_support_alias() {
  case "$1" in
    "active") printf "current" ;;
    "lts_active") printf "lts" ;;
    "lts_latest") printf "lts" ;;
    "lts") printf "lts" ;;
    "current") printf "current" ;;
    "supported") printf "current" ;;
    *) printf "unexpected-version"
  esac
}

#
# Functions used when showing versions installed
#

enter_fullscreen() {
  # Set cursor to be invisible
  tput civis 2> /dev/null
  # Save screen contents
  tput smcup 2> /dev/null
  stty -echo
}

leave_fullscreen() {
  # Set cursor to normal
  tput cnorm 2> /dev/null
  # Restore screen contents
  tput rmcup 2> /dev/null
  stty echo
}

handle_sigint() {
  leave_fullscreen
  S="$?"
  kill 0
  exit $S
}

handle_sigtstp() {
  leave_fullscreen
  kill -s SIGSTOP $$
}

#
# Output usage information.
#

display_help() {
  cat <<-EOF

Usage: n [options] [COMMAND] [args]

Commands:

  n                              Display downloaded Node.js versions and install selection
  n latest                       Install the latest Node.js release (downloading if necessary)
  n lts                          Install the latest LTS Node.js release (downloading if necessary)
  n <version>                    Install Node.js <version> (downloading if necessary)
  n install <version>            Install Node.js <version> (downloading if necessary)
  n run <version> [args ...]     Execute downloaded Node.js <version> with [args ...]
  n which <version>              Output path for downloaded node <version>
  n exec <vers> <cmd> [args...]  Execute command with modified PATH, so downloaded node <version> and npm first
  n rm <version ...>             Remove the given downloaded version(s)
  n prune                        Remove all downloaded versions except the installed version
  n --latest                     Output the latest Node.js version available
  n --lts                        Output the latest LTS Node.js version available
  n ls                           Output downloaded versions
  n ls-remote [version]          Output matching versions available for download
  n uninstall                    Remove the installed Node.js
  n download <version>           Download Node.js <version> into cache

Options:

  -V, --version         Output version of n
  -h, --help            Display help information
  -p, --preserve        Preserve npm and npx during install of Node.js
  -q, --quiet           Disable curl output. Disable log messages processing "auto" and "engine" labels.
  -d, --download        Download if necessary. Used with run/exec/which.
  --cleanup             Remove cached version after install
  -a, --arch            Override system architecture
  --offline             Resolve target version against cached downloads instead of internet lookup
  --all                 ls-remote displays all matches instead of last 20
  --insecure            Turn off certificate checking for https requests (may be needed from behind a proxy server)
  --use-xz/--no-use-xz  Override automatic detection of xz support and enable/disable use of xz compressed node downloads.

Aliases:

  install: i
  latest: current
  ls: list
  lsr: ls-remote
  lts: stable
  rm: -
  run: use, as
  which: bin

Versions:

  Numeric version numbers can be complete or incomplete, with an optional leading 'v'.
  Versions can also be specified by label, or codename,
  and other downloadable releases by <remote-folder>/<version>

    4.9.1, 8, v6.1    Numeric versions
    lts               Newest Long Term Support official release
    latest, current   Newest official release
    auto              Read version from file: .n-node-version, .node-version, .nvmrc, or package.json
    engine            Read version from package.json
    boron, carbon     Codenames for release streams
    lts_latest        Node.js support aliases

    and nightly, rc/10 et al

EOF
}

err_no_installed_print_help() {
  display_help
  abort "no downloaded versions yet, see above help for commands"
}

#
# Synopsis: next_version_installed selected_version
# Output version after selected (which may be blank under some circumstances).
#

function next_version_installed() {
  display_cache_versions | n_grep "$1" -A 1 | tail -n 1
}

#
# Synopsis: prev_version_installed selected_version
# Output version before selected  (which may be blank under some circumstances).
#

function prev_version_installed() {
  display_cache_versions | n_grep "$1" -B 1 | head -n 1
}

#
# Output n version.
#

display_n_version() {
  echo "$VERSION" && exit 0
}

#
# Synopsis: set_active_node
# Checks cached downloads for a binary matching the active node.
# Globals modified:
# - g_active_node
#

function set_active_node() {
  g_active_node=
  local node_path="$(command -v node)"
  if [[ -x "${node_path}" ]]; then
    local installed_version=$(node --version)
    installed_version=${installed_version#v}
    for dir in "${CACHE_DIR}"/*/ ; do
      local folder_name="${dir%/}"
      folder_name="${folder_name##*/}"
      if diff &> /dev/null \
        "${CACHE_DIR}/${folder_name}/${installed_version}/bin/node" \
        "${node_path}" ; then
        g_active_node="${folder_name}/${installed_version}"
        break
      fi
    done
  fi
}

#
# Display sorted versions directories paths.
#

display_versions_paths() {
  find "$CACHE_DIR" -maxdepth 2 -type d \
    | sed 's|'"$CACHE_DIR"'/||g' \
    | n_grep -E "/[0-9]+\.[0-9]+\.[0-9]+" \
    | sed 's|/|.|' \
    | sort -k 1,1 -k 2,2n -k 3,3n -k 4,4n -t . \
    | sed 's|\.|/|'
}

#
# Display installed versions with <selected>
#

display_versions_with_selected() {
  local selected="$1"
  echo
  for version in $(display_versions_paths); do
    if test "$version" = "$selected"; then
      printf "  ${SGR_CYAN}ο${SGR_RESET} %s\n" "$version"
    else
      printf "    ${SGR_FAINT}%s${SGR_RESET}\n" "$version"
    fi
  done
  echo
  printf "Use up/down arrow keys to select a version, return key to install, d to delete, q to quit"
}

#
# Synopsis: display_cache_versions
#

function display_cache_versions() {
  for folder_and_version in $(display_versions_paths); do
    echo "${folder_and_version}"
  done
}

#
# Display current node --version and others installed.
#

menu_select_cache_versions() {
  enter_fullscreen
  set_active_node
  local selected="${g_active_node}"

  clear
  display_versions_with_selected "${selected}"

  trap handle_sigint INT
  trap handle_sigtstp SIGTSTP

  ESCAPE_SEQ=$'\033'
  UP=$'A'
  DOWN=$'B'
  CTRL_P=$'\020'
  CTRL_N=$'\016'

  while true; do
    read -rsn 1 key
    case "$key" in
      "$ESCAPE_SEQ")
        # Handle ESC sequences followed by other characters, i.e. arrow keys
        read -rsn 1 -t 1 tmp
        # See "[" if terminal in normal mode, and "0" in application mode
        if [[ "$tmp" == "[" || "$tmp" == "O" ]]; then
          read -rsn 1 -t 1 arrow
          case "$arrow" in
            "$UP")
              clear
              selected="$(prev_version_installed "${selected}")"
              display_versions_with_selected "${selected}"
              ;;
            "$DOWN")
              clear
              selected="$(next_version_installed "${selected}")"
              display_versions_with_selected "${selected}"
              ;;
          esac
        fi
        ;;
      "d")
        if [[ -n "${selected}" ]]; then
          clear
          # Note: prev/next is constrained to min/max
          local after_delete_selection="$(next_version_installed "${selected}")"
          if [[ "${after_delete_selection}" == "${selected}"  ]]; then
            after_delete_selection="$(prev_version_installed "${selected}")"
          fi
          remove_versions "${selected}"

          if [[ "${after_delete_selection}" == "${selected}" ]]; then
            clear
            leave_fullscreen
            echo "All downloaded versions have been deleted from cache."
            exit
          fi

          selected="${after_delete_selection}"
          display_versions_with_selected "${selected}"
        fi
        ;;
      # Vim or Emacs 'up' key
      "k"|"$CTRL_P")
        clear
        selected="$(prev_version_installed "${selected}")"
        display_versions_with_selected "${selected}"
        ;;
      # Vim or Emacs 'down' key
      "j"|"$CTRL_N")
        clear
        selected="$(next_version_installed "${selected}")"
        display_versions_with_selected "${selected}"
        ;;
      "q")
        clear
        leave_fullscreen
        exit
        ;;
      "")
        # enter key returns empty string
        leave_fullscreen
        [[ -n "${selected}" ]] && activate "${selected}"
        exit
        ;;
    esac
  done
}

#
# Move up a line and erase.
#

erase_line() {
  printf "\033[1A\033[2K"
}

#
# Disable PaX mprotect for <binary>
#

disable_pax_mprotect() {
  test -z "$1" && abort "binary required"
  local binary="$1"

  # try to disable mprotect via XATTR_PAX header
  local PAXCTL="$(PATH="/sbin:/usr/sbin:$PATH" command -v paxctl-ng 2>&1)"
  local PAXCTL_ERROR=1
  if [ -x "$PAXCTL" ]; then
    $PAXCTL -l && $PAXCTL -m "$binary" >/dev/null 2>&1
    PAXCTL_ERROR="$?"
  fi

  # try to disable mprotect via PT_PAX header
  if [ "$PAXCTL_ERROR" != 0 ]; then
    PAXCTL="$(PATH="/sbin:/usr/sbin:$PATH" command -v paxctl 2>&1)"
    if [ -x "$PAXCTL" ]; then
      $PAXCTL -Cm "$binary" >/dev/null 2>&1
    fi
  fi
}

#
# clean_copy_folder <source> <target>
#

clean_copy_folder() {
  local source="$1"
  local target="$2"
  if [[ -d "${source}" ]]; then
    rm -rf "${target}"
    cp -fR "${source}" "${target}"
  fi
}

#
# Activate <version>
#

activate() {
  local version="$1"
  local dir="$CACHE_DIR/$version"
  local original_node="$(command -v node)"
  local installed_node="${N_PREFIX}/bin/node"
  log "copying" "$version"


  # Ideally we would just copy from cache to N_PREFIX, but there are some complications
  # - various linux versions use symlinks for folders in /usr/local and also error when copy folder onto symlink
  # - we have used cp for years, so keep using it for backwards compatibility (instead of say rsync)
  # - we allow preserving npm
  # - we want to be somewhat robust to changes in tarball contents, so use find instead of hard-code expected subfolders
  #
  # This code was purist and concise for a long time.
  # Now twice as much code, but using same code path for all uses, and supporting more setups.

  # Copy lib before bin so symlink targets exist.
  # lib
  mkdir -p "$N_PREFIX/lib"
  # Copy everything except node_modules.
  find "$dir/lib" -mindepth 1 -maxdepth 1 \! -name node_modules -exec cp -fR "{}" "$N_PREFIX/lib" \;
  if [[ -z "${N_PRESERVE_NPM}" ]]; then
    mkdir -p "$N_PREFIX/lib/node_modules"
    # Copy just npm, skipping possible added global modules after download. Clean copy to avoid version change problems.
    clean_copy_folder "$dir/lib/node_modules/npm" "$N_PREFIX/lib/node_modules/npm"
  fi
  # Takes same steps for corepack (experimental in node 16.9.0) as for npm, to avoid version problems.
  if [[ -e "$dir/lib/node_modules/corepack" && -z "${N_PRESERVE_COREPACK}" ]]; then
    mkdir -p "$N_PREFIX/lib/node_modules"
    clean_copy_folder "$dir/lib/node_modules/corepack" "$N_PREFIX/lib/node_modules/corepack"
  fi

  # bin
  mkdir -p "$N_PREFIX/bin"
  # Remove old node to avoid potential problems with firewall getting confused on Darwin by overwrite.
  rm -f "$N_PREFIX/bin/node"
  # Copy bin items by hand, in case user has installed global npm modules into cache.
  cp -f "$dir/bin/node" "$N_PREFIX/bin"
  [[ -e "$dir/bin/node-waf" ]] && cp -f "$dir/bin/node-waf" "$N_PREFIX/bin" # v0.8.x
  if [[ -z "${N_PRESERVE_COREPACK}" ]]; then
    [[ -e "$dir/bin/corepack" ]] && cp -fR "$dir/bin/corepack" "$N_PREFIX/bin" # from 16.9.0
  fi
  if [[ -z "${N_PRESERVE_NPM}" ]]; then
    [[ -e "$dir/bin/npm" ]] && cp -fR "$dir/bin/npm" "$N_PREFIX/bin"
    [[ -e "$dir/bin/npx" ]] && cp -fR "$dir/bin/npx" "$N_PREFIX/bin"
  fi

  # include
  mkdir -p "$N_PREFIX/include"
  find "$dir/include" -mindepth 1 -maxdepth 1 -exec cp -fR "{}" "$N_PREFIX/include" \;

  # share
  mkdir -p "$N_PREFIX/share"
  # Copy everything except man, at it is a symlink on some Linux (e.g. archlinux).
  find "$dir/share" -mindepth 1 -maxdepth 1 \! -name man -exec cp -fR "{}" "$N_PREFIX/share" \;
  mkdir -p "$N_PREFIX/share/man"
  find "$dir/share/man" -mindepth 1 -maxdepth 1 -exec cp -fR "{}" "$N_PREFIX/share/man" \;

  disable_pax_mprotect "${installed_node}"

  local active_node="$(command -v node)"
  if [[ -e "${active_node}" && -e "${installed_node}" && "${active_node}" != "${installed_node}" ]]; then
    # Installed and active are different which might be a PATH problem. List both to give user some clues.
    log "installed" "$("${installed_node}" --version) to ${installed_node}"
    log "active" "$("${active_node}" --version) at ${active_node}"
  else
    local npm_version_str=""
    local installed_npm="${N_PREFIX}/bin/npm"
    local active_npm="$(command -v npm)"
    if [[ -z "${N_PRESERVE_NPM}" && -e "${active_npm}" && -e "${installed_npm}" && "${active_npm}" = "${installed_npm}" ]]; then
      npm_version_str=" (with npm $(npm --version))"
    fi

    log "installed" "$("${installed_node}" --version)${npm_version_str}"

    # Extra tips for changed location.
    if [[ -e "${active_node}" && -e "${original_node}" && "${active_node}" != "${original_node}" ]]; then
      printf '\nNote: the node command changed location and the old location may be remembered in your current shell.\n'
      log old "${original_node}"
      log new "${active_node}"
      printf 'If "node --version" shows the old version then start a new shell, or reset the location hash with:\nhash -r  (for bash, zsh, ash, dash, and ksh)\nrehash   (for csh and tcsh)\n'
    fi
  fi

  if [[ "$CLEANUP" == "true" ]]; then
    log "cleanup" "removing cached $version"
    remove_versions "$version"
  fi

}

#
# Install <version>
#

install() {
  [[ -z "$1" ]] && abort "version required"
  local version
  get_latest_resolved_version "$1" || return 2
  version="${g_target_node}"
  [[ -n "${version}" ]] || abort "no version found for '$1'"
  update_mirror_settings_for_version "$1"
  update_xz_settings_for_version "${version}"
  update_arch_settings_for_version "${version}"

  local dir="${CACHE_DIR}/${g_mirror_folder_name}/${version}"

  # Note: decompression flags ignored with default Darwin tar which autodetects.
  if test "$N_USE_XZ" = "true"; then
    local tarflag="-Jx"
  else
    local tarflag="-zx"
  fi

  if test -d "$dir"; then
    if [[ ! -e "$dir/n.lock" ]] ; then
      if [[ "$DOWNLOAD" == "false" ]] ; then
        activate "${g_mirror_folder_name}/${version}"
      else
        log downloaded "${g_mirror_folder_name}/${version} already in cache"
      fi
      exit
    fi
  fi
  if [[ "$OFFLINE" == "true" ]]; then
    abort "version unavailable offline"
  fi

  if [[ "$DOWNLOAD" == "false" ]]; then
    log installing "${g_mirror_folder_name}-v$version"
  else
    log download "${g_mirror_folder_name}-v$version"
  fi

  local url="$(tarball_url "$version")"
  is_ok "${url}" || abort "download preflight failed for '$version' ($(display_masked_url "${url}"))"

  log mkdir "$dir"
  mkdir -p "$dir" || abort "sudo required (or change ownership, or define N_PREFIX)"
  touch "$dir/n.lock"

  cd "${dir}" || abort "Failed to cd to ${dir}"

  log fetch "$(display_masked_url "${url}")"
  do_get "${url}" | tar "$tarflag" --strip-components=1 --no-same-owner -f -
  pipe_results=( "${PIPESTATUS[@]}" )
  if [[ "${pipe_results[0]}" -ne 0 ]]; then
    abort "failed to download archive for $version"
  fi
  if [[ "${pipe_results[1]}" -ne 0 ]]; then
    abort "failed to extract archive for $version"
  fi
  [ "$GET_SHOWS_PROGRESS" = "true" ] && erase_line
  rm -f "$dir/n.lock"

  disable_pax_mprotect bin/node

  if [[ "$DOWNLOAD" == "false" ]]; then
    activate "${g_mirror_folder_name}/$version"
  fi
}

#
# Be more silent.
#

set_quiet() {
  SHOW_VERBOSE_LOG="false"
  command -v curl > /dev/null && CURL_OPTIONS+=( "--silent" ) && GET_SHOWS_PROGRESS="false"
}

#
# Synopsis: do_get [option...] url
# Call curl or wget with combination of global and passed options.
#

function do_get() {
  if command -v curl &> /dev/null; then
    curl "${CURL_OPTIONS[@]}" "$@"
  elif command -v wget &> /dev/null; then
    wget "${WGET_OPTIONS[@]}" "$@"
  else
    abort "curl or wget command required"
  fi
}

#
# Synopsis: do_get_index [option...] url
# Call curl or wget with combination of global and passed options,
# with options tweaked to be more suitable for getting index.
#

function do_get_index() {
  if command -v curl &> /dev/null; then
    # --silent to suppress progress et al
    curl --silent "${CURL_OPTIONS[@]}" "$@"
  elif command -v wget &> /dev/null; then
    wget "${WGET_OPTIONS[@]}" "$@"
  else
    abort "curl or wget command required"
  fi
}

#
# Synopsis: remove_versions version ...
#

function remove_versions() {
  [[ -z "$1" ]] && abort "version(s) required"
  while [[ $# -ne 0 ]]; do
    local version
    get_latest_resolved_version "$1" || break
    version="${g_target_node}"
    if [[ -n "${version}" ]]; then
      update_mirror_settings_for_version "$1"
      local dir="${CACHE_DIR}/${g_mirror_folder_name}/${version}"
      if [[ -s "${dir}" ]]; then
        rm -rf "${dir}"
      else
        echo "$1 (${version}) not in downloads cache"
      fi
    else
      echo "No version found for '$1'"
    fi
    shift
  done
}

#
# Synopsis: prune_cache
#

function prune_cache() {
  set_active_node

  for folder_and_version in $(display_versions_paths); do
    if [[ "${folder_and_version}" != "${g_active_node}" ]]; then
      echo "${folder_and_version}"
      rm -rf "${CACHE_DIR:?}/${folder_and_version}"
    fi
  done
}

#
# Synopsis: find_cached_version version
# Finds cache directory for resolved version.
# Globals modified:
# - g_cached_version

function find_cached_version() {
  [[ -z "$1" ]] && abort "version required"
  local version
  get_latest_resolved_version "$1" || exit 1
  version="${g_target_node}"
  [[ -n "${version}" ]] || abort "no version found for '$1'"

  update_mirror_settings_for_version "$1"
  g_cached_version="${CACHE_DIR}/${g_mirror_folder_name}/${version}"
  if [[ ! -d "${g_cached_version}" && "${DOWNLOAD}" == "true" ]]; then
    (install "${version}")
  fi
  [[ -d "${g_cached_version}" ]] || abort "'$1' (${version}) not in downloads cache"
}


#
# Synopsis: display_bin_path_for_version version
#

function display_bin_path_for_version() {
  find_cached_version "$1"
  echo "${g_cached_version}/bin/node"
}

#
# Synopsis: run_with_version version [args...]
# Run the given <version> of node with [args ..]
#

function run_with_version() {
  find_cached_version "$1"
  shift # remove version from parameters
  exec "${g_cached_version}/bin/node" "$@"
}

#
# Synopsis: exec_with_version <version> command [args...]
# Modify the path to include <version> and execute command.
#

function exec_with_version() {
  find_cached_version "$1"
  shift # remove version from parameters
  PATH="${g_cached_version}/bin:$PATH" exec "$@"
}

#
# Synopsis: is_ok url
# Check the HEAD response of <url>.
#

function is_ok() {
  # Note: both curl and wget can follow redirects, as present on some mirrors (e.g. https://npm.taobao.org/mirrors/node).
  # The output is complicated with redirects, so keep it simple and use command status rather than parse output.
  if command -v curl &> /dev/null; then
    do_get --silent --head "$1" > /dev/null || return 1
  else
    do_get --spider "$1" > /dev/null || return 1
  fi
}

#
# Synopsis: can_use_xz
# Test system to see if xz decompression is supported by tar.
#

function can_use_xz() {
  # Be conservative and only enable if xz is likely to work. Unfortunately we can't directly query tar itself.
  # For research, see https://github.com/shadowspawn/nvh/issues/8
  local uname_s="$(uname -s)"
  if [[ "${uname_s}" = "Linux" ]] && command -v xz &> /dev/null ; then
    # tar on linux is likely to support xz if it is available as a command
    return 0
  elif [[ "${uname_s}" = "Darwin" ]]; then
    local macos_version="$(sw_vers -productVersion)"
    local macos_major_version="$(echo "${macos_version}" | cut -d '.' -f 1)"
    local macos_minor_version="$(echo "${macos_version}" | cut -d '.' -f 2)"
    if [[ "${macos_major_version}" -gt 10 || "${macos_minor_version}" -gt 8 ]]; then
      # tar on recent Darwin has xz support built-in
      return 0
    fi
  fi
  return 2 # not supported
}

#
# Synopsis: display_tarball_platform
#

function display_tarball_platform() {
  # https://en.wikipedia.org/wiki/Uname

  local os="unexpected_os"
  local uname_a="$(uname -a)"
  case "${uname_a}" in
    Linux*) os="linux" ;;
    Darwin*) os="darwin" ;;
    SunOS*) os="sunos" ;;
    AIX*) os="aix" ;;
    CYGWIN*) >&2 echo_red "Cygwin is not supported by n" ;;
    MINGW*) >&2 echo_red "Git BASH (MSYS) is not supported by n" ;;
  esac

  # architecture might already be known from (priority order):
  # * --arch flag on the command line,
  # * otherwise, from $N_ARCH
  # * otherwise from version specific adjustment (if applicable, see update_arch_settings_for_version)
  local arch="$ARCH"
  if [[ -z "$arch" ]]; then
    arch="unexpected_arch"
    local uname_m="$(uname -m)"
    case "${uname_m}" in
      x86_64) arch=x64 ;;
      i386 | i686) arch="x86" ;;
      aarch64) arch=arm64 ;;
      armv8l) arch=arm64 ;; # armv8l probably supports arm64, and there is no specific armv8l build so give it a go
      *)
        # e.g. armv6l, armv7l, arm64
        arch="${uname_m}"
        ;;
    esac
  fi

  echo "${os}-${arch}"
}

#
# Synopsis: display_compatible_file_field
# display <file> for current platform, as per <file> field in index.tab, which is different than actual download
#

function display_compatible_file_field {
  local compatible_file_field="$(display_tarball_platform)"
  if [[ -z "${ARCH}" && "${compatible_file_field}" = "darwin-arm64" ]]; then
    # Look for arm64 for native but also x64 for older versions which can run in rosetta.
    # (Downside is will get an install error if install version above 16 with x64 and not arm64.)
    compatible_file_field="osx-arm64-tar|osx-x64-tar"
  elif [[ "${compatible_file_field}" =~ darwin-(.*) ]]; then
    compatible_file_field="osx-${BASH_REMATCH[1]}-tar"
  fi
  echo "${compatible_file_field}"
}

#
# Synopsis: tarball_url version
#

function tarball_url() {
  local version="$1"
  local ext=gz
  [ "$N_USE_XZ" = "true" ] && ext="xz"
  echo "${g_mirror_url}/v${version}/node-v${version}-$(display_tarball_platform).tar.${ext}"
}

#
# Synopsis: get_file_node_version filename
# Sets g_target_node
#

function get_file_node_version() {
  g_target_node=
  local filepath="$1"
  verbose_log "found" "${filepath}"
  # read returns a non-zero status but does still work if there is no line ending
  local version
  <"${filepath}" read -r version
  # trim possible trailing \d from a Windows created file
  version="${version%%[[:space:]]}"
  verbose_log "read" "${version}"
  g_target_node="${version}"
}

#
# Synopsis: get_package_engine_version\
# Sets g_target_node
#

function get_package_engine_version() {
  g_target_node=
  local filepath="$1"
  verbose_log "found" "${filepath}"
  local range
  if command -v jq &> /dev/null; then
    range="$(jq -r '.engines.node // ""' < "${filepath}")"
  elif command -v node &> /dev/null; then
    range="$(node -e "package = require('${filepath}'); if (package && package.engines && package.engines.node) console.log(package.engines.node)")"
  else
    abort "either jq or an active version of node is required to read 'engines' from package.json"
  fi
  verbose_log "read" "${range}"
  [[ -n "${range}" ]] || return 2
  if [[ "*" == "${range}" ]]; then
    verbose_log "target" "current"
    g_target_node="current"
    return
  fi

  local version
  if [[ "${range}" =~ ^([>~^=]|\>\=)?v?([0-9]+(\.[0-9]+){0,2})(.[xX*])?$ ]]; then
    local operator="${BASH_REMATCH[1]}"
    version="${BASH_REMATCH[2]}"
    case "${operator}" in
      '' | =) ;;
      \> | \>=) version="current" ;;
      \~) [[ "${version}" =~ ^([0-9]+\.[0-9]+)\.[0-9]+$ ]] && version="${BASH_REMATCH[1]}" ;;
      ^) [[ "${version}" =~ ^([0-9]+) ]] && version="${BASH_REMATCH[1]}" ;;
    esac
    verbose_log "target" "${version}"
  else
    command -v npx &> /dev/null || abort "an active version of npx is required to use complex 'engine' ranges from package.json"
    [[ "$OFFLINE" != "true" ]] || abort "offline: an internet connection is required for looking up complex 'engine' ranges from package.json"
    verbose_log "resolving" "${range}"
    local version_per_line="$(n lsr --all)"
    local versions_one_line=$(echo "${version_per_line}" | tr '\n' ' ')
    # Using semver@7 so works with older versions of node.
    # shellcheck disable=SC2086
    version=$(npm_config_yes=true npx --quiet semver@7 -r "${range}" ${versions_one_line} | tail -n 1)
  fi
  g_target_node="${version}"
}

#
# Synopsis: get_nvmrc_version
# Sets g_target_node
#

function get_nvmrc_version() {
  g_target_node=
  local filepath="$1"
  verbose_log "found" "${filepath}"
  local version
  <"${filepath}" read -r version
  # remove trailing comment, after #
  version="$(echo "${version}" | sed 's/[[:space:]]*#.*//')"
  verbose_log "read" "${version}"
  # Translate from nvm aliases
  case "${version}" in
    lts/\*) version="lts" ;;
    lts/*) version="${version:4}" ;;
    node) version="current" ;;
    *) ;;
  esac
  g_target_node="${version}"
}

#
# Synopsis: get_engine_version [error-message]
# Sets g_target_node
#

function get_engine_version() {
  g_target_node=
  local error_message="${1-package.json not found}"
  local parent
  parent="${PWD}"
  while [[ -n "${parent}" ]]; do
    if [[ -e "${parent}/package.json" ]]; then
      get_package_engine_version "${parent}/package.json"
    else
      parent=${parent%/*}
      continue
    fi
    break
  done
  [[ -n "${parent}" ]] || abort "${error_message}"
  [[ -n "${g_target_node}" ]] || abort "did not find supported version of node in 'engines' field of package.json"
}

#
# Synopsis: get_auto_version
# Sets g_target_node
#

function get_auto_version() {
  g_target_node=
  # Search for a version control file first
  local parent
  parent="${PWD}"
  while [[ -n "${parent}" ]]; do
    if [[ -e "${parent}/.n-node-version" ]]; then
      get_file_node_version "${parent}/.n-node-version"
    elif [[ -e "${parent}/.node-version" ]]; then
      get_file_node_version "${parent}/.node-version"
    elif [[ -e "${parent}/.nvmrc" ]]; then
      get_nvmrc_version "${parent}/.nvmrc"
    else
      parent=${parent%/*}
      continue
    fi
    break
  done
  # Fallback to package.json
  [[ -n "${parent}" ]] || get_engine_version "no file found for auto version (.n-node-version, .node-version, .nvmrc, or package.json)"
  [[ -n "${g_target_node}" ]] || abort "file found for auto did not contain target version of node"
}

#
# Synopsis: get_latest_resolved_version version
# Sets g_target_node
#

function get_latest_resolved_version() {
  g_target_node=
  local version=${1}

  # Transform some labels before processing further to allow fast-track for exact numeric versions.
  if [[ "${version}" = "auto" ]]; then
    get_auto_version || return 2
    version="${g_target_node}"
  elif [[ "${version}" = "engine" ]]; then
    get_engine_version || return 2
    version="${g_target_node}"
  fi

  simple_version=${version#node/} # Only place supporting node/ [sic]
  if is_exact_numeric_version "${simple_version}"; then
    # Just numbers, already resolved, no need to lookup first.
    simple_version="${simple_version#v}"
    g_target_node="${simple_version}"
  elif [[ "$OFFLINE" == "true" ]]; then
    g_target_node=$(display_local_versions "${version}")
  else
    # Complicated recognising exact version, KISS and lookup.
    g_target_node=$(N_MAX_REMOTE_MATCHES=1 display_remote_versions "$version")
  fi
}

#
# Synopsis: display_remote_index
# index.tab reference: https://github.com/nodejs/nodejs-dist-indexer
# Index fields are: version	date	files	npm	v8	uv	zlib	openssl	modules	lts security
# KISS and just return fields we currently care about: version files lts
#

display_remote_index() {
  local index_url="${g_mirror_url}/index.tab"
  # tail to remove header line
  do_get_index "${index_url}" | tail -n +2 | cut -f 1,3,10
  if [[ "${PIPESTATUS[0]}" -ne 0 ]]; then
    # Reminder: abort will only exit subshell, but consistent error display
    abort "failed to download version index ($(display_masked_url "${index_url}"))"
  fi
}

#
# Synopsis: display_match_limit limit
#

function display_match_limit(){
  if [[ "$1" -gt 1 && "$1" -lt 32000 ]]; then
    echo "Listing remote... Displaying $1 matches (use --all to see all)."
  fi
}

#
# Synopsis: display_local_versions version
#

function display_local_versions() {
  local version="$1"
  local match='.'
  verbose_log "offline" "matching cached versions"

    # Transform some labels before processing further.
  if is_node_support_version "${version}"; then
    version="$(display_latest_node_support_alias "${version}")"
    match_count=1
  elif [[ "${version}" = "auto" ]]; then
    get_auto_version || return 2
    version="${g_target_node}"
  elif [[ "${version}" = "engine" ]]; then
    get_engine_version || return 2
    version="${g_target_node}"
  fi

  if [[ "${version}" = "latest" || "${version}" = "current" ]]; then
    match='^node/.'
  elif is_exact_numeric_version "${version}"; then
    # Quote any dots in version so they are literal for expression
    match="^node/${version//\./\.}"
  elif is_numeric_version "${version}"; then
    version="${version#v}"
    # Quote any dots in version so they are literal for expression
    match="${version//\./\.}"
    # Avoid 1.2 matching 1.23
    match="^node/${match}[^0-9]"
  # elif is_lts_codename "${version}"; then
  # see if demand
  elif is_download_folder "${version}"; then
    match="^${version}/"
  # elif is_download_version "${version}"; then
  # see if demand
  else
    abort "invalid version '$1' for offline matching"
  fi

  display_versions_paths \
    | n_grep -E "${match}" \
    | tail -n 1 \
    | sed 's|node/||'
}

#
# Synopsis: display_remote_versions version
#

function display_remote_versions() {
  local version="$1"
  update_mirror_settings_for_version "${version}"
  local match='.'
  local match_count="${N_MAX_REMOTE_MATCHES}"

  # Transform some labels before processing further.
  if is_node_support_version "${version}"; then
    version="$(display_latest_node_support_alias "${version}")"
    match_count=1
  elif [[ "${version}" = "auto" ]]; then
    get_auto_version || return 2
    version="${g_target_node}"
  elif [[ "${version}" = "engine" ]]; then
    get_engine_version || return 2
    version="${g_target_node}"
  fi

  if [[ -z "${version}" ]]; then
    match='.'
  elif [[ "${version}" = "lts" || "${version}" = "stable" ]]; then
    match_count=1
    # Codename is last field, first one with a name is newest lts
    match="${TAB_CHAR}[a-zA-Z]+\$"
  elif [[ "${version}" = "latest" || "${version}" = "current" ]]; then
    match_count=1
    match='.'
  elif is_numeric_version "${version}"; then
    version="v${version#v}"
    # Avoid restriction message if exact version
    is_exact_numeric_version "${version}" && match_count=1
    # Quote any dots in version so they are literal for expression
    match="${version//\./\.}"
    # Avoid 1.2 matching 1.23
    match="^${match}[^0-9]"
  elif is_lts_codename "${version}"; then
    # Capitalise (could alternatively make grep case insensitive)
    codename="$(echo "${version:0:1}" | tr '[:lower:]' '[:upper:]')${version:1}"
    # Codename is last field
    match="${TAB_CHAR}${codename}\$"
  elif is_download_folder "${version}"; then
    match='.'
  elif is_download_version "${version}"; then
    version="${version#"${g_mirror_folder_name}"/}"
    if [[ "${version}" = "latest" || "${version}" = "current" ]]; then
      match_count=1
      match='.'
    else
      version="v${version#v}"
      match="${version//\./\.}"
      match="^${match}" # prefix
      if is_numeric_version "${version}"; then
        # Exact numeric match
        match="${match}[^0-9]"
      fi
    fi
  else
    abort "invalid version '$1'"
  fi
  display_match_limit "${match_count}"

  # Implementation notes:
  # - using awk rather than head so do not close pipe early on curl
  # - restrict search to compatible files as not always available, or not at same time
  # - return status of curl command (i.e. PIPESTATUS[0])
  display_remote_index \
    | n_grep -E "$(display_compatible_file_field)" \
    | n_grep -E "${match}" \
    | awk "NR<=${match_count}" \
    | cut -f 1 \
    | n_grep -E -o '[^v].*'
  return "${PIPESTATUS[0]}"
}

#
# Synopsis: delete_with_echo target
#

function delete_with_echo() {
  if [[ -e "$1" ]]; then
    echo "$1"
    rm -rf "$1"
  fi
}

#
# Synopsis: uninstall_installed
# Uninstall the installed node and npm (leaving alone the cache),
# so undo install, and may expose possible system installed versions.
#

uninstall_installed() {
  # npm: https://docs.npmjs.com/misc/removing-npm
  #   rm -rf /usr/local/{lib/node{,/.npm,_modules},bin,share/man}/npm*
  # node: https://stackabuse.com/how-to-uninstall-node-js-from-mac-osx/
  # Doing it by hand rather than scanning cache, so still works if cache deleted first.
  # This covers tarballs for at least node 4 through 10.

  while true; do
      read -r -p "Do you wish to delete node and npm from ${N_PREFIX}? " yn
      case $yn in
          [Yy]* ) break ;;
          [Nn]* ) exit ;;
          * ) echo "Please answer yes or no.";;
      esac
  done

  echo ""
  echo "Uninstalling node and npm"
  delete_with_echo "${N_PREFIX}/bin/node"
  delete_with_echo "${N_PREFIX}/bin/npm"
  delete_with_echo "${N_PREFIX}/bin/npx"
  delete_with_echo "${N_PREFIX}/bin/corepack"
  delete_with_echo "${N_PREFIX}/include/node"
  delete_with_echo "${N_PREFIX}/lib/dtrace/node.d"
  delete_with_echo "${N_PREFIX}/lib/node_modules/npm"
  delete_with_echo "${N_PREFIX}/lib/node_modules/corepack"
  delete_with_echo "${N_PREFIX}/share/doc/node"
  delete_with_echo "${N_PREFIX}/share/man/man1/node.1"
  delete_with_echo "${N_PREFIX}/share/systemtap/tapset/node.stp"
}

#
# Synopsis: show_permission_suggestions
#

function show_permission_suggestions() {
  echo "Suggestions:"
  echo "- run n with sudo, or"
  if [[ "${N_CACHE_PREFIX}" == "${N_PREFIX}" ]]; then
    echo "- define N_PREFIX to a writeable location, or"
  else
    echo "- define N_PREFIX and N_CACHE_PREFIX to writeable locations, or"
  fi
}

#
# Synopsis: show_diagnostics
# Show environment and check for common problems.
#

function show_diagnostics() {
  echo "This information is to help you diagnose issues, and useful when reporting an issue."
  echo "Note: some output may contain passwords. Redact before sharing."

  printf "\n\nCOMMAND LOCATIONS AND VERSIONS\n"

  printf "\nbash\n"
  command -v bash && bash --version

  printf "\nn\n"
  command -v n && n --version

  printf "\nnode\n"
  if command -v node &> /dev/null; then
    node --version
    node -e 'if (process.versions.v8) console.log("JavaScript engine: v8");'

    printf "\nnpm\n"
    command -v npm && npm --version
  fi

  printf "\ntar\n"
  if command -v tar &> /dev/null; then
    tar --version
  else
    echo_red "tar not found. Needed for extracting downloads."
  fi

  printf "\ncurl or wget\n"
  if command -v curl &> /dev/null; then
    curl --version
  elif command -v wget &> /dev/null; then
    wget --version
  else
    echo_red "Neither curl nor wget found. Need one of them for downloads."
  fi

  printf "\njq\n"
  command -v jq && jq --version

  printf "\nuname\n"
  uname -a

  printf "\n\nSETTINGS\n"

  printf "\nn\n"
  echo "node mirror: $(display_masked_url "${N_NODE_MIRROR}")"
  echo "node downloads mirror: $(display_masked_url "${N_NODE_DOWNLOAD_MIRROR}")"
  echo "install destination: ${N_PREFIX}"
  [[ -n "${N_PREFIX}" ]] && echo "PATH: ${PATH}"
  [[ -n "$N_ARCH" ]] && echo "default arch: $N_ARCH"
  echo "ls-remote max matches: ${N_MAX_REMOTE_MATCHES}"
   [[ -n "${N_PRESERVE_NPM}" ]] && echo "installs preserve npm by default"
   [[ -n "${N_PRESERVE_COREPACK}" ]] && echo "installs preserve corepack by default"

  printf "\nProxy\n"
  # disable "var is referenced but not assigned": https://github.com/koalaman/shellcheck/wiki/SC2154
  # shellcheck disable=SC2154
  [[ -n "${http_proxy}" ]] && echo "http_proxy: ${http_proxy}"
  # shellcheck disable=SC2154
  [[ -n "${https_proxy}" ]] && echo "https_proxy: ${https_proxy}"
  if command -v curl &> /dev/null; then
    # curl supports lower case and upper case!
    # shellcheck disable=SC2154
    [[ -n "${all_proxy}" ]] && echo "all_proxy: ${all_proxy}"
    [[ -n "${ALL_PROXY}" ]] && echo "ALL_PROXY: ${ALL_PROXY}"
    [[ -n "${HTTP_PROXY}" ]] && echo "HTTP_PROXY: ${HTTP_PROXY}"
    [[ -n "${HTTPS_PROXY}" ]] && echo "HTTPS_PROXY: ${HTTPS_PROXY}"
    if [[ -e "${CURL_HOME}/.curlrc" ]]; then
       echo "have \$CURL_HOME/.curlrc"
    elif [[ -e "${HOME}/.curlrc" ]]; then
      echo "have \$HOME/.curlrc"
    fi
  elif command -v wget &> /dev/null; then
    if [[ -e "${WGETRC}" ]]; then
      echo "have \$WGETRC"
    elif [[ -e "${HOME}/.wgetrc" ]]; then
      echo "have \$HOME/.wgetrc"
    fi
  fi

  printf "\n\nCHECKS\n"

  printf "\nChecking n install destination is in PATH...\n"
  local install_bin="${N_PREFIX}/bin"
  local path_wth_guards=":${PATH}:"
  if [[ "${path_wth_guards}" =~ :${install_bin}/?: ]]; then
    printf "good\n"
  else
    echo_red "'${install_bin}' is not in PATH"
  fi
  if command -v node &> /dev/null; then
    printf "\nChecking n install destination priority in PATH...\n"
    local node_dir="$(dirname "$(command -v node)")"

    local index=0
    local path_entry
    local path_entries
    local install_bin_index=0
    local node_index=999
    IFS=':' read -ra path_entries <<< "${PATH}"
    for path_entry in "${path_entries[@]}"; do
      (( index++ ))
      [[ "${path_entry}" =~ ^${node_dir}/?$ ]] && node_index="${index}"
      [[ "${path_entry}" =~ ^${install_bin}/?$ ]] && install_bin_index="${index}"
    done
    if [[ "${node_index}" -lt "${install_bin_index}" ]]; then
      echo_red "There is a version of node installed which will be found in PATH before the n installed version."
    else
      printf "good\n"
    fi
  fi

  # Check npm too. Simpler check than for PATH and node, more like the runtime logging for active/installed node.
  if [[ -z "${N_PRESERVE_NPM}" ]]; then
    printf "\nChecking npm install destination...\n"
    local installed_npm="${N_PREFIX}/bin/npm"
    local active_npm="$(command -v npm)"
    if [[ -e "${active_npm}" && -e "${installed_npm}" && "${active_npm}" != "${installed_npm}" ]]; then
      echo_red "There is an active version of npm shadowing the version installed by n. Check order of entries in PATH."
      log "installed" "${installed_npm}"
      log "active" "${active_npm}"
    else
      printf "good\n"
    fi
  fi

  printf "\nChecking prefix folders...\n"
  if [[ ! -e "${N_PREFIX}" ]]; then
    echo "Folder does not exist: ${N_PREFIX}"
    echo "- This folder will be created when you do an install."
  fi
  if [[ "${N_PREFIX}" != "${N_CACHE_PREFIX}" && ! -e "${N_CACHE_PREFIX}" ]]; then
    echo "Folder does not exist: ${N_CACHE_PREFIX}"
    echo "- This folder will be created when you do an install."
  fi
  if [[ -e "${N_PREFIX}" && -e "${N_CACHE_PREFIX}" ]]; then
    echo "good"
  fi

  if [[ -e "${N_CACHE_PREFIX}" ]]; then
    printf "\nChecking permissions for cache folder...\n"
    # Using knowledge cache path ends in /n/versions in following check.
    if [[ ! -e "${CACHE_DIR}" && (( -e "${N_CACHE_PREFIX}/n" && ! -w "${N_CACHE_PREFIX}/n" ) || ( ! -e "${N_CACHE_PREFIX}/n" && ! -w "${N_CACHE_PREFIX}" )) ]]; then
      echo_red "You do not have write permission to create: ${CACHE_DIR}"
      show_permission_suggestions
      echo "- make a folder you own:"
      echo "      sudo mkdir -p \"${CACHE_DIR}\""
      echo "      sudo chown $(whoami) \"${CACHE_DIR}\""
    elif [[ ! -e "${CACHE_DIR}" ]]; then
      echo "Cache folder does not exist: ${CACHE_DIR}"
      echo "- This is normal if you have not done an install yet, as cache is only created when needed."
    elif [[ ! -w "${CACHE_DIR}" ]]; then
      echo_red "You do not have write permission to: ${CACHE_DIR}"
      show_permission_suggestions
      echo "- change folder ownership to yourself:"
      echo "      sudo chown -R $(whoami) \"${CACHE_DIR}\""
    else
      echo "good"
    fi
  fi

  if [[ -e "${N_PREFIX}" ]]; then
    printf "\nChecking permissions for install folders...\n"
    local install_writeable="true"
    for subdir in bin lib include share; do
      if [[ -e "${N_PREFIX}/${subdir}" && ! -w "${N_PREFIX}/${subdir}" ]]; then
        install_writeable="false"
        echo_red "You do not have write permission to: ${N_PREFIX}/${subdir}"
        break
      fi
      if [[ ! -e "${N_PREFIX}/${subdir}" && ! -w "${N_PREFIX}" ]]; then
        install_writeable="false"
        echo_red "You do not have write permission to create: ${N_PREFIX}/${subdir}"
        break
      fi
    done
    if [[ "${install_writeable}" = "true" ]]; then
      echo "good"
    else
      show_permission_suggestions
      echo "- change folder ownerships to yourself:"
      echo "      cd \"${N_PREFIX}\""
      echo "      sudo mkdir -p bin lib include share"
      echo "      sudo chown -R $(whoami) bin lib include share"
    fi
  fi

  printf "\nChecking mirror is reachable...\n"
  if is_ok "${N_NODE_MIRROR}/"; then
    printf "good\n"
  else
    echo_red "mirror not reachable"
    printf "Showing failing command and output\n"
    if command -v curl &> /dev/null; then
      ( set -x; do_get --head "${N_NODE_MIRROR}/" )
    else
      ( set -x; do_get --spider "${N_NODE_MIRROR}/" )
    printf "\n"
   fi
  fi
}

#
# Handle arguments.
#

# First pass. Process the options so they can come before or after commands,
# particularly for `n lsr --all` and `n install --arch x686`
# which feel pretty natural.

unprocessed_args=()
positional_arg="false"

while [[ $# -ne 0 ]]; do
  case "$1" in
    --all) N_MAX_REMOTE_MATCHES=32000 ;;
    -V|--version) display_n_version ;;
    -h|--help|help) display_help; exit ;;
    -q|--quiet) set_quiet ;;
    -d|--download) DOWNLOAD="true" ;;
    --cleanup) CLEANUP="true" ;;
    --offline) OFFLINE="true" ;;
    --insecure) set_insecure ;;
    -p|--preserve) N_PRESERVE_NPM="true" N_PRESERVE_COREPACK="true" ;;
    --no-preserve) N_PRESERVE_NPM="" N_PRESERVE_COREPACK="" ;;
    --use-xz) N_USE_XZ="true" ;;
    --no-use-xz) N_USE_XZ="false" ;;
    --latest) display_remote_versions latest; exit ;;
    --stable) display_remote_versions lts; exit ;; # [sic] old terminology
    --lts) display_remote_versions lts; exit ;;
    -a|--arch) shift; set_arch "$1";; # set arch and continue
    exec|run|as|use)
      unprocessed_args+=( "$1" )
      positional_arg="true"
      ;;
    *)
      if [[ "${positional_arg}" == "true" ]]; then
        unprocessed_args+=( "$@" )
        break
      fi
      unprocessed_args+=( "$1" )
      ;;
  esac
  shift
done

if [[ -z "${N_USE_XZ+defined}" ]]; then
  N_USE_XZ="true" # Default to using xz
  can_use_xz || N_USE_XZ="false"
fi

set -- "${unprocessed_args[@]}"

if test $# -eq 0; then
  test -z "$(display_versions_paths)" && err_no_installed_print_help
  menu_select_cache_versions
else
  case "$1" in
    bin|which) display_bin_path_for_version "$2"; exit ;;
    run|as|use) shift; run_with_version "$@"; exit ;;
    exec) shift; exec_with_version "$@"; exit ;;
    doctor) show_diagnostics; exit ;;
    rm|-) shift; remove_versions "$@"; exit ;;
    prune) prune_cache; exit ;;
    latest) install latest; exit ;;
    stable) install stable; exit ;;
    lts) install lts; exit ;;
    ls|list) display_versions_paths; exit ;;
    lsr|ls-remote|list-remote) shift; display_remote_versions "$1"; exit ;;
    uninstall) uninstall_installed; exit ;;
    i|install) shift; install "$1"; exit ;;
    download) shift; DOWNLOAD="true"; install "$1"; exit ;;
    N_TEST_DISPLAY_LATEST_RESOLVED_VERSION) shift; get_latest_resolved_version "$1" > /dev/null || exit 2; echo "${g_target_node}"; exit ;;
    *) install "$1"; exit ;;
  esac
fi

Youez - 2016 - github.com/yon3zu
LinuXploit