Check for empty dpkg status file
[zeroinstall/solver.git] / 0alias
blobaabe5c2cc75e639b1a4a65ebb3c9d541492a9f23
1 #!/usr/bin/env python
2 # Copyright (C) 2010, Thomas Leonard
3 # See the README file for details, or visit http://0install.net.
5 from __future__ import print_function
7 import locale
8 import logging
9 from logging import warn
10 try:
11 locale.setlocale(locale.LC_ALL, '')
12 except locale.Error:
13 warn('Error setting locale (eg. Invalid locale)')
15 import os, sys
17 ## PATH ##
19 from optparse import OptionParser
21 from zeroinstall.injector import reader, model
22 from zeroinstall import support, alias, helpers, _
23 from zeroinstall.support import basedir
25 def export(name, value):
26 """Try to guess the command to set an environment variable."""
27 shell = os.environ.get('SHELL', '?')
28 if 'csh' in shell:
29 return "setenv %s %s" % (name, value)
30 return "export %s=%s" % (name, value)
32 def find_path(paths):
33 """Find the first writable path in : separated list."""
34 for path in paths:
35 if os.path.realpath(path).startswith(basedir.xdg_cache_home):
36 pass # print "Skipping cache", first_path
37 elif not os.access(path, os.W_OK):
38 pass # print "No access", first_path
39 else:
40 break
41 else:
42 return None
44 return path
46 # Do this here so we can include it in the help message.
47 # But, don't abort if there isn't one because we might
48 # be doing something else (e.g. --manpage)
49 first_path = find_path(os.environ['PATH'].split(':'))
50 in_path = first_path is not None
51 if not in_path:
52 first_path = os.path.expanduser('~/bin/')
54 parser = OptionParser(usage="usage: %%prog [options] alias [interface [main]]\n\n"
55 "Creates a script to run 'interface' (will be created in\n"
56 "%s unless overridden by --dir).\n\n"
57 "If no interface is given, edits the policy for an existing alias.\n"
58 "For interfaces providing more than one executable, the desired\n"
59 "'main' binary or command may also be given." % first_path)
60 parser.add_option("-c", "--command", help="the command the alias will run (default 'run')", metavar='COMMNAD')
61 parser.add_option("-m", "--manpage", help="show the manual page for an existing alias", action='store_true')
62 parser.add_option("-r", "--resolve", help="show the URI for an alias", action='store_true')
63 parser.add_option("-v", "--verbose", help="more verbose output", action='count')
64 parser.add_option("-V", "--version", help="display version information", action='store_true')
65 parser.add_option("-d", "--dir", help="install in DIR", dest="user_path", metavar="DIR")
67 (options, args) = parser.parse_args()
69 if options.verbose:
70 logger = logging.getLogger()
71 if options.verbose == 1:
72 logger.setLevel(logging.INFO)
73 else:
74 logger.setLevel(logging.DEBUG)
75 hdlr = logging.StreamHandler()
76 fmt = logging.Formatter("%(levelname)s:%(message)s")
77 hdlr.setFormatter(fmt)
78 logger.addHandler(hdlr)
80 if options.version:
81 import zeroinstall
82 print("0alias (zero-install) " + zeroinstall.version)
83 print("Copyright (C) 2010 Thomas Leonard")
84 print("This program comes with ABSOLUTELY NO WARRANTY,")
85 print("to the extent permitted by law.")
86 print("You may redistribute copies of this program")
87 print("under the terms of the GNU Lesser General Public License.")
88 print("For more information about these matters, see the file named COPYING.")
89 sys.exit(0)
91 if options.manpage:
92 if len(args) != 1:
93 os.execlp('man', 'man', *args)
94 sys.exit(1)
96 if len(args) < 1 or len(args) > 3:
97 parser.print_help()
98 sys.exit(1)
99 alias_prog, interface_uri, main = (list(args) + [None, None])[:3]
100 command = options.command
102 if options.resolve or options.manpage:
103 if interface_uri is not None:
104 parser.print_help()
105 sys.exit(1)
107 if options.user_path:
108 first_path = options.user_path
110 if interface_uri is None:
111 if options.command:
112 print("Can't use --command when editing an existing alias", file=sys.stderr)
113 sys.exit(1)
114 try:
115 if not os.path.isabs(alias_prog):
116 full_path = support.find_in_path(alias_prog)
117 if not full_path:
118 raise alias.NotAnAliasScript("Not found in $PATH: " + alias_prog)
119 else:
120 full_path = alias_prog
122 alias_info = alias.parse_script(full_path)
123 interface_uri = alias_info.uri
124 main = alias_info.main
125 command = alias_info.command
126 except (alias.NotAnAliasScript, IOError) as ex:
127 # (we get IOError if e.g. the script isn't readable)
128 if options.manpage:
129 logging.debug("not a 0alias script '%s': %s", alias_prog, ex)
130 os.execlp('man', 'man', *args)
131 print(str(ex), file=sys.stderr)
132 sys.exit(1)
134 interface_uri = model.canonical_iface_uri(interface_uri)
136 if options.resolve:
137 print(interface_uri)
138 sys.exit(0)
140 if options.manpage:
141 sels = helpers.ensure_cached(interface_uri, command = command or 'run')
142 if not sels:
143 # Cancelled by user
144 sys.exit(1)
146 if sels.commands:
147 selected_command = sels.commands[0]
148 else:
149 print("No <command> in selections!", file=sys.stderr)
150 selected_impl = sels.selections[interface_uri]
152 from zeroinstall.injector.iface_cache import iface_cache
153 impl_path = selected_impl.local_path or iface_cache.stores.lookup_any(selected_impl.digests)
155 if main is None:
156 main = selected_command.path
157 if main is None:
158 print("No main program for interface '%s'" % interface_uri, file=sys.stderr)
159 sys.exit(1)
161 prog_name = os.path.basename(main)
162 alias_name = os.path.basename(args[0])
164 assert impl_path
166 # TODO: the feed should say where the man-pages are, but for now we'll accept
167 # a directory called man in some common locations...
168 for mandir in ['man', 'share/man', 'usr/man', 'usr/share/man']:
169 manpath = os.path.join(impl_path, mandir)
170 if os.path.isdir(manpath):
171 # Note: unlike "man -M", this also copes with LANG settings...
172 os.environ['MANPATH'] = manpath
173 os.execlp('man', 'man', prog_name)
174 sys.exit(1)
176 # No man directory given or found, so try searching for man files
178 manpages = []
179 for root, dirs, files in os.walk(impl_path):
180 for f in files:
181 if f.endswith('.gz'):
182 manpage_file = f[:-3]
183 else:
184 manpage_file = f
185 if manpage_file.endswith('.1') or \
186 manpage_file.endswith('.6') or \
187 manpage_file.endswith('.8'):
188 manpage_prog = manpage_file[:-2]
189 if manpage_prog == prog_name or manpage_prog == alias_name:
190 os.execlp('man', 'man', os.path.join(root, f))
191 sys.exit(1)
192 else:
193 manpages.append((root, f))
195 print("No matching manpage was found for '%s' (%s)" % (alias_name, interface_uri))
196 if manpages:
197 print("These non-matching man-pages were found, however:")
198 for root, file in manpages:
199 print(os.path.join(root, file))
200 sys.exit(1)
202 if not os.path.isdir(first_path):
203 print("(creating directory %s)" % first_path)
204 os.makedirs(first_path)
206 if len(args) == 1:
207 os.execlp('0launch', '0launch', '-gd', '--', interface_uri)
208 sys.exit(1)
210 try:
211 if not options.user_path:
212 if alias_prog == '0launch':
213 raise model.SafeException(_('Refusing to create an alias named "0launch" (to avoid an infinite loop)'))
215 interface = model.Interface(interface_uri)
216 if not reader.update_from_cache(interface):
217 print("Interface '%s' not currently in cache. Fetching..." % interface_uri, file=sys.stderr)
218 if os.spawnlp(os.P_WAIT, '0launch', '0launch', '-d', interface_uri):
219 raise model.SafeException("0launch failed")
220 if not reader.update_from_cache(interface):
221 raise model.SafeException("Interface still not in cache. Aborting.")
223 script = os.path.join(first_path, alias_prog)
224 if os.path.exists(script):
225 raise model.SafeException("File '%s' already exists. Delete it first." % script)
226 sys.exit(1)
227 except model.SafeException as ex:
228 print(ex, file=sys.stderr)
229 sys.exit(1)
231 wrapper = open(script, 'w')
232 alias.write_script(wrapper, interface_uri, main, command = command)
234 # Make new script executable
235 os.chmod(script, 0o111 | os.fstat(wrapper.fileno()).st_mode)
236 wrapper.close()
238 #print "Created script '%s'." % script
239 #print "To edit policy: 0alias %s" % alias_prog
240 if options.user_path:
241 pass # Assume user knows what they're doing
242 elif not in_path:
243 print('Warning: %s is not in $PATH. Add it with:\n%s' % (first_path, export('PATH', first_path + ':$PATH')), file=sys.stderr)
244 else:
245 shell = os.environ.get('SHELL', '?')
246 if not (shell.endswith('/zsh') or shell.endswith('/bash')):
247 print("(note: some shells require you to type 'rehash' now)")