Release 0.31
[zeroinstall.git] / 0alias
bloba1caf116430fbb4a92f52c0924817401c8b51f4c
1 #!/usr/bin/env python
2 import os, sys
3 from optparse import OptionParser
5 from zeroinstall.injector import reader, model, basedir
6 from zeroinstall import support, alias, helpers
8 def export(name, value):
9 """Try to guess the command to set an environment variable."""
10 shell = os.environ.get('SHELL', '?')
11 if 'csh' in shell:
12 return "setenv %s %s" % (name, value)
13 return "export %s=%s" % (name, value)
15 def find_path(paths):
16 """Find the first writable path in : separated list."""
17 for path in paths:
18 if os.path.realpath(path).startswith(basedir.xdg_cache_home):
19 pass # print "Skipping cache", first_path
20 elif not os.access(path, os.W_OK):
21 pass # print "No access", first_path
22 else:
23 break
24 else:
25 print >>sys.stderr, ("No writable non-cache directory in $PATH, which currently contains:\n\n%s\n\n"
26 "To create a directory for your scripts, use these commands:\n"
27 "$ mkdir ~/bin\n"
28 "$ %s\n"
29 "or specify a writable path with --path"
30 % ('\n'.join(os.environ['PATH'].split(':')), export('PATH', '$HOME/bin:$PATH')))
31 sys.exit(1)
33 return path
35 first_path = find_path(os.environ['PATH'].split(':'))
37 parser = OptionParser(usage="usage: %%prog [options] alias [interface [command]]\n\n"
38 "Creates a script in the first usable directory in $PATH\n"
39 "(%s) to run 'interface' unless overridden by --dir.\n"
40 "If no interface is given, edits the policy for an existing alias.\n"
41 "For interfaces providing more than one command, the desired command\n"
42 "may also be given." % first_path)
43 parser.add_option("-m", "--manpage", help="show the manual page for an existing alias", action='store_true')
44 parser.add_option("-r", "--resolve", help="show the URI for an alias", action='store_true')
45 parser.add_option("-V", "--version", help="display version information", action='store_true')
46 parser.add_option("-d", "--dir", help="install in DIR", dest="user_path", metavar="DIR")
47 parser.disable_interspersed_args()
49 (options, args) = parser.parse_args()
51 if options.version:
52 import zeroinstall
53 print "0alias (zero-install) " + zeroinstall.version
54 print "Copyright (C) 2007 Thomas Leonard"
55 print "This program comes with ABSOLUTELY NO WARRANTY,"
56 print "to the extent permitted by law."
57 print "You may redistribute copies of this program"
58 print "under the terms of the GNU Lesser General Public License."
59 print "For more information about these matters, see the file named COPYING."
60 sys.exit(0)
62 if options.manpage:
63 if len(args) != 1:
64 os.execlp('man', 'man', *args)
65 sys.exit(1)
67 if len(args) < 1 or len(args) > 3:
68 parser.print_help()
69 sys.exit(1)
70 alias_prog, interface_uri, main = (list(args) + [None, None])[:3]
72 if options.resolve or options.manpage:
73 if interface_uri is not None:
74 parser.print_help()
75 sys.exit(1)
77 if options.user_path:
78 first_path = options.user_path
79 if not os.path.isdir(first_path):
80 print >>sys.stderr, "Directory '%s' does not exist." % first_path
81 sys.exit(1)
83 if interface_uri is None:
84 try:
85 if not os.path.isabs(alias_prog):
86 full_path = support.find_in_path(alias_prog)
87 if not full_path:
88 raise alias.NotAnAliasScript("Not found in $PATH: " + alias_prog)
89 else:
90 full_path = alias_prog
92 interface_uri, main = alias.parse_script(full_path)
93 except alias.NotAnAliasScript, ex:
94 if options.manpage:
95 os.execlp('man', 'man', *args)
96 print >>sys.stderr, str(ex)
97 sys.exit(1)
99 interface_uri = model.canonical_iface_uri(interface_uri)
101 if options.resolve:
102 print interface_uri
103 sys.exit(0)
105 if options.manpage:
106 sels = helpers.ensure_cached(interface_uri)
107 if not sels:
108 # Cancelled by user
109 sys.exit(1)
111 selected_impl = sels.selections[interface_uri]
112 if selected_impl.id.startswith('/'):
113 impl_path = selected_impl.id
114 else:
115 from zeroinstall.injector.iface_cache import iface_cache
116 impl_path = iface_cache.stores.lookup(selected_impl.id)
118 if main is None:
119 main = selected_impl.main
120 if main is None:
121 print >>sys.stderr, "No main program for interface '%s'" % interface_uri
122 sys.exit(1)
124 # TODO: the feed should say where the man-page is, but for now we'll just search
125 # the whole implementation for one
127 prog_name = os.path.basename(main)
128 alias_name = os.path.basename(args[0])
130 assert impl_path
131 manpages = []
132 for root, dirs, files in os.walk(impl_path):
133 for f in files:
134 if f.endswith('.gz'):
135 manpage_file = f[:-3]
136 else:
137 manpage_file = f
138 if manpage_file.endswith('.1') or \
139 manpage_file.endswith('.6') or \
140 manpage_file.endswith('.8'):
141 manpage_prog = manpage_file[:-2]
142 if manpage_prog == prog_name or manpage_prog == alias_name:
143 os.execlp('man', 'man', os.path.join(root, f))
144 sys.exit(1)
145 else:
146 manpages.append((root, f))
148 print "No matching manpage was found for '%s' (%s)" % (alias_name, interface_uri)
149 if manpages:
150 print "These non-matching man-pages were found, however:"
151 for root, file in manpages:
152 print os.path.join(root, file)
153 sys.exit(1)
155 if len(args) == 1:
156 os.execlp('0launch', '0launch', '-gd', '--', interface_uri)
157 sys.exit(1)
159 try:
160 interface = model.Interface(interface_uri)
161 if not reader.update_from_cache(interface):
162 print >>sys.stderr, "Interface '%s' not currently in cache. Fetching..." % interface_uri
163 if os.spawnlp(os.P_WAIT, '0launch', '0launch', '-d', interface_uri):
164 raise model.SafeException("0launch failed")
165 if not reader.update_from_cache(interface):
166 raise model.SafeException("Interface still not in cache. Aborting.")
168 script = os.path.join(first_path, alias_prog)
169 if os.path.exists(script):
170 raise model.SafeException("File '%s' already exists. Delete it first." % script)
171 sys.exit(1)
172 except model.SafeException, ex:
173 print >>sys.stderr, ex
174 sys.exit(1)
176 wrapper = file(script, 'w')
177 alias.write_script(wrapper, interface_uri, main)
179 # Make new script executable
180 os.chmod(script, 0111 | os.fstat(wrapper.fileno()).st_mode)
181 wrapper.close()
183 print "Created script '%s'." % script
184 print "To edit policy: 0alias %s" % alias_prog
185 print "(note: some shells require you to type 'rehash' now)"