1 """Code for the B{0store} command-line interface."""
3 # Copyright (C) 2009, Thomas Leonard
4 # See the README file for details, or visit http://0install.net.
6 from zeroinstall
import _
8 from zeroinstall
.zerostore
.manifest
import verify
, get_algorithm
, copy_tree_with_verify
9 from zeroinstall
import zerostore
, SafeException
, support
17 stores
= zerostore
.Stores()
19 class UsageError(SafeException
): pass
21 def do_manifest(args
):
22 """manifest DIRECTORY [ALGORITHM]"""
23 if len(args
) < 1 or len(args
) > 2: raise UsageError(_("Wrong number of arguments"))
25 alg
= get_algorithm(args
[1])
27 # If no algorithm was given, guess from the directory name
28 name
= os
.path
.basename(args
[0])
30 alg
= get_algorithm(name
.split('=', 1)[0])
32 alg
= get_algorithm('sha1new')
33 digest
= alg
.new_digest()
34 for line
in alg
.generate_manifest(args
[0]):
36 digest
.update(line
+ '\n')
37 print alg
.getID(digest
)
42 if len(args
) != 1: raise UsageError(_("Wrong number of arguments"))
44 print stores
.lookup(args
[0])
46 except zerostore
.BadDigest
as ex
:
47 print >>sys
.stderr
, ex
48 except zerostore
.NotStored
as ex
:
49 print >>sys
.stderr
, ex
53 """add DIGEST (DIRECTORY | (ARCHIVE [EXTRACT]))"""
54 from zeroinstall
.zerostore
import unpack
55 if len(args
) < 2: raise UsageError(_("Missing arguments"))
57 if os
.path
.isdir(args
[1]):
58 if len(args
) > 2: raise UsageError(_("Too many arguments"))
59 stores
.add_dir_to_cache(digest
, args
[1])
60 elif os
.path
.isfile(args
[1]):
61 if len(args
) > 3: raise UsageError(_("Too many arguments"))
67 type = unpack
.type_from_url(args
[1])
69 raise SafeException(_("Unknown extension in '%s' - can't guess MIME type") % args
[1])
70 unpack
.check_type_ok(type)
72 stores
.add_archive_to_cache(digest
, file(args
[1]), args
[1], extract
, type = type)
77 if ex
.errno
!= 2: # No such file or directory
78 raise UsageError(str(ex
)) # E.g. permission denied
79 raise UsageError(_("No such file or directory '%s'") % args
[1])
81 def do_optimise(args
):
82 """optimise [ CACHE ]"""
86 cache_dir
= stores
.stores
[0].dir
88 cache_dir
= os
.path
.realpath(cache_dir
)
91 info
= os
.stat(cache_dir
)
92 if not stat
.S_ISDIR(info
.st_mode
):
93 raise UsageError(_("Not a directory: '%s'") % cache_dir
)
95 impl_name
= os
.path
.basename(cache_dir
)
96 if impl_name
!= 'implementations':
97 raise UsageError(_("Cache directory should be named 'implementations', not\n"
98 "'%(name)s' (in '%(cache_dir)s')") % {'name': impl_name
, 'cache_dir': cache_dir
})
100 print _("Optimising"), cache_dir
102 from . import optimise
103 uniq_size
, dup_size
, already_linked
, man_size
= optimise
.optimise(cache_dir
)
104 print _("Original size : %(size)s (excluding the %(manifest_size)s of manifests)") % {'size': support
.pretty_size(uniq_size
+ dup_size
), 'manifest_size': support
.pretty_size(man_size
)}
105 print _("Already saved : %s") % support
.pretty_size(already_linked
)
107 print _("No duplicates found; no changes made.")
109 print _("Optimised size : %s") % support
.pretty_size(uniq_size
)
110 perc
= (100 * float(dup_size
)) / (uniq_size
+ dup_size
)
111 print _("Space freed up : %(size)s (%(percentage).2f%%)") % {'size': support
.pretty_size(dup_size
), 'percentage': perc
}
112 print _("Optimisation complete.")
115 """verify (DIGEST | (DIRECTORY [DIGEST])"""
117 required_digest
= args
[1]
120 root
= get_stored(args
[0])
121 required_digest
= None # Get from name
123 raise UsageError(_("Missing DIGEST or DIRECTORY"))
125 print _("Verifying"), root
127 verify(root
, required_digest
)
129 except zerostore
.BadDigest
as ex
:
137 """audit [DIRECTORY]"""
139 audit_stores
= stores
.stores
141 audit_stores
= [zerostore
.Store(x
) for x
in args
]
145 for a
in audit_stores
:
146 if os
.path
.isdir(a
.dir):
147 items
= sorted(os
.listdir(a
.dir))
148 audit_ls
.append((a
.dir, items
))
151 raise SafeException(_("No such directory '%s'") % a
.dir)
156 for root
, impls
in audit_ls
:
157 print _("Scanning %s") % root
158 for required_digest
in impls
:
160 path
= os
.path
.join(root
, required_digest
)
161 if '=' not in required_digest
:
162 print _("Skipping non-implementation directory %s") % path
165 msg
= _("[%(done)d / %(total)d] Verifying %(digest)s") % {'done': i
, 'total': total
, 'digest': required_digest
}
168 verify(path
, required_digest
)
169 print "\r" + (" " * len(msg
)) + "\r",
171 except zerostore
.BadDigest
as ex
:
173 failures
.append(path
)
179 print '\n' + _("List of corrupted or modified implementations:")
183 print _("Checked %d items") % i
184 print _("Successfully verified implementations: %d") % verified
185 print _("Corrupted or modified implementations: %d") % len(failures
)
191 if args
: raise UsageError(_("List takes no arguments"))
192 print _("User store (writable) : %s") % stores
.stores
[0].dir
193 for s
in stores
.stores
[1:]:
194 print _("System store : %s") % s
.dir
195 if len(stores
.stores
) < 2:
196 print _("No system stores.")
198 def get_stored(dir_or_digest
):
199 if os
.path
.isdir(dir_or_digest
):
203 return stores
.lookup(dir_or_digest
)
204 except zerostore
.NotStored
as ex
:
205 print >>sys
.stderr
, ex
209 """copy SOURCE [ TARGET ]"""
211 source
, target
= args
214 target
= stores
.stores
[0].dir
216 raise UsageError(_("Wrong number of arguments."))
218 if not os
.path
.isdir(source
):
219 raise UsageError(_("Source directory '%s' not found") % source
)
220 if not os
.path
.isdir(target
):
221 raise UsageError(_("Target directory '%s' not found") % target
)
222 manifest_path
= os
.path
.join(source
, '.manifest')
223 if not os
.path
.isfile(manifest_path
):
224 raise UsageError(_("Source manifest '%s' not found") % manifest_path
)
225 required_digest
= os
.path
.basename(source
)
226 manifest_data
= file(manifest_path
, 'rb').read()
228 copy_tree_with_verify(source
, target
, manifest_data
, required_digest
)
233 raise UsageError(_("manage command takes no arguments"))
238 from zeroinstall
.gtkui
import cache
239 from zeroinstall
.injector
.iface_cache
import iface_cache
240 cache_explorer
= cache
.CacheExplorer(iface_cache
)
241 cache_explorer
.window
.connect('destroy', gtk
.main_quit
)
242 cache_explorer
.show()
245 commands
= [do_add
, do_audit
, do_copy
, do_find
, do_list
, do_manifest
, do_optimise
, do_verify
, do_manage
]