
    b                        d Z ddlmZ ddlmZ ddlmZ ddlZddlZddlZddlZddl	Z	ddl
Z
ddlmZ ddlmZ ddlmZ 	 dd	lmZ 	 dd
lmZ dZdZ G d de      Z G d de      Z G d de      Z G d de      Z G d de      Z G d de      Z  G d de       Z! G d de      Z"d Z# G d de      Z$ G d  d!e      Z% G d" d#e      Z&ejN                  ejP                  ejR                  ejT                  d$Z+ ejX                   ejZ                  e.%      &      Z/ ejX                   ejZ                  e.%       ejZ                  e.%       ejZ                  e.%       ejZ                  e.%       ejX                   ejZ                  e.%       ej`                   ejZ                  e.%      '      (      e/e/e/e/)	      Z1d*Z2d+Z3 G d, d-e      Z4y# e$ r
 dd	lmZ Y w xY w# e$ r ddlZY w xY w).z"Support for externalized runtimes.    )absolute_import)division)print_functionN   )comm)input)schema)py27_subprocesszWriting [{0}] to [{1}].z%Not writing [{0}], it already exists.c                       e Zd ZdZy)Errorz)Base class for exceptions in this module.N__name__
__module____qualname____doc__     .lib/third_party/gae_ext_runtime/ext_runtime.pyr   r   /   s    1r   r   c                       e Zd ZdZy)PluginInvocationFailedz?Raised when a plugin invocation returns a non-zero result code.Nr   r   r   r   r   r   3   s    Gr   r   c                       e Zd ZdZy)InvalidRuntimeDefinitionz@Raised when an inconsistency is found in the runtime definition.Nr   r   r   r   r   r   7   s    Hr   r   c                       e Zd ZdZddZd Zy)Paramsa  Parameters passed to the the runtime module Fingerprint() methods.

  Attributes:
    appinfo: (apphosting.api.appinfo.AppInfoExternal or None) The parsed
      app.yaml file for the module if it exists.
    custom: (bool) True if the Configurator should generate a custom runtime.
    runtime (str or None) Runtime (alias allowed) that should be enforced.
    deploy: (bool) True if this is happening from deployment.
  Nc                 <    || _         || _        || _        || _        y Nappinfocustomruntimedeploy)selfr   r   r    r!   s        r   __init__zParams.__init__G   s    DLDKDLDKr   c                     | j                   xr | j                   j                         | j                  | j                  | j                  dS )zReturns the object converted to a dictionary.

    Returns:
      ({str: object}) A dictionary that can be converted to json using
      json.dump().
    r   )r   ToDictr   r    r!   r"   s    r   r%   zParams.ToDictM   s<     ||=(;(;(=kk||kk# #r   )NFNF)r   r   r   r   r#   r%   r   r   r   r   r   <   s    
#r   r   c                   "    e Zd ZdZd Zd Zd Zy)ConfiguratorzBase configurator class.

  Configurators generate config files for specific classes of runtimes.  They
  are returned by the Fingerprint functions in the runtimes sub-package after
  a successful match of the runtime's heuristics.
  c                      y)a`  Collect all information on this application.

    This is called after the runtime type is detected and may gather
    additional information from the source code and from the user.  Whereas
    performing user queries during detection is deprecated, user queries are
    allowed in CollectData().

    The base class version of this does nothing.
    Nr   r&   s    r   CollectDatazConfigurator.CollectDatab       r   c                      y)zRun additional build behavior before the application is deployed.

    This is called after the runtime type has been detected and after any
    additional data has been collected.

    The base class version of this does nothing.
    Nr   r&   s    r   PrebuildzConfigurator.Prebuildm   r+   r   c                     t               )zGenerate all configuration files for the module.

    Generates config files in the current working directory.

    Returns:
      (callable()) Function that will delete all of the generated files.
    NotImplementedErrorr&   s    r   GenerateConfigszConfigurator.GenerateConfigsv   s     
r   N)r   r   r   r   r*   r-   r1   r   r   r   r(   r(   Z   s    	 r   r(   c                   (    e Zd ZdZd Zd Zd Zd Zy)ExecutionEnvironmentzAn interface for providing system functionality to a runtime definition.

  Abstract interface containing methods for console IO and system
  introspection.  This exists to allow gcloud to inject special functionality.
  c                     t               )z5Returns the full path of the python executable (str).r/   r&   s    r   GetPythonExecutablez(ExecutionEnvironment.GetPythonExecutable       

r   c                     t               )zReturns true r/   r&   s    r   	CanPromptzExecutionEnvironment.CanPrompt   r6   r   c                     t               r   r/   r"   messages     r   PromptResponsez#ExecutionEnvironment.PromptResponse   s    

r   c                     t               )zDPrint a message to the console.

    Args:
      message: (str)
    r/   r:   s     r   PrintzExecutionEnvironment.Print   s     
r   Nr   r   r   r   r5   r8   r<   r>   r   r   r   r3   r3      s        r   r3   c                   (    e Zd ZdZd Zd Zd Zd Zy)DefaultExecutionEnvironmentz4Standard implementation of the ExecutionEnvironment.c                 "    t         j                  S r   )sys
executabler&   s    r   r5   z/DefaultExecutionEnvironment.GetPythonExecutable   s    >>r   c                 >    t         j                  j                         S r   )rC   stdinisattyr&   s    r   r8   z%DefaultExecutionEnvironment.CanPrompt   s    99r   c                     t         j                  j                  |       t         j                  j                          t	        d      S )Nz> )rC   stdoutwriteflushr   r:   s     r   r<   z*DefaultExecutionEnvironment.PromptResponse   s.    JJWJJ;r   c                     t        |       y r   )printr:   s     r   r>   z!DefaultExecutionEnvironment.Print   s	    	'Nr   Nr?   r   r   r   rA   rA      s    <
r   rA   c                   :    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
y	)
ExternalRuntimeConfiguratora  Configurator for general externalized runtimes.

  Attributes:
    runtime: (ExternalizedRuntime) The runtime that produced this.
    params: (Params) Runtime parameters.
    data: ({str: object, ...} or None) Optional dictionary of runtime data
      passed back through a runtime_parameters message.
    generated_appinfo: ({str: object, ...} or None) Generated appinfo if any
      is produced by the runtime.
    path: (str) Path to the user's source directory.
  c                     || _         || _        || _        |r6i | _        d|vrd| j                  d<   | j                  j	                  |       nd| _        || _        || _        y)a  Constructor.

    Args:
      runtime: (ExternalizedRuntime) The runtime that produced this.
      params: (Params) Runtime parameters.
      data: ({str: object, ...} or None) Optional dictionary of runtime data
        passed back through a runtime_parameters message.
      generated_appinfo: ({str: object, ...} or None) Optional dictionary
        representing the contents of app.yaml if the runtime produces this.
      path: (str) Path to the user's source directory.
      env: (ExecutionEnvironment)
    envflexN)r    paramsdatagenerated_appinfoupdatepathrQ   )r"   r    rS   rT   rU   rW   rQ   s          r   r#   z$ExternalRuntimeConfigurator.__init__   sl     DLDKDI  "d''(.u% ##$56#dDIDHr   c                 ^   | j                   sy| j                  j                  rt        j                  n| j
                  j                  }t        j                  j                  | j                  d      }| j                  j                  st        j                  j                  |      r |t        j                  d             y |t        j                  d| j                               t        |d      5 }t!        j"                  | j                   |d       ddd       y# 1 sw Y   yxY w)z8Generates the app.yaml file if it doesn't already exist.Napp.yamlwF)default_flow_style)rU   rS   r!   logginginforQ   r>   osrW   joinr   existsFILE_EXISTS_MESSAGEformatWRITING_FILE_MESSAGEopenyaml	safe_dump)r"   notifyfilenamefs       r   MaybeWriteAppYamlz-ExternalRuntimeConfigurator.MaybeWriteAppYaml   s     !!![[//W\\TXX^^F ww||DIIz2H {{bggnnX6 ''
34
&&z499=>	h	
nnT++Q5I 
		s   7#D##D,c                     || _         y)zSets the generated appinfo.N)rU   )r"   rU   s     r   SetGeneratedAppInfoz/ExternalRuntimeConfigurator.SetGeneratedAppInfo   s
    .Dr   c                 :    | j                   j                  |        y r   )r    r*   r&   s    r   r*   z'ExternalRuntimeConfigurator.CollectData   s    LLT"r   c                 :    | j                   j                  |        y r   )r    r-   r&   s    r   r-   z$ExternalRuntimeConfigurator.Prebuild   s    LL$r   c                     | j                          | j                  j                  s:| j                  r.t	        j
                  | j                        | j                  _        | j                  j                  |       S r   )rj   rS   r   rU   r   dict_to_objectr    r1   r&   s    r   r1   z+ExternalRuntimeConfigurator.GenerateConfigs   sW    
 ;;4#9#9 //0F0FGdkk<<''--r   c                     | j                          | j                  j                  s:| j                  r.t	        j
                  | j                        | j                  _        | j                  j                  |       S r   )rj   rS   r   rU   r   rp   r    GenerateConfigDatar&   s    r   rr   z.ExternalRuntimeConfigurator.GenerateConfigData  sW    
 ;;4#9#9 //0F0FGdkk<<**400r   N)r   r   r   r   r#   rj   rl   r*   r-   r1   rr   r   r   r   rO   rO      s+    
<J0/# .1r   rO   c                 d    |j                  d      }t        j                  j                  | g| S )a  Get the absolute path from a unix-style relative path.

  Args:
    basedir: (str) Platform-specific encoding of the base directory.
    pathname: (str) A unix-style (forward slash separated) path relative to
      the runtime definition root directory.

  Returns:
    (str) An absolute path conforming to the conventions of the operating
    system.  Note: in order for this to work, 'pathname' must not contain
    any characters with special meaning in any of the targeted operating
    systems.  Keep those names simple.
  /)splitr^   rW   r_   )basedirpathname
componentss      r   _NormalizePathry     s*     ~~c"*	g	+
	++r   c                       e Zd ZdZd Zd Zy)GeneratedFilez0Wraps the name and contents of a generated file.c                      || _         || _        y)zConstructor.

    Args:
      filename: (str) Unix style file path relative to the target source
        directory.
      contents: (str) File contents.
    N)rh   contents)r"   rh   r}   s      r   r#   zGeneratedFile.__init__&  s     DMDMr   c                    t        || j                        }t        j                  j	                  |      sX |t
        j                  | j                  |             t        |d      5 }|j                  | j                         ddd       |S  |t        j                  | j                               y# 1 sw Y   |S xY w)a:  Write the file to the destination directory.

    Args:
      dest_dir: (str) Destination directory.
      notify: (callable(str)) Function to notify the user.

    Returns:
      (str or None) The full normalized path name of the destination file,
      None if it wasn't generated because it already exists.
    rZ   N)ry   rh   r^   rW   r`   rc   rb   rd   rJ   r}   ra   )r"   dest_dirrg   rW   ri   s        r   WriteTozGeneratedFile.WriteTo1  s     (DMM2D77>>$!((ABc?a	 k ''67 ks   (B44B>N)r   r   r   r   r#   r   r   r   r   r{   r{   #  s    8	r   r{   c                       e Zd Zd Zy)PluginResultc                 J    d| _         d | _        d | _        d | _        g | _        y )N)	exit_coderuntime_datarU   docker_contextfilesr&   s    r   r#   zPluginResult.__init__J  s(    DND!DDDJr   N)r   r   r   r#   r   r   r   r   r   H  s    r   r   c                       e Zd ZdZd Zy)
_Collectorz0Manages a PluginResult in a thread-safe context.c                 T    t               | _        t        j                         | _        y r   )r   result	threadingLocklockr&   s    r   r#   z_Collector.__init__U  s    .DK DIr   N)r   r   r   r   r#   r   r   r   r   r   R  s
    8!r   r   )r]   errorwarndebug)	converter)python)element)r   files_to_copy)	namedescriptionauthorapi_versiongenerate_configsdetectcollect_dataprebuild	postbuildz$Missing [{0}] field in [{1}] messagezTUser input requested: [{0}] while running non-interactive with no default specified.c                   ~    e Zd ZdZd Zed        Zed        Zd Z	d Z
d Z	 	 	 dd	Zd
 Zd Zd ZeZd Zd Zd Zy)ExternalizedRuntimez%Encapsulates an externalized runtime.c                     || _         || _        	 t        j                  |      | _        y# t
        $ r)}t        dj                  |j                              d}~ww xY w)z
    Args:
      path: (str) Path to the root of the runtime definition.
      config: ({str: object, ...}) The runtime definition configuration (from
        runtime.yaml).
      env: (ExecutionEnvironment)
    zInvalid runtime definition: {0}N)	rootrQ   _RUNTIME_SCHEMAConvertValueconfig
ValueErrorr   rb   r;   )r"   rW   r   rQ   exs        r   r#   zExternalizedRuntime.__init__{  s_     DIDH@ $008dk @$
+
2
22::
>@ @@s   + 	A$AAc                 :    | j                   j                  dd      S )Nr   unnamed)r   getr&   s    r   r   zExternalizedRuntime.name  s    ;;??69--r   c                     t        t        j                  j                  | d            5 }t	        | t        j                  |      |      cddd       S # 1 sw Y   yxY w)zLoads the externalized runtime from the specified path.

    Args:
      path: (str) root directory of the runtime definition.  Should
        contain a "runtime.yaml" file.

    Returns:
      (ExternalizedRuntime)
    zruntime.yamlN)rd   r^   rW   r_   r   re   load)rW   rQ   ri   s      r   LoadzExternalizedRuntime.Load  s=     
bggll40	1Q tyy|S9 
2	1	1s    AAc                 |    	 |j                         }|syt        j                  |d|j                                <)a!  Process the standard error stream of a plugin.

    Standard error output is just written to the log at "warning" priority and
    otherwise ignored.

    Args:
      section_name: (str) Section name, to be attached to log messages.
      stderr: (file) Process standard error stream.
    : N)readliner\   r   rstrip)r"   section_namestderrlines       r   _ProcessPluginStderrz(ExternalizedRuntime._ProcessPluginStderr  s5     __dll|T[[];<	 r   c                 B   fd}|j                  d      }|"t        j                  dt        |      z         y|t        v rt	        |   |j                  d             y|dk(  r"	 |d   |_        |j                  d      |_	        y|d	k(  r1	 |d
   }|d   }	|j                  j                  t        ||	             y|dk(  rd|j                         |d} ||       y|dk(  r	 |d   }|j                  d      }| j                  j                         r6|rdj                  ||      }n|dz   }| j                  j!                  |      }n/||}n*d}t        j                  t"        j                  |              |d|d       y|dk(  r	 |d   |_        yt        j                  d|z         y# t        $ r- t        j                  t        j                  d|             Y uw xY w# t        $ r3}
t        j                  t        j                  |
|             Y d}
~
yd}
~
ww xY w# t        $ r3}
t        j                  t        j                  d|             Y d}
~
yd}
~
ww xY w# t        $ r, t        j                  t        j                  d|             Y yw xY w)a  Process a message received from the plugin.

    Args:
      plugin_stdin: (file) The standard input stream of the plugin process.
      message: ({str: object, ...}) The message (this maps directly to the
        message's json object).
      result: (PluginResult) A result object in which to store data collected
        from some types of message.
      params: (Params) Parameters passed in through the
        fingerprinter.
      runtime_data: (object or None) Arbitrary runtime data obtained from the
        "detect" plugin.  This will be None if we are processing a message for
        the detect plugin itself or if no runtime data was provided.
    c                 t    t        j                  |        j                  d       j                          y )N
)jsondumprJ   rK   )responseplugin_stdins    r   SendResponsez9ExternalizedRuntime._ProcessMessage.<locals>.SendResponse  s+    
ii,'r   typeNzMissing type in message: %0.80sr;   runtime_parametersr   r   gen_filerh   r}   
get_configget_config_response)r   rS   r   
query_userpromptdefaultz{0} [{1}]: : query_user_response)r   r   set_docker_contextrW   zUnknown message type %s)r   r\   r   str
_LOG_FUNCSr   KeyError_MISSING_FIELD_ERRORrb   rU   r   appendr{   r%   rQ   r8   r<   _NO_DEFAULT_ERRORr   )r"   r   r;   r   rS   r   r   msg_typerh   r}   r   r   r   r   s    `            r   _ProcessMessagez#ExternalizedRuntime._ProcessMessage  sn   "
 {{6"Hmm5GDE	Z	7;;y12	)	)M%n5 ")Y!7f	Z	A:&:&M(H=> 
\	!/"MMO".0h 8	\	!" I&g				!((9'SL'((1 &&
--)008
91VDE	)	) ' mm-89a  M*11.(KLM  A*11"h?@@A  *11(HEF0  *11&(CDsT   (
F2 /G+ 'H* 
I) 22G('G(+	H'4)H""H'*	I&3)I!!I&)2JJc                    	 |j                   j                         }|sy	 t        j                  |      }| j	                  |j
                  ||||       S# t        $ r+ t        j                  |d|j                                Y 4w xY w)z:Process the standard output and input streams of a plugin.r   N)
rI   r   r   loadsr   rF   r   r\   r]   r   )r"   r   procr   rS   r   r   r;   s           r   _ProcessPluginPipesz'ExternalizedRuntime._ProcessPluginPipes   sy     [[!!#d?**T"TZZ&&,O   ?t{{}=>?s   4A 1B	B	Nc                    d|v rht        | j                  |d         }t               }t        j                  | j
                  j                         |g|r|ng z   t        j                  t        j                  t        j                        }	t        j                  | j                  ||	j                  f      }
|
j                          t        j                  | j                  ||	|||f      }|j                          |
j                          |j                          |	j                         }||_        ||vr1t#        d|d|d| j$                  j'                  dd      d	|      |S t)        j*                  d
|z         y)a  Run a plugin.

    Args:
      section_name: (str) Name of the config section that the plugin spec is
        from.
      plugin_spec: ({str: str, ...}) A dictionary mapping plugin locales to
        script names
      params: (Params or None) Parameters for the plugin.
      args: ([str, ...] or None) Command line arguments for the plugin.
      valid_exit_codes: (int, ...) Exit codes that will be accepted without
        raising an exception.
      runtime_data: ({str: object, ...}) A dictionary of runtime data passed
        back from detect.

    Returns:
      (PluginResult) A bundle of the exit code and data produced by the plugin.

    Raises:
      PluginInvocationFailed: The plugin terminated with a non-zero exit code.
    r   )rI   rF   r   )targetargsz"Failed during execution of plugin z for section z of runtime r   unknownz. rc = z"No usable plugin type found for %sN)ry   r   r   
subprocessPopenrQ   r5   PIPEr   Threadr   r   startr   r_   waitr   r   r   r   r\   r   )r"   r   plugin_specrS   r   valid_exit_codesr   normalized_pathr   pstderr_threadstdout_threadr   s                r   	RunPluginzExternalizedRuntime.RunPlugin  sT   0 ;&tyy+h2GHo
 ~f


DHH88:OL$(Db2",//!+",//	3a
  &&d.G.G-9188,EGm&&d.F.F-91f-3\-CDm &&(i"f	*	*$&5|&*kkoofi&H&/	&1 2 	2
 mmm8<GHr   c                     | j                   j                  d      }|rQ| j                  d|||gd      }|j                  ryt	        | ||j
                  |j                  || j                        S y)an  Determine if 'path' contains an instance of the runtime type.

    Checks to see if the 'path' directory looks like an instance of the
    runtime type.

    Args:
      path: (str) The path name.
      params: (Params) Parameters used by the framework.

    Returns:
      (Configurator) An object containing parameters inferred from source
        inspection.
    r   )r   r   N)r   r   r   r   rO   r   rU   rQ   )r"   rW   rS   r   r   s        r   DetectzExternalizedRuntime.DetectJ  sn     [[__X&F~~hGf			*49L9L+1+C+C+/+/885 	5 r   c                     | j                   j                  d      }|rR| j                  d||j                  |j                        }|j
                  r|j                  |j
                         yyy)a  Do data collection on a detected runtime.

    Args:
      configurator: (ExternalRuntimeConfigurator) The configurator retuned by
        Detect().

    Raises:
      InvalidRuntimeDefinition: For a variety of problems with the runtime
        definition.
    collectDatar   r   N)r   r   r   rS   rT   rU   rl   )r"   configuratorr   r   s       r   r*   zExternalizedRuntime.CollectDataf  sk     ;;??=1L~~nl*11+7+<+<  >f 
	!	!(()A)AB 
"	 r   c                     | j                   j                  d      }|rT| j                  d||j                  |j                  g|j
                        }|j                  r|j                  |_        yyy)zPerform any additional build behavior before the application is deployed.

    Args:
      configurator: (ExternalRuntimeConfigurator) The configurator returned by
      Detect().
    r   )r   r   N)r   r   r   rS   rW   rT   r   )r"   r   r   r   s       r   r-   zExternalizedRuntime.Prebuildy  sp     {{z*H~~j(L4G4G!!"1B1B  Df 
		"11 
	 r   c                 4   | j                   j                  d      }|r|j                  d      }|rg }t        |      dk7  rt        d      |D ]  }t	        | j
                  |      }t        j                  j                  |      st        d|z        t        |d      5 }|j                         }ddd       |j                  t        |              |S | j                  d||j                  |j                  	      }	|	j                   S y# 1 sw Y   ^xY w)
a#  Generate list of GeneratedFile objects.

    Args:
      configurator: Configurator, the runtime configurator

    Returns:
      [GeneratedFile] a list of GeneratedFile objects.

    Raises:
      InvalidRuntimeDefinition: For a variety of problems with the runtime
        definition.
    generateConfigsfilesToCopyr   zOIf "files_to_copy" is specified, it must be the only field in generate_configs.zKFile [%s] specified in files_to_copy, but is not in the runtime definition.rNr   r   )r   r   lenr   ry   r   r^   rW   isfilerd   readr   r{   r   rS   rT   r   )
r"   r   r   r   all_config_filesrh   	full_namefile_to_readfile_contentsr   s
             r   GetAllConfigFilesz%ExternalizedRuntime.GetAllConfigFiles  s#    {{'89&**=9m	  A%( *= > > &H$TYY9)	** ,E ,4,4 5 5 Is#|(--/M $

!
!--"H
I &   24D , 3 3-9->->   @ ||3 " $#s    DD	c                    |j                   j                  rt        j                  n| j                  j
                  }| j                  |      }|g S |D ].  }|j                  dk(  s|j                  |j                  |       0 g }|D ]Q  }t        j                  j                  t        |j                  |j                              rA|j                  |       S |S )zDo config generation on the runtime, return file objects.

    Args:
      configurator: (ExternalRuntimeConfigurator) The configurator retuned by
        Detect().

    Returns:
      [GeneratedFile] list of generated file objects.
    rY   )rS   r!   r\   r]   rQ   r>   r   rh   r   rW   r^   r`   ry   r   )r"   r   rg   r   config_fileconfig_filess         r   rr   z&ExternalizedRuntime.GenerateConfigData  s     *0077W\\TXX^^F--l;i'				+L--v6 ( L'WW^^N<+<+<+6+?+?A BK( ( r   c                 
   |j                   j                  rt        j                  n| j                  j
                  }| j                  |      }|yd}|D ]!  }|j                  |j                  |       d}# |s |d       |S )a5  Do config generation on the runtime.

    This should generally be called from the configurator's GenerateConfigs()
    method.

    Args:
      configurator: (ExternalRuntimeConfigurator) The configurator retuned by
        Detect().

    Returns:
      (bool) True if files were generated, False if not
    NFTz8All config files already exist, not generating anything.)	rS   r!   r\   r]   rQ   r>   r   r   rW   )r"   r   rg   r   createdr   s         r   r1   z#ExternalizedRuntime.GenerateConfigs  s     *0077W\\TXX^^F--l;G$			,++V	4	@ % GHNr   )N)r   N)r   r   r   r   r#   propertyr   staticmethodr   r   r   r   r   r   r*   r-   Fingerprintr   rr   r1   r   r   r   r   r   x  s~    -@& . . : := N:`?  ?C!%!8It8C&2$ +(T4r   r   )5r   
__future__r   r   r   r   r\   r^   r   rC   r   r   r   ruamel.yamlre   	six.movesr   /googlecloudsdk.appengine.admin.tools.conversionr	   ImportErroryaml_conversiongooglecloudsdk.third_party.py27r
   rc   ra   	Exceptionr   r   r   objectr   r(   r3   rA   rO   ry   r{   r   r   r]   r   warningr   r   MessageValuer   _EXEC_SECTIONRepeatedFieldr   r   r   r   r   r   r   <module>r     s   ) &  %   	  
    %DK
 1 = 2I 2HU Hu 
#V #<$ 6 $ N 6  8"6 $_1, _1D,$"F "J6 ! ! LL]]OO]]	
 6<<#&( !&..		$s+6<<#&s+#V^^v||c**f**<6<<#3NO
  > B r& rm
  %$$%
  s$    G G& G#"G#&	G32G3