[PATCH] Bundlebuilder to use MANIFEST, fix_manifest function, some
chema (none)
chema at chema-laptop.
Fri Jun 6 12:01:21 EDT 2008
functions
pushed upstream to bundle.py and activitybundle.py
---
src/sugar/activity/bundlebuilder.py | 69 ++++++++++--------
src/sugar/bundle/activitybundle.py | 140
++++++++++++++++++++++++++++++++---
src/sugar/bundle/bundle.py | 23 ++++++
3 files changed, 189 insertions(+), 43 deletions(-)
diff --git a/src/sugar/activity/bundlebuilder.py
b/src/sugar/activity/bundlebuilder.py
index 1063f72..72c246c 100644
--- a/src/sugar/activity/bundlebuilder.py
+++ b/src/sugar/activity/bundlebuilder.py
@@ -19,28 +19,16 @@ import os
import zipfile
import tarfile
import shutil
-import subprocess
+from subprocess import Popen, PIPE
import re
import gettext
from optparse import OptionParser
+import warnings
+import subprocess
from sugar import env
-from sugar.bundle.activitybundle import ActivityBundle
-
-def list_files(base_dir, ignore_dirs=None, ignore_files=None):
- result = []
+from sugar.bundle.activitybundle import ActivityBundle,MANIFEST,list_files
- for root, dirs, files in os.walk(base_dir):
- for f in files:
- if ignore_files and f not in ignore_files:
- rel_path = root[len(base_dir) + 1:]
- result.append(os.path.join(rel_path, f))
- if ignore_dirs and root == base_dir:
- for ignore in ignore_dirs:
- if ignore in dirs:
- dirs.remove(ignore)
-
- return result
class Config(object):
def __init__(self, bundle_name, source_dir=None):
@@ -53,6 +41,7 @@ class Config(object):
bundle = ActivityBundle(self.source_dir)
version = bundle.get_activity_version()
+ self.bundle = bundle
self.bundle_name = bundle_name
self.xo_name = '%s-%d.xo' % (self.bundle_name, version)
self.tarball_name = '%s-%d.tar.bz2' % (self.bundle_name, version)
@@ -103,9 +92,9 @@ class Builder(object):
f.close()
class Packager(object):
- def __init__(self, config):
+ def __init__(self, config, dist_dir = None):
self.config = config
- self.dist_dir = os.path.join(self.config.source_dir, 'dist')
+ self.dist_dir = dist_dir or os.path.join(self.config.source_dir,
'dist')
self.package_path = None
if not os.path.exists(self.dist_dir):
@@ -113,19 +102,28 @@ class Packager(object):
class BuildPackager(Packager):
- def __init__(self, config):
- Packager.__init__(self, config)
+ def __init__(self, config,dist_dir = None):
+ Packager.__init__(self, config,dist_dir )
self.build_dir = self.config.source_dir
def get_files(self):
- return list_files(self.build_dir,
- ignore_dirs=['po', 'dist', '.git'],
- ignore_files=['.gitignore'])
+ return self.config.bundle.get_files()
+
+ def fix_manifest(self):
+ allfiles = list_files(self.build_dir,
+ ignore_dirs=['dist', '.git'],
+ ignore_files=['.gitignore', 'MANIFEST', '*.pyc',
'*~', '*.bak'])
+ for file in allfiles:
+ if file not in self.config.bundle.manifest:
+ self.config.bundle.manifest.append(file)
+ manifestfile =
open(os.path.join(self.config.source_dir,MANIFEST),"wb")
+ for line in self.config.bundle.manifest:
+ manifestfile.write(line + "\n")
class XOPackager(BuildPackager):
- def __init__(self, config):
+ def __init__(self, config,dist_dir = None,dist_name = None):
BuildPackager.__init__(self, config)
- self.package_path = os.path.join(self.dist_dir,
self.config.xo_name)
+ self.package_path = os.path.join(self.dist_dir, dist_name or
self.config.xo_name)
def package(self):
bundle_zip = zipfile.ZipFile(self.package_path, 'w',
@@ -137,16 +135,25 @@ class XOPackager(BuildPackager):
bundle_zip.close()
-class SourcePackager(Packager):
- def __init__(self, config):
- Packager.__init__(self, config)
+class SourcePackager(BuildPackager):
+ def __init__(self, config,dist_dir = None):
+ BuildPackager.__init__(self, config,dist_dir)
self.package_path = os.path.join(self.dist_dir,
self.config.tarball_name)
def get_files(self):
- return list_files(self.config.source_dir,
- ignore_dirs=['locale', 'dist', '.git'],
- ignore_files=['.gitignore'])
+ git_ls =
Popen('git-ls-files',stdout=PIPE,cwd=self.config.source_dir)
+ if git_ls.wait():
+ #non-0 return code - failed
+ return Packager.get_files(self)
+ f = git_ls.stdout
+ files = []
+ for line in f.readlines():
+ filename = line.strip()
+ if not filename.startswith('.'):
+ files.append(filename)
+ f.close()
+ return files
def package(self):
tar = tarfile.open(self.package_path, "w")
diff --git a/src/sugar/bundle/activitybundle.py
b/src/sugar/bundle/activitybundle.py
index db30555..1ff7d39 100644
--- a/src/sugar/bundle/activitybundle.py
+++ b/src/sugar/bundle/activitybundle.py
@@ -21,15 +21,31 @@ from ConfigParser import ConfigParser
import locale
import os
import tempfile
+from fnmatch import fnmatch
from sugar.bundle.bundle import Bundle, MalformedBundleException, \
AlreadyInstalledException, RegistrationException, \
NotInstalledException
-from sugar import activity
-from sugar import env
-
import logging
+import warnings
+
+MANIFEST = "MANIFEST"
+
+def list_files(base_dir, ignore_dirs=None, ignore_files=None):
+ result = []
+
+ for root, dirs, files in os.walk(base_dir):
+ for f in files:
+ if not (ignore_files and [True for pat in ignore_files if
fnmatch(f,pat)]):
+ #result matches a pattern in ignore_files, ignore it
+ rel_path = root[len(base_dir) + 1:]
+ result.append(os.path.join(rel_path, f))
+ if ignore_dirs and root == base_dir:
+ for ignore in ignore_dirs:
+ if ignore in dirs:
+ dirs.remove(ignore)
+ return result
class ActivityBundle(Bundle):
"""A Sugar activity bundle
@@ -64,7 +80,62 @@ class ActivityBundle(Bundle):
linfo_file = self._get_linfo_file()
if linfo_file:
self._parse_linfo(linfo_file)
-
+
+ self.read_manifest()
+
+ def _raw_manifest(self):
+ try:
+ f = self._get_file(MANIFEST)
+ except IOError:
+ f = none
+ if not f:
+ warnings.warn(MalformedBundleException("Activity directory
lacks a MANIFEST file."))
+ return []
+ ret = map(lambda x:x.strip(),f.readlines()) #strip trailing \n and
other whitespace
+ f.close()
+ return ret
+
+ def read_manifest(self):
+ """read_manifest: sets self.manifest to list of lines in MANIFEST,
+ with invalid lines replaced by empty lines.
+
+ Since absolute order carries information on file history, it should
be preserved.
+ For instance, when renaming a file, you should leave the new name
on the same line
+ as the old one.
+ """
+ manifestlines = self._raw_manifest()
+ for num,line in enumerate(manifestlines):
+ if line:
+ if line in manifestlines[0:num]:
+ manifestlines[num] = ""
+ warnings.warn(MalformedBundleException("Bundle %s:
duplicate entry in MANIFEST: %s"
+ %(self._name,line)))
+ continue
+ if line == MANIFEST:
+ manifestlines[num] = ""
+ warnings.warn(MalformedBundleException("Bundle %s:
MANIFEST includes itself: %s"
+ %(self._name,line)))
+ if line.endswith("/"):
+ if not self._is_dir(line):
+ manifestlines[num] = ""
+ warnings.warn(MalformedBundleException("Bundle %s:
invalid dir entry in MANIFEST: %s"
+ %(self._name,line)))
+ else:
+ if not self._is_file(line):
+ manifestlines[num] = ""
+ warnings.warn(MalformedBundleException("Bundle %s:
invalid entry in MANIFEST: %s"
+ %(self._name,line)))
+ #remove trailing newlines - unlike internal newlines, they do not
help keep absolute position
+ while manifestlines[-1]=="":
+ manifestlines = manifestlines[:-1]
+ self.manifest = manifestlines
+
+ def get_files(self,manifest = None):
+ return [MANIFEST] + filter(lambda line: line and not
line.endswith("/"),manifest or self.manifest)
+
+ def get_dirs(self):
+ return filter(lambda line: line and
line.endswith("/"),self.manifest)
+
def _parse_info(self, info_file):
cp = ConfigParser()
cp.readfp(info_file)
@@ -116,6 +187,7 @@ class ActivityBundle(Bundle):
raise MalformedBundleException(
'Activity bundle %s has invalid version number %s' %
(self._path, version))
+
def _get_linfo_file(self):
lang = locale.getdefaultlocale()[0]
@@ -209,28 +281,53 @@ class ActivityBundle(Bundle):
return self._show_launcher
def is_installed(self):
+ from sugar import activity
+
if activity.get_registry().get_activity(self._bundle_id):
return True
else:
return False
def need_upgrade(self):
+ from sugar import activity
+
act = activity.get_registry().get_activity(self._bundle_id)
if act is None or act.version != self._activity_version:
return True
else:
return False
-
- def install(self):
- activities_path = env.get_user_activities_path()
- act = activity.get_registry().get_activity(self._bundle_id)
- if act is not None and act.path.startswith(activities_path):
- raise AlreadyInstalledException
-
- install_dir = env.get_user_activities_path()
+
+ def unpack(self,install_dir,strict_manifest=False):
self._unzip(install_dir)
install_path = os.path.join(install_dir, self._zip_root_dir)
+
+ #check installed files against the MANIFEST
+ manifestfiles = self.get_files(self._raw_manifest())
+ for file in list_files(install_path):
+ if file in manifestfiles:
+ manifestfiles.remove(file)
+ elif file != MANIFEST:
+ #warnings.warn(MalformedBundleException("Bundle %s: %s not
in MANIFEST"%
+ # (self._name,file)))
+ if strict_manifest:
+ os.remove(os.path.join(install_path,file))
+ if manifestfiles:
+ err = MalformedBundleException("Bundle %s: files in MANIFEST
not included: %s"%
+
(self._name,str(manifestfiles)))
+ if strict_manifest:
+ raise err
+ else:
+ warnings.warn(err)
+
+ #create empty directories
+ for dir in self.get_dirs():
+ dirpath = os.path.join(install_path,dir)
+ if os.path.isdir(dirpath):
+ warnings.warn(MalformedBundleException("Bunldle %s:
non-empty dir %s in MANIFEST"%
+
(self._name,dirpath)))
+ else:
+ os.makedirs(os.path.join(install_path,dir))
xdg_data_home = os.getenv('XDG_DATA_HOME',
os.path.expanduser('~/.local/share'))
@@ -267,11 +364,27 @@ class ActivityBundle(Bundle):
os.symlink(info_file,
os.path.join(installed_icons_dir,
os.path.basename(info_file)))
+ return install_path
+ def install(self):
+ from sugar import activity
+ from sugar import env
+
+ activities_path = env.get_user_activities_path()
+ act = activity.get_registry().get_activity(self._bundle_id)
+ if act is not None and act.path.startswith(activities_path):
+ raise AlreadyInstalledException
+
+ install_dir = env.get_user_activities_path()
+ install_path = self.unpack(install_dir)
+
if not activity.get_registry().add_bundle(install_path):
raise RegistrationException
def uninstall(self, force=False):
+ from sugar import activity
+ from sugar import env
+
if self._unpacked:
install_path = self._path
else:
@@ -316,6 +429,9 @@ class ActivityBundle(Bundle):
raise RegistrationException
def upgrade(self):
+ from sugar import activity
+ from sugar import env
+
act = activity.get_registry().get_activity(self._bundle_id)
if act is None:
logging.warning('Activity not installed')
diff --git a/src/sugar/bundle/bundle.py b/src/sugar/bundle/bundle.py
index 47d08b2..44e30b0 100644
--- a/src/sugar/bundle/bundle.py
+++ b/src/sugar/bundle/bundle.py
@@ -114,6 +114,29 @@ class Bundle:
zip_file.close()
return f
+
+ def _is_dir(self, filename):
+ if self._unpacked:
+ path = os.path.join(self._path, filename)
+ return os.path.isdir(path)
+ else:
+ return True #zip files contain all dirs you care about!
+
+ def _is_file(self, filename):
+ if self._unpacked:
+ path = os.path.join(self._path, filename)
+ return os.path.isfile(path)
+ else:
+ zip_file = zipfile.ZipFile(self._path)
+ path = os.path.join(self._zip_root_dir, filename)
+ try:
+ data = zip_file.getinfo(path)
+ return True
+ except KeyError:
+ return False
+ finally:
+ zip_file.close()
+
def get_path(self):
"""Get the bundle path."""
--
1.5.2.5
------=_Part_21853_28463409.1212768784608
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline
Note: this patch does not expose the fix_manifest function when running bun=
dlebuilder as a script. That would be a 2 or 3 line patch, seperately.<br><=
br>From 9ab619888a9d4ef40ca3e984d4485c5a22558105 Mon Sep 17 00:00:00 2001<b=
r>
From: chema <chema at chema-laptop.(none)><br>Date: Fri, 6 Jun 2008 10:0=
1:21 -0600<br>Subject: [PATCH] Bundlebuilder to use MANIFEST, fix_manifest =
function, some functions<br>pushed upstream to bundle.py and activitybundle=
.py<br>
---<br> src/sugar/activity/bundlebuilder.py | 69 +++++++++=
+--------<br> src/sugar/bundle/activitybundle.py | 140 +++=
+++++++++++++++++++++++++++++---<br> src/sugar/bundle/bundle.py &=
nbsp; | 23 ++++++<br>=
3 files changed, 189 insertions(+), 43 deletions(-)<br>
<br>diff --git a/src/sugar/activity/bundlebuilder.py b/src/sugar/activity/b=
undlebuilder.py<br>index 1063f72..72c246c 100644<br>--- a/src/sugar/activit=
y/bundlebuilder.py<br>+++ b/src/sugar/activity/bundlebuilder.py<br>@@ -19,2=
8 +19,16 @@ import os<br>
import zipfile<br> import tarfile<br> import shutil<br>-imp=
ort subprocess<br>+from subprocess import Popen, PIPE<br> import re<br=
> import gettext<br> from optparse import OptionParser<br>+import=
warnings<br>+import subprocess<br>
<br> from sugar import env<br>-from sugar.bundle.activitybundle =
import ActivityBundle<br>-<br>-def list_files(base_dir, ignore_dirs=3DNone,=
ignore_files=3DNone):<br>- result =3D []<br>+from sugar.=
bundle.activitybundle import ActivityBundle,MANIFEST,list_files<br>
<br>- for root, dirs, files in os.walk(base_dir):<b=
r>- for f in files:<br>- &nb=
sp; if ignore_files a=
nd f not in ignore_files:<br>- &nb=
sp; rel_path =3D root[len(base_di=
r) + 1:]<br>- &n=
bsp; result.append(os.path.join(rel_path, f))<br>
- if ignore_dirs and root =3D=3D =
base_dir:<br>- &=
nbsp; for ignore in ignore_dirs:<br>- &n=
bsp; if ignore in dirs:<br>=
- &n=
bsp; dirs.remove(ignore)<br>-<br>- =
; return result<br> <br> class Config(object):<br>
def __init__(self, bundle_name, source_dir=3DNone)=
:<br>@@ -53,6 +41,7 @@ class Config(object):<br> &nb=
sp; bundle =3D ActivityBundle(self.source_dir)<br> &=
nbsp; version =3D bundle.get_activity_v=
ersion()<br> <br>+ self.bund=
le =3D bundle<br>
self.bundle_name =3D bundl=
e_name<br> self.xo_name =3D=
'%s-%d.xo' % (self.bundle_name, version)<br> &nbs=
p; self.tarball_name =3D '%s-%d.tar.bz2' % =
(self.bundle_name, version)<br>@@ -103,9 +92,9 @@ class Builder(object):<br=
>
f.=
close()<br> <br> class Packager(object):<br>- d=
ef __init__(self, config):<br>+ def __init__(self, config=
, dist_dir =3D None):<br> s=
elf.config =3D config<br>- self.d=
ist_dir =3D os.path.join(self.config.source_dir, 'dist')<br>
+ self.dist_dir =3D dist_dir or o=
s.path.join(self.config.source_dir, 'dist')<br> &n=
bsp; self.package_path =3D None<br> <br> =
if not os.path.exists(self.dist_=
dir):<br>@@ -113,19 +102,28 @@ class Packager(object):<br>
<b=
r> <br> class BuildPackager(Packager):<br>- def=
__init__(self, config):<br>- Pac=
kager.__init__(self, config)<br>+ def __init__(self, conf=
ig,dist_dir =3D None):<br>+ Packa=
ger.__init__(self, config,dist_dir )<br>
self.build_dir =3D self.co=
nfig.source_dir<br> <br> def get_files(self):<=
br>- return list_files(self.build=
_dir,<br>-  =
; &n=
bsp; ignore_dirs=3D['po', 'dist', '.git'],<br=
>
- &n=
bsp;  =
; ignore_files=3D['.gitignore'])<br>+ =
return self.config.bundle.get_files()<br>+ <=
br>+ def fix_manifest(self):<br>+ =
allfiles =3D list_files(self.build_dir,<br>+ =
&nb=
sp; ignor=
e_dirs=3D['dist', '.git'],<br>
+ &n=
bsp;  =
; ignore_files=3D['.gitignore', 'MANIFEST', '*.pyc'=
, '*~', '*.bak'])<br>+ &=
nbsp; for file in allfiles:<br>+ &=
nbsp; if file not in self.config.bundle.manifest:<br>
+ &n=
bsp; self.config.bundle.manifest.append(file)<br>+ &=
nbsp; manifestfile =3D open(os.path.join(self.confi=
g.source_dir,MANIFEST),"wb")<br>+ &n=
bsp; for line in self.config.bundle.manifest:<br>+ &=
nbsp; manifestfile.write(line + &=
quot;\n")<br>
<br> class XOPackager(BuildPackager):<br>- def=
__init__(self, config):<br>+ def __init__(self, config,d=
ist_dir =3D None,dist_name =3D None):<br> &nbs=
p; BuildPackager.__init__(self, config)<br>- &=
nbsp; self.package_path =3D os.path.join(self.dist_dir, s=
elf.config.xo_name)<br>
+ self.package_path =3D os.path.j=
oin(self.dist_dir, dist_name or self.config.xo_name)<br> <br> &nb=
sp; def package(self):<br> &=
nbsp; bundle_zip =3D zipfile.ZipFile(self.package_path, 'w',<=
br>@@ -137,16 +135,25 @@ class XOPackager(BuildPackager):<br>
<br> bundle_zip.close=
()<br> <br>-class SourcePackager(Packager):<br>- def=
__init__(self, config):<br>- Pac=
kager.__init__(self, config)<br>+class SourcePackager(BuildPackager):<br>+&=
nbsp; def __init__(self, config,dist_dir =3D None):<br>
+ BuildPackager.__init__(self, co=
nfig,dist_dir)<br> self.pac=
kage_path =3D os.path.join(self.dist_dir,<br> =
&nb=
sp; =
self.con=
fig.tarball_name)<br> <br> def get_files(self)=
:<br>
- return list_files(self.config.s=
ource_dir,<br>- =
&nb=
sp; ignore_dirs=3D['locale', 'dist', '.git&=
#39;],<br>- &nbs=
p; &=
nbsp; ignore_files=3D['.gitignore'])<br>+ &n=
bsp; git_ls =3D Popen('git-ls-files',stdout=3DPIP=
E,cwd=3Dself.config.source_dir)<br>
+ if git_ls.wait():<br>+ &nb=
sp; #non-0 return cod=
e - failed<br>+ =
return Packager.get_files(self)<br>+ &n=
bsp; f =3D git_ls.stdout<br>+ &nbs=
p; files =3D []<br>+ for line in =
f.readlines():<br>+ &n=
bsp; filename =3D line.strip()<br>
+ if not =
filename.startswith('.'):<br>+ &=
nbsp; files.append(filename=
)<br>+ f.close()<br>+ =
return files<br> <br> =
def package(self):<br> &nbs=
p; tar =3D tarfile.open(self.package_path, "w")<br>
diff --git a/src/sugar/bundle/activitybundle.py b/src/sugar/bundle/activity=
bundle.py<br>index db30555..1ff7d39 100644<br>--- a/src/sugar/bundle/activi=
tybundle.py<br>+++ b/src/sugar/bundle/activitybundle.py<br>@@ -21,15 +21,31=
@@ from ConfigParser import ConfigParser<br>
import locale<br> import os<br> import tempfile<br>+from fn=
match import fnmatch<br> <br> from sugar.bundle.bundle import Bun=
dle, MalformedBundleException, \<br> AlreadyInstall=
edException, RegistrationException, \<br>
NotInstalledException<br> <br>-from sugar imp=
ort activity<br>-from sugar import env<br>-<br> import logging<br>+imp=
ort warnings<br>+<br>+MANIFEST =3D "MANIFEST"<br>+<br>+def list_f=
iles(base_dir, ignore_dirs=3DNone, ignore_files=3DNone):<br>
+ result =3D []<br>+ <br>+ &=
nbsp; for root, dirs, files in os.walk(base_dir):<br>+ &nb=
sp; for f in files:<br>+ &nb=
sp; if not (ignore_files and [True for pat in=
ignore_files if fnmatch(f,pat)]):<br>+ =
#result matches a pa=
ttern in ignore_files, ignore it<br>
+ &n=
bsp; rel_path =3D root[len(base_dir) + 1:]<br>+ &nbs=
p; =
result.append(os.path.join(rel_path, f))<br>+ =
if ignore_dirs and root =3D=3D base_dir:<br>+  =
; for ignore in ignore_dirs=
:<br>+ &nb=
sp; if ignore in dirs:<br>
+ &n=
bsp; dirs.remove(ignore)<br>+ &nbs=
p; return result<br> <br> class ActivityBundle(Bundle):<br>=
"""A Sugar activity bundle<br>@@ -6=
4,7 +80,62 @@ class ActivityBundle(Bundle):<br> &nbs=
p; linfo_file =3D self._get_linfo_file()<br>
if linfo_file:<br> &n=
bsp; self._pars=
e_linfo(linfo_file)<br>-<br>+ <br=
>+ self.read_manifest()<br>+<br>+=
def _raw_manifest(self):<br>+ &nb=
sp; try:<br>+ &n=
bsp; f =3D self._get_file(MANIFEST)<br>
+ except IOError:<br>+  =
; f =3D none<br>+&nbs=
p; if not f:<br>+ &nbs=
p; warnings.warn(MalformedBundleE=
xception("Activity directory lacks a MANIFEST file."))<br>+ =
return []<br>+=
ret =3D map(lambda x:x.strip(),f=
.readlines()) #strip trailing \n and other whitespace<br>
+ f.close()<br>+  =
; return ret<br>+ &nbs=
p; <br>+ def read_manifest(self):<br>+ &=
nbsp; """read_manifest: sets self.ma=
nifest to list of lines in MANIFEST, <br>+ &nb=
sp; with invalid lines replaced by empty lines.<br>
+ <br>+ &n=
bsp; Since absolute order carries information on file history, =
it should be preserved.<br>+ For =
instance, when renaming a file, you should leave the new name on the same l=
ine<br>+ as the old one.<br>
+ """<br>+ &n=
bsp; manifestlines =3D self._raw_manifest()<b=
r>+ for num,line in enumerate(man=
ifestlines):<br>+ &nbs=
p; if line:<br>+  =
; if line in manifestlines[0:num]:<br>+=
&nb=
sp; manifestlines[num] =3D ""=
<br>
+ &n=
bsp; warnings.warn(MalformedBundleExcep=
tion("Bundle %s: duplicate entry in MANIFEST: %s"<br>+  =
; &n=
bsp;  =
; &n=
bsp; %(self._name,lin=
e)))<br>+ =
continue<br>
+ &n=
bsp; if line =3D=3D MANIFEST:<br>+  =
; &n=
bsp; manifestlines[num] =3D ""<br>+ =
&nb=
sp; warnings.warn(MalformedBundleException("Bundle %s: MAN=
IFEST includes itself: %s"<br>+ &nb=
sp; =
&nb=
sp; =
%(self._name,line)))<br>
+ &n=
bsp; if line.endswith("/"):<br>+ &nb=
sp; =
if not self._is_dir(line):<br>+ &=
nbsp; &nbs=
p; manifestlines[num] =3D ""<=
br>+  =
; warning=
s.warn(MalformedBundleException("Bundle %s: invalid dir entry in MANIF=
EST: %s"<br>
+ &n=
bsp;  =
; &n=
bsp;  =
; %(self._name,line)))<br>+ =
else:<br>+&nbs=
p; &=
nbsp; if not self._is_file(line):<br>+ &=
nbsp; &nbs=
p; manifestlines[num]=
=3D ""<br>+  =
; &n=
bsp; warnings.warn(MalformedBundleException("Bundle %s: invalid =
entry in MANIFEST: %s"<br>
+ &n=
bsp;  =
; &n=
bsp;  =
; %(self._name,line)))<br>+ =
#remove trailing newlines - unlike internal newlines, they do =
not help keep absolute position<br>+ &nb=
sp; while manifestlines[-1]=3D=3D"":<br>
+ manifes=
tlines =3D manifestlines[:-1]<br>+  =
; self.manifest =3D manifestlines<br>+ <br>+ &=
nbsp; def get_files(self,manifest =3D None):<br>+ &n=
bsp; return [MANIFEST] + filter(lambda line: line and not line.=
endswith("/"),manifest or self.manifest)<br>
+ <br>+ def get_dirs(self):<br>+ &=
nbsp; return filter(lambda line: line and lin=
e.endswith("/"),self.manifest)<br>+ =
<br> def _parse_info(self, info_file):<br> &n=
bsp; cp =3D ConfigParser()<br> &nb=
sp; cp.readfp(info_file)<br>
@@ -116,6 +187,7 @@ class ActivityBundle(Bundle):<br> &nbs=
p; =
raise MalformedBundleException(<br> &nbs=
p; &=
nbsp; 'Activity bundle %s has invalid version number %s' %<br> =
; &n=
bsp; (self._path, version))<br>
+ <br> <br> def _get_linfo_f=
ile(self):<br> lang =3D loc=
ale.getdefaultlocale()[0]<br>@@ -209,28 +281,53 @@ class ActivityBundle(Bun=
dle):<br> return self._show=
_launcher<br> <br> def is_installed(self):<br>
+ from sugar import activity<br>+=
<br> &nbs=
p; if activity.get_registry().get_activity(self._bundle_i=
d):<br> &n=
bsp; return True<br> else:<=
br> =
return False<br> <br> def need_upgrade(self):=
<br>
+ from sugar import activity<br>+=
<br> &nbs=
p; act =3D activity.get_registry().get_activity(self._bun=
dle_id)<br> if act is None =
or act.version !=3D self._activity_version:<br> &nbs=
p; return True<br>
else:<br>  =
; return False<br>-<b=
r>- def install(self):<br>- =
activities_path =3D env.get_user_activities_path()<br>- &=
nbsp; act =3D activity.get_registry().get_act=
ivity(self._bundle_id)<br>- if ac=
t is not None and act.path.startswith(activities_path):<br>
- raise A=
lreadyInstalledException<br>-<br>-  =
; install_dir =3D env.get_user_activities_path()<br>+ <br=
>+ def unpack(self,install_dir,strict_manifest=3DFalse):<=
br> self._unzip(install_dir=
)<br> <br>
install_path =3D os.path.j=
oin(install_dir, self._zip_root_dir)<br>+ &nbs=
p; <br>+ #check installed f=
iles against the MANIFEST<br>+ ma=
nifestfiles =3D self.get_files(self._raw_manifest())<br>+ =
for file in list_files(install_path):<br>
+ if file=
in manifestfiles:<br>+ &nbs=
p; manifestfiles.remove(file)<br>+ =
; elif file !=
=3D MANIFEST:<br>+ &nb=
sp; #warnings.warn(MalformedBundleException(&=
quot;Bundle %s: %s not in MANIFEST"%<br>
+ &n=
bsp; # &nb=
sp; =
&nb=
sp; (self._name,file)))<br>+  =
; if strict_man=
ifest:<br>+ &nbs=
p; os.remove(os.path.join(i=
nstall_path,file))<br>+ if manife=
stfiles:<br>+ &n=
bsp; err =3D MalformedBundleException("Bundle %s: files in MANIFEST no=
t included: %s"%<br>
+ &n=
bsp;  =
; &n=
bsp;  =
; (self._name,str(manifestfiles)))<br>+ =
if strict_manifest:<=
br>+  =
; raise err<br>+  =
; else:<br>+ &nb=
sp; warnings.warn(err)<br>+=
<br>
+ #create empty directories<br>+&=
nbsp; for dir in self.get_dirs():<br>+&=
nbsp; dirpath =
=3D os.path.join(install_path,dir)<br>+ =
if os.path.isdir(dirpath):<br>+ &=
nbsp; &nbs=
p; warnings.warn(MalformedBundleException("Bunldle %s: non-empty dir %=
s in MANIFEST"%<br>
+ &n=
bsp;  =
; &n=
bsp;  =
; (self._name,dirpath)))<br>+ &nbs=
p; else:<br>+ &n=
bsp; os.m=
akedirs(os.path.join(install_path,dir))<br> <br> &nbs=
p; xdg_data_home =3D os.getenv('XDG_DATA_HOME&#=
39;,<br>
&nb=
sp; =
os.path.expanduser(&=
#39;~/.local/share'))<br>@@ -267,11 +364,27 @@ class ActivityBundle(Bun=
dle):<br> =
os.symlink(info_file=
,<br> &nbs=
p; &=
nbsp; os.path.join(installed_icons_dir,=
<br>
&nb=
sp; =
&nb=
sp; os.path.basename(info_file)))<br>+&=
nbsp; return install_path<br> <br>=
+ def install(self):<br>+ &n=
bsp; from sugar import activity<br>+ &nb=
sp; from sugar import env<br>+ &nb=
sp; <br>
+ activities_path =3D env.get_use=
r_activities_path()<br>+ act =3D =
activity.get_registry().get_activity(self._bundle_id)<br>+  =
; if act is not None and act.path.startswith(activi=
ties_path):<br>+  =
; raise AlreadyInstalledException<br>
+<br>+ install_dir =3D env.get_us=
er_activities_path()<br>+ install=
_path =3D self.unpack(install_dir)<br>+ =
<br> if not activity=
.get_registry().add_bundle(install_path):<br> =
raise RegistrationException<br>
<br> def uninstall(self, force=3DFalse):<br>+=
from sugar import activity<br>+&=
nbsp; from sugar import env<br>+ &=
nbsp; <br>  =
; if self._unpacked:<br> &nb=
sp; install_path =3D self._path<br> &nbs=
p; else:<br>
@@ -316,6 +429,9 @@ class ActivityBundle(Bundle):<br> &nbs=
p; raise RegistrationExcept=
ion<br> <br> def upgrade(self):<br>+ &nbs=
p; from sugar import activity<br>+  =
; from sugar import env<br>+  =
; <br> &nb=
sp; act =3D activity.get_registry().get_activity(self._bundle_id)<br>
if act is None:<br> &=
nbsp; logging.w=
arning('Activity not installed')<br>diff --git a/src/sugar/bundle/b=
undle.py b/src/sugar/bundle/bundle.py<br>index 47d08b2..44e30b0 100644<br>-=
-- a/src/sugar/bundle/bundle.py<br>
+++ b/src/sugar/bundle/bundle.py<br>@@ -114,6 +114,29 @@ class Bundle:<br>&=
nbsp; zip=
_file.close()<br> <br> =
return f<br>+ <br>+ def _is_dir(self, =
filename):<br>+ if self._unpacked=
:<br>+ pa=
th =3D os.path.join(self._path, filename)<br>
+ return =
os.path.isdir(path)<br>+ else:<br=
>+ return=
True #zip files contain all dirs you care about!<br>+ &nb=
sp; <br>+ def _=
is_file(self, filename):<br>+ if =
self._unpacked:<br>+ &=
nbsp; path =3D os.path.join(self._path, filename)<br>
+ return =
os.path.isfile(path)<br>+ else:<b=
r>+ zip_f=
ile =3D zipfile.ZipFile(self._path)<br>+  =
; path =3D os.path.join(self._zip_root_dir, f=
ilename)<br>+ &n=
bsp; try:<br>+ &=
nbsp; data =3D zip_file.getinfo(path)<br>
+ &n=
bsp; return True<br>+ =
except KeyError:<br>+  =
; return False<=
br>+ fina=
lly:<br>+ =
zip_file.close()<br>+  =
; <br> <br=
> def get_path(self):<br> &n=
bsp; """Get the bundle path.""&q=
uot;<br>
-- <br><a href=3D"http://1.5.2.5">1.5.2.5</a><br><br><br>
------=_Part_21853_28463409.1212768784608--
------=_Part_21852_24326587.1212768784608
Content-Type: text/x-diff;
name=0001-Bundlebuilder-to-use-MANIFEST-fix_manifest-function.patch
Content-Transfer-Encoding: base64
X-Attachment-Id: f_fh4z1n1z1
Content-Disposition: attachment;
filename=0001-Bundlebuilder-to-use-MANIFEST-fix_manifest-function.patch
RnJvbSA5YWI2MTk4ODhhOWQ0ZWY0MGNhM2U5ODRkNDQ4NWM1YTIyNTU4MTA1IE1vbiBTZXAgMTcg
MDA6MDA6MDAgMjAwMQpGcm9tOiBjaGVtYSA8Y2hlbWFAY2hlbWEtbGFwdG9wLihub25lKT4KRGF0
ZTogRnJpLCA2IEp1biAyMDA4IDEwOjAxOjIxIC0wNjAwClN1YmplY3Q6IFtQQVRDSF0gQnVuZGxl
YnVpbGRlciB0byB1c2UgTUFOSUZFU1QsIGZpeF9tYW5pZmVzdCBmdW5jdGlvbiwgc29tZSBmdW5j
dGlvbnMKcHVzaGVkIHVwc3RyZWFtIHRvIGJ1bmRsZS5weSBhbmQgYWN0aXZpdHlidW5kbGUucHkK
LS0tCiBzcmMvc3VnYXIvYWN0aXZpdHkvYnVuZGxlYnVpbGRlci5weSB8ICAgNjkgKysrKysrKysr
Ky0tLS0tLS0tCiBzcmMvc3VnYXIvYnVuZGxlL2FjdGl2aXR5YnVuZGxlLnB5ICB8ICAxNDAgKysr
KysrKysrKysrKysrKysrKysrKysrKysrKysrKystLS0KIHNyYy9zdWdhci9idW5kbGUvYnVuZGxl
LnB5ICAgICAgICAgIHwgICAyMyArKysrKysKIDMgZmlsZXMgY2hhbmdlZCwgMTg5IGluc2VydGlv
bnMoKyksIDQzIGRlbGV0aW9ucygtKQoKZGlmZiAtLWdpdCBhL3NyYy9zdWdhci9hY3Rpdml0eS9i
dW5kbGVidWlsZGVyLnB5IGIvc3JjL3N1Z2FyL2FjdGl2aXR5L2J1bmRsZWJ1aWxkZXIucHkKaW5k
ZXggMTA2M2Y3Mi4uNzJjMjQ2YyAxMDA2NDQKLS0tIGEvc3JjL3N1Z2FyL2FjdGl2aXR5L2J1bmRs
ZWJ1aWxkZXIucHkKKysrIGIvc3JjL3N1Z2FyL2FjdGl2aXR5L2J1bmRsZWJ1aWxkZXIucHkKQEAg
LTE5LDI4ICsxOSwxNiBAQCBpbXBvcnQgb3MKIGltcG9ydCB6aXBmaWxlCiBpbXBvcnQgdGFyZmls
ZQogaW1wb3J0IHNodXRpbAotaW1wb3J0IHN1YnByb2Nlc3MKK2Zyb20gc3VicHJvY2VzcyBpbXBv
cnQgUG9wZW4sIFBJUEUKIGltcG9ydCByZQogaW1wb3J0IGdldHRleHQKIGZyb20gb3B0cGFyc2Ug
aW1wb3J0IE9wdGlvblBhcnNlcgoraW1wb3J0IHdhcm5pbmdzCitpbXBvcnQgc3VicHJvY2Vzcwog
CiBmcm9tIHN1Z2FyIGltcG9ydCBlbnYKLWZyb20gc3VnYXIuYnVuZGxlLmFjdGl2aXR5YnVuZGxl
IGltcG9ydCBBY3Rpdml0eUJ1bmRsZQotCi1kZWYgbGlzdF9maWxlcyhiYXNlX2RpciwgaWdub3Jl
X2RpcnM9Tm9uZSwgaWdub3JlX2ZpbGVzPU5vbmUpOgotICAgIHJlc3VsdCA9IFtdCitmcm9tIHN1
Z2FyLmJ1bmRsZS5hY3Rpdml0eWJ1bmRsZSBpbXBvcnQgQWN0aXZpdHlCdW5kbGUsTUFOSUZFU1Qs
bGlzdF9maWxlcwogCi0gICAgZm9yIHJvb3QsIGRpcnMsIGZpbGVzIGluIG9zLndhbGsoYmFzZV9k
aXIpOgotICAgICAgICBmb3IgZiBpbiBmaWxlczoKLSAgICAgICAgICAgIGlmIGlnbm9yZV9maWxl
cyBhbmQgZiBub3QgaW4gaWdub3JlX2ZpbGVzOgotICAgICAgICAgICAgICAgIHJlbF9wYXRoID0g
cm9vdFtsZW4oYmFzZV9kaXIpICsgMTpdCi0gICAgICAgICAgICAgICAgcmVzdWx0LmFwcGVuZChv
cy5wYXRoLmpvaW4ocmVsX3BhdGgsIGYpKQotICAgICAgICBpZiBpZ25vcmVfZGlycyBhbmQgcm9v
dCA9PSBiYXNlX2RpcjoKLSAgICAgICAgICAgIGZvciBpZ25vcmUgaW4gaWdub3JlX2RpcnM6Ci0g
ICAgICAgICAgICAgICAgaWYgaWdub3JlIGluIGRpcnM6Ci0gICAgICAgICAgICAgICAgICAgIGRp
cnMucmVtb3ZlKGlnbm9yZSkKLQotICAgIHJldHVybiByZXN1bHQKIAogY2xhc3MgQ29uZmlnKG9i
amVjdCk6CiAgICAgZGVmIF9faW5pdF9fKHNlbGYsIGJ1bmRsZV9uYW1lLCBzb3VyY2VfZGlyPU5v
bmUpOgpAQCAtNTMsNiArNDEsNyBAQCBjbGFzcyBDb25maWcob2JqZWN0KToKICAgICAgICAgYnVu
ZGxlID0gQWN0aXZpdHlCdW5kbGUoc2VsZi5zb3VyY2VfZGlyKQogICAgICAgICB2ZXJzaW9uID0g
YnVuZGxlLmdldF9hY3Rpdml0eV92ZXJzaW9uKCkKIAorICAgICAgICBzZWxmLmJ1bmRsZSA9IGJ1
bmRsZQogICAgICAgICBzZWxmLmJ1bmRsZV9uYW1lID0gYnVuZGxlX25hbWUKICAgICAgICAgc2Vs
Zi54b19uYW1lID0gJyVzLSVkLnhvJyAlIChzZWxmLmJ1bmRsZV9uYW1lLCB2ZXJzaW9uKQogICAg
ICAgICBzZWxmLnRhcmJhbGxfbmFtZSA9ICclcy0lZC50YXIuYnoyJyAlIChzZWxmLmJ1bmRsZV9u
YW1lLCB2ZXJzaW9uKQpAQCAtMTAzLDkgKzkyLDkgQEAgY2xhc3MgQnVpbGRlcihvYmplY3QpOgog
ICAgICAgICAgICAgZi5jbG9zZSgpCiAKIGNsYXNzIFBhY2thZ2VyKG9iamVjdCk6Ci0gICAgZGVm
IF9faW5pdF9fKHNlbGYsIGNvbmZpZyk6CisgICAgZGVmIF9faW5pdF9fKHNlbGYsIGNvbmZpZywg
ZGlzdF9kaXIgPSBOb25lKToKICAgICAgICAgc2VsZi5jb25maWcgPSBjb25maWcKLSAgICAgICAg
c2VsZi5kaXN0X2RpciA9IG9zLnBhdGguam9pbihzZWxmLmNvbmZpZy5zb3VyY2VfZGlyLCAnZGlz
dCcpCisgICAgICAgIHNlbGYuZGlzdF9kaXIgPSBkaXN0X2RpciBvciBvcy5wYXRoLmpvaW4oc2Vs
Zi5jb25maWcuc291cmNlX2RpciwgJ2Rpc3QnKQogICAgICAgICBzZWxmLnBhY2thZ2VfcGF0aCA9
IE5vbmUKIAogICAgICAgICBpZiBub3Qgb3MucGF0aC5leGlzdHMoc2VsZi5kaXN0X2Rpcik6CkBA
IC0xMTMsMTkgKzEwMiwyOCBAQCBjbGFzcyBQYWNrYWdlcihvYmplY3QpOgogICAgICAgICAgICAg
CiAKIGNsYXNzIEJ1aWxkUGFja2FnZXIoUGFja2FnZXIpOgotICAgIGRlZiBfX2luaXRfXyhzZWxm
LCBjb25maWcpOgotICAgICAgICBQYWNrYWdlci5fX2luaXRfXyhzZWxmLCBjb25maWcpCisgICAg
ZGVmIF9faW5pdF9fKHNlbGYsIGNvbmZpZyxkaXN0X2RpciA9IE5vbmUpOgorICAgICAgICBQYWNr
YWdlci5fX2luaXRfXyhzZWxmLCBjb25maWcsZGlzdF9kaXIgKQogICAgICAgICBzZWxmLmJ1aWxk
X2RpciA9IHNlbGYuY29uZmlnLnNvdXJjZV9kaXIKIAogICAgIGRlZiBnZXRfZmlsZXMoc2VsZik6
Ci0gICAgICAgIHJldHVybiBsaXN0X2ZpbGVzKHNlbGYuYnVpbGRfZGlyLAotICAgICAgICAgICAg
ICAgICAgICAgICAgICBpZ25vcmVfZGlycz1bJ3BvJywgJ2Rpc3QnLCAnLmdpdCddLAotICAgICAg
ICAgICAgICAgICAgICAgICAgICBpZ25vcmVfZmlsZXM9WycuZ2l0aWdub3JlJ10pCisgICAgICAg
IHJldHVybiBzZWxmLmNvbmZpZy5idW5kbGUuZ2V0X2ZpbGVzKCkKKyAgICAKKyAgICBkZWYgZml4
X21hbmlmZXN0KHNlbGYpOgorICAgICAgICBhbGxmaWxlcyA9ICBsaXN0X2ZpbGVzKHNlbGYuYnVp
bGRfZGlyLAorICAgICAgICAgICAgICAgICAgICAgICAgICBpZ25vcmVfZGlycz1bJ2Rpc3QnLCAn
LmdpdCddLAorICAgICAgICAgICAgICAgICAgICAgICAgICBpZ25vcmVfZmlsZXM9WycuZ2l0aWdu
b3JlJywgJ01BTklGRVNUJywgJyoucHljJywgJyp+JywgJyouYmFrJ10pCisgICAgICAgIGZvciBm
aWxlIGluIGFsbGZpbGVzOgorICAgICAgICAgICAgaWYgZmlsZSBub3QgaW4gc2VsZi5jb25maWcu
YnVuZGxlLm1hbmlmZXN0OgorICAgICAgICAgICAgICAgIHNlbGYuY29uZmlnLmJ1bmRsZS5tYW5p
ZmVzdC5hcHBlbmQoZmlsZSkKKyAgICAgICAgbWFuaWZlc3RmaWxlID0gb3Blbihvcy5wYXRoLmpv
aW4oc2VsZi5jb25maWcuc291cmNlX2RpcixNQU5JRkVTVCksIndiIikKKyAgICAgICAgZm9yIGxp
bmUgaW4gc2VsZi5jb25maWcuYnVuZGxlLm1hbmlmZXN0OgorICAgICAgICAgICAgbWFuaWZlc3Rm
aWxlLndyaXRlKGxpbmUgKyAiXG4iKQogCiBjbGFzcyBYT1BhY2thZ2VyKEJ1aWxkUGFja2FnZXIp
OgotICAgIGRlZiBfX2luaXRfXyhzZWxmLCBjb25maWcpOgorICAgIGRlZiBfX2luaXRfXyhzZWxm
LCBjb25maWcsZGlzdF9kaXIgPSBOb25lLGRpc3RfbmFtZSA9IE5vbmUpOgogICAgICAgICBCdWls
ZFBhY2thZ2VyLl9faW5pdF9fKHNlbGYsIGNvbmZpZykKLSAgICAgICAgc2VsZi5wYWNrYWdlX3Bh
dGggPSBvcy5wYXRoLmpvaW4oc2VsZi5kaXN0X2Rpciwgc2VsZi5jb25maWcueG9fbmFtZSkKKyAg
ICAgICAgc2VsZi5wYWNrYWdlX3BhdGggPSBvcy5wYXRoLmpvaW4oc2VsZi5kaXN0X2RpciwgZGlz
dF9uYW1lIG9yIHNlbGYuY29uZmlnLnhvX25hbWUpCiAKICAgICBkZWYgcGFja2FnZShzZWxmKToK
ICAgICAgICAgYnVuZGxlX3ppcCA9IHppcGZpbGUuWmlwRmlsZShzZWxmLnBhY2thZ2VfcGF0aCwg
J3cnLApAQCAtMTM3LDE2ICsxMzUsMjUgQEAgY2xhc3MgWE9QYWNrYWdlcihCdWlsZFBhY2thZ2Vy
KToKIAogICAgICAgICBidW5kbGVfemlwLmNsb3NlKCkKIAotY2xhc3MgU291cmNlUGFja2FnZXIo
UGFja2FnZXIpOgotICAgIGRlZiBfX2luaXRfXyhzZWxmLCBjb25maWcpOgotICAgICAgICBQYWNr
YWdlci5fX2luaXRfXyhzZWxmLCBjb25maWcpCitjbGFzcyBTb3VyY2VQYWNrYWdlcihCdWlsZFBh
Y2thZ2VyKToKKyAgICBkZWYgX19pbml0X18oc2VsZiwgY29uZmlnLGRpc3RfZGlyID0gTm9uZSk6
CisgICAgICAgIEJ1aWxkUGFja2FnZXIuX19pbml0X18oc2VsZiwgY29uZmlnLGRpc3RfZGlyKQog
ICAgICAgICBzZWxmLnBhY2thZ2VfcGF0aCA9IG9zLnBhdGguam9pbihzZWxmLmRpc3RfZGlyLAog
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxmLmNvbmZpZy50YXJi
YWxsX25hbWUpCiAKICAgICBkZWYgZ2V0X2ZpbGVzKHNlbGYpOgotICAgICAgICByZXR1cm4gbGlz
dF9maWxlcyhzZWxmLmNvbmZpZy5zb3VyY2VfZGlyLAotICAgICAgICAgICAgICAgICAgICAgICAg
ICBpZ25vcmVfZGlycz1bJ2xvY2FsZScsICdkaXN0JywgJy5naXQnXSwKLSAgICAgICAgICAgICAg
ICAgICAgICAgICAgaWdub3JlX2ZpbGVzPVsnLmdpdGlnbm9yZSddKQorICAgICAgICBnaXRfbHMg
PSBQb3BlbignZ2l0LWxzLWZpbGVzJyxzdGRvdXQ9UElQRSxjd2Q9c2VsZi5jb25maWcuc291cmNl
X2RpcikKKyAgICAgICAgaWYgZ2l0X2xzLndhaXQoKToKKyAgICAgICAgICAgICNub24tMCByZXR1
cm4gY29kZSAtIGZhaWxlZAorICAgICAgICAgICAgcmV0dXJuIFBhY2thZ2VyLmdldF9maWxlcyhz
ZWxmKQorICAgICAgICBmID0gZ2l0X2xzLnN0ZG91dAorICAgICAgICBmaWxlcyA9IFtdCisgICAg
ICAgIGZvciBsaW5lIGluIGYucmVhZGxpbmVzKCk6CisgICAgICAgICAgICBmaWxlbmFtZSA9IGxp
bmUuc3RyaXAoKQorICAgICAgICAgICAgaWYgbm90IGZpbGVuYW1lLnN0YXJ0c3dpdGgoJy4nKToK
KyAgICAgICAgICAgICAgICBmaWxlcy5hcHBlbmQoZmlsZW5hbWUpCisgICAgICAgIGYuY2xvc2Uo
KQorICAgICAgICByZXR1cm4gZmlsZXMKIAogICAgIGRlZiBwYWNrYWdlKHNlbGYpOgogICAgICAg
ICB0YXIgPSB0YXJmaWxlLm9wZW4oc2VsZi5wYWNrYWdlX3BhdGgsICJ3IikKZGlmZiAtLWdpdCBh
L3NyYy9zdWdhci9idW5kbGUvYWN0aXZpdHlidW5kbGUucHkgYi9zcmMvc3VnYXIvYnVuZGxlL2Fj
dGl2aXR5YnVuZGxlLnB5CmluZGV4IGRiMzA1NTUuLjFmZjdkMzkgMTAwNjQ0Ci0tLSBhL3NyYy9z
dWdhci9idW5kbGUvYWN0aXZpdHlidW5kbGUucHkKKysrIGIvc3JjL3N1Z2FyL2J1bmRsZS9hY3Rp
dml0eWJ1bmRsZS5weQpAQCAtMjEsMTUgKzIxLDMxIEBAIGZyb20gQ29uZmlnUGFyc2VyIGltcG9y
dCBDb25maWdQYXJzZXIKIGltcG9ydCBsb2NhbGUKIGltcG9ydCBvcwogaW1wb3J0IHRlbXBmaWxl
Citmcm9tIGZubWF0Y2ggaW1wb3J0IGZubWF0Y2gKIAogZnJvbSBzdWdhci5idW5kbGUuYnVuZGxl
IGltcG9ydCBCdW5kbGUsIE1hbGZvcm1lZEJ1bmRsZUV4Y2VwdGlvbiwgXAogICAgIEFscmVhZHlJ
bnN0YWxsZWRFeGNlcHRpb24sIFJlZ2lzdHJhdGlvbkV4Y2VwdGlvbiwgXAogICAgIE5vdEluc3Rh
bGxlZEV4Y2VwdGlvbgogCi1mcm9tIHN1Z2FyIGltcG9ydCBhY3Rpdml0eQotZnJvbSBzdWdhciBp
bXBvcnQgZW52Ci0KIGltcG9ydCBsb2dnaW5nCitpbXBvcnQgd2FybmluZ3MKKworTUFOSUZFU1Qg
PSAiTUFOSUZFU1QiCisKK2RlZiBsaXN0X2ZpbGVzKGJhc2VfZGlyLCBpZ25vcmVfZGlycz1Ob25l
LCBpZ25vcmVfZmlsZXM9Tm9uZSk6CisgICAgcmVzdWx0ID0gW10KKyAgICAKKyAgICBmb3Igcm9v
dCwgZGlycywgZmlsZXMgaW4gb3Mud2FsayhiYXNlX2Rpcik6CisgICAgICAgIGZvciBmIGluIGZp
bGVzOgorICAgICAgICAgICAgaWYgbm90IChpZ25vcmVfZmlsZXMgYW5kIFtUcnVlIGZvciBwYXQg
aW4gaWdub3JlX2ZpbGVzIGlmIGZubWF0Y2goZixwYXQpXSk6CisgICAgICAgICAgICAgICAgI3Jl
c3VsdCBtYXRjaGVzIGEgcGF0dGVybiBpbiBpZ25vcmVfZmlsZXMsIGlnbm9yZSBpdAorICAgICAg
ICAgICAgICAgIHJlbF9wYXRoID0gcm9vdFtsZW4oYmFzZV9kaXIpICsgMTpdCisgICAgICAgICAg
ICAgICAgcmVzdWx0LmFwcGVuZChvcy5wYXRoLmpvaW4ocmVsX3BhdGgsIGYpKQorICAgICAgICBp
ZiBpZ25vcmVfZGlycyBhbmQgcm9vdCA9PSBiYXNlX2RpcjoKKyAgICAgICAgICAgIGZvciBpZ25v
cmUgaW4gaWdub3JlX2RpcnM6CisgICAgICAgICAgICAgICAgaWYgaWdub3JlIGluIGRpcnM6Cisg
ICAgICAgICAgICAgICAgICAgIGRpcnMucmVtb3ZlKGlnbm9yZSkKKyAgICByZXR1cm4gcmVzdWx0
CiAKIGNsYXNzIEFjdGl2aXR5QnVuZGxlKEJ1bmRsZSk6CiAgICAgIiIiQSBTdWdhciBhY3Rpdml0
eSBidW5kbGUKQEAgLTY0LDcgKzgwLDYyIEBAIGNsYXNzIEFjdGl2aXR5QnVuZGxlKEJ1bmRsZSk6
CiAgICAgICAgIGxpbmZvX2ZpbGUgPSBzZWxmLl9nZXRfbGluZm9fZmlsZSgpCiAgICAgICAgIGlm
IGxpbmZvX2ZpbGU6CiAgICAgICAgICAgICBzZWxmLl9wYXJzZV9saW5mbyhsaW5mb19maWxlKQot
CisgICAgICAgIAorICAgICAgICBzZWxmLnJlYWRfbWFuaWZlc3QoKQorCisgICAgZGVmIF9yYXdf
bWFuaWZlc3Qoc2VsZik6CisgICAgICAgIHRyeToKKyAgICAgICAgICAgIGYgPSBzZWxmLl9nZXRf
ZmlsZShNQU5JRkVTVCkKKyAgICAgICAgZXhjZXB0IElPRXJyb3I6CisgICAgICAgICAgICBmID0g
bm9uZQorICAgICAgICBpZiBub3QgZjoKKyAgICAgICAgICAgIHdhcm5pbmdzLndhcm4oTWFsZm9y
bWVkQnVuZGxlRXhjZXB0aW9uKCJBY3Rpdml0eSBkaXJlY3RvcnkgbGFja3MgYSBNQU5JRkVTVCBm
aWxlLiIpKQorICAgICAgICAgICAgcmV0dXJuIFtdCisgICAgICAgIHJldCA9IG1hcChsYW1iZGEg
eDp4LnN0cmlwKCksZi5yZWFkbGluZXMoKSkgI3N0cmlwIHRyYWlsaW5nIFxuIGFuZCBvdGhlciB3
aGl0ZXNwYWNlCisgICAgICAgIGYuY2xvc2UoKQorICAgICAgICByZXR1cm4gcmV0CisgICAgICAg
IAorICAgIGRlZiByZWFkX21hbmlmZXN0KHNlbGYpOgorICAgICAgICAiIiJyZWFkX21hbmlmZXN0
OiBzZXRzIHNlbGYubWFuaWZlc3QgdG8gbGlzdCBvZiBsaW5lcyBpbiBNQU5JRkVTVCwgCisgICAg
ICAgIHdpdGggaW52YWxpZCBsaW5lcyByZXBsYWNlZCBieSBlbXB0eSBsaW5lcy4KKyAgICAgICAg
CisgICAgICAgIFNpbmNlIGFic29sdXRlIG9yZGVyIGNhcnJpZXMgaW5mb3JtYXRpb24gb24gZmls
ZSBoaXN0b3J5LCBpdCBzaG91bGQgYmUgcHJlc2VydmVkLgorICAgICAgICBGb3IgaW5zdGFuY2Us
IHdoZW4gcmVuYW1pbmcgYSBmaWxlLCB5b3Ugc2hvdWxkIGxlYXZlIHRoZSBuZXcgbmFtZSBvbiB0
aGUgc2FtZSBsaW5lCisgICAgICAgIGFzIHRoZSBvbGQgb25lLgorICAgICAgICAiIiIKKyAgICAg
ICAgbWFuaWZlc3RsaW5lcyA9IHNlbGYuX3Jhd19tYW5pZmVzdCgpCisgICAgICAgIGZvciBudW0s
bGluZSBpbiBlbnVtZXJhdGUobWFuaWZlc3RsaW5lcyk6CisgICAgICAgICAgICBpZiBsaW5lOgor
ICAgICAgICAgICAgICAgIGlmIGxpbmUgaW4gbWFuaWZlc3RsaW5lc1swOm51bV06CisgICAgICAg
ICAgICAgICAgICAgIG1hbmlmZXN0bGluZXNbbnVtXSA9ICIiCisgICAgICAgICAgICAgICAgICAg
IHdhcm5pbmdzLndhcm4oTWFsZm9ybWVkQnVuZGxlRXhjZXB0aW9uKCJCdW5kbGUgJXM6IGR1cGxp
Y2F0ZSBlbnRyeSBpbiBNQU5JRkVTVDogJXMiCisgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICUoc2VsZi5fbmFtZSxsaW5lKSkpCisgICAgICAgICAgICAg
ICAgICAgIGNvbnRpbnVlCisgICAgICAgICAgICAgICAgaWYgbGluZSA9PSBNQU5JRkVTVDoKKyAg
ICAgICAgICAgICAgICAgICAgbWFuaWZlc3RsaW5lc1tudW1dID0gIiIKKyAgICAgICAgICAgICAg
ICAgICAgd2FybmluZ3Mud2FybihNYWxmb3JtZWRCdW5kbGVFeGNlcHRpb24oIkJ1bmRsZSAlczog
TUFOSUZFU1QgaW5jbHVkZXMgaXRzZWxmOiAlcyIKKyAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgJShzZWxmLl9uYW1lLGxpbmUpKSkKKyAgICAgICAgICAg
ICAgICBpZiBsaW5lLmVuZHN3aXRoKCIvIik6CisgICAgICAgICAgICAgICAgICAgIGlmIG5vdCBz
ZWxmLl9pc19kaXIobGluZSk6CisgICAgICAgICAgICAgICAgICAgICAgICBtYW5pZmVzdGxpbmVz
W251bV0gPSAiIgorICAgICAgICAgICAgICAgICAgICAgICAgd2FybmluZ3Mud2FybihNYWxmb3Jt
ZWRCdW5kbGVFeGNlcHRpb24oIkJ1bmRsZSAlczogaW52YWxpZCBkaXIgZW50cnkgaW4gTUFOSUZF
U1Q6ICVzIgorICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgJShzZWxmLl9uYW1lLGxpbmUpKSkKKyAgICAgICAgICAgICAgICBlbHNlOgorICAgICAg
ICAgICAgICAgICAgICBpZiBub3Qgc2VsZi5faXNfZmlsZShsaW5lKToKKyAgICAgICAgICAgICAg
ICAgICAgICAgIG1hbmlmZXN0bGluZXNbbnVtXSA9ICIiCisgICAgICAgICAgICAgICAgICAgICAg
ICB3YXJuaW5ncy53YXJuKE1hbGZvcm1lZEJ1bmRsZUV4Y2VwdGlvbigiQnVuZGxlICVzOiBpbnZh
bGlkIGVudHJ5IGluIE1BTklGRVNUOiAlcyIKKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICUoc2VsZi5fbmFtZSxsaW5lKSkpCisgICAgICAgICNy
ZW1vdmUgdHJhaWxpbmcgbmV3bGluZXMgLSB1bmxpa2UgaW50ZXJuYWwgbmV3bGluZXMsIHRoZXkg
ZG8gbm90IGhlbHAga2VlcCBhYnNvbHV0ZSBwb3NpdGlvbgorICAgICAgICB3aGlsZSBtYW5pZmVz
dGxpbmVzWy0xXT09IiI6CisgICAgICAgICAgICBtYW5pZmVzdGxpbmVzID0gbWFuaWZlc3RsaW5l
c1s6LTFdCisgICAgICAgIHNlbGYubWFuaWZlc3QgPSBtYW5pZmVzdGxpbmVzCisgICAgCisgICAg
ZGVmIGdldF9maWxlcyhzZWxmLG1hbmlmZXN0ID0gTm9uZSk6CisgICAgICAgIHJldHVybiBbTUFO
SUZFU1RdICsgZmlsdGVyKGxhbWJkYSBsaW5lOiBsaW5lIGFuZCBub3QgbGluZS5lbmRzd2l0aCgi
LyIpLG1hbmlmZXN0IG9yIHNlbGYubWFuaWZlc3QpCisgICAgCisgICAgZGVmIGdldF9kaXJzKHNl
bGYpOgorICAgICAgICByZXR1cm4gZmlsdGVyKGxhbWJkYSBsaW5lOiBsaW5lIGFuZCBsaW5lLmVu
ZHN3aXRoKCIvIiksc2VsZi5tYW5pZmVzdCkKKyAgICAgIAogICAgIGRlZiBfcGFyc2VfaW5mbyhz
ZWxmLCBpbmZvX2ZpbGUpOgogICAgICAgICBjcCA9IENvbmZpZ1BhcnNlcigpCiAgICAgICAgIGNw
LnJlYWRmcChpbmZvX2ZpbGUpCkBAIC0xMTYsNiArMTg3LDcgQEAgY2xhc3MgQWN0aXZpdHlCdW5k
bGUoQnVuZGxlKToKICAgICAgICAgICAgICAgICByYWlzZSBNYWxmb3JtZWRCdW5kbGVFeGNlcHRp
b24oCiAgICAgICAgICAgICAgICAgICAgICdBY3Rpdml0eSBidW5kbGUgJXMgaGFzIGludmFsaWQg
dmVyc2lvbiBudW1iZXIgJXMnICUKICAgICAgICAgICAgICAgICAgICAgKHNlbGYuX3BhdGgsIHZl
cnNpb24pKQorICAgIAogCiAgICAgZGVmIF9nZXRfbGluZm9fZmlsZShzZWxmKToKICAgICAgICAg
bGFuZyA9IGxvY2FsZS5nZXRkZWZhdWx0bG9jYWxlKClbMF0KQEAgLTIwOSwyOCArMjgxLDUzIEBA
IGNsYXNzIEFjdGl2aXR5QnVuZGxlKEJ1bmRsZSk6CiAgICAgICAgIHJldHVybiBzZWxmLl9zaG93
X2xhdW5jaGVyCiAKICAgICBkZWYgaXNfaW5zdGFsbGVkKHNlbGYpOgorICAgICAgICBmcm9tIHN1
Z2FyIGltcG9ydCBhY3Rpdml0eQorICAgICAgICAKICAgICAgICAgaWYgYWN0aXZpdHkuZ2V0X3Jl
Z2lzdHJ5KCkuZ2V0X2FjdGl2aXR5KHNlbGYuX2J1bmRsZV9pZCk6CiAgICAgICAgICAgICByZXR1
cm4gVHJ1ZQogICAgICAgICBlbHNlOgogICAgICAgICAgICAgcmV0dXJuIEZhbHNlCiAKICAgICBk
ZWYgbmVlZF91cGdyYWRlKHNlbGYpOgorICAgICAgICBmcm9tIHN1Z2FyIGltcG9ydCBhY3Rpdml0
eQorICAgICAgICAKICAgICAgICAgYWN0ID0gYWN0aXZpdHkuZ2V0X3JlZ2lzdHJ5KCkuZ2V0X2Fj
dGl2aXR5KHNlbGYuX2J1bmRsZV9pZCkKICAgICAgICAgaWYgYWN0IGlzIE5vbmUgb3IgYWN0LnZl
cnNpb24gIT0gc2VsZi5fYWN0aXZpdHlfdmVyc2lvbjoKICAgICAgICAgICAgIHJldHVybiBUcnVl
CiAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICByZXR1cm4gRmFsc2UKLQotICAgIGRlZiBpbnN0
YWxsKHNlbGYpOgotICAgICAgICBhY3Rpdml0aWVzX3BhdGggPSBlbnYuZ2V0X3VzZXJfYWN0aXZp
dGllc19wYXRoKCkKLSAgICAgICAgYWN0ID0gYWN0aXZpdHkuZ2V0X3JlZ2lzdHJ5KCkuZ2V0X2Fj
dGl2aXR5KHNlbGYuX2J1bmRsZV9pZCkKLSAgICAgICAgaWYgYWN0IGlzIG5vdCBOb25lIGFuZCBh
Y3QucGF0aC5zdGFydHN3aXRoKGFjdGl2aXRpZXNfcGF0aCk6Ci0gICAgICAgICAgICByYWlzZSBB
bHJlYWR5SW5zdGFsbGVkRXhjZXB0aW9uCi0KLSAgICAgICAgaW5zdGFsbF9kaXIgPSBlbnYuZ2V0
X3VzZXJfYWN0aXZpdGllc19wYXRoKCkKKyAgICAKKyAgICBkZWYgdW5wYWNrKHNlbGYsaW5zdGFs
bF9kaXIsc3RyaWN0X21hbmlmZXN0PUZhbHNlKToKICAgICAgICAgc2VsZi5fdW56aXAoaW5zdGFs
bF9kaXIpCiAKICAgICAgICAgaW5zdGFsbF9wYXRoID0gb3MucGF0aC5qb2luKGluc3RhbGxfZGly
LCBzZWxmLl96aXBfcm9vdF9kaXIpCisgICAgICAgIAorICAgICAgICAjY2hlY2sgaW5zdGFsbGVk
IGZpbGVzIGFnYWluc3QgdGhlIE1BTklGRVNUCisgICAgICAgIG1hbmlmZXN0ZmlsZXMgPSBzZWxm
LmdldF9maWxlcyhzZWxmLl9yYXdfbWFuaWZlc3QoKSkKKyAgICAgICAgZm9yIGZpbGUgaW4gbGlz
dF9maWxlcyhpbnN0YWxsX3BhdGgpOgorICAgICAgICAgICAgaWYgZmlsZSBpbiBtYW5pZmVzdGZp
bGVzOgorICAgICAgICAgICAgICAgIG1hbmlmZXN0ZmlsZXMucmVtb3ZlKGZpbGUpCisgICAgICAg
ICAgICBlbGlmIGZpbGUgIT0gTUFOSUZFU1Q6CisgICAgICAgICAgICAgICAgI3dhcm5pbmdzLndh
cm4oTWFsZm9ybWVkQnVuZGxlRXhjZXB0aW9uKCJCdW5kbGUgJXM6ICVzIG5vdCBpbiBNQU5JRkVT
VCIlCisgICAgICAgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgIChzZWxmLl9uYW1lLGZpbGUpKSkKKyAgICAgICAgICAgICAgICBpZiBzdHJpY3RfbWFuaWZl
c3Q6CisgICAgICAgICAgICAgICAgICAgIG9zLnJlbW92ZShvcy5wYXRoLmpvaW4oaW5zdGFsbF9w
YXRoLGZpbGUpKQorICAgICAgICBpZiBtYW5pZmVzdGZpbGVzOgorICAgICAgICAgICAgZXJyID0g
TWFsZm9ybWVkQnVuZGxlRXhjZXB0aW9uKCJCdW5kbGUgJXM6IGZpbGVzIGluIE1BTklGRVNUIG5v
dCBpbmNsdWRlZDogJXMiJQorICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgIChzZWxmLl9uYW1lLHN0cihtYW5pZmVzdGZpbGVzKSkpCisgICAgICAg
ICAgICBpZiBzdHJpY3RfbWFuaWZlc3Q6CisgICAgICAgICAgICAgICAgcmFpc2UgZXJyCisgICAg
ICAgICAgICBlbHNlOgorICAgICAgICAgICAgICAgIHdhcm5pbmdzLndhcm4oZXJyKQorICAgICAg
ICAKKyAgICAgICAgI2NyZWF0ZSBlbXB0eSBkaXJlY3RvcmllcworICAgICAgICBmb3IgZGlyIGlu
IHNlbGYuZ2V0X2RpcnMoKToKKyAgICAgICAgICAgIGRpcnBhdGggPSBvcy5wYXRoLmpvaW4oaW5z
dGFsbF9wYXRoLGRpcikKKyAgICAgICAgICAgIGlmIG9zLnBhdGguaXNkaXIoZGlycGF0aCk6Cisg
ICAgICAgICAgICAgICAgd2FybmluZ3Mud2FybihNYWxmb3JtZWRCdW5kbGVFeGNlcHRpb24oIkJ1
bmxkbGUgJXM6IG5vbi1lbXB0eSBkaXIgJXMgaW4gTUFOSUZFU1QiJQorICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChzZWxmLl9uYW1lLGRpcnBh
dGgpKSkKKyAgICAgICAgICAgIGVsc2U6CisgICAgICAgICAgICAgICAgb3MubWFrZWRpcnMob3Mu
cGF0aC5qb2luKGluc3RhbGxfcGF0aCxkaXIpKQogCiAgICAgICAgIHhkZ19kYXRhX2hvbWUgPSBv
cy5nZXRlbnYoJ1hER19EQVRBX0hPTUUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgIG9zLnBhdGguZXhwYW5kdXNlcignfi8ubG9jYWwvc2hhcmUnKSkKQEAgLTI2NywxMSArMzY0
LDI3IEBAIGNsYXNzIEFjdGl2aXR5QnVuZGxlKEJ1bmRsZSk6CiAgICAgICAgICAgICAgICAgICAg
IG9zLnN5bWxpbmsoaW5mb19maWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9z
LnBhdGguam9pbihpbnN0YWxsZWRfaWNvbnNfZGlyLAogICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICBvcy5wYXRoLmJhc2VuYW1lKGluZm9fZmlsZSkpKQorICAgICAg
ICByZXR1cm4gaW5zdGFsbF9wYXRoCiAKKyAgICBkZWYgaW5zdGFsbChzZWxmKToKKyAgICAgICAg
ZnJvbSBzdWdhciBpbXBvcnQgYWN0aXZpdHkKKyAgICAgICAgZnJvbSBzdWdhciBpbXBvcnQgZW52
CisgICAgICAgIAorICAgICAgICBhY3Rpdml0aWVzX3BhdGggPSBlbnYuZ2V0X3VzZXJfYWN0aXZp
dGllc19wYXRoKCkKKyAgICAgICAgYWN0ID0gYWN0aXZpdHkuZ2V0X3JlZ2lzdHJ5KCkuZ2V0X2Fj
dGl2aXR5KHNlbGYuX2J1bmRsZV9pZCkKKyAgICAgICAgaWYgYWN0IGlzIG5vdCBOb25lIGFuZCBh
Y3QucGF0aC5zdGFydHN3aXRoKGFjdGl2aXRpZXNfcGF0aCk6CisgICAgICAgICAgICByYWlzZSBB
bHJlYWR5SW5zdGFsbGVkRXhjZXB0aW9uCisKKyAgICAgICAgaW5zdGFsbF9kaXIgPSBlbnYuZ2V0
X3VzZXJfYWN0aXZpdGllc19wYXRoKCkKKyAgICAgICAgaW5zdGFsbF9wYXRoID0gc2VsZi51bnBh
Y2soaW5zdGFsbF9kaXIpCisgICAgICAgIAogICAgICAgICBpZiBub3QgYWN0aXZpdHkuZ2V0X3Jl
Z2lzdHJ5KCkuYWRkX2J1bmRsZShpbnN0YWxsX3BhdGgpOgogICAgICAgICAgICAgcmFpc2UgUmVn
aXN0cmF0aW9uRXhjZXB0aW9uCiAKICAgICBkZWYgdW5pbnN0YWxsKHNlbGYsIGZvcmNlPUZhbHNl
KToKKyAgICAgICAgZnJvbSBzdWdhciBpbXBvcnQgYWN0aXZpdHkKKyAgICAgICAgZnJvbSBzdWdh
ciBpbXBvcnQgZW52CisgICAgICAgIAogICAgICAgICBpZiBzZWxmLl91bnBhY2tlZDoKICAgICAg
ICAgICAgIGluc3RhbGxfcGF0aCA9IHNlbGYuX3BhdGgKICAgICAgICAgZWxzZToKQEAgLTMxNiw2
ICs0MjksOSBAQCBjbGFzcyBBY3Rpdml0eUJ1bmRsZShCdW5kbGUpOgogICAgICAgICAgICAgcmFp
c2UgUmVnaXN0cmF0aW9uRXhjZXB0aW9uCiAKICAgICBkZWYgdXBncmFkZShzZWxmKToKKyAgICAg
ICAgZnJvbSBzdWdhciBpbXBvcnQgYWN0aXZpdHkKKyAgICAgICAgZnJvbSBzdWdhciBpbXBvcnQg
ZW52CisgICAgICAgIAogICAgICAgICBhY3QgPSBhY3Rpdml0eS5nZXRfcmVnaXN0cnkoKS5nZXRf
YWN0aXZpdHkoc2VsZi5fYnVuZGxlX2lkKQogICAgICAgICBpZiBhY3QgaXMgTm9uZToKICAgICAg
ICAgICAgIGxvZ2dpbmcud2FybmluZygnQWN0aXZpdHkgbm90IGluc3RhbGxlZCcpCmRpZmYgLS1n
aXQgYS9zcmMvc3VnYXIvYnVuZGxlL2J1bmRsZS5weSBiL3NyYy9zdWdhci9idW5kbGUvYnVuZGxl
LnB5CmluZGV4IDQ3ZDA4YjIuLjQ0ZTMwYjAgMTAwNjQ0Ci0tLSBhL3NyYy9zdWdhci9idW5kbGUv
YnVuZGxlLnB5CisrKyBiL3NyYy9zdWdhci9idW5kbGUvYnVuZGxlLnB5CkBAIC0xMTQsNiArMTE0
LDI5IEBAIGNsYXNzIEJ1bmRsZToKICAgICAgICAgICAgIHppcF9maWxlLmNsb3NlKCkKIAogICAg
ICAgICByZXR1cm4gZgorICAgIAorICAgIGRlZiBfaXNfZGlyKHNlbGYsIGZpbGVuYW1lKToKKyAg
ICAgICAgaWYgc2VsZi5fdW5wYWNrZWQ6CisgICAgICAgICAgICBwYXRoID0gb3MucGF0aC5qb2lu
KHNlbGYuX3BhdGgsIGZpbGVuYW1lKQorICAgICAgICAgICAgcmV0dXJuIG9zLnBhdGguaXNkaXIo
cGF0aCkKKyAgICAgICAgZWxzZToKKyAgICAgICAgICAgIHJldHVybiBUcnVlICN6aXAgZmlsZXMg
Y29udGFpbiBhbGwgZGlycyB5b3UgY2FyZSBhYm91dCEKKyAgICAgICAgICAgIAorICAgIGRlZiBf
aXNfZmlsZShzZWxmLCBmaWxlbmFtZSk6CisgICAgICAgIGlmIHNlbGYuX3VucGFja2VkOgorICAg
ICAgICAgICAgcGF0aCA9IG9zLnBhdGguam9pbihzZWxmLl9wYXRoLCBmaWxlbmFtZSkKKyAgICAg
ICAgICAgIHJldHVybiBvcy5wYXRoLmlzZmlsZShwYXRoKQorICAgICAgICBlbHNlOgorICAgICAg
ICAgICAgemlwX2ZpbGUgPSB6aXBmaWxlLlppcEZpbGUoc2VsZi5fcGF0aCkKKyAgICAgICAgICAg
IHBhdGggPSBvcy5wYXRoLmpvaW4oc2VsZi5femlwX3Jvb3RfZGlyLCBmaWxlbmFtZSkKKyAgICAg
ICAgICAgIHRyeToKKyAgICAgICAgICAgICAgICBkYXRhID0gemlwX2ZpbGUuZ2V0aW5mbyhwYXRo
KQorICAgICAgICAgICAgICAgIHJldHVybiBUcnVlCisgICAgICAgICAgICBleGNlcHQgS2V5RXJy
b3I6CisgICAgICAgICAgICAgICAgcmV0dXJuIEZhbHNlCisgICAgICAgICAgICBmaW5hbGx5Ogor
ICAgICAgICAgICAgICAgIHppcF9maWxlLmNsb3NlKCkKKyAgICAgICAgICAgICAgICAKIAogICAg
IGRlZiBnZXRfcGF0aChzZWxmKToKICAgICAgICAgIiIiR2V0IHRoZSBidW5kbGUgcGF0aC4iIiIK
LS0gCjEuNS4yLjUKCg==
------=_Part_21852_24326587.1212768784608--
More information about the Sugar
mailing list