diff --git a/README.md b/README.md
index 5526c8b..29da480 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,29 @@
-tisbackup
-=========
+# -----------------------------------------------------------------------
+# This file is part of TISBackup
+#
+# TISBackup is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# TISBackup is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with TISBackup. If not, see .
+#
+# -----------------------------------------------------------------------
-backup server side executed python scripts for managing linux and windows system and application data backups, developed by adminsys for adminsys
\ No newline at end of file
+
+Le script tisbackup se base sur un fichier de configuration .ini. Cf le fichier d'exemple pour le format
+
+Pour lancer le backup, lancer la commande
+./tisbackup.py -c fichierconf.ini
+
+Pour lancer une section particulière du fichier .ini
+./tisbackup.py -c fichierconf.ini -s section_choisi
+
+Pour mettre le mode debug
+./tisbackup.py -c fichierconf.ini -l debug
diff --git a/iniparse/__init__.py b/iniparse/__init__.py
new file mode 100755
index 0000000..8de756f
--- /dev/null
+++ b/iniparse/__init__.py
@@ -0,0 +1,25 @@
+# Copyright (c) 2001, 2002, 2003 Python Software Foundation
+# Copyright (c) 2004-2008 Paramjit Oberoi
+# Copyright (c) 2007 Tim Lauridsen
+# All Rights Reserved. See LICENSE-PSF & LICENSE for details.
+
+from ini import INIConfig, change_comment_syntax
+from config import BasicConfig, ConfigNamespace
+from compat import RawConfigParser, ConfigParser, SafeConfigParser
+from utils import tidy
+
+from ConfigParser import DuplicateSectionError, \
+ NoSectionError, NoOptionError, \
+ InterpolationMissingOptionError, \
+ InterpolationDepthError, \
+ InterpolationSyntaxError, \
+ DEFAULTSECT, MAX_INTERPOLATION_DEPTH
+
+__all__ = [
+ 'BasicConfig', 'ConfigNamespace',
+ 'INIConfig', 'tidy', 'change_comment_syntax',
+ 'RawConfigParser', 'ConfigParser', 'SafeConfigParser',
+ 'DuplicateSectionError', 'NoSectionError', 'NoOptionError',
+ 'InterpolationMissingOptionError', 'InterpolationDepthError',
+ 'InterpolationSyntaxError', 'DEFAULTSECT', 'MAX_INTERPOLATION_DEPTH',
+]
diff --git a/iniparse/compat.py b/iniparse/compat.py
new file mode 100755
index 0000000..db89ed8
--- /dev/null
+++ b/iniparse/compat.py
@@ -0,0 +1,343 @@
+# Copyright (c) 2001, 2002, 2003 Python Software Foundation
+# Copyright (c) 2004-2008 Paramjit Oberoi
+# All Rights Reserved. See LICENSE-PSF & LICENSE for details.
+
+"""Compatibility interfaces for ConfigParser
+
+Interfaces of ConfigParser, RawConfigParser and SafeConfigParser
+should be completely identical to the Python standard library
+versions. Tested with the unit tests included with Python-2.3.4
+
+The underlying INIConfig object can be accessed as cfg.data
+"""
+
+import re
+from ConfigParser import DuplicateSectionError, \
+ NoSectionError, NoOptionError, \
+ InterpolationMissingOptionError, \
+ InterpolationDepthError, \
+ InterpolationSyntaxError, \
+ DEFAULTSECT, MAX_INTERPOLATION_DEPTH
+
+# These are imported only for compatiability.
+# The code below does not reference them directly.
+from ConfigParser import Error, InterpolationError, \
+ MissingSectionHeaderError, ParsingError
+
+import ini
+
+class RawConfigParser(object):
+ def __init__(self, defaults=None, dict_type=dict):
+ if dict_type != dict:
+ raise ValueError('Custom dict types not supported')
+ self.data = ini.INIConfig(defaults=defaults, optionxformsource=self)
+
+ def optionxform(self, optionstr):
+ return optionstr.lower()
+
+ def defaults(self):
+ d = {}
+ secobj = self.data._defaults
+ for name in secobj._options:
+ d[name] = secobj._compat_get(name)
+ return d
+
+ def sections(self):
+ """Return a list of section names, excluding [DEFAULT]"""
+ return list(self.data)
+
+ def add_section(self, section):
+ """Create a new section in the configuration.
+
+ Raise DuplicateSectionError if a section by the specified name
+ already exists. Raise ValueError if name is DEFAULT or any of
+ its case-insensitive variants.
+ """
+ # The default section is the only one that gets the case-insensitive
+ # treatment - so it is special-cased here.
+ if section.lower() == "default":
+ raise ValueError, 'Invalid section name: %s' % section
+
+ if self.has_section(section):
+ raise DuplicateSectionError(section)
+ else:
+ self.data._new_namespace(section)
+
+ def has_section(self, section):
+ """Indicate whether the named section is present in the configuration.
+
+ The DEFAULT section is not acknowledged.
+ """
+ return (section in self.data)
+
+ def options(self, section):
+ """Return a list of option names for the given section name."""
+ if section in self.data:
+ return list(self.data[section])
+ else:
+ raise NoSectionError(section)
+
+ def read(self, filenames):
+ """Read and parse a filename or a list of filenames.
+
+ Files that cannot be opened are silently ignored; this is
+ designed so that you can specify a list of potential
+ configuration file locations (e.g. current directory, user's
+ home directory, systemwide directory), and all existing
+ configuration files in the list will be read. A single
+ filename may also be given.
+ """
+ files_read = []
+ if isinstance(filenames, basestring):
+ filenames = [filenames]
+ for filename in filenames:
+ try:
+ fp = open(filename)
+ except IOError:
+ continue
+ files_read.append(filename)
+ self.data._readfp(fp)
+ fp.close()
+ return files_read
+
+ def readfp(self, fp, filename=None):
+ """Like read() but the argument must be a file-like object.
+
+ The `fp' argument must have a `readline' method. Optional
+ second argument is the `filename', which if not given, is
+ taken from fp.name. If fp has no `name' attribute, `??>' is
+ used.
+ """
+ self.data._readfp(fp)
+
+ def get(self, section, option, vars=None):
+ if not self.has_section(section):
+ raise NoSectionError(section)
+ if vars is not None and option in vars:
+ value = vars[option]
+
+ sec = self.data[section]
+ if option in sec:
+ return sec._compat_get(option)
+ else:
+ raise NoOptionError(option, section)
+
+ def items(self, section):
+ if section in self.data:
+ ans = []
+ for opt in self.data[section]:
+ ans.append((opt, self.get(section, opt)))
+ return ans
+ else:
+ raise NoSectionError(section)
+
+ def getint(self, section, option):
+ return int(self.get(section, option))
+
+ def getfloat(self, section, option):
+ return float(self.get(section, option))
+
+ _boolean_states = {'1': True, 'yes': True, 'true': True, 'on': True,
+ '0': False, 'no': False, 'false': False, 'off': False}
+
+ def getboolean(self, section, option):
+ v = self.get(section, option)
+ if v.lower() not in self._boolean_states:
+ raise ValueError, 'Not a boolean: %s' % v
+ return self._boolean_states[v.lower()]
+
+ def has_option(self, section, option):
+ """Check for the existence of a given option in a given section."""
+ if section in self.data:
+ sec = self.data[section]
+ else:
+ raise NoSectionError(section)
+ return (option in sec)
+
+ def set(self, section, option, value):
+ """Set an option."""
+ if section in self.data:
+ self.data[section][option] = value
+ else:
+ raise NoSectionError(section)
+
+ def write(self, fp):
+ """Write an .ini-format representation of the configuration state."""
+ fp.write(str(self.data))
+
+ def remove_option(self, section, option):
+ """Remove an option."""
+ if section in self.data:
+ sec = self.data[section]
+ else:
+ raise NoSectionError(section)
+ if option in sec:
+ del sec[option]
+ return 1
+ else:
+ return 0
+
+ def remove_section(self, section):
+ """Remove a file section."""
+ if not self.has_section(section):
+ return False
+ del self.data[section]
+ return True
+
+
+class ConfigDict(object):
+ """Present a dict interface to a ini section."""
+
+ def __init__(self, cfg, section, vars):
+ self.cfg = cfg
+ self.section = section
+ self.vars = vars
+
+ def __getitem__(self, key):
+ try:
+ return RawConfigParser.get(self.cfg, self.section, key, self.vars)
+ except (NoOptionError, NoSectionError):
+ raise KeyError(key)
+
+
+class ConfigParser(RawConfigParser):
+
+ def get(self, section, option, raw=False, vars=None):
+ """Get an option value for a given section.
+
+ All % interpolations are expanded in the return values, based on the
+ defaults passed into the constructor, unless the optional argument
+ `raw' is true. Additional substitutions may be provided using the
+ `vars' argument, which must be a dictionary whose contents overrides
+ any pre-existing defaults.
+
+ The section DEFAULT is special.
+ """
+ if section != DEFAULTSECT and not self.has_section(section):
+ raise NoSectionError(section)
+
+ option = self.optionxform(option)
+ value = RawConfigParser.get(self, section, option, vars)
+
+ if raw:
+ return value
+ else:
+ d = ConfigDict(self, section, vars)
+ return self._interpolate(section, option, value, d)
+
+ def _interpolate(self, section, option, rawval, vars):
+ # do the string interpolation
+ value = rawval
+ depth = MAX_INTERPOLATION_DEPTH
+ while depth: # Loop through this until it's done
+ depth -= 1
+ if "%(" in value:
+ try:
+ value = value % vars
+ except KeyError, e:
+ raise InterpolationMissingOptionError(
+ option, section, rawval, e.args[0])
+ else:
+ break
+ if value.find("%(") != -1:
+ raise InterpolationDepthError(option, section, rawval)
+ return value
+
+ def items(self, section, raw=False, vars=None):
+ """Return a list of tuples with (name, value) for each option
+ in the section.
+
+ All % interpolations are expanded in the return values, based on the
+ defaults passed into the constructor, unless the optional argument
+ `raw' is true. Additional substitutions may be provided using the
+ `vars' argument, which must be a dictionary whose contents overrides
+ any pre-existing defaults.
+
+ The section DEFAULT is special.
+ """
+ if section != DEFAULTSECT and not self.has_section(section):
+ raise NoSectionError(section)
+ if vars is None:
+ options = list(self.data[section])
+ else:
+ options = []
+ for x in self.data[section]:
+ if x not in vars:
+ options.append(x)
+ options.extend(vars.keys())
+
+ if "__name__" in options:
+ options.remove("__name__")
+
+ d = ConfigDict(self, section, vars)
+ if raw:
+ return [(option, d[option])
+ for option in options]
+ else:
+ return [(option, self._interpolate(section, option, d[option], d))
+ for option in options]
+
+
+class SafeConfigParser(ConfigParser):
+ _interpvar_re = re.compile(r"%\(([^)]+)\)s")
+ _badpercent_re = re.compile(r"%[^%]|%$")
+
+ def set(self, section, option, value):
+ if not isinstance(value, basestring):
+ raise TypeError("option values must be strings")
+ # check for bad percent signs:
+ # first, replace all "good" interpolations
+ tmp_value = self._interpvar_re.sub('', value)
+ # then, check if there's a lone percent sign left
+ m = self._badpercent_re.search(tmp_value)
+ if m:
+ raise ValueError("invalid interpolation syntax in %r at "
+ "position %d" % (value, m.start()))
+
+ ConfigParser.set(self, section, option, value)
+
+ def _interpolate(self, section, option, rawval, vars):
+ # do the string interpolation
+ L = []
+ self._interpolate_some(option, L, rawval, section, vars, 1)
+ return ''.join(L)
+
+ _interpvar_match = re.compile(r"%\(([^)]+)\)s").match
+
+ def _interpolate_some(self, option, accum, rest, section, map, depth):
+ if depth > MAX_INTERPOLATION_DEPTH:
+ raise InterpolationDepthError(option, section, rest)
+ while rest:
+ p = rest.find("%")
+ if p < 0:
+ accum.append(rest)
+ return
+ if p > 0:
+ accum.append(rest[:p])
+ rest = rest[p:]
+ # p is no longer used
+ c = rest[1:2]
+ if c == "%":
+ accum.append("%")
+ rest = rest[2:]
+ elif c == "(":
+ m = self._interpvar_match(rest)
+ if m is None:
+ raise InterpolationSyntaxError(option, section,
+ "bad interpolation variable reference %r" % rest)
+ var = m.group(1)
+ rest = rest[m.end():]
+ try:
+ v = map[var]
+ except KeyError:
+ raise InterpolationMissingOptionError(
+ option, section, rest, var)
+ if "%" in v:
+ self._interpolate_some(option, accum, v,
+ section, map, depth + 1)
+ else:
+ accum.append(v)
+ else:
+ raise InterpolationSyntaxError(
+ option, section,
+ "'%' must be followed by '%' or '(', found: " + repr(rest))
diff --git a/iniparse/config.py b/iniparse/config.py
new file mode 100755
index 0000000..5cfa2ea
--- /dev/null
+++ b/iniparse/config.py
@@ -0,0 +1,294 @@
+class ConfigNamespace(object):
+ """Abstract class representing the interface of Config objects.
+
+ A ConfigNamespace is a collection of names mapped to values, where
+ the values may be nested namespaces. Values can be accessed via
+ container notation - obj[key] - or via dotted notation - obj.key.
+ Both these access methods are equivalent.
+
+ To minimize name conflicts between namespace keys and class members,
+ the number of class members should be minimized, and the names of
+ all class members should start with an underscore.
+
+ Subclasses must implement the methods for container-like access,
+ and this class will automatically provide dotted access.
+
+ """
+
+ # Methods that must be implemented by subclasses
+
+ def _getitem(self, key):
+ return NotImplementedError(key)
+
+ def __setitem__(self, key, value):
+ raise NotImplementedError(key, value)
+
+ def __delitem__(self, key):
+ raise NotImplementedError(key)
+
+ def __iter__(self):
+ return NotImplementedError()
+
+ def _new_namespace(self, name):
+ raise NotImplementedError(name)
+
+ def __contains__(self, key):
+ try:
+ self._getitem(key)
+ except KeyError:
+ return False
+ return True
+
+ # Machinery for converting dotted access into container access,
+ # and automatically creating new sections/namespaces.
+ #
+ # To distinguish between accesses of class members and namespace
+ # keys, we first call object.__getattribute__(). If that succeeds,
+ # the name is assumed to be a class member. Otherwise it is
+ # treated as a namespace key.
+ #
+ # Therefore, member variables should be defined in the class,
+ # not just in the __init__() function. See BasicNamespace for
+ # an example.
+
+ def __getitem__(self, key):
+ try:
+ return self._getitem(key)
+ except KeyError:
+ return Undefined(key, self)
+
+ def __getattr__(self, name):
+ try:
+ return self._getitem(name)
+ except KeyError:
+ if name.startswith('__') and name.endswith('__'):
+ raise AttributeError
+ return Undefined(name, self)
+
+ def __setattr__(self, name, value):
+ try:
+ object.__getattribute__(self, name)
+ object.__setattr__(self, name, value)
+ except AttributeError:
+ self.__setitem__(name, value)
+
+ def __delattr__(self, name):
+ try:
+ object.__getattribute__(self, name)
+ object.__delattr__(self, name)
+ except AttributeError:
+ self.__delitem__(name)
+
+ # During unpickling, Python checks if the class has a __setstate__
+ # method. But, the data dicts have not been initialised yet, which
+ # leads to _getitem and hence __getattr__ raising an exception. So
+ # we explicitly impement default __setstate__ behavior.
+ def __setstate__(self, state):
+ self.__dict__.update(state)
+
+class Undefined(object):
+ """Helper class used to hold undefined names until assignment.
+
+ This class helps create any undefined subsections when an
+ assignment is made to a nested value. For example, if the
+ statement is "cfg.a.b.c = 42", but "cfg.a.b" does not exist yet.
+ """
+
+ def __init__(self, name, namespace):
+ object.__setattr__(self, 'name', name)
+ object.__setattr__(self, 'namespace', namespace)
+
+ def __setattr__(self, name, value):
+ obj = self.namespace._new_namespace(self.name)
+ obj[name] = value
+
+ def __setitem__(self, name, value):
+ obj = self.namespace._new_namespace(self.name)
+ obj[name] = value
+
+
+# ---- Basic implementation of a ConfigNamespace
+
+class BasicConfig(ConfigNamespace):
+ """Represents a hierarchical collection of named values.
+
+ Values are added using dotted notation:
+
+ >>> n = BasicConfig()
+ >>> n.x = 7
+ >>> n.name.first = 'paramjit'
+ >>> n.name.last = 'oberoi'
+
+ ...and accessed the same way, or with [...]:
+
+ >>> n.x
+ 7
+ >>> n.name.first
+ 'paramjit'
+ >>> n.name.last
+ 'oberoi'
+ >>> n['x']
+ 7
+ >>> n['name']['first']
+ 'paramjit'
+
+ Iterating over the namespace object returns the keys:
+
+ >>> l = list(n)
+ >>> l.sort()
+ >>> l
+ ['name', 'x']
+
+ Values can be deleted using 'del' and printed using 'print'.
+
+ >>> n.aaa = 42
+ >>> del n.x
+ >>> print n
+ aaa = 42
+ name.first = paramjit
+ name.last = oberoi
+
+ Nested namepsaces are also namespaces:
+
+ >>> isinstance(n.name, ConfigNamespace)
+ True
+ >>> print n.name
+ first = paramjit
+ last = oberoi
+ >>> sorted(list(n.name))
+ ['first', 'last']
+
+ Finally, values can be read from a file as follows:
+
+ >>> from StringIO import StringIO
+ >>> sio = StringIO('''
+ ... # comment
+ ... ui.height = 100
+ ... ui.width = 150
+ ... complexity = medium
+ ... have_python
+ ... data.secret.password = goodness=gracious me
+ ... ''')
+ >>> n = BasicConfig()
+ >>> n._readfp(sio)
+ >>> print n
+ complexity = medium
+ data.secret.password = goodness=gracious me
+ have_python
+ ui.height = 100
+ ui.width = 150
+ """
+
+ # this makes sure that __setattr__ knows this is not a namespace key
+ _data = None
+
+ def __init__(self):
+ self._data = {}
+
+ def _getitem(self, key):
+ return self._data[key]
+
+ def __setitem__(self, key, value):
+ self._data[key] = value
+
+ def __delitem__(self, key):
+ del self._data[key]
+
+ def __iter__(self):
+ return iter(self._data)
+
+ def __str__(self, prefix=''):
+ lines = []
+ keys = self._data.keys()
+ keys.sort()
+ for name in keys:
+ value = self._data[name]
+ if isinstance(value, ConfigNamespace):
+ lines.append(value.__str__(prefix='%s%s.' % (prefix,name)))
+ else:
+ if value is None:
+ lines.append('%s%s' % (prefix, name))
+ else:
+ lines.append('%s%s = %s' % (prefix, name, value))
+ return '\n'.join(lines)
+
+ def _new_namespace(self, name):
+ obj = BasicConfig()
+ self._data[name] = obj
+ return obj
+
+ def _readfp(self, fp):
+ while True:
+ line = fp.readline()
+ if not line:
+ break
+
+ line = line.strip()
+ if not line: continue
+ if line[0] == '#': continue
+ data = line.split('=', 1)
+ if len(data) == 1:
+ name = line
+ value = None
+ else:
+ name = data[0].strip()
+ value = data[1].strip()
+ name_components = name.split('.')
+ ns = self
+ for n in name_components[:-1]:
+ if n in ns:
+ ns = ns[n]
+ if not isinstance(ns, ConfigNamespace):
+ raise TypeError('value-namespace conflict', n)
+ else:
+ ns = ns._new_namespace(n)
+ ns[name_components[-1]] = value
+
+
+# ---- Utility functions
+
+def update_config(target, source):
+ """Imports values from source into target.
+
+ Recursively walks the