Salome HOME
Only load product config of the products that are in the application. If no applicati...
[tools/sat.git] / commands / config.py
index 28e7976b92317cc9f672625ef39276619a366217..3246548027688e2420455a982ebfde4ed811b6c4 100644 (file)
 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 
 import os
 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 
 import os
-import sys
 import platform
 import datetime
 import shutil
 import gettext
 import platform
 import datetime
 import shutil
 import gettext
+import sys
 
 import src
 
 
 import src
 
@@ -31,21 +31,33 @@ gettext.install('salomeTools', os.path.join(satdir, 'src', 'i18n'))
 
 # Define all possible option for config command :  sat config <options>
 parser = src.options.Options()
 
 # Define all possible option for config command :  sat config <options>
 parser = src.options.Options()
-parser.add_option('v', 'value', 'string', 'value', _("print the value of CONFIG_VARIABLE."))
-parser.add_option('e', 'edit', 'boolean', 'edit', _("edit the product configuration file."))
-parser.add_option('l', 'list', 'boolean', 'list',_("list all available applications."))
+parser.add_option('v', 'value', 'string', 'value',
+    _("Optional: print the value of CONFIG_VARIABLE."))
+parser.add_option('e', 'edit', 'boolean', 'edit',
+    _("Optional: edit the product configuration file."))
+parser.add_option('i', 'info', 'string', 'info',
+    _("Optional: get information on a product."))
+parser.add_option('l', 'list', 'boolean', 'list',
+    _("Optional: list all available applications."))
+parser.add_option('', 'show_patchs', 'boolean', 'show_patchs',
+    _("Optional: synthetic view of all patches used in the application"))
 parser.add_option('c', 'copy', 'boolean', 'copy',
 parser.add_option('c', 'copy', 'boolean', 'copy',
-    _("""copy a config file to the personnal config files directory.
+    _("""Optional: copy a config file to the personal config files directory.
 \tWARNING the included files are not copied.
 \tIf a name is given the new config file takes the given name."""))
 \tWARNING the included files are not copied.
 \tIf a name is given the new config file takes the given name."""))
+parser.add_option('n', 'no_label', 'boolean', 'no_label',
+    _("Internal use: do not print labels, Works only with --value and --list."))
+parser.add_option('s', 'schema', 'boolean', 'schema',
+    _("Internal use."))
 
 class ConfigOpener:
 
 class ConfigOpener:
-    ''' Class that helps to find an application pyconf in all the possible directories (pathList)
+    '''Class that helps to find an application pyconf 
+       in all the possible directories (pathList)
     '''
     def __init__(self, pathList):
         '''Initialization
         
     '''
     def __init__(self, pathList):
         '''Initialization
         
-        :param pathList list: The list of paths where to serach a pyconf.
+        :param pathList list: The list of paths where to search a pyconf.
         '''
         self.pathList = pathList
 
         '''
         self.pathList = pathList
 
@@ -53,10 +65,11 @@ class ConfigOpener:
         if os.path.isabs(name):
             return src.pyconf.ConfigInputStream(open(name, 'rb'))
         else:
         if os.path.isabs(name):
             return src.pyconf.ConfigInputStream(open(name, 'rb'))
         else:
-            return src.pyconf.ConfigInputStream( open(os.path.join( self.getPath(name), name ), 'rb') )
+            return src.pyconf.ConfigInputStream( 
+                        open(os.path.join( self.get_path(name), name ), 'rb') )
         raise IOError(_("Configuration file '%s' not found") % name)
 
         raise IOError(_("Configuration file '%s' not found") % name)
 
-    def getPath( self, name ):
+    def get_path( self, name ):
         '''The method that returns the entire path of the pyconf searched
         :param name str: The name of the searched pyconf.
         '''
         '''The method that returns the entire path of the pyconf searched
         :param name str: The name of the searched pyconf.
         '''
@@ -68,37 +81,67 @@ class ConfigOpener:
 class ConfigManager:
     '''Class that manages the read of all the configuration files of salomeTools
     '''
 class ConfigManager:
     '''Class that manages the read of all the configuration files of salomeTools
     '''
-    def __init__(self, dataDir=None):
+    def __init__(self, datadir=None):
         pass
 
         pass
 
-    def _create_vars(self, application=None, command=None, dataDir=None):
-        '''Create a dictionary that stores all information about machine, user, date, repositories, etc...
+    def _create_vars(self, application=None, command=None, datadir=None):
+        '''Create a dictionary that stores all information about machine,
+           user, date, repositories, etc...
         
         :param application str: The application for which salomeTools is called.
         :param command str: The command that is called.
         
         :param application str: The application for which salomeTools is called.
         :param command str: The command that is called.
-        :param dataDir str: The repository that contain external data for salomeTools.
+        :param datadir str: The repository that contain external data 
+                            for salomeTools.
         :return: The dictionary that stores all information.
         :rtype: dict
         '''
         var = {}      
         var['user'] = src.architecture.get_user()
         :return: The dictionary that stores all information.
         :rtype: dict
         '''
         var = {}      
         var['user'] = src.architecture.get_user()
-        var['salometoolsway'] = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+        var['salometoolsway'] = os.path.dirname(
+                                    os.path.dirname(os.path.abspath(__file__)))
         var['srcDir'] = os.path.join(var['salometoolsway'], 'src')
         var['srcDir'] = os.path.join(var['salometoolsway'], 'src')
+        var['internal_dir'] = os.path.join(var['srcDir'], 'internal_config')
         var['sep']= os.path.sep
         
         var['sep']= os.path.sep
         
-        # dataDir has a default location
-        var['dataDir'] = os.path.join(var['salometoolsway'], 'data')
-        if dataDir is not None:
-            var['dataDir'] = dataDir
+        # datadir has a default location
+        var['datadir'] = os.path.join(var['salometoolsway'], 'data')
+        if datadir is not None:
+            var['datadir'] = datadir
+
+        var['personalDir'] = os.path.join(os.path.expanduser('~'),
+                                           '.salomeTools')
+        src.ensure_path_exists(var['personalDir'])
+
+        var['personal_applications_dir'] = os.path.join(var['personalDir'],
+                                                        "Applications")
+        src.ensure_path_exists(var['personal_applications_dir'])
+        
+        var['personal_products_dir'] = os.path.join(var['personalDir'],
+                                                    "products")
+        src.ensure_path_exists(var['personal_products_dir'])
+        
+        var['personal_archives_dir'] = os.path.join(var['personalDir'],
+                                                    "Archives")
+        src.ensure_path_exists(var['personal_archives_dir'])
+
+        var['personal_jobs_dir'] = os.path.join(var['personalDir'],
+                                                "Jobs")
+        src.ensure_path_exists(var['personal_jobs_dir'])
 
 
-        var['personalDir'] = os.path.join(os.path.expanduser('~'), '.salomeTools')
+        var['personal_machines_dir'] = os.path.join(var['personalDir'],
+                                                    "Machines")
+        src.ensure_path_exists(var['personal_machines_dir'])
 
         # read linux distributions dictionary
 
         # read linux distributions dictionary
-        distrib_cfg = src.pyconf.Config(os.path.join(var['srcDir'], 'internal_config', 'distrib.pyconf'))
+        distrib_cfg = src.pyconf.Config(os.path.join(var['srcDir'],
+                                                      'internal_config',
+                                                      'distrib.pyconf'))
         
         # set platform parameters
         
         # set platform parameters
-        dist_name = src.architecture.get_distribution(codes=distrib_cfg.DISTRIBUTIONS)
-        dist_version = src.architecture.get_distrib_version(dist_name, codes=distrib_cfg.VERSIONS)
+        dist_name = src.architecture.get_distribution(
+                                            codes=distrib_cfg.DISTRIBUTIONS)
+        dist_version = src.architecture.get_distrib_version(dist_name, 
+                                                    codes=distrib_cfg.VERSIONS)
         dist = dist_name + dist_version
         
         var['dist_name'] = dist_name
         dist = dist_name + dist_version
         
         var['dist_name'] = dist_name
@@ -131,28 +174,34 @@ class ConfigManager:
     def get_command_line_overrides(self, options, sections):
         '''get all the overwrites that are in the command line
         
     def get_command_line_overrides(self, options, sections):
         '''get all the overwrites that are in the command line
         
-        :param options: the options from salomeTools class initialization (like -l5 or --overwrite)
+        :param options: the options from salomeTools class 
+                        initialization (like -l5 or --overwrite)
         :param sections str: The config section to overwrite.
         :return: The list of all the overwrites to apply.
         :rtype: list
         '''
         :param sections str: The config section to overwrite.
         :return: The list of all the overwrites to apply.
         :rtype: list
         '''
-        # when there are no options or not the overwrite option, return an empty list
+        # when there are no options or not the overwrite option, 
+        # return an empty list
         if options is None or options.overwrite is None:
             return []
         
         over = []
         for section in sections:
             # only overwrite the sections that correspond to the option 
         if options is None or options.overwrite is None:
             return []
         
         over = []
         for section in sections:
             # only overwrite the sections that correspond to the option 
-            over.extend(filter(lambda l: l.startswith(section + "."), options.overwrite))
+            over.extend(filter(lambda l: l.startswith(section + "."), 
+                               options.overwrite))
         return over
 
         return over
 
-    def getConfig(self, application=None, options=None, command=None, dataDir=None):
+    def get_config(self, application=None, options=None, command=None,
+                    datadir=None):
         '''get the config from all the configuration files.
         
         :param application str: The application for which salomeTools is called.
         '''get the config from all the configuration files.
         
         :param application str: The application for which salomeTools is called.
-        :param options calss Options: The general salomeToosl options (--overwrite or -l5, for example)
+        :param options class Options: The general salomeToos
+                                      options (--overwrite or -l5, for example)
         :param command str: The command that is called.
         :param command str: The command that is called.
-        :param dataDir str: The repository that contain external data for salomeTools.
+        :param datadir str: The repository that contain 
+                            external data for salomeTools.
         :return: The final config.
         :rtype: class 'src.pyconf.Config'
         '''        
         :return: The final config.
         :rtype: class 'src.pyconf.Config'
         '''        
@@ -163,9 +212,10 @@ class ConfigManager:
         # create the configuration instance
         cfg = src.pyconf.Config()
         
         # create the configuration instance
         cfg = src.pyconf.Config()
         
-        # =======================================================================================
+        # =====================================================================
         # create VARS section
         # create VARS section
-        var = self._create_vars(application=application, command=command, dataDir=dataDir)
+        var = self._create_vars(application=application, command=command, 
+                                datadir=datadir)
         # add VARS to config
         cfg.VARS = src.pyconf.Mapping(cfg)
         for variable in var:
         # add VARS to config
         cfg.VARS = src.pyconf.Mapping(cfg)
         for variable in var:
@@ -175,178 +225,510 @@ class ConfigManager:
         for rule in self.get_command_line_overrides(options, ["VARS"]):
             exec('cfg.' + rule) # this cannot be factorized because of the exec
         
         for rule in self.get_command_line_overrides(options, ["VARS"]):
             exec('cfg.' + rule) # this cannot be factorized because of the exec
         
-        # =======================================================================================
+        # =====================================================================
         # Load INTERNAL config
         # read src/internal_config/salomeTools.pyconf
         # Load INTERNAL config
         # read src/internal_config/salomeTools.pyconf
-        src.pyconf.streamOpener = ConfigOpener([os.path.join(cfg.VARS.srcDir, 'internal_config')])
+        src.pyconf.streamOpener = ConfigOpener([
+                            os.path.join(cfg.VARS.srcDir, 'internal_config')])
         try:
         try:
-            internal_cfg = src.pyconf.Config(open(os.path.join(cfg.VARS.srcDir, 'internal_config', 'salomeTools.pyconf')))
+            internal_cfg = src.pyconf.Config(open(os.path.join(cfg.VARS.srcDir, 
+                                    'internal_config', 'salomeTools.pyconf')))
         except src.pyconf.ConfigError as e:
         except src.pyconf.ConfigError as e:
-            raise src.SatException(_("Error in configuration file: salomeTools.pyconf\n  %(error)s") % \
-                {'error': str(e) })
+            raise src.SatException(_("Error in configuration file:"
+                                     " salomeTools.pyconf\n  %(error)s") % \
+                                   {'error': str(e) })
         
         merger.merge(cfg, internal_cfg)
 
         # apply overwrite from command line if needed
         for rule in self.get_command_line_overrides(options, ["INTERNAL"]):
             exec('cfg.' + rule) # this cannot be factorized because of the exec        
         
         merger.merge(cfg, internal_cfg)
 
         # apply overwrite from command line if needed
         for rule in self.get_command_line_overrides(options, ["INTERNAL"]):
             exec('cfg.' + rule) # this cannot be factorized because of the exec        
-        
-        # =======================================================================================
+               
+        # =====================================================================
         # Load SITE config file
         # search only in the data directory
         # Load SITE config file
         # search only in the data directory
-        src.pyconf.streamOpener = ConfigOpener([cfg.VARS.dataDir])
+        src.pyconf.streamOpener = ConfigOpener([cfg.VARS.datadir])
         try:
         try:
-            site_cfg = src.pyconf.Config(open(os.path.join(cfg.VARS.dataDir, 'site.pyconf')))
+            site_cfg = src.pyconf.Config(open(os.path.join(cfg.VARS.datadir, 
+                                                           'site.pyconf')),
+                                         PWD = ('SITE', cfg.VARS.datadir) )
         except src.pyconf.ConfigError as e:
         except src.pyconf.ConfigError as e:
-            raise src.SatException(_("Error in configuration file: site.pyconf\n  %(error)s") % \
+            raise src.SatException(_("Error in configuration file: "
+                                     "site.pyconf\n  %(error)s") % \
                 {'error': str(e) })
         except IOError as error:
             e = str(error)
             if "site.pyconf" in e :
                 {'error': str(e) })
         except IOError as error:
             e = str(error)
             if "site.pyconf" in e :
-                e += "\nYou can copy data" + cfg.VARS.sep + "site.template.pyconf to data" + cfg.VARS.sep + "site.pyconf and edit the file"
+                e += ("\nYou can copy data"
+                  + cfg.VARS.sep
+                  + "site.template.pyconf to data"
+                  + cfg.VARS.sep 
+                  + "site.pyconf and edit the file")
             raise src.SatException( e );
             raise src.SatException( e );
-        
-        # add user local path for configPath
-        site_cfg.SITE.config.configPath.append(os.path.join(cfg.VARS.personalDir, 'Applications'), "User applications path")
-        
         merger.merge(cfg, site_cfg)
 
         # apply overwrite from command line if needed
         for rule in self.get_command_line_overrides(options, ["SITE"]):
             exec('cfg.' + rule) # this cannot be factorized because of the exec
         merger.merge(cfg, site_cfg)
 
         # apply overwrite from command line if needed
         for rule in self.get_command_line_overrides(options, ["SITE"]):
             exec('cfg.' + rule) # this cannot be factorized because of the exec
-  
         
         
-        # =======================================================================================
+        # =====================================================================
+        # Load the PROJECTS
+        projects_cfg = src.pyconf.Config()
+        projects_cfg.addMapping("PROJECTS",
+                                src.pyconf.Mapping(projects_cfg),
+                                "The projects\n")
+        projects_cfg.PROJECTS.addMapping("projects",
+                                src.pyconf.Mapping(cfg.PROJECTS),
+                                "The projects definition\n")
+        
+        for project_pyconf_path in cfg.PROJECTS.project_file_paths:
+            if not os.path.exists(project_pyconf_path):
+                msg = _("WARNING: The project file %s cannot be found. "
+                        "It will be ignored\n" % project_pyconf_path)
+                sys.stdout.write(msg)
+                continue
+            project_name = os.path.basename(
+                                    project_pyconf_path)[:-len(".pyconf")]
+            try:
+                project_pyconf_dir = os.path.dirname(project_pyconf_path)
+                project_cfg = src.pyconf.Config(open(project_pyconf_path),
+                                                PWD=("", project_pyconf_dir))
+            except Exception as e:
+                msg = _("ERROR: Error in configuration file: "
+                                 "%(file_path)s\n  %(error)s\n") % \
+                            {'file_path' : project_pyconf_path, 'error': str(e) }
+                sys.stdout.write(msg)
+                continue
+            projects_cfg.PROJECTS.projects.addMapping(project_name,
+                             src.pyconf.Mapping(projects_cfg.PROJECTS.projects),
+                             "The %s project\n" % project_name)
+            projects_cfg.PROJECTS.projects[project_name]=project_cfg
+            projects_cfg.PROJECTS.projects[project_name]["file_path"] = \
+                                                        project_pyconf_path
+                   
+        merger.merge(cfg, projects_cfg)
+
+        # apply overwrite from command line if needed
+        for rule in self.get_command_line_overrides(options, ["PROJECTS"]):
+            exec('cfg.' + rule) # this cannot be factorized because of the exec
+        
+        # =====================================================================
+        # Create the paths where to search the application configurations, 
+        # the product configurations, the products archives, 
+        # the jobs configurations and the machines configurations
+        cfg.addMapping("PATHS", src.pyconf.Mapping(cfg), "The paths\n")
+        cfg.PATHS["APPLICATIONPATH"] = src.pyconf.Sequence(cfg.PATHS)
+        cfg.PATHS.APPLICATIONPATH.append(cfg.VARS.personal_applications_dir, "")
+        
+        cfg.PATHS["PRODUCTPATH"] = src.pyconf.Sequence(cfg.PATHS)
+        cfg.PATHS.PRODUCTPATH.append(cfg.VARS.personal_products_dir, "")
+        cfg.PATHS["ARCHIVEPATH"] = src.pyconf.Sequence(cfg.PATHS)
+        cfg.PATHS.ARCHIVEPATH.append(cfg.VARS.personal_archives_dir, "")
+        cfg.PATHS["JOBPATH"] = src.pyconf.Sequence(cfg.PATHS)
+        cfg.PATHS.JOBPATH.append(cfg.VARS.personal_jobs_dir, "")
+        cfg.PATHS["MACHINEPATH"] = src.pyconf.Sequence(cfg.PATHS)
+        cfg.PATHS.MACHINEPATH.append(cfg.VARS.personal_machines_dir, "")
+        # Loop over the projects in order to complete the PATHS variables
+        for project in cfg.PROJECTS.projects:
+            for PATH in ["APPLICATIONPATH",
+                         "PRODUCTPATH",
+                         "ARCHIVEPATH",
+                         "JOBPATH",
+                         "MACHINEPATH"]:
+                if PATH not in cfg.PROJECTS.projects[project]:
+                    continue
+                cfg.PATHS[PATH].append(cfg.PROJECTS.projects[project][PATH], "")
+        
+        # apply overwrite from command line if needed
+        for rule in self.get_command_line_overrides(options, ["PATHS"]):
+            exec('cfg.' + rule) # this cannot be factorized because of the exec
+
+        # =====================================================================
         # Load APPLICATION config file
         if application is not None:
             # search APPLICATION file in all directories in configPath
         # Load APPLICATION config file
         if application is not None:
             # search APPLICATION file in all directories in configPath
-            cp = cfg.SITE.config.configPath
+            cp = cfg.PATHS.APPLICATIONPATH
             src.pyconf.streamOpener = ConfigOpener(cp)
             src.pyconf.streamOpener = ConfigOpener(cp)
+            do_merge = True
             try:
                 application_cfg = src.pyconf.Config(application + '.pyconf')
             except IOError as e:
             try:
                 application_cfg = src.pyconf.Config(application + '.pyconf')
             except IOError as e:
-                raise src.SatException(_("%s, use 'config --list' to get the list of available applications.") %e)
+                raise src.SatException(_("%s, use 'config --list' to get the"
+                                         " list of available applications.") %e)
             except src.pyconf.ConfigError as e:
             except src.pyconf.ConfigError as e:
-                raise src.SatException(_("Error in configuration file: %(application)s.pyconf\n  %(error)s") % \
-                    { 'application': application, 'error': str(e) } )
-
-            merger.merge(cfg, application_cfg)
-
+                if (not ('-e' in parser.parse_args()[1]) 
+                                         or ('--edit' in parser.parse_args()[1]) 
+                                         and command == 'config'):
+                    raise src.SatException(_("Error in configuration file: "
+                                             "%(application)s.pyconf\n "
+                                             " %(error)s") % \
+                        { 'application': application, 'error': str(e) } )
+                else:
+                    sys.stdout.write(src.printcolors.printcWarning(
+                                        "There is an error in the file"
+                                        " %s.pyconf.\n" % cfg.VARS.application))
+                    do_merge = False
+            except Exception as e:
+                if (not ('-e' in parser.parse_args()[1]) 
+                                        or ('--edit' in parser.parse_args()[1]) 
+                                        and command == 'config'):
+                    sys.stdout.write(src.printcolors.printcWarning("%s\n" % str(e)))
+                    raise src.SatException(_("Error in configuration file:"
+                                             " %(application)s.pyconf\n") % \
+                        { 'application': application} )
+                else:
+                    sys.stdout.write(src.printcolors.printcWarning(
+                                "There is an error in the file"
+                                " %s.pyconf. Opening the file with the"
+                                " default viewer\n" % cfg.VARS.application))
+                    sys.stdout.write("The error:"
+                                 " %s\n" % src.printcolors.printcWarning(
+                                                                      str(e)))
+                    do_merge = False
+
+            if do_merge:
+                merger.merge(cfg, application_cfg)
+            
+                # apply overwrite from command line if needed
+                for rule in self.get_command_line_overrides(options,
+                                                             ["APPLICATION"]):
+                    # this cannot be factorized because of the exec
+                    exec('cfg.' + rule)
+                    
+                # default launcher name ('salome')
+                if ('profile' in cfg.APPLICATION and 
+                    'launcher_name' not in cfg.APPLICATION.profile):
+                    cfg.APPLICATION.profile.launcher_name = 'salome'
+        
+            else:
+                cfg['open_application'] = 'yes'
+
+        # =====================================================================
+        # Load product config files in PRODUCTS section
+        products_cfg = src.pyconf.Config()
+        products_cfg.addMapping("PRODUCTS",
+                                src.pyconf.Mapping(products_cfg),
+                                "The products\n")
+        if application is not None:
+            src.pyconf.streamOpener = ConfigOpener(cfg.PATHS.PRODUCTPATH)
+            for product_name in cfg.APPLICATION.products.keys():
+                # Loop on all files that are in softsDir directory
+                # and read their config
+                product_file_name = product_name + ".pyconf"
+                product_file_path = src.find_file_in_lpath(product_file_name, cfg.PATHS.PRODUCTPATH)
+                if product_file_path:
+                    products_dir = os.path.dirname(product_file_path)
+                    try:
+                        prod_cfg = src.pyconf.Config(open(
+                                                    os.path.join(products_dir,
+                                                                 product_file_name)),
+                                                     PWD=("", products_dir))
+                        products_cfg.PRODUCTS[product_name] = prod_cfg
+                    except Exception as e:
+                        msg = _(
+                            "WARNING: Error in configuration file"
+                            ": %(prod)s\n  %(error)s" % \
+                            {'prod' :  product_name, 'error': str(e) })
+                        sys.stdout.write(msg)
+            
+            merger.merge(cfg, products_cfg)
+            
             # apply overwrite from command line if needed
             # apply overwrite from command line if needed
-            for rule in self.get_command_line_overrides(options, ["APPLICATION"]):
+            for rule in self.get_command_line_overrides(options, ["PRODUCTS"]):
                 exec('cfg.' + rule) # this cannot be factorized because of the exec
                 exec('cfg.' + rule) # this cannot be factorized because of the exec
-        
-        # =======================================================================================
-        # Load softwares config files in SOFTWARE section
-       
-        # The directory containing the softwares definition
-        softsDir = os.path.join(cfg.VARS.dataDir, 'softwares')
-        
-        # Loop on all files that are in softsDir directory and read their config
-        for fName in os.listdir(softsDir):
-            if fName.endswith(".pyconf"):
-                src.pyconf.streamOpener = ConfigOpener([softsDir])
-                try:
-                    soft_cfg = src.pyconf.Config(open(os.path.join(softsDir, fName)))
-                except src.pyconf.ConfigError as e:
-                    raise src.SatException(_("Error in configuration file: %(soft)s\n  %(error)s") % \
-                        {'soft' :  fName, 'error': str(e) })
-                except IOError as error:
-                    e = str(error)
-                    raise src.SatException( e );
-                
-                merger.merge(cfg, soft_cfg)
-
-        # apply overwrite from command line if needed
-        for rule in self.get_command_line_overrides(options, ["SOFTWARE"]):
-            exec('cfg.' + rule) # this cannot be factorized because of the exec
-
-        
-        # =======================================================================================
+            
+        # =====================================================================
         # load USER config
         # load USER config
-        self.setUserConfigFile(cfg)
-        user_cfg_file = self.getUserConfigFile()
+        self.set_user_config_file(cfg)
+        user_cfg_file = self.get_user_config_file()
         user_cfg = src.pyconf.Config(open(user_cfg_file))
         merger.merge(cfg, user_cfg)
 
         # apply overwrite from command line if needed
         for rule in self.get_command_line_overrides(options, ["USER"]):
             exec('cfg.' + rule) # this cannot be factorize because of the exec
         user_cfg = src.pyconf.Config(open(user_cfg_file))
         merger.merge(cfg, user_cfg)
 
         # apply overwrite from command line if needed
         for rule in self.get_command_line_overrides(options, ["USER"]):
             exec('cfg.' + rule) # this cannot be factorize because of the exec
-
+        
         return cfg
 
         return cfg
 
-    def setUserConfigFile(self, config):
+    def set_user_config_file(self, config):
         '''Set the user config file name and path.
         If necessary, build it from another one or create it from scratch.
         
         '''Set the user config file name and path.
         If necessary, build it from another one or create it from scratch.
         
-        :param config class 'src.pyconf.Config': The global config (containing all pyconf).
+        :param config class 'src.pyconf.Config': The global config 
+                                                 (containing all pyconf).
         '''
         # get the expected name and path of the file
         self.config_file_name = 'salomeTools.pyconf'
         '''
         # get the expected name and path of the file
         self.config_file_name = 'salomeTools.pyconf'
-        self.user_config_file_path = os.path.join(config.VARS.personalDir, self.config_file_name)
+        self.user_config_file_path = os.path.join(config.VARS.personalDir,
+                                                   self.config_file_name)
         
         # if pyconf does not exist, create it from scratch
         if not os.path.isfile(self.user_config_file_path): 
         
         # if pyconf does not exist, create it from scratch
         if not os.path.isfile(self.user_config_file_path): 
-            self.createConfigFile(config)
+            self.create_config_file(config)
     
     
-    def createConfigFile(self, config):
-        '''This method is called when there are no user config file. It build it from scratch.
+    def create_config_file(self, config):
+        '''This method is called when there are no user config file. 
+           It build it from scratch.
         
         :param config class 'src.pyconf.Config': The global config.
         :return: the config corresponding to the file created.
         :rtype: config class 'src.pyconf.Config'
         '''
         
         
         :param config class 'src.pyconf.Config': The global config.
         :return: the config corresponding to the file created.
         :rtype: config class 'src.pyconf.Config'
         '''
         
-        cfg_name = self.getUserConfigFile()
+        cfg_name = self.get_user_config_file()
 
         user_cfg = src.pyconf.Config()
         #
         user_cfg.addMapping('USER', src.pyconf.Mapping(user_cfg), "")
 
         #
 
         user_cfg = src.pyconf.Config()
         #
         user_cfg.addMapping('USER', src.pyconf.Mapping(user_cfg), "")
 
         #
-        user_cfg.USER.addMapping('workDir', os.path.expanduser('~'),
-            "This is where salomeTools will work. You may (and probably do) change it.\n")
+        user_cfg.USER.addMapping('workdir', os.path.expanduser('~'),
+            "This is where salomeTools will work. "
+            "You may (and probably do) change it.\n")
         user_cfg.USER.addMapping('cvs_user', config.VARS.user,
             "This is the user name used to access salome cvs base.\n")
         user_cfg.USER.addMapping('svn_user', config.VARS.user,
             "This is the user name used to access salome svn base.\n")
         user_cfg.USER.addMapping('cvs_user', config.VARS.user,
             "This is the user name used to access salome cvs base.\n")
         user_cfg.USER.addMapping('svn_user', config.VARS.user,
             "This is the user name used to access salome svn base.\n")
-        user_cfg.USER.addMapping('output_level', 3,
-            "This is the default output_level you want. 0=>no output, 5=>debug.\n")
-        user_cfg.USER.addMapping('publish_dir', os.path.join(os.path.expanduser('~'), 'websupport', 'satreport'), "")
-        user_cfg.USER.addMapping('editor', 'vi', "This is the editor used to modify configuration files\n")
-        user_cfg.USER.addMapping('browser', 'firefox', "This is the browser used to read html documentation\n")
-        user_cfg.USER.addMapping('pdf_viewer', 'evince', "This is the pdf_viewer used to read pdf documentation\n")
+        user_cfg.USER.addMapping('output_verbose_level', 3,
+            "This is the default output_verbose_level you want."
+            " 0=>no output, 5=>debug.\n")
+        user_cfg.USER.addMapping('publish_dir', 
+                                 os.path.join(os.path.expanduser('~'),
+                                 'websupport', 
+                                 'satreport'), 
+                                 "")
+        user_cfg.USER.addMapping('editor',
+                                 'vi', 
+                                 "This is the editor used to "
+                                 "modify configuration files\n")
+        user_cfg.USER.addMapping('browser', 
+                                 'firefox', 
+                                 "This is the browser used to "
+                                 "read html documentation\n")
+        user_cfg.USER.addMapping('pdf_viewer', 
+                                 'evince', 
+                                 "This is the pdf_viewer used "
+                                 "to read pdf documentation\n")
+        user_cfg.USER.addMapping("base",
+                                 src.pyconf.Reference(
+                                            user_cfg,
+                                            src.pyconf.DOLLAR,
+                                            'workdir  + $VARS.sep + "BASE"'),
+                                 "The products installation base (could be "
+                                 "ignored if this key exists in the site.pyconf"
+                                 " file of salomTools).\n")
+        
+        user_cfg.USER.addMapping("log_dir",
+                                 src.pyconf.Reference(
+                                            user_cfg,
+                                            src.pyconf.DOLLAR,
+                                            'workdir  + $VARS.sep + "LOGS"'),
+                                 "The log repository\n")
+        
         # 
         src.ensure_path_exists(config.VARS.personalDir)
         # 
         src.ensure_path_exists(config.VARS.personalDir)
-        src.ensure_path_exists(os.path.join(config.VARS.personalDir, 'Applications'))
+        src.ensure_path_exists(os.path.join(config.VARS.personalDir, 
+                                            'Applications'))
 
         f = open(cfg_name, 'w')
         user_cfg.__save__(f)
         f.close()
 
         f = open(cfg_name, 'w')
         user_cfg.__save__(f)
         f.close()
-        print(_("You can edit it to configure salomeTools (use: sat config --edit).\n"))
 
         return user_cfg   
 
 
         return user_cfg   
 
-    def getUserConfigFile(self):
+    def get_user_config_file(self):
         '''Get the user config file
         :return: path to the user config file.
         :rtype: str
         '''
         if not self.user_config_file_path:
         '''Get the user config file
         :return: path to the user config file.
         :rtype: str
         '''
         if not self.user_config_file_path:
-            raise src.SatException(_("Error in getUserConfigFile: missing user config file path"))
+            raise src.SatException(_("Error in get_user_config_file: "
+                                     "missing user config file path"))
         return self.user_config_file_path     
         return self.user_config_file_path     
+
+def check_path(path, ext=[]):
+    '''Construct a text with the input path and "not found" if it does not
+       exist.
+    
+    :param path Str: the path to check.
+    :param ext List: An extension. Verify that the path extension 
+                     is in the list
+    :return: The string of the path with information
+    :rtype: Str
+    '''
+    # check if file exists
+    if not os.path.exists(path):
+        return "'%s'" % path + " " + src.printcolors.printcError(_(
+                                                            "** not found"))
+
+    # check extension
+    if len(ext) > 0:
+        fe = os.path.splitext(path)[1].lower()
+        if fe not in ext:
+            return "'%s'" % path + " " + src.printcolors.printcError(_(
+                                                        "** bad extension"))
+
+    return path
+
+def show_product_info(config, name, logger):
+    '''Display on the terminal and logger information about a product.
+    
+    :param config Config: the global configuration.
+    :param name Str: The name of the product
+    :param logger Logger: The logger instance to use for the display
+    '''
+    
+    logger.write(_("%s is a product\n") % src.printcolors.printcLabel(name), 2)
+    pinfo = src.product.get_product_config(config, name)
+    
+    # Type of the product
+    ptype = src.get_cfg_param(pinfo, "type", "")
+    src.printcolors.print_value(logger, "type", ptype, 2)
+    if "depend" in pinfo:
+        src.printcolors.print_value(logger, 
+                                    "depends on", 
+                                    ', '.join(pinfo.depend), 2)
+
+    if "opt_depend" in pinfo:
+        src.printcolors.print_value(logger, 
+                                    "optional", 
+                                    ', '.join(pinfo.opt_depend), 2)
+
+    # information on prepare
+    logger.write("\n", 2)
+    logger.write(src.printcolors.printcLabel("prepare:") + "\n", 2)
+
+    is_dev = src.product.product_is_dev(pinfo)
+    method = pinfo.get_source
+    if is_dev:
+        method += " (dev)"
+    src.printcolors.print_value(logger, "get method", method, 2)
+
+    if method == 'cvs':
+        src.printcolors.print_value(logger, "server", pinfo.cvs_info.server, 2)
+        src.printcolors.print_value(logger, "base module",
+                                    pinfo.cvs_info.module_base, 2)
+        src.printcolors.print_value(logger, "source", pinfo.cvs_info.source, 2)
+        src.printcolors.print_value(logger, "tag", pinfo.cvs_info.tag, 2)
+
+    elif method == 'svn':
+        src.printcolors.print_value(logger, "repo", pinfo.svn_info.repo, 2)
+
+    elif method == 'git':
+        src.printcolors.print_value(logger, "repo", pinfo.git_info.repo, 2)
+        src.printcolors.print_value(logger, "tag", pinfo.git_info.tag, 2)
+
+    elif method == 'archive':
+        src.printcolors.print_value(logger, 
+                                    "get from", 
+                                    check_path(pinfo.archive_info.archive_name), 
+                                    2)
+
+    if 'patches' in pinfo:
+        for patch in pinfo.patches:
+            src.printcolors.print_value(logger, "patch", check_path(patch), 2)
+
+    if src.product.product_is_fixed(pinfo):
+        src.printcolors.print_value(logger, "install_dir", 
+                                    check_path(pinfo.install_dir), 2)
+
+    if src.product.product_is_native(pinfo) or src.product.product_is_fixed(pinfo):
+        return
+    
+    # information on compilation
+    if src.product.product_compiles(pinfo):
+        logger.write("\n", 2)
+        logger.write(src.printcolors.printcLabel("compile:") + "\n", 2)
+        src.printcolors.print_value(logger, 
+                                    "compilation method", 
+                                    pinfo.build_source, 
+                                    2)
         
         
+        if pinfo.build_source == "script" and "compil_script" in pinfo:
+            src.printcolors.print_value(logger, 
+                                        "Compilation script", 
+                                        pinfo.compil_script, 
+                                        2)
+        
+        if 'nb_proc' in pinfo:
+            src.printcolors.print_value(logger, "make -j", pinfo.nb_proc, 2)
     
     
-def print_value(config, path, show_label, level=0, show_full_path=False):
-    '''Prints a value from the configuration. Prints recursively the values under the initial path.
+        src.printcolors.print_value(logger, 
+                                    "source dir", 
+                                    check_path(pinfo.source_dir), 
+                                    2)
+        if 'install_dir' in pinfo:
+            src.printcolors.print_value(logger, 
+                                        "build dir", 
+                                        check_path(pinfo.build_dir), 
+                                        2)
+            src.printcolors.print_value(logger, 
+                                        "install dir", 
+                                        check_path(pinfo.install_dir), 
+                                        2)
+        else:
+            logger.write("  " + 
+                         src.printcolors.printcWarning(_("no install dir")) + 
+                         "\n", 2)
+    else:
+        logger.write("\n", 2)
+        msg = _("This product does not compile")
+        logger.write("%s\n" % msg, 2)
+
+    # information on environment
+    logger.write("\n", 2)
+    logger.write(src.printcolors.printcLabel("environ :") + "\n", 2)
+    if "environ" in pinfo and "env_script" in pinfo.environ:
+        src.printcolors.print_value(logger, 
+                                    "script", 
+                                    check_path(pinfo.environ.env_script), 
+                                    2)
+
+    zz = src.environment.SalomeEnviron(config, 
+                                       src.fileEnviron.ScreenEnviron(logger), 
+                                       False)
+    zz.set_python_libdirs()
+    zz.set_a_product(name, logger)
+        
+def show_patchs(config, logger):
+    '''Prints all the used patchs in the application.
+    
+    :param config Config: the global configuration.
+    :param logger Logger: The logger instance to use for the display
+    '''
+    len_max = max([len(p) for p in config.APPLICATION.products]) + 2
+    for product in config.APPLICATION.products:
+        product_info = src.product.get_product_config(config, product)
+        if src.product.product_has_patches(product_info):
+            logger.write("%s: " % product, 1)
+            logger.write(src.printcolors.printcInfo(
+                                            " " * (len_max - len(product) -2) +
+                                            "%s\n" % product_info.patches[0]),
+                         1)
+            if len(product_info.patches) > 1:
+                for patch in product_info.patches[1:]:
+                    logger.write(src.printcolors.printcInfo(len_max*" " +
+                                                            "%s\n" % patch), 1)
+            logger.write("\n", 1)
+
+def print_value(config, path, show_label, logger, level=0, show_full_path=False):
+    '''Prints a value from the configuration. Prints recursively the values 
+       under the initial path.
     
     
-    :param config class 'src.pyconf.Config': The configuration from which the value is displayed.
+    :param config class 'src.pyconf.Config': The configuration 
+                                             from which the value is displayed.
     :param path str : the path in the configuration of the value to print.
     :param path str : the path in the configuration of the value to print.
-    :param show_label boolean: if True, do a basic display. (useful for bash completion)
+    :param show_label boolean: if True, do a basic display. 
+                               (useful for bash completion)
+    :param logger Logger: the logger instance
     :param level int: The number of spaces to add before display.
     :param show_full_path :
     '''            
     
     :param level int: The number of spaces to add before display.
     :param show_full_path :
     '''            
     
+    # Make sure that the path does not ends with a point
+    if path.endswith('.'):
+        path = path[:-1]
+    
     # display all the path or not
     if show_full_path:
         vname = path
     # display all the path or not
     if show_full_path:
         vname = path
@@ -360,28 +742,68 @@ def print_value(config, path, show_label, level=0, show_full_path=False):
     try:
         val = config.getByPath(path)
     except Exception as e:
     try:
         val = config.getByPath(path)
     except Exception as e:
-        sys.stdout.write(tab_level)
-        sys.stdout.write("%s: ERROR %s\n" % (src.printcolors.printcLabel(vname), src.printcolors.printcError(str(e))))
+        logger.write(tab_level)
+        logger.write("%s: ERROR %s\n" % (src.printcolors.printcLabel(vname), 
+                                         src.printcolors.printcError(str(e))))
         return
 
     # in this case, display only the value
     if show_label:
         return
 
     # in this case, display only the value
     if show_label:
-        sys.stdout.write(tab_level)
-        sys.stdout.write("%s: " % src.printcolors.printcLabel(vname))
+        logger.write(tab_level)
+        logger.write("%s: " % src.printcolors.printcLabel(vname))
 
 
-    # The case where the value has under values, do a recursive call to the function
+    # The case where the value has under values, 
+    # do a recursive call to the function
     if dir(val).__contains__('keys'):
     if dir(val).__contains__('keys'):
-        if show_label: sys.stdout.write("\n")
+        if show_label: logger.write("\n")
         for v in sorted(val.keys()):
         for v in sorted(val.keys()):
-            print_value(config, path + '.' + v, show_label, level + 1)
-    elif val.__class__ == src.pyconf.Sequence or isinstance(val, list): # in this case, value is a list (or a Sequence)
-        if show_label: sys.stdout.write("\n")
+            print_value(config, path + '.' + v, show_label, logger, level + 1)
+    elif val.__class__ == src.pyconf.Sequence or isinstance(val, list): 
+        # in this case, value is a list (or a Sequence)
+        if show_label: logger.write("\n")
         index = 0
         for v in val:
         index = 0
         for v in val:
-            print_value(config, path + "[" + str(index) + "]", show_label, level + 1)
+            print_value(config, path + "[" + str(index) + "]", 
+                        show_label, logger, level + 1)
             index = index + 1
     else: # case where val is just a str
             index = index + 1
     else: # case where val is just a str
-        sys.stdout.write("%s\n" % val)
+        logger.write("%s\n" % val)
+
+def get_config_children(config, args):
+    '''Gets the names of the children of the given parameter.
+       Useful only for completion mechanism
+    
+    :param config Config: The configuration where to read the values
+    :param args: The path in the config from which get the keys
+    '''
+    vals = []
+    rootkeys = config.keys()
+    
+    if len(args) == 0:
+        # no parameter returns list of root keys
+        vals = rootkeys
+    else:
+        parent = args[0]
+        pos = parent.rfind('.')
+        if pos < 0:
+            # Case where there is only on key as parameter.
+            # For example VARS
+            vals = [m for m in rootkeys if m.startswith(parent)]
+        else:
+            # Case where there is a part from a key
+            # for example VARS.us  (for VARS.user)
+            head = parent[0:pos]
+            tail = parent[pos+1:]
+            try:
+                a = config.getByPath(head)
+                if dir(a).__contains__('keys'):
+                    vals = map(lambda x: head + '.' + x,
+                               [m for m in a.keys() if m.startswith(tail)])
+            except:
+                pass
+
+    for v in sorted(vals):
+        sys.stdout.write("%s\n" % v)
 
 def description():
     '''method that is called when salomeTools is called with --help option.
 
 def description():
     '''method that is called when salomeTools is called with --help option.
@@ -389,40 +811,65 @@ def description():
     :return: The text to display for the config command description.
     :rtype: str
     '''
     :return: The text to display for the config command description.
     :rtype: str
     '''
-    return _("The config command allows manipulation and operation on config files.")
+    return _("The config command allows manipulation "
+             "and operation on config files.\n\nexample:\nsat config "
+             "SALOME-master --info ParaView")
     
 
     
 
-def run(args, runner):
+def run(args, runner, logger):
     '''method that is called when salomeTools is called with config parameter.
     '''
     # Parse the options
     (options, args) = parser.parse_args(args)
     '''method that is called when salomeTools is called with config parameter.
     '''
     # Parse the options
     (options, args) = parser.parse_args(args)
+
+    # Only useful for completion mechanism : print the keys of the config
+    if options.schema:
+        get_config_children(runner.cfg, args)
+        return
     
     # case : print a value of the config
     if options.value:
         if options.value == ".":
             # if argument is ".", print all the config
             for val in sorted(runner.cfg.keys()):
     
     # case : print a value of the config
     if options.value:
         if options.value == ".":
             # if argument is ".", print all the config
             for val in sorted(runner.cfg.keys()):
-                print_value(runner.cfg, val, True)
-            return
-        print_value(runner.cfg, options.value, True, level=0, show_full_path=False)
-        return
+                print_value(runner.cfg, val, not options.no_label, logger)
+        else:
+            print_value(runner.cfg, options.value, not options.no_label, logger, 
+                        level=0, show_full_path=False)
     
     # case : edit user pyconf file or application file
     elif options.edit:
         editor = runner.cfg.USER.editor
     
     # case : edit user pyconf file or application file
     elif options.edit:
         editor = runner.cfg.USER.editor
-        if 'APPLICATION' not in runner.cfg: # edit user pyconf
-            usercfg = os.path.join(runner.cfg.VARS.personalDir, 'salomeTools.pyconf')
-            src.system.show_in_editor(editor, usercfg)
+        if ('APPLICATION' not in runner.cfg and
+                       'open_application' not in runner.cfg): # edit user pyconf
+            usercfg = os.path.join(runner.cfg.VARS.personalDir, 
+                                   'salomeTools.pyconf')
+            logger.write(_("Openning %s\n" % usercfg), 3)
+            src.system.show_in_editor(editor, usercfg, logger)
         else:
             # search for file <application>.pyconf and open it
         else:
             # search for file <application>.pyconf and open it
-            for path in runner.cfg.SITE.config.configPath:
-                pyconf_path = os.path.join(path, runner.cfg.VARS.application + ".pyconf")
+            for path in runner.cfg.PATHS.APPLICATIONPATH:
+                pyconf_path = os.path.join(path, 
+                                    runner.cfg.VARS.application + ".pyconf")
                 if os.path.exists(pyconf_path):
                 if os.path.exists(pyconf_path):
-                    src.system.show_in_editor(editor, pyconf_path)
+                    logger.write(_("Openning %s\n" % pyconf_path), 3)
+                    src.system.show_in_editor(editor, pyconf_path, logger)
                     break
     
                     break
     
-    # case : copy an existing <application>.pyconf to ~/.salomeTools/Applications/LOCAL_<application>.pyconf
+    # case : give information about the product in parameter
+    elif options.info:
+        src.check_config_has_application(runner.cfg)
+        if options.info in runner.cfg.APPLICATION.products:
+            show_product_info(runner.cfg, options.info, logger)
+            return
+        raise src.SatException(_("%(product_name)s is not a product "
+                                 "of %(application_name)s.") % 
+                               {'product_name' : options.info,
+                                'application_name' : 
+                                runner.cfg.VARS.application})
+    
+    # case : copy an existing <application>.pyconf 
+    # to ~/.salomeTools/Applications/LOCAL_<application>.pyconf
     elif options.copy:
         # product is required
         src.check_config_has_application( runner.cfg )
     elif options.copy:
         # product is required
         src.check_config_has_application( runner.cfg )
@@ -430,7 +877,7 @@ def run(args, runner):
         # get application file path 
         source = runner.cfg.VARS.application + '.pyconf'
         source_full_path = ""
         # get application file path 
         source = runner.cfg.VARS.application + '.pyconf'
         source_full_path = ""
-        for path in runner.cfg.SITE.config.configPath:
+        for path in runner.cfg.PATHS.APPLICATIONPATH:
             # ignore personal directory
             if path == runner.cfg.VARS.personalDir:
                 continue
             # ignore personal directory
             if path == runner.cfg.VARS.personalDir:
                 continue
@@ -441,37 +888,43 @@ def run(args, runner):
                 break
 
         if len(source_full_path) == 0:
                 break
 
         if len(source_full_path) == 0:
-            raise src.SatException(_("Config file for product %s not found\n") % source)
+            raise src.SatException(_(
+                        "Config file for product %s not found\n") % source)
         else:
             if len(args) > 0:
                 # a name is given as parameter, use it
                 dest = args[0]
         else:
             if len(args) > 0:
                 # a name is given as parameter, use it
                 dest = args[0]
-            elif 'copy_prefix' in runner.cfg.SITE.config:
+            elif 'copy_prefix' in runner.cfg.INTERNAL.config:
                 # use prefix
                 # use prefix
-                dest = runner.cfg.SITE.config.copy_prefix + runner.cfg.VARS.application
+                dest = (runner.cfg.INTERNAL.config.copy_prefix 
+                        + runner.cfg.VARS.application)
             else:
                 # use same name as source
                 dest = runner.cfg.VARS.application
                 
             # the full path
             else:
                 # use same name as source
                 dest = runner.cfg.VARS.application
                 
             # the full path
-            dest_file = os.path.join(runner.cfg.VARS.personalDir, 'Applications', dest + '.pyconf')
+            dest_file = os.path.join(runner.cfg.VARS.personalDir, 
+                                     'Applications', dest + '.pyconf')
             if os.path.exists(dest_file):
             if os.path.exists(dest_file):
-                raise src.SatException(_("A personal application '%s' already exists") % dest)
+                raise src.SatException(_("A personal application"
+                                         " '%s' already exists") % dest)
             
             # perform the copy
             shutil.copyfile(source_full_path, dest_file)
             
             # perform the copy
             shutil.copyfile(source_full_path, dest_file)
-            print(_("%s has been created.") % dest_file)
+            logger.write(_("%s has been created.\n") % dest_file)
     
     # case : display all the available pyconf applications
     elif options.list:
         lproduct = list()
         # search in all directories that can have pyconf applications
     
     # case : display all the available pyconf applications
     elif options.list:
         lproduct = list()
         # search in all directories that can have pyconf applications
-        for path in runner.cfg.SITE.config.configPath:
+        for path in runner.cfg.PATHS.APPLICATIONPATH:
             # print a header
             # print a header
-            runner.logger.write("------ %s\n" % src.printcolors.printcHeader(path))
+            if not options.no_label:
+                logger.write("------ %s\n" % src.printcolors.printcHeader(path))
 
             if not os.path.exists(path):
 
             if not os.path.exists(path):
-                runner.logger.write(src.printcolors.printcError(_("Directory not found")) + "\n")
+                logger.write(src.printcolors.printcError(_(
+                                            "Directory not found")) + "\n")
             else:
                 for f in sorted(os.listdir(path)):
                     # ignore file that does not ends with .pyconf
             else:
                 for f in sorted(os.listdir(path)):
                     # ignore file that does not ends with .pyconf
@@ -481,12 +934,20 @@ def run(args, runner):
                     appliname = f[:-len('.pyconf')]
                     if appliname not in lproduct:
                         lproduct.append(appliname)
                     appliname = f[:-len('.pyconf')]
                     if appliname not in lproduct:
                         lproduct.append(appliname)
-                        if path.startswith(runner.cfg.VARS.personalDir):
-                            runner.logger.write("%s*\n" % appliname)
+                        if path.startswith(runner.cfg.VARS.personalDir) \
+                                    and not options.no_label:
+                            logger.write("%s*\n" % appliname)
                         else:
                         else:
-                            runner.logger.write("%s\n" % appliname)
+                            logger.write("%s\n" % appliname)
                             
                             
-            runner.logger.write("\n")
-
+            logger.write("\n")
+    # case : give a synthetic view of all patches used in the application
+    elif options.show_patchs:
+        src.check_config_has_application(runner.cfg)
+        # Print some informations
+        logger.write(_('Show the patchs of application %s\n') % 
+                    src.printcolors.printcLabel(runner.cfg.VARS.application), 3)
+        logger.write("\n", 2, False)
+        show_patchs(runner.cfg, logger)
+        
     
     
-    
\ No newline at end of file