def merge_file(self, filename, child, parent): # check for infinite loops if filename in self._merged_files: if debug: raise ParsingError('Infinite MergeFile loop detected', filename) else: return self._merged_files.add(filename) # load file try: tree = etree.parse(filename) except IOError: if self.debug: raise ParsingError('File not found', filename) else: return except: if self.debug: raise ParsingError('Not a valid .menu file', filename) else: return root = tree.getroot() # append file for child in root: self.parse_node(child, filename, parent) break
def __mergeFile(filename, child, parent): # check for infinite loops if filename in tmp["mergeFiles"]: if debug: raise ParsingError('Infinite MergeFile loop detected', filename) else: return tmp["mergeFiles"].append(filename) # load file try: doc = xml.dom.minidom.parse(filename) except IOError: if debug: raise ParsingError('File not found', filename) else: return except xml.parsers.expat.ExpatError: if debug: raise ParsingError('Not a valid .menu file', filename) else: return # append file for child in doc.childNodes: if child.nodeType == ELEMENT_NODE: __parse(child,filename,parent) break
def parse(self, filename=None): """Parse a list of recently used files. filename defaults to ``~/.recently-used``. """ if not filename: filename = os.path.join(os.getenv("HOME"), ".recently-used") try: doc = xml.dom.minidom.parse(filename) except IOError: raise ParsingError('File not found', filename) except xml.parsers.expat.ExpatError: raise ParsingError('Not a valid .menu file', filename) self.filename = filename for child in doc.childNodes: if child.nodeType == xml.dom.Node.ELEMENT_NODE: if child.tagName == "RecentFiles": for recent in child.childNodes: if recent.nodeType == xml.dom.Node.ELEMENT_NODE: if recent.tagName == "RecentItem": self.__parseRecentItem(recent) self.sort()
def parse(filename=None): """Load an applications.menu file. filename : str, optional The default is ``$XDG_CONFIG_DIRS/menus/${XDG_MENU_PREFIX}applications.menu``. """ # convert to absolute path if filename and not os.path.isabs(filename): filename = __getFileName(filename) # use default if no filename given if not filename: # Upstream's default, we leave it as default in case someone produces # their own applications.menu and is depending on it. candidate = os.environ.get('XDG_MENU_PREFIX', '') + "applications.menu" filename = __getFileName(candidate) # Since applications.menu isn't provided in Debian, we'll fallback to # debian.menu, typically in /etc/xdg/menus/debian-menu.menu # (Closes: #654978) if not filename: candidate = os.environ.get('XDG_MENU_PREFIX', '') + \ "debian-menu.menu" filename = __getFileName(candidate) if not filename: raise ParsingError('File not found', "/etc/xdg/menus/%s" % candidate) # check if it is a .menu file if not os.path.splitext(filename)[1] == ".menu": raise ParsingError('Not a .menu file', filename) # create xml parser try: doc = xml.dom.minidom.parse(filename) except xml.parsers.expat.ExpatError: raise ParsingError('Not a valid .menu file', filename) # parse menufile tmp["Root"] = "" tmp["mergeFiles"] = [] tmp["DirectoryDirs"] = [] tmp["cache"] = MenuEntryCache() __parse(doc, filename, tmp["Root"]) __parsemove(tmp["Root"]) __postparse(tmp["Root"]) tmp["Root"].Doc = doc tmp["Root"].Filename = filename # generate the menu __genmenuNotOnlyAllocated(tmp["Root"]) __genmenuOnlyAllocated(tmp["Root"]) # and finally sort sort(tmp["Root"]) return tmp["Root"]
def parse(filename=None): """Load an applications.menu file. filename : str, optional The default is ``$XDG_CONFIG_DIRS/menus/${XDG_MENU_PREFIX}applications.menu``. """ # convert to absolute path if filename and not os.path.isabs(filename): filename = __getFileName(filename) # use default if no filename given if not filename: candidate = os.environ.get('XDG_MENU_PREFIX', '') + "applications.menu" filename = __getFileName(candidate) if not filename: raise ParsingError('File not found', "/etc/xdg/menus/%s" % candidate) # check if it is a .menu file if not os.path.splitext(filename)[1] == ".menu": raise ParsingError('Not a .menu file', filename) # create xml parser try: doc = xml.dom.minidom.parse(filename) except xml.parsers.expat.ExpatError: raise ParsingError('Not a valid .menu file', filename) # parse menufile tmp["Root"] = "" tmp["mergeFiles"] = [] tmp["DirectoryDirs"] = [] tmp["cache"] = MenuEntryCache() __parse(doc, filename, tmp["Root"]) __parsemove(tmp["Root"]) __postparse(tmp["Root"]) tmp["Root"].Doc = doc tmp["Root"].Filename = filename # generate the menu __genmenuNotOnlyAllocated(tmp["Root"]) __genmenuOnlyAllocated(tmp["Root"]) # and finally sort sort(tmp["Root"]) return tmp["Root"]
def write(self, filename=None): """Write the list of recently used files to disk. If the instance is already associated with a file, filename can be omitted to save it there again. """ if not filename and not self.filename: raise ParsingError('File not found', filename) elif not filename: filename = self.filename with open(filename, "w") as f: fcntl.lockf(f, fcntl.LOCK_EX) f.write('<?xml version="1.0"?>\n') f.write("<RecentFiles>\n") for r in self.RecentFiles: f.write(" <RecentItem>\n") f.write(" <URI>%s</URI>\n" % xml.sax.saxutils.escape(r.URI)) f.write(" <Mime-Type>%s</Mime-Type>\n" % r.MimeType) f.write(" <Timestamp>%s</Timestamp>\n" % r.Timestamp) if r.Private == True: f.write(" <Private/>\n") if len(r.Groups) > 0: f.write(" <Groups>\n") for group in r.Groups: f.write(" <Group>%s</Group>\n" % group) f.write(" </Groups>\n") f.write(" </RecentItem>\n") f.write("</RecentFiles>\n") fcntl.lockf(f, fcntl.LOCK_UN)
def parse(self, menu=None, filename=None, root=False): if root: setRootMode(True) if isinstance(menu, Menu): self.menu = menu elif menu: self.menu = self.parser.parse(menu) else: self.menu = self.parser.parse() if root: self.filename = self.menu.Filename elif filename: self.filename = filename else: self.filename = os.path.join(xdg_config_dirs[0], "menus", os.path.split(self.menu.Filename)[1]) try: self.tree = etree.parse(self.filename) except IOError: root = etree.fromtring(""" <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN" "http://standards.freedesktop.org/menu-spec/menu-1.0.dtd"> <Menu> <Name>Applications</Name> <MergeFile type="parent">%s</MergeFile> </Menu> """ % self.menu.Filename) self.tree = etree.ElementTree(root) except ParsingError: raise ParsingError('Not a valid .menu file', self.filename) #FIXME: is this needed with etree ? self.__remove_whitespace_nodes(self.tree)
def parse(self, filename=None): """Load an applications.menu file. filename : str, optional The default is ``$XDG_CONFIG_DIRS/menus/${XDG_MENU_PREFIX}applications.menu``. """ # convert to absolute path if filename and not os.path.isabs(filename): filename = _get_menu_file_path(filename) # use default if no filename given if not filename: candidate = os.environ.get('XDG_MENU_PREFIX', '') + "applications.menu" filename = _get_menu_file_path(candidate) if not filename: raise ParsingError('File not found', "/etc/xdg/menus/%s" % candidate) # check if it is a .menu file if not filename.endswith(".menu"): raise ParsingError('Not a .menu file', filename) # create xml parser try: tree = etree.parse(filename) except: raise ParsingError('Not a valid .menu file', filename) # parse menufile self._merged_files = set() self._directory_dirs = set() self.cache = MenuEntryCache() menu = self.parse_menu(tree.getroot(), filename) menu.tree = tree menu.filename = filename self.handle_moves(menu) self.post_parse(menu) # generate the menu self.generate_not_only_allocated(menu) self.generate_only_allocated(menu) # and finally sort menu.sort() return menu
def new(self, filename): """Make this instance into a new desktop entry. If filename has a .desktop extension, Type is set to Application. If it has a .directory extension, Type is Directory. """ if os.path.splitext(filename)[1] == ".desktop": type = "Application" elif os.path.splitext(filename)[1] == ".directory": type = "Directory" else: raise ParsingError("Unknown extension", filename) self.content = dict() self.addGroup(self.defaultGroup) self.set("Type", type) self.filename = filename
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)) with io.open(filename, 'w', encoding='utf-8') as fp: # 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(u("#!/usr/bin/env xdg-open\n")) if self.defaultGroup: fp.write(u("[%s]\n") % self.defaultGroup) for (key, value) in self.content[self.defaultGroup].items(): fp.write(u("%s=%s\n") % (key, value)) fp.write(u("\n")) for (name, group) in self.content.items(): if name != self.defaultGroup: fp.write(u("[%s]\n") % name) for (key, value) in group.items(): fp.write(u("%s=%s\n") % (key, value)) fp.write(u("\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 parse(self, filename, headers=None): '''Parse an INI file. headers -- list of headers the parser will try to select as a default header ''' # for performance reasons content = self.content if not os.path.isfile(filename): raise ParsingError("File not found", filename) try: # The content should be UTF-8, but legacy files can have other # encodings, including mixed encodings in one file. We don't attempt # to decode them, but we silence the errors. fd = io.open(filename, 'r', encoding='utf-8', errors='replace') except IOError as e: if debug: raise e else: return # parse file with fd: 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: try: key, value = line.split("=", 1) except ValueError: raise ParsingError("Invalid line: " + line, filename) key = key.strip( ) # Spaces before/after '=' should be ignored try: if debug and self.hasKey(key, currentGroup): raise DuplicateKeyError(key, currentGroup, filename) else: content[currentGroup][key] = value.strip() except (IndexError, UnboundLocalError): raise ParsingError( "Parsing error on key, group missing", filename) self.filename = filename self.tainted = False # check header if headers: for header in headers: if header in content: self.defaultGroup = header break else: raise ParsingError("[%s]-Header missing" % headers[0], filename)