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