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
.injector
.config
import load_config
23 from zeroinstall
import support
, alias
, helpers
, apps
, _
24 from zeroinstall
.support
import basedir
26 config
= load_config()
28 def export(name
, value
):
29 """Try to guess the command to set an environment variable."""
30 shell
= os
.environ
.get('SHELL', '?')
32 return "setenv %s %s" % (name
, value
)
33 return "export %s=%s" % (name
, value
)
35 # Do this here so we can include it in the help message.
36 # But, don't abort if there isn't one because we might
37 # be doing something else (e.g. --manpage)
38 first_path
= apps
.find_bin_dir()
39 in_path
= first_path
is not None
41 first_path
= os
.path
.expanduser('~/bin/')
43 parser
= OptionParser(usage
="usage: %%prog [options] alias [interface [main]]\n\n"
44 "Creates a script to run 'interface' (will be created in\n"
45 "%s unless overridden by --dir).\n\n"
46 "If no interface is given, edits the policy for an existing alias.\n"
47 "For interfaces providing more than one executable, the desired\n"
48 "'main' binary or command may also be given." % first_path
)
49 parser
.add_option("-c", "--command", help="the command the alias will run (default 'run')", metavar
='COMMNAD')
50 parser
.add_option("-m", "--manpage", help="show the manual page for an existing alias", action
='store_true')
51 parser
.add_option("-r", "--resolve", help="show the URI for an alias", action
='store_true')
52 parser
.add_option("-v", "--verbose", help="more verbose output", action
='count')
53 parser
.add_option("-V", "--version", help="display version information", action
='store_true')
54 parser
.add_option("-d", "--dir", help="install in DIR", dest
="user_path", metavar
="DIR")
56 (options
, args
) = parser
.parse_args()
59 logger
= logging
.getLogger()
60 if options
.verbose
== 1:
61 logger
.setLevel(logging
.INFO
)
63 logger
.setLevel(logging
.DEBUG
)
64 hdlr
= logging
.StreamHandler()
65 fmt
= logging
.Formatter("%(levelname)s:%(message)s")
66 hdlr
.setFormatter(fmt
)
67 logger
.addHandler(hdlr
)
71 print("0alias (zero-install) " + zeroinstall
.version
)
72 print("Copyright (C) 2010 Thomas Leonard")
73 print("This program comes with ABSOLUTELY NO WARRANTY,")
74 print("to the extent permitted by law.")
75 print("You may redistribute copies of this program")
76 print("under the terms of the GNU Lesser General Public License.")
77 print("For more information about these matters, see the file named COPYING.")
82 os
.execlp('man', 'man', *args
)
85 if len(args
) < 1 or len(args
) > 3:
88 alias_prog
, interface_uri
, main
= (list(args
) + [None, None])[:3]
89 command
= options
.command
91 if options
.resolve
or options
.manpage
:
92 if interface_uri
is not None:
97 first_path
= options
.user_path
99 if interface_uri
is None:
101 print("Can't use --command when editing an existing alias", file=sys
.stderr
)
104 if not os
.path
.isabs(alias_prog
):
105 full_path
= support
.find_in_path(alias_prog
)
107 raise alias
.NotAnAliasScript("Not found in $PATH: " + alias_prog
)
109 full_path
= alias_prog
111 alias_info
= alias
.parse_script(full_path
)
112 interface_uri
= alias_info
.uri
113 main
= alias_info
.main
114 command
= alias_info
.command
115 except (alias
.NotAnAliasScript
, IOError) as ex
:
116 # (we get IOError if e.g. the script isn't readable)
118 logging
.debug("not a 0alias script '%s': %s", alias_prog
, ex
)
119 os
.execlp('man', 'man', *args
)
120 print(str(ex
), file=sys
.stderr
)
123 interface_uri
= model
.canonical_iface_uri(interface_uri
)
130 sels
= helpers
.ensure_cached(interface_uri
, command
= command
or 'run')
136 selected_command
= sels
.commands
[0]
138 print("No <command> in selections!", file=sys
.stderr
)
139 selected_impl
= sels
.selections
[interface_uri
]
141 impl_path
= selected_impl
.local_path
or config
.iface_cache
.stores
.lookup_any(selected_impl
.digests
)
144 main
= selected_command
.path
146 print("No main program for interface '%s'" % interface_uri
, file=sys
.stderr
)
149 prog_name
= os
.path
.basename(main
)
150 alias_name
= os
.path
.basename(args
[0])
154 # TODO: the feed should say where the man-pages are, but for now we'll accept
155 # a directory called man in some common locations...
156 for mandir
in ['man', 'share/man', 'usr/man', 'usr/share/man']:
157 manpath
= os
.path
.join(impl_path
, mandir
)
158 if os
.path
.isdir(manpath
):
159 # Note: unlike "man -M", this also copes with LANG settings...
160 os
.environ
['MANPATH'] = manpath
161 os
.execlp('man', 'man', prog_name
)
164 # No man directory given or found, so try searching for man files
167 for root
, dirs
, files
in os
.walk(impl_path
):
169 if f
.endswith('.gz'):
170 manpage_file
= f
[:-3]
173 if manpage_file
.endswith('.1') or \
174 manpage_file
.endswith('.6') or \
175 manpage_file
.endswith('.8'):
176 manpage_prog
= manpage_file
[:-2]
177 if manpage_prog
== prog_name
or manpage_prog
== alias_name
:
178 os
.execlp('man', 'man', os
.path
.join(root
, f
))
181 manpages
.append((root
, f
))
183 print("No matching manpage was found for '%s' (%s)" % (alias_name
, interface_uri
))
185 print("These non-matching man-pages were found, however:")
186 for root
, file in manpages
:
187 print(os
.path
.join(root
, file))
190 if not os
.path
.isdir(first_path
):
191 print("(creating directory %s)" % first_path
)
192 os
.makedirs(first_path
)
195 os
.execlp('0launch', '0launch', '-gd', '--', interface_uri
)
199 if not options
.user_path
:
200 if alias_prog
== '0launch':
201 raise model
.SafeException(_('Refusing to create an alias named "0launch" (to avoid an infinite loop)'))
203 interface
= model
.Interface(interface_uri
)
204 if not reader
.update_from_cache(interface
):
205 print("Interface '%s' not currently in cache. Fetching..." % interface_uri
, file=sys
.stderr
)
206 if os
.spawnlp(os
.P_WAIT
, '0launch', '0launch', '-d', interface_uri
):
207 raise model
.SafeException("0launch failed")
208 if not reader
.update_from_cache(interface
):
209 raise model
.SafeException("Interface still not in cache. Aborting.")
211 script
= os
.path
.join(first_path
, alias_prog
)
212 if os
.path
.exists(script
):
213 raise model
.SafeException("File '%s' already exists. Delete it first." % script
)
215 except model
.SafeException
as ex
:
216 print(ex
, file=sys
.stderr
)
219 seen
= set([interface_uri
])
221 feed
= config
.iface_cache
.get_feed(interface_uri
)
222 replacement_uri
= feed
.get_replaced_by()
223 if replacement_uri
is None:
225 print(_("(interface {old} has been replaced by {new}; using that instead)").format(old
= interface_uri
, new
= replacement_uri
))
226 assert replacement_uri
not in seen
, "loop detected!"
227 seen
.add(replacement_uri
)
228 interface_uri
= replacement_uri
230 wrapper
= open(script
, 'w')
231 alias
.write_script(wrapper
, interface_uri
, main
, command
= command
)
233 # Make new script executable
234 os
.chmod(script
, 0o111 | os
.fstat(wrapper
.fileno()).st_mode
)
237 #print "Created script '%s'." % script
238 #print "To edit policy: 0alias %s" % alias_prog
239 if options
.user_path
:
240 pass # Assume user knows what they're doing
242 print('Warning: %s is not in $PATH. Add it with:\n%s' % (first_path
, export('PATH', first_path
+ ':$PATH')), file=sys
.stderr
)
244 shell
= os
.environ
.get('SHELL', '?')
245 if not (shell
.endswith('/zsh') or shell
.endswith('/bash')):
246 print("(note: some shells require you to type 'rehash' now)")