Don't check that $PATH is writable unless we're actually trying to add something.
[zeroinstall.git] / 0alias
blob232266274b9a7cc8077cc98d2c15ca9c1f2ef2d7
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 return None
27 return path
29 # Do this here so we can include it in the help message.
30 # But, don't abort if there isn't one because we might
31 # be doing something else (e.g. --manpage)
32 first_path = find_path(os.environ['PATH'].split(':'))
34 parser = OptionParser(usage="usage: %%prog [options] alias [interface [command]]\n\n"
35 "Creates a script in the first usable directory in $PATH\n"
36 "(%s) to run 'interface' unless overridden by --dir.\n"
37 "If no interface is given, edits the policy for an existing alias.\n"
38 "For interfaces providing more than one command, the desired command\n"
39 "may also be given." % first_path)
40 parser.add_option("-m", "--manpage", help="show the manual page for an existing alias", action='store_true')
41 parser.add_option("-r", "--resolve", help="show the URI for an alias", action='store_true')
42 parser.add_option("-V", "--version", help="display version information", action='store_true')
43 parser.add_option("-d", "--dir", help="install in DIR", dest="user_path", metavar="DIR")
44 parser.disable_interspersed_args()
46 (options, args) = parser.parse_args()
48 if options.version:
49 import zeroinstall
50 print "0alias (zero-install) " + zeroinstall.version
51 print "Copyright (C) 2007 Thomas Leonard"
52 print "This program comes with ABSOLUTELY NO WARRANTY,"
53 print "to the extent permitted by law."
54 print "You may redistribute copies of this program"
55 print "under the terms of the GNU Lesser General Public License."
56 print "For more information about these matters, see the file named COPYING."
57 sys.exit(0)
59 if options.manpage:
60 if len(args) != 1:
61 os.execlp('man', 'man', *args)
62 sys.exit(1)
64 if len(args) < 1 or len(args) > 3:
65 parser.print_help()
66 sys.exit(1)
67 alias_prog, interface_uri, main = (list(args) + [None, None])[:3]
69 if options.resolve or options.manpage:
70 if interface_uri is not None:
71 parser.print_help()
72 sys.exit(1)
74 if options.user_path:
75 first_path = options.user_path
76 if not os.path.isdir(first_path):
77 print >>sys.stderr, "Directory '%s' does not exist." % first_path
78 sys.exit(1)
80 if interface_uri is None:
81 try:
82 if not os.path.isabs(alias_prog):
83 full_path = support.find_in_path(alias_prog)
84 if not full_path:
85 raise alias.NotAnAliasScript("Not found in $PATH: " + alias_prog)
86 else:
87 full_path = alias_prog
89 interface_uri, main = alias.parse_script(full_path)
90 except alias.NotAnAliasScript, ex:
91 if options.manpage:
92 os.execlp('man', 'man', *args)
93 print >>sys.stderr, str(ex)
94 sys.exit(1)
96 interface_uri = model.canonical_iface_uri(interface_uri)
98 if options.resolve:
99 print interface_uri
100 sys.exit(0)
102 if options.manpage:
103 sels = helpers.ensure_cached(interface_uri)
104 if not sels:
105 # Cancelled by user
106 sys.exit(1)
108 selected_impl = sels.selections[interface_uri]
109 if selected_impl.id.startswith('/'):
110 impl_path = selected_impl.id
111 else:
112 from zeroinstall.injector.iface_cache import iface_cache
113 impl_path = iface_cache.stores.lookup(selected_impl.id)
115 if main is None:
116 main = selected_impl.main
117 if main is None:
118 print >>sys.stderr, "No main program for interface '%s'" % interface_uri
119 sys.exit(1)
121 # TODO: the feed should say where the man-page is, but for now we'll just search
122 # the whole implementation for one
124 prog_name = os.path.basename(main)
125 alias_name = os.path.basename(args[0])
127 assert impl_path
128 manpages = []
129 for root, dirs, files in os.walk(impl_path):
130 for f in files:
131 if f.endswith('.gz'):
132 manpage_file = f[:-3]
133 else:
134 manpage_file = f
135 if manpage_file.endswith('.1') or \
136 manpage_file.endswith('.6') or \
137 manpage_file.endswith('.8'):
138 manpage_prog = manpage_file[:-2]
139 if manpage_prog == prog_name or manpage_prog == alias_name:
140 os.execlp('man', 'man', os.path.join(root, f))
141 sys.exit(1)
142 else:
143 manpages.append((root, f))
145 print "No matching manpage was found for '%s' (%s)" % (alias_name, interface_uri)
146 if manpages:
147 print "These non-matching man-pages were found, however:"
148 for root, file in manpages:
149 print os.path.join(root, file)
150 sys.exit(1)
152 if first_path is None:
153 print >>sys.stderr, ("No writable non-cache directory in $PATH, which currently contains:\n\n%s\n\n"
154 "To create a directory for your scripts, use these commands:\n"
155 "$ mkdir ~/bin\n"
156 "$ %s\n"
157 "or specify a writable path with --path"
158 % ('\n'.join(os.environ['PATH'].split(':')), export('PATH', '$HOME/bin:$PATH')))
159 sys.exit(1)
161 if len(args) == 1:
162 os.execlp('0launch', '0launch', '-gd', '--', interface_uri)
163 sys.exit(1)
165 try:
166 interface = model.Interface(interface_uri)
167 if not reader.update_from_cache(interface):
168 print >>sys.stderr, "Interface '%s' not currently in cache. Fetching..." % interface_uri
169 if os.spawnlp(os.P_WAIT, '0launch', '0launch', '-d', interface_uri):
170 raise model.SafeException("0launch failed")
171 if not reader.update_from_cache(interface):
172 raise model.SafeException("Interface still not in cache. Aborting.")
174 script = os.path.join(first_path, alias_prog)
175 if os.path.exists(script):
176 raise model.SafeException("File '%s' already exists. Delete it first." % script)
177 sys.exit(1)
178 except model.SafeException, ex:
179 print >>sys.stderr, ex
180 sys.exit(1)
182 wrapper = file(script, 'w')
183 alias.write_script(wrapper, interface_uri, main)
185 # Make new script executable
186 os.chmod(script, 0111 | os.fstat(wrapper.fileno()).st_mode)
187 wrapper.close()
189 print "Created script '%s'." % script
190 print "To edit policy: 0alias %s" % alias_prog
191 print "(note: some shells require you to type 'rehash' now)"