2 # Copyright (C) 2010, Thomas Leonard
3 # See the README file for details, or visit http://0install.net.
5 from __future__
import print_function
9 from logging
import warn
11 locale
.setlocale(locale
.LC_ALL
, '')
13 warn('Error setting locale (eg. Invalid locale)')
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', '?')
29 return "setenv %s %s" % (name
, value
)
30 return "export %s=%s" % (name
, value
)
33 """Find the first writable path in the list,
34 skipping /bin, /sbin and everything under /usr except /usr/local/bin"""
36 if path
.startswith('/usr/') and not path
.startswith('/usr/local/bin'):
37 # (/usr/local/bin is OK if we're running as root)
39 elif path
.startswith('/bin') or path
.startswith('/sbin'):
41 elif os
.path
.realpath(path
).startswith(basedir
.xdg_cache_home
):
42 pass # print "Skipping cache", first_path
43 elif not os
.access(path
, os
.W_OK
):
44 pass # print "No access", first_path
52 # Do this here so we can include it in the help message.
53 # But, don't abort if there isn't one because we might
54 # be doing something else (e.g. --manpage)
55 first_path
= find_path(os
.environ
['PATH'].split(':'))
56 in_path
= first_path
is not None
58 first_path
= os
.path
.expanduser('~/bin/')
60 parser
= OptionParser(usage
="usage: %%prog [options] alias [interface [main]]\n\n"
61 "Creates a script to run 'interface' (will be created in\n"
62 "%s unless overridden by --dir).\n\n"
63 "If no interface is given, edits the policy for an existing alias.\n"
64 "For interfaces providing more than one executable, the desired\n"
65 "'main' binary or command may also be given." % first_path
)
66 parser
.add_option("-c", "--command", help="the command the alias will run (default 'run')", metavar
='COMMNAD')
67 parser
.add_option("-m", "--manpage", help="show the manual page for an existing alias", action
='store_true')
68 parser
.add_option("-r", "--resolve", help="show the URI for an alias", action
='store_true')
69 parser
.add_option("-v", "--verbose", help="more verbose output", action
='count')
70 parser
.add_option("-V", "--version", help="display version information", action
='store_true')
71 parser
.add_option("-d", "--dir", help="install in DIR", dest
="user_path", metavar
="DIR")
73 (options
, args
) = parser
.parse_args()
76 logger
= logging
.getLogger()
77 if options
.verbose
== 1:
78 logger
.setLevel(logging
.INFO
)
80 logger
.setLevel(logging
.DEBUG
)
81 hdlr
= logging
.StreamHandler()
82 fmt
= logging
.Formatter("%(levelname)s:%(message)s")
83 hdlr
.setFormatter(fmt
)
84 logger
.addHandler(hdlr
)
88 print("0alias (zero-install) " + zeroinstall
.version
)
89 print("Copyright (C) 2010 Thomas Leonard")
90 print("This program comes with ABSOLUTELY NO WARRANTY,")
91 print("to the extent permitted by law.")
92 print("You may redistribute copies of this program")
93 print("under the terms of the GNU Lesser General Public License.")
94 print("For more information about these matters, see the file named COPYING.")
99 os
.execlp('man', 'man', *args
)
102 if len(args
) < 1 or len(args
) > 3:
105 alias_prog
, interface_uri
, main
= (list(args
) + [None, None])[:3]
106 command
= options
.command
108 if options
.resolve
or options
.manpage
:
109 if interface_uri
is not None:
113 if options
.user_path
:
114 first_path
= options
.user_path
116 if interface_uri
is None:
118 print("Can't use --command when editing an existing alias", file=sys
.stderr
)
121 if not os
.path
.isabs(alias_prog
):
122 full_path
= support
.find_in_path(alias_prog
)
124 raise alias
.NotAnAliasScript("Not found in $PATH: " + alias_prog
)
126 full_path
= alias_prog
128 alias_info
= alias
.parse_script(full_path
)
129 interface_uri
= alias_info
.uri
130 main
= alias_info
.main
131 command
= alias_info
.command
132 except (alias
.NotAnAliasScript
, IOError) as ex
:
133 # (we get IOError if e.g. the script isn't readable)
135 logging
.debug("not a 0alias script '%s': %s", alias_prog
, ex
)
136 os
.execlp('man', 'man', *args
)
137 print(str(ex
), file=sys
.stderr
)
140 interface_uri
= model
.canonical_iface_uri(interface_uri
)
147 sels
= helpers
.ensure_cached(interface_uri
, command
= command
or 'run')
153 selected_command
= sels
.commands
[0]
155 print("No <command> in selections!", file=sys
.stderr
)
156 selected_impl
= sels
.selections
[interface_uri
]
158 from zeroinstall
.injector
.iface_cache
import iface_cache
159 impl_path
= selected_impl
.local_path
or iface_cache
.stores
.lookup_any(selected_impl
.digests
)
162 main
= selected_command
.path
164 print("No main program for interface '%s'" % interface_uri
, file=sys
.stderr
)
167 prog_name
= os
.path
.basename(main
)
168 alias_name
= os
.path
.basename(args
[0])
172 # TODO: the feed should say where the man-pages are, but for now we'll accept
173 # a directory called man in some common locations...
174 for mandir
in ['man', 'share/man', 'usr/man', 'usr/share/man']:
175 manpath
= os
.path
.join(impl_path
, mandir
)
176 if os
.path
.isdir(manpath
):
177 # Note: unlike "man -M", this also copes with LANG settings...
178 os
.environ
['MANPATH'] = manpath
179 os
.execlp('man', 'man', prog_name
)
182 # No man directory given or found, so try searching for man files
185 for root
, dirs
, files
in os
.walk(impl_path
):
187 if f
.endswith('.gz'):
188 manpage_file
= f
[:-3]
191 if manpage_file
.endswith('.1') or \
192 manpage_file
.endswith('.6') or \
193 manpage_file
.endswith('.8'):
194 manpage_prog
= manpage_file
[:-2]
195 if manpage_prog
== prog_name
or manpage_prog
== alias_name
:
196 os
.execlp('man', 'man', os
.path
.join(root
, f
))
199 manpages
.append((root
, f
))
201 print("No matching manpage was found for '%s' (%s)" % (alias_name
, interface_uri
))
203 print("These non-matching man-pages were found, however:")
204 for root
, file in manpages
:
205 print(os
.path
.join(root
, file))
208 if not os
.path
.isdir(first_path
):
209 print("(creating directory %s)" % first_path
)
210 os
.makedirs(first_path
)
213 os
.execlp('0launch', '0launch', '-gd', '--', interface_uri
)
217 if not options
.user_path
:
218 if alias_prog
== '0launch':
219 raise model
.SafeException(_('Refusing to create an alias named "0launch" (to avoid an infinite loop)'))
221 interface
= model
.Interface(interface_uri
)
222 if not reader
.update_from_cache(interface
):
223 print("Interface '%s' not currently in cache. Fetching..." % interface_uri
, file=sys
.stderr
)
224 if os
.spawnlp(os
.P_WAIT
, '0launch', '0launch', '-d', interface_uri
):
225 raise model
.SafeException("0launch failed")
226 if not reader
.update_from_cache(interface
):
227 raise model
.SafeException("Interface still not in cache. Aborting.")
229 script
= os
.path
.join(first_path
, alias_prog
)
230 if os
.path
.exists(script
):
231 raise model
.SafeException("File '%s' already exists. Delete it first." % script
)
233 except model
.SafeException
as ex
:
234 print(ex
, file=sys
.stderr
)
237 wrapper
= open(script
, 'w')
238 alias
.write_script(wrapper
, interface_uri
, main
, command
= command
)
240 # Make new script executable
241 os
.chmod(script
, 0o111 | os
.fstat(wrapper
.fileno()).st_mode
)
244 #print "Created script '%s'." % script
245 #print "To edit policy: 0alias %s" % alias_prog
246 if options
.user_path
:
247 pass # Assume user knows what they're doing
249 print('Warning: %s is not in $PATH. Add it with:\n%s' % (first_path
, export('PATH', first_path
+ ':$PATH')), file=sys
.stderr
)
251 shell
= os
.environ
.get('SHELL', '?')
252 if not (shell
.endswith('/zsh') or shell
.endswith('/bash')):
253 print("(note: some shells require you to type 'rehash' now)")