Release 0.42.1
[zeroinstall/zeroinstall-rsl.git] / 0alias
blobb081024ee0396ceae11a413babff1b645cd3c92c
1 #!/usr/bin/env python
3 import locale
4 from logging import warn
5 try:
6 locale.setlocale(locale.LC_ALL, '')
7 except locale.Error:
8 warn('Error setting locale (eg. Invalid locale)')
10 import os, sys
11 from optparse import OptionParser
13 from zeroinstall.injector import reader, model
14 from zeroinstall import support, alias, helpers
15 from zeroinstall.support import basedir
17 def export(name, value):
18 """Try to guess the command to set an environment variable."""
19 shell = os.environ.get('SHELL', '?')
20 if 'csh' in shell:
21 return "setenv %s %s" % (name, value)
22 return "export %s=%s" % (name, value)
24 def find_path(paths):
25 """Find the first writable path in : separated list."""
26 for path in paths:
27 if os.path.realpath(path).startswith(basedir.xdg_cache_home):
28 pass # print "Skipping cache", first_path
29 elif not os.access(path, os.W_OK):
30 pass # print "No access", first_path
31 else:
32 break
33 else:
34 return None
36 return path
38 # Do this here so we can include it in the help message.
39 # But, don't abort if there isn't one because we might
40 # be doing something else (e.g. --manpage)
41 first_path = find_path(os.environ['PATH'].split(':'))
43 parser = OptionParser(usage="usage: %%prog [options] alias [interface [command]]\n\n"
44 "Creates a script in the first usable directory in $PATH\n"
45 "(%s) to run 'interface' unless overridden by --dir.\n"
46 "If no interface is given, edits the policy for an existing alias.\n"
47 "For interfaces providing more than one command, the desired command\n"
48 "may also be given." % first_path)
49 parser.add_option("-m", "--manpage", help="show the manual page for an existing alias", action='store_true')
50 parser.add_option("-r", "--resolve", help="show the URI for an alias", action='store_true')
51 parser.add_option("-V", "--version", help="display version information", action='store_true')
52 parser.add_option("-d", "--dir", help="install in DIR", dest="user_path", metavar="DIR")
53 parser.disable_interspersed_args()
55 (options, args) = parser.parse_args()
57 if options.version:
58 import zeroinstall
59 print "0alias (zero-install) " + zeroinstall.version
60 print "Copyright (C) 2009 Thomas Leonard"
61 print "This program comes with ABSOLUTELY NO WARRANTY,"
62 print "to the extent permitted by law."
63 print "You may redistribute copies of this program"
64 print "under the terms of the GNU Lesser General Public License."
65 print "For more information about these matters, see the file named COPYING."
66 sys.exit(0)
68 if options.manpage:
69 if len(args) != 1:
70 os.execlp('man', 'man', *args)
71 sys.exit(1)
73 if len(args) < 1 or len(args) > 3:
74 parser.print_help()
75 sys.exit(1)
76 alias_prog, interface_uri, main = (list(args) + [None, None])[:3]
78 if options.resolve or options.manpage:
79 if interface_uri is not None:
80 parser.print_help()
81 sys.exit(1)
83 if options.user_path:
84 first_path = options.user_path
85 if not os.path.isdir(first_path):
86 print >>sys.stderr, "Directory '%s' does not exist." % first_path
87 sys.exit(1)
89 if interface_uri is None:
90 try:
91 if not os.path.isabs(alias_prog):
92 full_path = support.find_in_path(alias_prog)
93 if not full_path:
94 raise alias.NotAnAliasScript("Not found in $PATH: " + alias_prog)
95 else:
96 full_path = alias_prog
98 interface_uri, main = alias.parse_script(full_path)
99 except alias.NotAnAliasScript, ex:
100 if options.manpage:
101 os.execlp('man', 'man', *args)
102 print >>sys.stderr, str(ex)
103 sys.exit(1)
105 interface_uri = model.canonical_iface_uri(interface_uri)
107 if options.resolve:
108 print interface_uri
109 sys.exit(0)
111 if options.manpage:
112 sels = helpers.ensure_cached(interface_uri)
113 if not sels:
114 # Cancelled by user
115 sys.exit(1)
117 selected_impl = sels.selections[interface_uri]
118 if selected_impl.id.startswith('/'):
119 impl_path = selected_impl.id
120 else:
121 from zeroinstall.injector.iface_cache import iface_cache
122 impl_path = iface_cache.stores.lookup(selected_impl.id)
124 if main is None:
125 main = selected_impl.main
126 if main is None:
127 print >>sys.stderr, "No main program for interface '%s'" % interface_uri
128 sys.exit(1)
130 prog_name = os.path.basename(main)
131 alias_name = os.path.basename(args[0])
133 assert impl_path
135 # TODO: the feed should say where the man-pages are, but for now we'll accept
136 # a directory called man in some common locations...
137 for mandir in ['man', 'share/man', 'usr/man', 'usr/share/man']:
138 manpath = os.path.join(impl_path, mandir)
139 if os.path.isdir(manpath):
140 # Note: unlike "man -M", this also copes with LANG settings...
141 os.environ['MANPATH'] = manpath
142 os.execlp('man', 'man', prog_name)
143 sys.exit(1)
145 # No man directory given or found, so try searching for man files
147 manpages = []
148 for root, dirs, files in os.walk(impl_path):
149 for f in files:
150 if f.endswith('.gz'):
151 manpage_file = f[:-3]
152 else:
153 manpage_file = f
154 if manpage_file.endswith('.1') or \
155 manpage_file.endswith('.6') or \
156 manpage_file.endswith('.8'):
157 manpage_prog = manpage_file[:-2]
158 if manpage_prog == prog_name or manpage_prog == alias_name:
159 os.execlp('man', 'man', os.path.join(root, f))
160 sys.exit(1)
161 else:
162 manpages.append((root, f))
164 print "No matching manpage was found for '%s' (%s)" % (alias_name, interface_uri)
165 if manpages:
166 print "These non-matching man-pages were found, however:"
167 for root, file in manpages:
168 print os.path.join(root, file)
169 sys.exit(1)
171 if first_path is None:
172 print >>sys.stderr, ("No writable non-cache directory in $PATH, which currently contains:\n\n%s\n\n"
173 "To create a directory for your scripts, use these commands:\n"
174 "$ mkdir ~/bin\n"
175 "$ %s\n"
176 "or specify a writable path with --path"
177 % ('\n'.join(os.environ['PATH'].split(':')), export('PATH', '$HOME/bin:$PATH')))
178 sys.exit(1)
180 if len(args) == 1:
181 os.execlp('0launch', '0launch', '-gd', '--', interface_uri)
182 sys.exit(1)
184 try:
185 interface = model.Interface(interface_uri)
186 if not reader.update_from_cache(interface):
187 print >>sys.stderr, "Interface '%s' not currently in cache. Fetching..." % interface_uri
188 if os.spawnlp(os.P_WAIT, '0launch', '0launch', '-d', interface_uri):
189 raise model.SafeException("0launch failed")
190 if not reader.update_from_cache(interface):
191 raise model.SafeException("Interface still not in cache. Aborting.")
193 script = os.path.join(first_path, alias_prog)
194 if os.path.exists(script):
195 raise model.SafeException("File '%s' already exists. Delete it first." % script)
196 sys.exit(1)
197 except model.SafeException, ex:
198 print >>sys.stderr, ex
199 sys.exit(1)
201 wrapper = file(script, 'w')
202 alias.write_script(wrapper, interface_uri, main)
204 # Make new script executable
205 os.chmod(script, 0111 | os.fstat(wrapper.fileno()).st_mode)
206 wrapper.close()
208 print "Created script '%s'." % script
209 print "To edit policy: 0alias %s" % alias_prog
210 print "(note: some shells require you to type 'rehash' now)"