aboutsummaryrefslogtreecommitdiffstats
path: root/dex
diff options
context:
space:
mode:
authorJohannes Löthberg <johannes@kyriasis.com>2014-10-12 14:35:37 +0200
committerJohannes Löthberg <johannes@kyriasis.com>2014-10-12 14:35:37 +0200
commitb3daa2e23394780f7ab191fc3d2927774e47e54e (patch)
treecd0dc250c78b3c436430ee24bfa69e0d5bc746b4 /dex
parente298c9a6de1f93e1dc20e56535a9f9d6f4c9f0a7 (diff)
downloadbin-b3daa2e23394780f7ab191fc3d2927774e47e54e.tar.xz
add dex script with -s switch
Diffstat (limited to 'dex')
-rwxr-xr-xdex781
1 files changed, 781 insertions, 0 deletions
diff --git a/dex b/dex
new file mode 100755
index 0000000..651a2aa
--- /dev/null
+++ b/dex
@@ -0,0 +1,781 @@
+#!/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))