????

Your IP : 13.59.219.20


Current Path : /opt/cloudlinux/venv/lib/python3.11/site-packages/clselect/__pycache__/
Upload File :
Current File : //opt/cloudlinux/venv/lib/python3.11/site-packages/clselect/__pycache__/clselect.cpython-311.pyc

�

��f�T���ddlmZddlmZddlmZddlZddlZddlZddlmZddlm	Z	ddl
mZddlm
Zd	d
lmZmZd	dlmZGd�d
e��ZdS)�)�absolute_import)�print_function)�divisionN)�glob)�map)�FormattedException)�configparser�)�ClSelectExcept�BaseClSelectException)�clprintc�x�eZdZdZdZdZdZdZdZdZ	dZ
e
d	zZe
d
zZe
d���Ze
d���Zd/d�Zd�Zd�Zd�Zd�Zd�Zd�Zd0d�Zd�Zd�Zd�Zd�Zd0d�Zd�Zd1d�Zd�Ze
e��Zd �Z e
e ��Z d!�Z!e
e!��Z!d2d#�Z"d$�Z#d%�Z$d&�Z%d'�Z&d3d(�Z'd3d)�Z(d*�Z)d1d+�Z*d,�Z+d0d-�Z,d3d.�Z-dS)4�ClSelectz/etc/cl.selector/selector.confz/etc/cl.selector/defaults.cfgz/usr/bin/phpz/etc/cl.selector/native.confz/etc/cl.selector/user.confz"/etc/cl.selector/php.extensions.d/z/usr/sbin/cagefsctlz/var/lvez
/php%s.datz/php_native_ver.datc��	dtjvrtj�d��ddlm}m}n#t$rtd���wxYw|��s|��std���dSdS)Nz/usr/share/cagefsr��!multiphp_system_default_is_ea_php�selector_modules_must_be_usedzCageFS not installed.zesystem default PHP version is alt-php. PHP Selector is disabled. Use cPanel MultiPHP manager instead.)�sys�path�append�	cagefsctlrr�ImportErrorrrs  �H/opt/cloudlinux/venv/lib64/python3.11/site-packages/clselect/clselect.py�%check_multiphp_system_default_versionz.ClSelect.check_multiphp_system_default_version's���	A�"�#�(�2�2����� 3�4�4�4�b�b�b�b�b�b�b�b�b���	A�	A�	A�'�(?�@�@�@�	A����1�0�2�2�	Q�;X�;X�;Z�;Z�	Q�'�)P�Q�Q�
Q�	Q�	Q�	Q�	Qs	�58�Ac�T�tj�tj��S�N)�osr�existsr�	USER_CONF��r�work_without_cagefszClSelect.work_without_cagefs3s���w�~�~�h�0�1�1�1r!�phpc��||_|���|_i|_i|_t��|_d|_t�	��|_
|���dSr)�_item�_get_default_config_handler�_dh�_selector_contents�_native_contents�set�_hidden_extensions�_native_versionrr"�without_cagefs�_load_config_files)�self�items  r�__init__zClSelect.__init__7sl����
��3�3�5�5���"$��� "���"%�%�%���#���&�:�:�<�<������!�!�!�!�!r!c��|�d��tj|�d����f|jtj��fg}|D]&\}}t
j�|��r�%|�dS)N�cli)�_get_native_pathr�NativeNotInstalled�	_CAGFSCTL�MissingCagefsPackagerrr)r/�	utilitiesr�errors    r�check_requirementszClSelect.check_requirementsAs����
"�
"�5�
)�
)�>�+L�T�Mb�Mb�ch�Mi�Mi�+j�+j�k�
�^�^�@�B�B�C�
�	�
%�	�	�K�D�%��w�~�~�d�#�#�
���K�	�	r!c��ttj�|jd����D]}|�|���dS)Nz*.cfg)rrr�join�CONFIGS_DIR�_load_config_file)r/�filenames  rr.zClSelect._load_config_filesMsM���R�W�\�\�$�*:�G�D�D�E�E�	-�	-�H��"�"�8�,�,�,�,�	-�	-r!c��tjdd���}	|�|��n=#tj$r+}t	j|dt
|��z����d}~wwxYw	|j�|�	dd���
d����dS#tjtjf$rYdSwxYw)NF��
interpolation�strictzConfig is malformed, error: %s��message�
extensions�hide_extensions�,)
�ConfigParser�SafeConfigParser�read�Errorr�FileProcessError�strr+�update�get�split�NoSectionError�
NoOptionError)r/�filepath�dh�es    rr>zClSelect._load_config_fileQs��
�
*��e�
L�
L�
L��	M��G�G�H�������!�	M�	M�	M� �1��"B�S��V�V�"K�M�M�M�
M�����	M����	��#�*�*����|�%6�7�7�=�=�c�B�B�
D�
D�
D�
D�
D���+�\�-G�H�	�	�	��D�D�	���s'�.�A(�&A#�#A(�,AB/�/C�
Cc�,�|���}g}t|�����D]N}	|�|||d||d|jf���?#t
$rY�KwxYwt
|��S)zM
        Returns alternatives summary as tuple
        :rtype: tuple
        �version�data)�get_all_alternatives_data�sorted�keysrr%�KeyError�tuple)r/�alternatives�list_of_alternatives�alts    r�list_alternativeszClSelect.list_alternatives`s���
�5�5�7�7��!���,�+�+�-�-�.�.�	�	�C�
�$�+�+��!�#�&�y�1�!�#�&�v�.�t�z�:�<�=�=�=�=���
�
�
���
�����)�*�*�*s�;A7�7
B�Bc��|js9	|���n##tjtjf$ricYSwxYw|jS)a�
        Returns dict of all selector config contents.
        If no data loads them
        :return:
        {'4.4': {'version': '4.4.9', 'data': {'lsphp': '/opt/alt/php44/usr/bin/lsphp',
                                              'php.ini': '/opt/alt/php44/etc/php.ini',
                                              'php': '/opt/alt/php44/usr/bin/php-cgi',
                                              'php-cli': '/opt/alt/php44/usr/bin/php'}}}
        :rtype: dict
        )r(�_load_alternatives_configr�ConfigNotFound�WrongConfigFormat)r/s rrZz"ClSelect.get_all_alternatives_datarsa���&�	�
��.�.�0�0�0�0��"�1�>�3S�T�
�
�
��	�	�	�
�����&�&s��>�>c��|js|���	||j|iS#t$rtj|���wxYw)z�
        Returns selector config contents of certain version as dict.
        If no data loads them
        @param version: string, selector version
        @return: dict
        )r(rdr]r�NoSuchAlternativeVersion�r/rXs  r�get_alternatives_datazClSelect.get_alternatives_data�sj���&�	-��*�*�,�,�,�	C��T�4�W�=�>�>���	C�	C�	C� �9�'�B�B�B�	C���s	�,�AFc��|���}	|j�d|j��}|||d||d|jfS#tjtf$r|�|��cYSwxYw)z/
        Gets default selector version
        �versionsrXrY)rZr'rPr%rIrRr]�_compose_native_info)r/�show_native_versionr_rXs    r�get_versionzClSelect.get_version�s����5�5�7�7��	B��h�l�l�:�t�z�:�:�G���W�%�i�0��W�%�f�-�d�j�9�;�
;���+�X�6�	B�	B�	B��,�,�-@�A�A�A�A�A�	B���s�AA�+B�Bc��|���}|�||��|�|jd|j|j|f��}|�d�|��|j��dS)z/
        Sets default selector version
        )rl�
N)rZ�_check_alternative�_process_ini_file�
DEFAULTS_PATH�_add_or_change_optionr%�_write_to_filer<�r/rXr_�defaults_contentss    r�set_versionzClSelect.set_version�s����5�5�7�7�������6�6�6� �2�2�����&�
�Z��!�	#�#��
	
����I�I�'�(�(�$�*<�	>�	>�	>�	>�	>r!c��|���}|�||��|�|j|j|f|jd��}|�d�|��|j��dS)z5
        Removes disabled state from version
        �staterqN)rZrrrsrtr%�_remove_optionrvr<rws    r�enable_versionzClSelect.enable_version�s����5�5�7�7�������6�6�6� �2�2���
�Z��!����	���
	
����I�I�'�(�(�$�*<�	>�	>�	>�	>�	>r!c��|���}|�||��|�|j|j|f|jd��}|�d�|��|j��dS)z-
        Marks a vesrion as disabled
        )r{�disabledrqN)rZrrrsrtr%rurvr<rws    r�disable_versionzClSelect.disable_version�s����5�5�7�7�������6�6�6� �2�2���
�Z��!��&�!�	#�#��
	
����I�I�'�(�(�$�*<�	>�	>�	>�	>�	>r!c�L�|j�|j�|��d��S)z�
        Method that allows you to check if some version is enabled in config.
        E.g. is_version_enabled('5.4') -> True
        :rtype: bool
        r{)r'�
has_optionr%ris  r�is_version_enabledzClSelect.is_version_enabled�s3���8�&�&��j�j�'�'�*�G�5�5�5�	5r!c�L�|���}|�|��}dddd�i}t|�����dgz}|D]4}||vri||<|�|��||d<d||d<�5	|j�d|j��}n##tj	tj
f$rd}YnwxYw	d||d<n"#t$rtj
|���wxYw|�d��||d<|�d��|�|d��t#t%|����D]+}||}	|	||	d||	dff||<�,t'|��S)	zl
        Returns state of alternatives
        @return: tuple[version, tuple[isEnabled, isDefault]]
        �nativeTF)�enabled�defaultr�r�rlr)rZrmr[r\r�r'rPr%rIrRrSr]rrh�pop�remover�range�lenr^)
r/rnr_�native_info�summary�alt_versionsrX�default_version�idx�vs
          r�get_summaryzClSelect.get_summary�s���
�5�5�7�7���/�/�0C�D�D����%�@�@�A���l�/�/�1�1�2�2�h�Z�?��#�	0�	0�G��g�%�%�#%��� �*.�*A�*A�'�*J�*J�G�G��Y�'�*/�G�G��Y�'�'�	'�"�h�l�l�:�t�z�B�B�O�O���+�\�-G�H�	'�	'�	'�&�O�O�O�	'����	K�26�G�O�$�Y�/�/���	K�	K�	K� �9�/�J�J�J�	K����")�+�+�h�"7�"7���A������H�%�%�%����K��N�+�+�+���\�*�*�+�+�	)�	)�C��S�!�A�����I�&����I�&�(�!)�L�����\�"�"�"s� B/�/C�C�C�C>c�Z�	t|j��}|D]�}|���}|dkr�|���\}}}}|j|vr�@||jvr
i|j|<||j|d<d|j|vri|j|d<||j|d|<��|jst
jdd����dS#ttf$r'}t
jd|j�d|�d	�d����d}~wt$rt
j|j���wxYw)
a
        Parses selector config file and fills an instance config dict. Example:
        {'4.4': {'version': '4.4.9', 'data': {'lsphp': '/opt/alt/php44/usr/bin/lsphp',
                                              'php.ini': '/opt/alt/php44/etc/php.ini',
                                              'php': '/opt/alt/php44/usr/bin/php-cgi',
                                              'php-cli': '/opt/alt/php44/usr/bin/php'}}}
        :raises ClSelectExcept.ConfigNotFound:
        :raises ClSelectExcept.WrongConfigFormat:
        �rXrYNzalt-php packages not foundrDzCannot read z: z
. Native used)�open�CONFIG_PATH�striprQr%r(rre�OSError�IOError�
ValueErrorrf)	r/�f�line�
stripped_liner0�
short_version�long_version�	item_pathrVs	         rrdz"ClSelect._load_alternatives_config�s���	E��T�%�&�&�A��

Q�

Q�� $�
�
���
� �B�&�&��!.�!4�!4�!6�!6���}�l���:�T�)�)�� ��(?�?�?�=?�D�+�M�:�DP��'�
�6�y�A���!8��!G�G�G�EG�D�+�M�:�6�B�GP��'�
�6�v�>�t�D�D��*�
<�$�3�D�:�<�<�<�<�
<�
<����!�	8�	8�	8� �/�/�59�5E�5E�5E�q�q�q�I�6�8�8�8�
8������	E�	E�	E� �2�4�3C�D�D�D�	E���s�CC�D*�!"D�'D*Nc��tjdd���}t|_|r|�|��n|�|j��|S)z:
        Gets ConfigParser handler for future use
        NFrA)rIrN�optionxformrKrt)r/rrUs   rr&z$ClSelect._get_default_config_handlersS���
&�T�%�
H�
H�
H������	(��G�G�D�M�M�M�M��G�G�D�&�'�'�'��	r!c�F�|dkr||vrtj|���dSdS)Nr�)rrh)rXr_s  rrrzClSelect._check_alternative$s6���h���7�,�#>�#>� �9�'�B�B�B���#>�#>r!c�^�dd�dgt|��z��z}||zS)z�
        Gets section header data tuple and returns ini section header string
        @param section_info: tuple
        @return: string
        z[%s]r�z%s)r<r�)�section_info�section_fmts  r�_make_section_headerzClSelect._make_section_header)s4���r�w�w��v��L�0A�0A�'A�B�B�B���\�)�)r!c��ttd�|����}|r|ddkr|�d��|S)zN
        Removes empty lines from list and appends newline if missing
        c��|dkS)Nr�r )�is r�<lambda>z'ClSelect._smooth_data.<locals>.<lambda>7s
��a�2�g�r!���rq)�list�filterr)rYs r�_smooth_datazClSelect._smooth_data3sL���F�-�-��5�5�6�6���	�t�B�x�4�'�'��K�K������r!Tc
�f�g}g}g}	d}
d}d}|�|��}
	t|��}|D]�}|���}|�d��r_d}
d|vrd}|
|krd}t	|��dkr|�|��g}|�|||	||����|g}	��|
r|	�|����|�|����|�|||	||����|���n#ttf$rYnwxYw|s(d|
vr$dd|j
zdg}|�|��|}|s�	|�d��}n#tj
$rYnwxYw|d	kr:|�|||
d
d�|��zg||����n`|dkr8|�|��|�|||
g||����n"|�|||
g||����|S)
z�
        Parses ini file by sections,
        calls supplied callable to modify section is question,
        returns file as list of strings
        F�[Tz
[versions]rz%s = nativer�r��disable_extentionsz
modules = rH�enable_extentions)r�r�r��
startswithr��extendr�closer�r�r%�
_get_builtinsr�UnableToGetExtensionsr<)r/rr��functionrY�trace�action�contents�no_section_contents�section�
in_section�found�has_default�section_headerr�r�r��build_ins                  rrszClSelect._process_ini_file=s����� �����
������2�2�<�@�@��	��T�
�
�A��
5�
5���z�z�|�|���?�?�3�'�'��!%�J�#�t�+�+�&*��%��-�-� $���.�/�/�1�4�4� ���(;�<�<�<�.0�+��O�O�H�H�\�7�D�%�$P�$P�Q�Q�Q�#�f�G���5��N�N�4�(�(�(�(�'�.�.�t�4�4�4�4��O�O�H�H�\�7�D�%�H�H�I�I�I�
�G�G�I�I�I�I����!�	�	�	��D�	�����	�|�>�A�A�#�]�T�Z�%?��D�G��N�N�8�$�$�$��H��	W�
��-�-�h�7�7����!�7�
�
�
���
�����-�-�-���������WZ�W_�W_�`h�Wi�Wi�Hi�7j�lp�rw� x� x�y�y�y�y��.�.�.����H�%�%�%��������7G��u� U� U�V�V�V�V��������7G��u� U� U�V�V�V��s$�C>D"�"D6�5D6�&E<�<F�
Fc��d|jz}|���}	||d|}n&#t$r|�d���}YnwxYw|S)z�
        Retrives path to php binary for supplied version
        :param version: php version to retrive path
        :return: path to php binary. If alternative version not found native php binary path returned
        z%s-clirYr3)�suffix)r%rZr]r4)r/rXr0r_rs     r�_get_php_binary_pathzClSelect._get_php_binary_pathtsv���$�*�$���5�5�7�7��	7���(��0��6�D�D���	7�	7�	7��(�(��(�6�6�D�D�D�	7�����s�5� A�Ac��|���}d|�d��i}|���D]}|�|��||<�|S)a
        Retrives paths to php binary for all versions
        :return: Dictionary version -> path. Example:
            { '5.2': '/opt/alt/php52/usr/bin/php',
              '5.3': '/opt/alt/php53/usr/bin/php',
              'native': '/usr/bin/php'
            }
        r�)rZr�r\)r/r_�
paths_dictrXs    r�get_all_php_binaries_pathsz#ClSelect.get_all_php_binaries_paths�sk���5�5�7�7���� 9� 9�(� C� C�D�
�#�(�(�*�*�	E�	E�G�"&�";�";�G�"D�"D�J�w����r!c��t|j|zd��}|���}|���|S)z�
        Retrives contents of cache file for supplied php version
        :param version: PHP version to read file
        :return: file contents
        �r)r��CACHEFILE_PATTERNrKr�)r/rXr��outputs    r�_read_php_cache_filezClSelect._read_php_cache_file�s:��
��'�'�1�3�7�7��������	���	�	�	��
r!c�d�g}	|�|��}n)#ttf$rtj|���wxYwd\}}|�|��|�|��}}|dkrdn|t
|��z}|||�}tjd��}	|�	d��D]p}
|	�
|
��s�d�tj	d|
�������}||j
vr|�|���q|S)z�
        Gets php extensions from the /var/lve/phpX.X.dat cache file,
        which contains list of modules that are either compiled-in
        or enabled in /opt/alt/phpXX/etc/php.ini config file
        )z
[PHP Modules]z[Zendr�rz\wrq�_z\s+)r�r�r�rr��findr��re�compilerQ�matchr<�lowerr+r)r/rX�builtinsr��
start_pattern�end_pattern�start_index�	end_index�modules_list�module_pattern�ext�modules            rr�zClSelect._get_builtins�sE����	@��.�.�w�7�7�F�F����!�	@�	@�	@� �6�w�?�?�?�	@����
&>�"�
�{�!'���]�!;�!;�V�[�[��=U�=U�Y��&�"�,�,�a�a�+��M�@R�@R�2R���k�)�3�4����E�*�*���%�%�d�+�+�	(�	(�C�!�'�'��,�,�
���X�X�b�h�u�c�i�i�k�k�:�:�;�;�F��T�4�4�4�����'�'�'���s	��&Ac����|�|��}t|��dks||dkr|S|�tt	�fd�|������S)z�
        Adds 'modules' option to section or extends it
        @param section_info: tuple (item and version)
        @param section: list
        @param data: string
        @return: list
        rc�0��|����Sr)r�)r�rYs �rr�z)ClSelect._remove_option.<locals>.<lambda>�s���q�|�|�D�'9�'9�#9�r!)r�r�r�r�r�)r/r�r�rYr�r�s   `  rr|zClSelect._remove_option�sv����2�2�<�@�@���w�<�<�1����'�!�*� <� <��N�� � ���9�9�9�9�G�D�D�E�E�G�G�	Gr!c�h�|�|��}t|��dks||dkr|Sd}tt|����D]'}||�|d��r|}n�(d|z}|r|||<n|�|��|�|��S)z�
        Adds 'modules' option to section or extends it
        @param section_info: tuple
        @param section: list
        @param data: tuple
        @return: list
        rNz%s = %s)r�r�r�r�rr�)	r/r�r�rYr�r��oidxr��options	         rruzClSelect._add_or_change_option�s����2�2�<�@�@���w�<�<�1����'�!�*� <� <��N�����W���&�&�	�	�C��s�|�&�&�t�A�w�/�/�
�����
��T�!���	#�"�G�D�M�M��N�N�6�"�"�"�� � ��)�)�)r!c���	t|d��}|�d|z��|���dS#ttf$r}tj||���d}~wwxYw)z$
        Saves data to file
        �wz%s
N)r��writer�r�r�r�UnableToSaveData)r/�
file_contents�	file_pathr�rVs     rrvzClSelect._write_to_file�sz��	@��Y��$�$�A�
�G�G�F�]�*�+�+�+�
�G�G�I�I�I�I�I����!�	@�	@�	@� �1�)�Q�?�?�?�����	@���s�<A�A+�A&�&A+c�\�dddd�}|j}|r||vr|j�||��}|js|�|j��||jvrM|j|}tj�|��r!tj�|��s|S|j|jS)z5
        Returns path for native interpreter
        z-cliz.iniz-fpm)r3�ini�fpm)r%r)�_load_native_contentsrr�isfile�islink)r/r��suffixesr0rs     rr4zClSelect._get_native_path�s���
�������z���	;�f��(�(�!�Z�Z��&�)9�)9�:�D��$�	3��&�&�t�z�2�2�2��4�(�(�(��(��.�D��w�~�~�d�#�#�
�B�G�N�N�4�,@�,@�
����$�T�Z�0�0r!c	��	t|j��}|D]`}|�d��r�||vr�d|vr�"tt	d�|�d������\}}||j|<�a||jvr|j|j|<|���dS#ttf$r|j|j|<YdSwxYw)z�
        Parses native.conf file and loads it as dict, for example:
        {'php-fpm': '/usr/local/sbin/php-fpm', 'php.ini': '/usr/local/lib/php.ini', 'php': '/usr/bin/php', 'php-cli': '/usr/bin/php'}
        �#�=c�*�|���Sr)r�)�xs rr�z0ClSelect._load_native_contents.<locals>.<lambda>
s�������r!N)r��NATIVE_CONFr�r�rrQr)�DEFAULT_PHP_PATHr�r�r�)r/�valuer�r�r0rs      rr�zClSelect._load_native_contents�s���
	A��T�%�&�&�A��
3�
3���?�?�3�'�'�����$�$���d�?�?��!�#�':�':�T�Z�Z��_�_�"M�"M�N�N�
��d�.2��%�d�+�+��D�1�1�1�/3�/D��%�e�,�
�G�G�I�I�I�I�I����!�	A�	A�	A�+/�+@�D�!�%�(�(�(�(�	A���s�B#B'�' C�
Cc���|sdd|���fS|�d���}|r+d|d�d�d|d�d�|���fSdd|���fS)Nr�F)�verboseznative (r�)r
)r4�get_native_version)r/�show_version�native_versions   rrmzClSelect._compose_native_infos����	?��X�t�'<�'<�'>�'>�>�>��0�0��0�?�?���	-�	-�%3�A�%6�%6�%6�8�8�%3�A�%6�%6�%6�8��)�)�+�+�-�
-���4�#8�#8�#:�#:�:�:r!c�"�|jr|jStjd��}	t|jd��}|���}|���nF#ttf$r2}|r%tj
ddt|��d���Yd}~dSd}~wwxYw|���D]R}|�
|��}|r9|�d��|�d��}	}||	f|_||	fcS�SdS)Nz(PHP\s+(?P<full>(?P<short>\d+\.\d+)\.\d+)r��text�ERROR)�statusrE�short�full)r,r�r�r��CACHEFILE_NATIVE_VER_PATTERNrKr�r�r�r
�
print_diagrN�
splitlinesr��group)
r/r��version_patternr�rYrVr��mrrs
          rr�zClSelect.get_native_versions1����	(��'�'��*�%P�Q�Q��	��T�6��<�<�A��6�6�8�8�D�
�G�G�I�I�I�I����!�	�	�	��
S��"�6�g�#�a�&�&�+Q�+Q�R�R�R��4�4�4�4�4�����	�����O�O�%�%�	#�	#�D��%�%�d�+�+�A��
#��g�g�g�.�.�������t��(-�t�}��$��d�{�"�"�"�
#��ts�=A"�"B%�3'B � B%)r#)Fr)TN)T).�__name__�
__module__�__qualname__r�rtr�r�rr=r6�
CACHEFILE_DIRr�r�staticmethodrr"r1r:r.r>rbrZrjroryr}r�r�r�rdr&rrr�r�rsr�r�r�r�r|rurvr4r�rmr�r r!rrrs�������2�K�3�M�%��0�K�,�I�6�K�%�I��M�%��4��#0�3H�#H� ��	Q�	Q��\�	Q��2�2��\�2�"�"�"�"�
�
�
�-�-�-�
�
�
�+�+�+�$'�'�'�$C�C�C�B�B�B�B�>�>�>�>�>�>�>�>�>�5�5�5�#�#�#�#�B"E�"E�"E�H
�
�
�
�C�C�C�&��&8�9�9��*�*�*�(�<�(<�=�=����� �<��-�-�L�5�5�5�5�n���
�
�
�	�	�	����:G�G�G�G�*�*�*�*�.	@�	@�	@�1�1�1�1�&A�A�A�,;�;�;�;������r!r)�
__future__rrrrr�rrr�r�clcommonr�future.movesr	rI�clselectexceptrr�
clselectprintr
�objectrr r!r�<module>rs��'�&�&�&�&�&�%�%�%�%�%�%�������	�	�	�	�	�	�	�	�
�
�
�
�������������'�'�'�'�'�'�5�5�5�5�5�5�A�A�A�A�A�A�A�A�"�"�"�"�"�"�V�V�V�V�V�v�V�V�V�V�Vr!