move drlaunch in drlaunch
[drlaunch] / drlaunch / src / xdg / IniFile.py
diff --git a/drlaunch/src/xdg/IniFile.py b/drlaunch/src/xdg/IniFile.py
new file mode 100644 (file)
index 0000000..f3f08c7
--- /dev/null
@@ -0,0 +1,406 @@
+"""
+Base Class for DesktopEntry, IconTheme and IconData
+"""
+
+import re, os, stat, codecs
+from Exceptions import *
+import xdg.Locale
+
+class IniFile:
+    defaultGroup = ''
+    fileExtension = ''
+
+    filename = ''
+
+    tainted = False
+
+    def __init__(self, filename=None):
+        self.content = dict()
+        if filename:
+            self.parse(filename)
+
+    def __cmp__(self, other):
+        return cmp(self.content, other.content)
+
+    def parse(self, filename, headers=None):
+        # for performance reasons
+        content = self.content
+
+        if not os.path.isfile(filename):
+            raise ParsingError("File not found", filename)
+
+        try:
+            fd = file(filename, 'r')
+        except IOError, e:
+            if debug:
+                raise e
+            else:
+                return
+
+        # parse file
+        for line in fd:
+            line = line.strip()
+            # empty line
+            if not line:
+                continue
+            # comment
+            elif line[0] == '#':
+                continue
+            # new group
+            elif line[0] == '[':
+                currentGroup = line.lstrip("[").rstrip("]")
+                if debug and self.hasGroup(currentGroup):
+                    raise DuplicateGroupError(currentGroup, filename)
+                else:
+                    content[currentGroup] = {}
+            # key
+            else:
+                index = line.find("=")
+                key = line[0:index].strip()
+                value = line[index+1:].strip()
+                try:
+                    if debug and self.hasKey(key, currentGroup):
+                        raise DuplicateKeyError(key, currentGroup, filename)
+                    else:
+                        content[currentGroup][key] = value
+                except (IndexError, UnboundLocalError):
+                    raise ParsingError("Parsing error on key, group missing", filename)
+
+        fd.close()
+
+        self.filename = filename
+        self.tainted = False
+
+        # check header
+        if headers:
+            for header in headers:
+                if content.has_key(header):
+                    self.defaultGroup = header
+                    break
+            else:
+                raise ParsingError("[%s]-Header missing" % headers[0], filename)
+
+    # start stuff to access the keys
+    def get(self, key, group=None, locale=False, type="string", list=False):
+        # set default group
+        if not group:
+            group = self.defaultGroup
+
+        # return key (with locale)
+        if self.content.has_key(group) and self.content[group].has_key(key):
+            if locale:
+                value = self.content[group][self.__addLocale(key, group)]
+            else:
+                value = self.content[group][key]
+        else:
+            if debug:
+                if not self.content.has_key(group):
+                    raise NoGroupError(group, self.filename)
+                elif not self.content[group].has_key(key):
+                    raise NoKeyError(key, group, self.filename)
+            else:
+                value = ""
+
+        if list == True:
+            values = self.getList(value)
+            result = []
+        else:
+            values = [value]
+
+        for value in values:
+            if type == "string" and locale == True:
+                value = value.decode("utf-8", "ignore")
+            elif type == "boolean":
+                value = self.__getBoolean(value)
+            elif type == "integer":
+                try:
+                    value = int(value)
+                except ValueError:
+                    value = 0
+            elif type == "numeric":
+                try:
+                    value = float(value)
+                except ValueError:
+                    value = 0.0
+            elif type == "regex":
+                value = re.compile(value)
+            elif type == "point":
+                value = value.split(",")
+
+            if list == True:
+                result.append(value)
+            else:
+                result = value
+
+        return result
+    # end stuff to access the keys
+
+    # start subget
+    def getList(self, string):
+        if re.search(r"(?<!\\)\;", string):
+            list = re.split(r"(?<!\\);", string)
+        elif re.search(r"(?<!\\)\|", string):
+            list = re.split(r"(?<!\\)\|", string)
+        elif re.search(r"(?<!\\),", string):
+            list = re.split(r"(?<!\\),", string)
+        else:
+            list = [string]
+        if list[-1] == "":
+            list.pop()
+        return list
+
+    def __getBoolean(self, boolean):
+        if boolean == 1 or boolean == "true" or boolean == "True":
+            return True
+        elif boolean == 0 or boolean == "false" or boolean == "False":
+            return False
+        return False
+    # end subget
+
+    def __addLocale(self, key, group=None):
+        "add locale to key according the current lc_messages"
+        # set default group
+        if not group:
+            group = self.defaultGroup
+
+        for lang in xdg.Locale.langs:
+            if self.content[group].has_key(key+'['+lang+']'):
+                return key+'['+lang+']'
+
+        return key
+
+    # start validation stuff
+    def validate(self, report="All"):
+        "validate ... report = All / Warnings / Errors"
+
+        self.warnings = []
+        self.errors = []
+
+        # get file extension
+        self.fileExtension = os.path.splitext(self.filename)[1]
+
+        # overwrite this for own checkings
+        self.checkExtras()
+
+        # check all keys
+        for group in self.content:
+            self.checkGroup(group)
+            for key in self.content[group]:
+                self.checkKey(key, self.content[group][key], group)
+                # check if value is empty
+                if self.content[group][key] == "":
+                    self.warnings.append("Value of Key '%s' is empty" % key)
+
+        # raise Warnings / Errors
+        msg = ""
+
+        if report == "All" or report == "Warnings":
+            for line in self.warnings:
+                msg += "\n- " + line
+
+        if report == "All" or report == "Errors":
+            for line in self.errors:
+                msg += "\n- " + line
+
+        if msg:
+            raise ValidationError(msg, self.filename)
+
+    # check if group header is valid
+    def checkGroup(self, group):
+        pass
+
+    # check if key is valid
+    def checkKey(self, key, value, group):
+        pass
+
+    # check random stuff
+    def checkValue(self, key, value, type="string", list=False):
+        if list == True:
+            values = self.getList(value)
+        else:
+            values = [value]
+
+        for value in values:
+            if type == "string":
+                code = self.checkString(value)
+            elif type == "boolean":
+                code = self.checkBoolean(value)
+            elif type == "numeric":
+                code = self.checkNumber(value)
+            elif type == "integer":
+                code = self.checkInteger(value)
+            elif type == "regex":
+                code = self.checkRegex(value)
+            elif type == "point":
+                code = self.checkPoint(value)
+            if code == 1:
+                self.errors.append("'%s' is not a valid %s" % (value, type))
+            elif code == 2:
+                self.warnings.append("Value of key '%s' is deprecated" % key)
+
+    def checkExtras(self):
+        pass
+
+    def checkBoolean(self, value):
+        # 1 or 0 : deprecated
+        if (value == "1" or value == "0"):
+            return 2
+        # true or false: ok
+        elif not (value == "true" or value == "false"):
+            return 1
+
+    def checkNumber(self, value):
+        # float() ValueError
+        try:
+            float(value)
+        except:
+            return 1
+
+    def checkInteger(self, value):
+        # int() ValueError
+        try:
+            int(value)
+        except:
+            return 1
+
+    def checkPoint(self, value):
+        if not re.match("^[0-9]+,[0-9]+$", value):
+            return 1
+
+    def checkString(self, value):
+        # convert to ascii
+        if not value.decode("utf-8", "ignore").encode("ascii", 'ignore') == value:
+            return 1
+
+    def checkRegex(self, value):
+        try:
+            re.compile(value)
+        except:
+            return 1
+
+    # write support
+    def write(self, filename=None, trusted=False):
+        if not filename and not self.filename:
+            raise ParsingError("File not found", "")
+
+        if filename:
+            self.filename = filename
+        else:
+            filename = self.filename
+
+        if os.path.dirname(filename) and not os.path.isdir(os.path.dirname(filename)):
+            os.makedirs(os.path.dirname(filename))
+
+        fp = codecs.open(filename, 'w')
+
+        # An executable bit signifies that the desktop file is
+        # trusted, but then the file can be executed. Add hashbang to
+        # make sure that the file is opened by something that
+        # understands desktop files.
+        if trusted:
+            fp.write("#!/usr/bin/env xdg-open\n")
+
+        if self.defaultGroup:
+            fp.write("[%s]\n" % self.defaultGroup)
+            for (key, value) in self.content[self.defaultGroup].items():
+                fp.write("%s=%s\n" % (key, value))
+            fp.write("\n")
+        for (name, group) in self.content.items():
+            if name != self.defaultGroup:
+                fp.write("[%s]\n" % name)
+                for (key, value) in group.items():
+                    fp.write("%s=%s\n" % (key, value))
+                fp.write("\n")
+
+        # Add executable bits to the file to show that it's trusted.
+        if trusted:
+            oldmode = os.stat(filename).st_mode
+            mode = oldmode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
+            os.chmod(filename, mode)
+
+        self.tainted = False
+
+    def set(self, key, value, group=None, locale=False):
+        # set default group
+        if not group:
+            group = self.defaultGroup
+
+        if locale == True and len(xdg.Locale.langs) > 0:
+            key = key + "[" + xdg.Locale.langs[0] + "]"
+
+        try:
+            if isinstance(value, unicode):
+                self.content[group][key] = value.encode("utf-8", "ignore")
+            else:
+                self.content[group][key] = value
+        except KeyError:
+            raise NoGroupError(group, self.filename)
+            
+        self.tainted = (value == self.get(key, group))
+
+    def addGroup(self, group):
+        if self.hasGroup(group):
+            if debug:
+                raise DuplicateGroupError(group, self.filename)
+            else:
+                pass
+        else:
+            self.content[group] = {}
+            self.tainted = True
+
+    def removeGroup(self, group):
+        existed = group in self.content
+        if existed:
+            del self.content[group]
+            self.tainted = True
+        else:
+            if debug:
+                raise NoGroupError(group, self.filename)
+        return existed
+
+    def removeKey(self, key, group=None, locales=True):
+        # set default group
+        if not group:
+            group = self.defaultGroup
+
+        try:
+            if locales:
+                for (name, value) in self.content[group].items():
+                    if re.match("^" + key + xdg.Locale.regex + "$", name) and name != key:
+                        value = self.content[group][name]
+                        del self.content[group][name]
+            value = self.content[group][key]
+            del self.content[group][key]
+            self.tainted = True
+            return value
+        except KeyError, e:
+            if debug:
+                if e == group:
+                    raise NoGroupError(group, self.filename)
+                else:
+                    raise NoKeyError(key, group, self.filename)
+            else:
+                return ""
+
+    # misc
+    def groups(self):
+        return self.content.keys()
+
+    def hasGroup(self, group):
+        if self.content.has_key(group):
+            return True
+        else:
+            return False
+
+    def hasKey(self, key, group=None):
+        # set default group
+        if not group:
+            group = self.defaultGroup
+
+        if self.content[group].has_key(key):
+            return True
+        else:
+            return False
+
+    def getFileName(self):
+        return self.filename