diff options
author | Johannes Löthberg <johannes@kyriasis.com> | 2015-04-26 20:54:41 +0200 |
---|---|---|
committer | Johannes Löthberg <johannes@kyriasis.com> | 2015-04-26 20:54:41 +0200 |
commit | 3966da435243ecf05fe054c44db8cc3cb43d704a (patch) | |
tree | 5d743e4cbb3623d22c0cd970f0df11b090d1b847 | |
parent | 2a1daf97626df183383e7889629837a8ec5fa489 (diff) | |
download | bin-3966da435243ecf05fe054c44db8cc3cb43d704a.tar.xz |
Delete dex
-rwxr-xr-x | dex | 781 |
1 files changed, 0 insertions, 781 deletions
@@ -1,781 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# vi: ft=python:tw=0:sw=4:ts=4:noet -# Author: Jan Christoph Ebersbach <jceb@e-jc.de> -# Last Modified: Thu 12. Jul 2012 20:16:13 +0200 CEST - -# dex -# DesktopEntry Execution, is a program to generate and execute DesktopEntry -# files of the type Application -# -# Depends: None -# -# Copyright (C) 2010, 2011, 2012, 2013 Jan Christoph Ebersbach -# -# http://www.e-jc.de/ -# -# All rights reserved. -# -# This program 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. -# -# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. - - -import glob -import os -import subprocess -import sys - -__version__ = "0.7" - - -# DesktopEntry exceptions -class DesktopEntryTypeException(Exception): - def __init__(self, value): - self.value = value - - def __str__(self): - return repr(self.value) - - -class ApplicationExecException(Exception): - def __init__(self, value): - self.value = value - Exception.__init__(self, value) - - def __str__(self): - return repr(self.value) - - -# DesktopEntry class definitions -class DesktopEntry(object): - """ - Implements some parts of Desktop Entry specification: - http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.1.html - """ - def __init__(self, filename=None): - """ - @param filename Desktop Entry File - """ - if filename is not None and os.path.islink(filename) and \ - os.readlink(filename) == os.path.sep + os.path.join('dev', 'null'): - # ignore links to /dev/null - pass - elif filename is None or not os.path.isfile(filename): - raise IOError('File does not exist: %s' % filename) - self._filename = filename - self.groups = {} - - def __str__(self): - if self.Name: - return self.Name - elif self.filename: - return self.filename - return repr(self) - - def __lt__(self, y): - return self.filename < y.filename - - @property - def filename(self): - """ - The absolute filename - """ - return self._filename - - @classmethod - def fromfile(cls, filename): - """Create DesktopEntry for file - - @params filename Create a DesktopEntry object for file and determine - the type automatically - """ - - de = cls(filename=filename) - - # determine filetype - de_type = 'Link' - if os.path.exists(filename): - if os.path.isdir(filename): - de_type = 'Directory' - # TODO fix the value for directories - de.set_value('??', filename) - else: - de_type = 'Application' - de.set_value('Exec', filename) - de.set_value('Name', os.path.basename(filename)) - if os.name == 'posix': - whatis = subprocess.Popen(['whatis', filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = whatis.communicate() - res = stdout.decode(sys.stdin.encoding).split('- ', 1) - if len(res) == 2: - de.set_value('Comment', res[1].split(os.linesep, 1)[0]) - else: - # type Link - de.set_value('URL', filename) - - de.set_value('Type', de_type) - - return de - - def load(self): - """Load or reload contents of desktop entry file""" - self.groups = {} # clear settings - grp_desktopentry = 'Desktop Entry' - - _f = open(self.filename, 'r') - current_group = None - try: - for l in _f.readlines(): - l = l.strip('\n') - # handle comments and empty lines - if l.startswith('#') or l.strip() == '': - continue - - # handle groups - if l.startswith('['): - if not l.endswith(']'): - raise DesktopEntryTypeException("'%s' is not a valid Desktop Entry because of line '%s'." % (self.filename, l)) - group = l[1:-1] - if self.groups.get(group, None): - raise DesktopEntryTypeException("'%s' is not a valid Desktop Entry because group '%s' is specified multiple times." % (self.filename, group)) - current_group = group - continue - - # handle all the other lines - if not current_group: - raise DesktopEntryTypeException("'%s' is not a valid Desktop Entry because line '%s' does not belong to a group." % (self.filename, l)) - kv = l.split('=', 1) - if len(kv) != 2 or kv[0] == '': - raise DesktopEntryTypeException("'%s' is not a valid Desktop Entry because line '%s' is not a valid key=value pair." % (self.filename, l)) - k = kv[0] - v = kv[1] - # TODO: parse k for locale specific settings - # TODO: parse v for multivalue fields - self.set_value(k, v, current_group) - except Exception as ex: - _f.close() - raise ex - finally: - _f.close() - - if grp_desktopentry not in self.groups: - raise DesktopEntryTypeException("'%s' is not a valid Desktop Entry group is missing." % (self.filename, )) - if not (self.Type and self.Name): - raise DesktopEntryTypeException("'%s' is not a valid Desktop Entry because Type or Name keys are missing." % (self.filename, )) - _type = self.Type - if _type == 'Application': - if not self.Exec: - raise DesktopEntryTypeException("'%s' is not a valid Desktop Entry of type '%s' because Exec is missing." % (self.filename, _type)) - elif _type == 'Link': - if not self.URL: - raise DesktopEntryTypeException("'%s' is not a valid Desktop Entry of type '%s' because URL is missing." % (self.filename, _type)) - elif _type == 'Directory': - pass - else: - raise DesktopEntryTypeException("'%s' is not a valid Desktop Entry because Type '%s' is unkown." % (self.filename, self.Type)) - - # another name for load - reload = load - - def write(self, fileobject): - """Write DesktopEntry to a file - - @param fileobject DesktopEntry is written to file - """ - for group in self.groups: - fileobject.write('[%s]\n' % (group, )) - for key in self.groups[group]: - fileobject.write('%s=%s\n' % (key, self.groups[group][key])) - - def set_value(self, key, value, group='Desktop Entry'): - """ Set a key, value pair in group - - @param key Key - @param value Value - @param group The group key and value are set in. Default: Desktop Entry - """ - if group not in self.groups: - self.groups[group] = {} - self.groups[group][key] = str(value) - return value - - def _get_value(self, key, group='Desktop Entry', default=None): - if not self.groups: - self.load() - if group not in self.groups: - raise KeyError("Group '%s' not found." % group) - grp = self.groups[group] - if key not in grp: - return default - return grp[key] - - def get_boolean(self, key, group='Desktop Entry', default=False): - val = self._get_value(key, group=group, default=default) - if type(val) == bool: - return val - if val in ['true', 'True']: - return True - if val in ['false', 'False']: - return False - raise ValueError("'%s's value '%s' in group '%s' is not a boolean value." % (key, val, group)) - - def get_list(self, key, group='Desktop Entry', default=None): - list_of_strings = [] - res = self.get_string(key, group=group, default=default) - if type(res) == str: - list_of_strings = [x for x in res.split(';') if x] - return list_of_strings - - def get_string(self, key, group='Desktop Entry', default=''): - return self._get_value(key, group=group, default=default) - - def get_strings(self, key, group='Desktop Entry', default=''): - raise Exception("Not implemented yet.") - - def get_localestring(self, key, group='Desktop Entry', default=''): - raise Exception("Not implemented yet.") - - def get_numeric(self, key, group='Desktop Entry', default=0.0): - val = self._get_value(key, group=group, default=default) - if type(val) == float: - return val - return float(val) - - @property - def Type(self): - return self.get_string('Type') - - @property - def Version(self): - return self.get_string('Version') - - @property - def Name(self): - # SHOULD be localestring! - return self.get_string('Name') - - @property - def GenericName(self): - return self.get_localestring('GenericName') - - @property - def NoDisplay(self): - return self.get_boolean('NoDisplay') - - @property - def Comment(self): - return self.get_localestring('Comment') - - @property - def Icon(self): - return self.get_localestring('Icon') - - @property - def Hidden(self): - return self.get_boolean('Hidden') - - @property - def OnlyShowIn(self): - return self.get_list('OnlyShowIn') - - @property - def NotShowIn(self): - return self.get_list('NotShowIn') - - @property - def TryExec(self): - return self.get_string('TryExec') - - @property - def Exec(self): - return self.get_string('Exec') - - @property - def Path(self): - return self.get_string('Path') - - @property - def Terminal(self): - return self.get_boolean('Terminal') - - @property - def MimeType(self): - return self.get_strings('MimeType') - - @property - def Categories(self): - return self.get_strings('Categories') - - @property - def StartupNotify(self): - return self.get_boolean('StartupNotify') - - @property - def StartupWMClass(self): - return self.get_string('StartupWMClass') - - @property - def URL(self): - return self.get_string('URL') - - -class Application(DesktopEntry): - """ - Implements application files - """ - - def __init__(self, filename): - """ - @param filename Absolute path to a Desktop Entry File - """ - if not os.path.isabs(filename): - filename = os.path.join(os.getcwd(), filename) - super(Application, self).__init__(filename) - self._basename = os.path.basename(filename) - if self.Type != 'Application': - raise DesktopEntryTypeException("'%s' is not of type 'Application'." % self.filename) - - def __cmp__(self, y): - """ - @param y The object to compare the current object with - comparison is made on the property of basename - """ - if isinstance(y, Application): - return cmp(y.basename, self.basename) - return -1 - - def __eq__(self, y): - """ - @param y The object to compare the current object with - comparison is made on the property of basename - """ - if isinstance(y, Application): - return y.basename == self.basename - return False - - @property - def basename(self): - """ - The basename of file - """ - return self._basename - - @classmethod - def _build_cmd(cls, exec_string, needs_terminal=False): - """ - # test single and multi argument commands - >>> Application._build_cmd('gvim') - ['gvim'] - >>> Application._build_cmd('gvim test') - ['gvim', 'test'] - - # test quotes - >>> Application._build_cmd('"gvim" test') - ['gvim', 'test'] - >>> Application._build_cmd('"gvim test"') - ['gvim test'] - - # test escape sequences - >>> Application._build_cmd('"gvim test" test2 "test \\\\" 3"') - ['gvim test', 'test2', 'test " 3'] - >>> Application._build_cmd(r'"test \\\\\\\\ \\" moin" test') - ['test \\\\ " moin', 'test'] - >>> Application._build_cmd(r'"gvim \\\\\\\\ \\`test\\$"') - ['gvim \\\\ `test$'] - >>> Application._build_cmd(r'vim ~/.vimrc', True) - ['x-terminal-emulator', '-e', 'vim', '~/.vimrc'] - >>> Application._build_cmd('vim ~/.vimrc', False) - ['vim', '~/.vimrc'] - >>> Application._build_cmd("vim '~/.vimrc test'", False) - ['vim', '~/.vimrc test'] - >>> Application._build_cmd('vim \\'~/.vimrc " test\\'', False) - ['vim', '~/.vimrc " test'] - >>> Application._build_cmd('sh -c \\'vim ~/.vimrc " test\\'', False) - ['sh', '-c', 'vim ~/.vimrc " test'] - >>> Application._build_cmd("sh -c 'vim ~/.vimrc \\" test\\"'", False) - ['sh', '-c', 'vim ~/.vimrc " test"'] - - # expand field codes by removing them - >>> Application._build_cmd("vim %u", False) - ['vim'] - >>> Application._build_cmd("vim ~/.vimrc %u", False) - ['vim', '~/.vimrc'] - >>> Application._build_cmd("vim '%u' ~/.vimrc", False) - ['vim', '%u', '~/.vimrc'] - >>> Application._build_cmd("vim %u ~/.vimrc", False) - ['vim', '~/.vimrc'] - >>> Application._build_cmd("vim /%u/.vimrc", False) - ['vim', '//.vimrc'] - >>> Application._build_cmd("vim %u/.vimrc", False) - ['vim', '/.vimrc'] - >>> Application._build_cmd("vim %U/.vimrc", False) - ['vim', '/.vimrc'] - >>> Application._build_cmd("vim /%U/.vimrc", False) - ['vim', '//.vimrc'] - >>> Application._build_cmd("vim %U .vimrc", False) - ['vim', '.vimrc'] - - # preserved escaped field codes - >>> Application._build_cmd("vim \\\\%u ~/.vimrc", False) - ['vim', '%u', '~/.vimrc'] - - # test for non-valid field codes, they should be preserved - >>> Application._build_cmd("vim %x .vimrc", False) - ['vim', '%x', '.vimrc'] - >>> Application._build_cmd("vim %x/.vimrc", False) - ['vim', '%x/.vimrc'] - """ - cmd = [] - if needs_terminal: - cmd += ['x-terminal-emulator', '-e'] - _tmp = exec_string.replace('\\\\', '\\') - _arg = '' - in_esc = False - in_quote = False - in_singlequote = False - in_fieldcode = False - - for c in _tmp: - if in_esc: - in_esc = False - else: - if in_fieldcode: - in_fieldcode = False - if c in ('u', 'U', 'f', 'F'): - # TODO ignore field codes for the moment; at some point - # field codes should be supported - # strip %-char at the end of the argument - _arg = _arg[:-1] - continue - - if c == '"': - if in_quote: - in_quote = False - cmd.append(_arg) - _arg = '' - continue - if not in_singlequote: - in_quote = True - continue - - elif c == "'": - if in_singlequote: - in_singlequote = False - cmd.append(_arg) - _arg = '' - continue - if not in_quote: - in_singlequote = True - continue - - elif c == '\\': - in_esc = True - continue - - elif c == '%' and not (in_quote or in_singlequote): - in_fieldcode = True - - elif c == ' ' and not (in_quote or in_singlequote): - if not _arg: - continue - cmd.append(_arg) - _arg = '' - continue - - _arg += c - - if _arg and not (in_esc or in_quote or in_singlequote): - cmd.append(_arg) - elif _arg: - raise ApplicationExecException('Exec value contains an unbalanced number of quote characters.') - - return cmd - - def execute(self, dryrun=False, verbose=False): - """ - Execute application - @return Return subprocess.Popen object - """ - _exec = True - _try = self.TryExec - if _try and not (os.path.isabs(_try) and os.path.isfile(_try)) and not which(_try): - _exec = False - - if _exec: - path = self.Path - cmd = self._build_cmd(self.Exec) - if not cmd: - raise ApplicationExecException('Failed to build command string.') - if dryrun or verbose: - if verbose: - print('Autostart file: %s' % self.filename) - if path: - print('Changing directory to: ' + path) - print('Executing command: ' + ' '.join(cmd)) - if dryrun: - return None - if path: - return subprocess.Popen(cmd, cwd=path) - return subprocess.Popen(cmd) - - -class AutostartFile(Application): - """ - Implements autostart files - """ - - def __init__(self, filename): - """ - @param filename Absolute path to a Desktop Entry File - """ - super(AutostartFile, self).__init__(filename) - - -class EmptyAutostartFile(Application): - """ - Workaround for empty autostart files that don't contain the necessary data - """ - - def __init__(self, filename): - """ - @param filename Absolute path to a Desktop Entry File - """ - try: - super(EmptyAutostartFile, self).__init__(filename) - except DesktopEntryTypeException: - # ignore the missing type information - pass - - -# local methods -def which(filename): - path = os.environ.get('PATH', None) - if path: - for _p in path.split(os.pathsep): - _f = os.path.join(_p, filename) - if os.path.isfile(_f): - return _f - - -def get_autostart_directories(): - """ - Generate the list of autostart directories - """ - autostart_directories = [] # autostart directories, ordered by preference - - if args.searchdir: - autostart_directories.append(args.searchdir[0]) - else: - # generate list of autostart directories - if os.environ.get('XDG_CONFIG_HOME', None): - autostart_directories.append(os.path.join(os.environ.get('XDG_CONFIG_HOME'), 'autostart')) - else: - autostart_directories.append(os.path.join(os.environ['HOME'], '.config', 'autostart')) - - if os.environ.get('XDG_CONFIG_DIRS', None): - for d in os.environ['XDG_CONFIG_DIRS'].split(os.pathsep): - if not d: - continue - autostart_dir = os.path.join(d, 'autostart') - if autostart_dir not in autostart_directories: - autostart_directories.append(autostart_dir) - else: - autostart_directories.append(os.path.sep + os.path.join('etc', 'xdg', 'autostart')) - - return autostart_directories - - -def get_autostart_files(args, verbose=False): - """ - Generate a list of autostart files according to autostart-spec 0.5 - - TODO: do filetype recognition according to spec - """ - environment = args.environment[0].lower() if args.environment else '' - - autostart_files = [] # autostart files, excluding files marked as hidden - non_autostart_files = [] - - for d in get_autostart_directories(): - for _f in glob.glob1(d, '*.desktop'): - _f = os.path.join(d, _f) - af = None - - if os.path.isfile(_f) or os.path.islink(_f): - try: - af = AutostartFile(_f) - except DesktopEntryTypeException as ex: - af = EmptyAutostartFile(_f) - if af not in autostart_files and not af in non_autostart_files: - non_autostart_files.append(af) - continue - except ValueError as ex: - if verbose: - print(ex, file=sys.stderr) - continue - except IOError as ex: - if verbose: - print(ex, file=sys.stderr) - continue - else: - if verbose: - print('Ignoring unknown file: %s' % _f, file=sys.stderr) - continue - - if verbose: - if af.NotShowIn: - print('Not show in environments %s: %s' % (', '.join(af.NotShowIn), af.filename), file=sys.stderr) - if af.OnlyShowIn: - print('Only show in environments %s: %s' % (', '.join(af.OnlyShowIn), af.filename), file=sys.stderr) - if af in autostart_files or af in non_autostart_files: - if verbose: - print('Ignoring file, overridden by other autostart file: %s' % af.filename, file=sys.stderr) - continue - elif af.Hidden: - if verbose: - print('Ignoring file, hidden attribute is set: %s' % af.filename, file=sys.stderr) - non_autostart_files.append(af) - continue - elif environment: - if environment in [x.lower() for x in af.NotShowIn]: - if verbose: - print('Ignoring file, it must not start in specific environments (%s): %s' % (', '.join(af.NotShowIn), af.filename), file=sys.stderr) - non_autostart_files.append(af) - continue - elif af.OnlyShowIn and environment not in [x.lower() for x in af.OnlyShowIn]: - if verbose: - print('Ignoring file, it must only start in specific environments (%s): %s' % (', '.join(af.OnlyShowIn), af.filename), file=sys.stderr) - non_autostart_files.append(af) - continue - autostart_files.append(af) - if verbose: - for i in non_autostart_files: - print('Ignoring empty file: %s' % i.filename, file=sys.stderr) - return sorted(autostart_files) - - -def _test(args): - """ - run tests - """ - import doctest - doctest.testmod() - - -def _autostart(args): - """ - perform autostart - """ - if args.dryrun and args.verbose: - print('Dry run, nothing is executed.', file=sys.stderr) - - exit_value = 0 - for app in get_autostart_files(args, verbose=args.verbose): - try: - app.execute(dryrun=args.dryrun, verbose=args.verbose) - except Exception as ex: - exit_value = 1 - print("Execution faild: %s%s%s" % (app.filename, os.linesep, ex), file=sys.stderr) - - -def _run(args): - """ - execute specified DesktopEntry files - """ - if args.dryrun and args.verbose: - print('Dry run, nothing is executed.', file=sys.stderr) - - exit_value = 0 - if not args.files: - print("Nothing to execute, no DesktopEntry files specified!", file=sys.stderr) - parser.print_help() - exit_value = 1 - else: - for f in args.files: - try: - app = Application(f) - app.execute(dryrun=args.dryrun, verbose=args.verbose) - except ValueError as ex: - print(ex, file=sys.stderr) - except IOError as ex: - print(ex, file=sys.stderr) - except Exception as ex: - exit_value = 1 - print("Execution faild: %s%s%s" % (f, os.linesep, ex), file=sys.stderr) - return exit_value - - -def _create(args): - """ - create a new DesktopEntry file from the given argument - """ - target = args.create[0] - if args.verbose: - print('Creating DesktopEntry for file %s.' % target) - - de = DesktopEntry.fromfile(target) - if args.verbose: - print('Type: %s' % de.Type) - - # determine output file - output = '.'.join((os.path.basename(target), 'directory' if - de.Type == 'Directory' else 'desktop')) - if args.targetdir: - output = os.path.join(args.targetdir[0], output) - elif len(args.create) > 1: - output = args.create[1] - - if args.verbose: - print('Output: %s' % output) - targetfile = sys.stdout if output == '-' else open(output, 'w') - de.write(targetfile) - - if args.targetdir and len(args.create) > 1: - args.create = args.create[1:] - return _create(args) - return 0 - - -# start execution -if __name__ == '__main__': - from argparse import ArgumentParser - parser = ArgumentParser(usage='%(prog)s [options] [DesktopEntryFile [DesktopEntryFile ...]]', - description='dex, DesktopEntry Execution, is a program to generate and execute DesktopEntry files of the type Application', epilog='Example usage: list autostart programs: dex -ad') - parser.add_argument("--test", action="store_true", dest="test", help="perform a self-test") - parser.add_argument("-v", "--verbose", action="store_true", dest="verbose", help="verbose output") - parser.add_argument("-V", "--version", action="store_true", dest="version", help="display version information") - parser.add_argument('files', nargs='*', help="DesktopEntry files") - - run = parser.add_argument_group('run') - run.add_argument("-a", "--autostart", action="store_true", dest="autostart", help="autostart programs") - run.add_argument("-d", "--dry-run", action="store_true", dest="dryrun", help="dry run, don't execute any command") - run.add_argument("-e", "--environment", nargs=1, dest="environment", help="specify the Desktop Environment an autostart should be performed for; works only in combination with --autostart") - run.add_argument("-s", "--search-directory", nargs=1, dest="searchdir", help="specify the directory to search for desktop files in, overriding the default search list") - - create = parser.add_argument_group('create') - create.add_argument("-c", "--create", nargs='+', dest="create", help="create a DesktopEntry file for the given program. If a second argument is provided it's taken as output filename or written to stdout (filname: -). By default a new file with the postfix .desktop is created") - create.add_argument("-t", "--target-directory", nargs=1, dest="targetdir", help="create files in target directory") - - parser.set_defaults(func=_run, dryrun=False, test=False, autostart=False, verbose=False) - - args = parser.parse_args() - if args.autostart: - args.func = _autostart - elif args.create: - args.func = _create - elif args.test: - args.func = _test - - # display version information - if args.version: - print("dex %s" % __version__) - else: - sys.exit(args.func(args)) |