From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) by finch.gentoo.org (Postfix) with ESMTP id BB8AA1392EF for ; Fri, 14 Mar 2014 05:52:26 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id AAB66E0B61; Fri, 14 Mar 2014 05:52:20 +0000 (UTC) Received: from mail-vc0-f169.google.com (mail-vc0-f169.google.com [209.85.220.169]) (using TLSv1 with cipher ECDHE-RSA-RC4-SHA (128/128 bits)) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id E379EE0B5F for ; Fri, 14 Mar 2014 05:52:19 +0000 (UTC) Received: by mail-vc0-f169.google.com with SMTP id ik5so2244167vcb.14 for ; Thu, 13 Mar 2014 22:52:19 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:sender:in-reply-to:references:date :message-id:subject:from:to:cc:content-type; bh=Odcx5LSO8E71aigyE0GVZreZVBlFKvdMl2FLosYcPmw=; b=ACuVecsuY0JcaaW3LLoJ5vVxkBcAQFWdff3HhX+fjjMGW2PvW4xkyHGxnBaq+Kdqxh SorJQfDBaF7NBoGDOlutK9vu0a7xU0s3leQvQOnw0Q2OBAFwibNL78ljS8CgRQY4s6Od AYoVA9fiOHzslT4YHspjKrs2J/oHOE3NIIoeGbOzE3GAFAcaCj6hZgcAr85zBBC2oZrG X/wbu3SqA2A0Op8BWzYLW561MU17KWlBBaEio+fLAY2rqXmV6bHFn130aIxzyiPRMp+7 HqdXFEdUNL8komPZldxFNdm3c7r3GrtB7Lo2Of9xhct5sTT0xO6GYlIGK7/NL/RTfQWJ cEwg== X-Gm-Message-State: ALoCoQlcVZEXyGRC/soaESRrB90+BApKxTdPZxrFdEKmy90RoYd13FyPSQshYqST37MNjun9EJzl Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-portage-dev@lists.gentoo.org Reply-to: gentoo-portage-dev@lists.gentoo.org MIME-Version: 1.0 X-Received: by 10.52.18.70 with SMTP id u6mr4051433vdd.11.1394776338875; Thu, 13 Mar 2014 22:52:18 -0700 (PDT) Sender: antarus@scriptkitty.com Received: by 10.220.59.71 with HTTP; Thu, 13 Mar 2014 22:52:18 -0700 (PDT) X-Originating-IP: [173.8.165.226] In-Reply-To: References: <1393029287-6842-1-git-send-email-nullishzero@gentoo.org> <1394691047-14156-1-git-send-email-nullishzero@gentoo.org> <1394691047-14156-3-git-send-email-nullishzero@gentoo.org> Date: Thu, 13 Mar 2014 22:52:18 -0700 X-Google-Sender-Auth: RDW9Hh8Jsyi6aTUXbQDwd8tLRbQ Message-ID: Subject: Re: [gentoo-portage-dev] [PATCH 3/3] Add an emaint module that can scan for failed merges and that can fix failed merges. From: Alec Warner To: gentoo-portage-dev@lists.gentoo.org Cc: Pavel Kazakov Content-Type: multipart/alternative; boundary=001a1136b666385f8a04f48aac71 X-Archives-Salt: 1192e64a-9161-44c9-876f-21a91fceeff9 X-Archives-Hash: c420473bc5dde60105ba08de1d9e80aa --001a1136b666385f8a04f48aac71 Content-Type: text/plain; charset=UTF-8 On Thu, Mar 13, 2014 at 3:09 PM, Alec Warner wrote: > > > > On Wed, Mar 12, 2014 at 11:10 PM, Pavel Kazakov wrote: > >> --- >> pym/portage/emaint/main.py | 6 +- >> pym/portage/emaint/modules/merges/__init__.py | 30 +++ >> pym/portage/emaint/modules/merges/merges.py | 281 >> ++++++++++++++++++++++++++ >> 3 files changed, 315 insertions(+), 2 deletions(-) >> create mode 100644 pym/portage/emaint/modules/merges/__init__.py >> create mode 100644 pym/portage/emaint/modules/merges/merges.py >> >> diff --git a/pym/portage/emaint/main.py b/pym/portage/emaint/main.py >> index 6a17027..646883d 100644 >> --- a/pym/portage/emaint/main.py >> +++ b/pym/portage/emaint/main.py >> @@ -98,10 +98,11 @@ def module_opts(module_controller, module): >> class TaskHandler(object): >> """Handles the running of the tasks it is given""" >> >> - def __init__(self, show_progress_bar=True, verbose=True, >> callback=None): >> + def __init__(self, show_progress_bar=True, verbose=True, >> callback=None, module_output=None): >> self.show_progress_bar = show_progress_bar >> self.verbose = verbose >> self.callback = callback >> + self.module_output = module_output >> self.isatty = os.environ.get('TERM') != 'dumb' and >> sys.stdout.isatty() >> self.progress_bar = ProgressBar(self.isatty, >> title="Emaint", max_desc_length=27) >> >> @@ -124,6 +125,7 @@ class TaskHandler(object): >> onProgress = None >> kwargs = { >> 'onProgress': onProgress, >> + 'module_output': self.module_output, >> # pass in a copy of the options so a >> module can not pollute or change >> # them for other tasks if there is more >> to do. >> 'options': options.copy() >> @@ -219,5 +221,5 @@ def emaint_main(myargv): >> # need to pass the parser options dict to the modules >> # so they are available if needed. >> task_opts = options.__dict__ >> - taskmaster = TaskHandler(callback=print_results) >> + taskmaster = TaskHandler(callback=print_results, >> module_output=sys.stdout) >> taskmaster.run_tasks(tasks, func, status, options=task_opts) >> diff --git a/pym/portage/emaint/modules/merges/__init__.py >> b/pym/portage/emaint/modules/merges/__init__.py >> new file mode 100644 >> index 0000000..96ee71b >> --- /dev/null >> +++ b/pym/portage/emaint/modules/merges/__init__.py >> @@ -0,0 +1,30 @@ >> +# Copyright 2005-2014 Gentoo Foundation >> +# Distributed under the terms of the GNU General Public License v2 >> + >> +"""Scan for failed merges and fix them.""" >> + >> + >> +module_spec = { >> + 'name': 'merges', >> + 'description': __doc__, >> + 'provides': { >> + 'merges': { >> + 'name': "merges", >> + 'class': "MergesHandler", >> + 'description': __doc__, >> + 'functions': ['check', 'fix', 'purge'], >> + 'func_desc': { >> + 'purge': { >> + 'short': '-P', 'long': >> '--purge-tracker', >> + 'help': 'Removes the list of >> previously failed merges.' + >> + ' WARNING: Only >> use this option if you plan on' + >> + ' manually fixing >> them or do not want them' >> + ' re-installed.', >> + 'status': "Removing %s", >> + 'action': 'store_true', >> + 'func': 'purge' >> + } >> + } >> + } >> + } >> +} >> diff --git a/pym/portage/emaint/modules/merges/merges.py >> b/pym/portage/emaint/modules/merges/merges.py >> new file mode 100644 >> index 0000000..a99dad4 >> --- /dev/null >> +++ b/pym/portage/emaint/modules/merges/merges.py >> @@ -0,0 +1,281 @@ >> +# Copyright 2005-2014 Gentoo Foundation >> +# Distributed under the terms of the GNU General Public License v2 >> + >> +from _emerge.actions import load_emerge_config >> + >> +import portage >> +from portage import os, _unicode_encode >> +from portage.const import MERGING_IDENTIFIER, PORTAGE_BIN_PATH, >> PRIVATE_PATH, \ >> + VDB_PATH >> +from portage.dep import isvalidatom >> + >> > > import shutil > import subprocess > import sys > import time > > Alphabetical order. > > >> +import time >> +import shutil >> +import sys >> +import subprocess >> + >> +class TrackingFile(object): >> + """File for keeping track of failed merges.""" >> + >> + >> + def __init__(self, tracking_path): >> + """ >> + Create a TrackingFile object. >> + >> + @param tracking_path: file path used to keep track of >> failed merges >> + @type tracking_path: String >> + """ >> + self._tracking_path = _unicode_encode(tracking_path) >> + >> + >> + def save(self, failed_pkgs): >> + """ >> + Save the specified packages that failed to merge. >> + >> + @param failed_pkgs: dictionary of failed packages >> + @type failed_pkgs: dict >> + """ >> + tracking_path = self._tracking_path >> > > You don't appear to do any atomic operations or file locking here, what if > 2 callers are trying to write to the same filename at once? > Normally we write to a temporary file and perform an atomic rename (which > prevents this sort of thing.) > portage.util.write_atomic can do this for you. -A > > >> + with open(tracking_path, 'w') as tracking_file: >> + for pkg, mtime in failed_pkgs.items(): >> + tracking_file.write('%s %s\n' % (pkg, >> mtime)) >> + >> + >> + def load(self): >> + """ >> + Load previously failed merges. >> + >> + @rtype: dict >> + @return: dictionary of packages that failed to merge >> + """ >> > + tracking_path = self._tracking_path >> + if not os.path.exists(tracking_path): >> + return {} >> > > if not self.exists(): > return {} > > >> + failed_pkgs = {} >> + with open(tracking_path, 'r') as tracking_file: >> + for failed_merge in tracking_file: >> + pkg, mtime = failed_merge.strip().split() >> + failed_pkgs[pkg] = mtime >> + return failed_pkgs >> + >> + >> + def exists(self): >> + """ >> + Check if tracking file exists. >> + >> + @rtype: bool >> + @return: true if tracking file exists, false otherwise >> + """ >> + return os.path.exists(self._tracking_path) >> + >> + >> + def purge(self): >> + """Delete previously saved tracking file if one exists.""" >> + if self.exists(): >> + os.remove(self._tracking_path) >> + >> + >> +class MergesHandler(object): >> + """Handle failed package merges.""" >> + >> + short_desc = "Remove failed merges" >> + >> + @staticmethod >> + def name(): >> + return "merges" >> + >> + >> + def __init__(self): >> + """Create MergesHandler object.""" >> + eroot = portage.settings['EROOT'] >> + tracking_path = os.path.join(eroot, PRIVATE_PATH, >> 'failed-merges'); >> + self._tracking_file = TrackingFile(tracking_path) >> + self._vardb_path = os.path.join(eroot, VDB_PATH) >> + >> + >> + def can_progressbar(self, func): >> + return func == 'check' >> + >> + >> + def _scan(self, onProgress=None): >> + """ >> + Scan the file system for failed merges and return any >> found. >> + >> + @param onProgress: function to call for updating progress >> + @type onProgress: Function >> + @rtype: dict >> + @return: dictionary of packages that failed to merges >> + """ >> + failed_pkgs = {} >> + for cat in os.listdir(self._vardb_path): >> + pkgs_path = os.path.join(self._vardb_path, cat) >> + if not os.path.isdir(pkgs_path): >> + continue >> + pkgs = os.listdir(pkgs_path) >> + maxval = len(pkgs) >> + for i, pkg in enumerate(pkgs): >> + if onProgress: >> + onProgress(maxval, i+1) >> + if MERGING_IDENTIFIER in pkg: >> + mtime = >> int(os.stat(os.path.join(pkgs_path, pkg)).st_mtime) >> + pkg = os.path.join(cat, pkg) >> + failed_pkgs[pkg] = mtime >> + return failed_pkgs >> + >> + >> + def _failed_pkgs(self, onProgress=None): >> + """ >> + Return failed packages from both the file system and >> tracking file. >> + >> + @rtype: dict >> + @return: dictionary of packages that failed to merges >> + """ >> + failed_pkgs = self._scan(onProgress) >> > > Perhaps add an __iter__ function to TrackingFile, that implicitly calls > load? > > Then this can be: > for pkg, mtime in self._tracking_file: > > ? > > Just a thought. > > >> + for pkg, mtime in self._tracking_file.load().items(): >> + if pkg not in failed_pkgs: >> + failed_pkgs[pkg] = mtime >> + return failed_pkgs >> + >> + >> + def _remove_failed_dirs(self, failed_pkgs): >> + """ >> + Remove the directories of packages that failed to merge. >> + >> + @param failed_pkgs: failed packages whose directories to >> remove >> + @type failed_pkg: dict >> + """ >> + for failed_pkg in failed_pkgs: >> + pkg_path = os.path.join(self._vardb_path, >> failed_pkg) >> + # delete failed merge directory if it exists (it >> might not exist >> + # if loaded from tracking file) >> + if os.path.exists(pkg_path): >> + shutil.rmtree(pkg_path) >> + # TODO: try removing package contents to prevent >> orphaned >> + # files >> > > I don't get this TODO, can you elaborate? > > >> + >> + >> + def _get_pkg_atoms(self, failed_pkgs, pkg_atoms, pkg_dne): >> + """ >> + Get the package atoms for the specified failed packages. >> + >> + @param failed_pkgs: failed packages to iterate >> + @type failed_pkgs: dict >> + @param pkg_atoms: append package atoms to this set >> + @type pkg_atoms: set >> + @param pkg_dne: append any packages atoms that are >> invalid to this set >> + @type pkg_dne: set >> > > Not following what dne means. > > >> + """ >> + >> + emerge_config = load_emerge_config() >> + portdb = >> emerge_config.target_config.trees['porttree'].dbapi >> + for failed_pkg in failed_pkgs: >> + # validate pkg name >> + pkg_name = '%s' % >> failed_pkg.replace(MERGING_IDENTIFIER, '') >> + pkg_atom = '=%s' % pkg_name >> + >> + if not isvalidatom(pkg_atom): >> + pkg_dne.append( "'%s' is an invalid >> package atom." % pkg_atom) >> + if not portdb.cpv_exists(pkg_name): >> + pkg_dne.append( "'%s' does not exist in >> the portage tree." >> + % pkg_name) >> + pkg_atoms.add(pkg_atom) >> + >> + >> + def _emerge_pkg_atoms(self, module_output, pkg_atoms): >> + """ >> + Emerge the specified packages atoms. >> + >> + @param module_output: output will be written to >> + @type module_output: Class >> + @param pkg_atoms: packages atoms to emerge >> + @type pkg_atoms: set >> + @rtype: list >> + @return: List of results >> + """ >> + # TODO: rewrite code to use portage's APIs instead of a >> subprocess >> + env = { >> + "FEATURES" : "-collision-detect -protect-owned", >> + "PATH" : os.environ["PATH"] >> + } >> + emerge_cmd = ( >> + portage._python_interpreter, >> + '-b', >> + os.path.join(PORTAGE_BIN_PATH, 'emerge'), >> + '--quiet', >> + '--oneshot', >> + '--complete-graph=y' >> + ) >> + results = [] >> + msg = 'Re-Emerging packages that failed to merge...\n' >> + if module_output: >> + module_output.write(msg) >> + else: >> + module_output = subprocess.PIPE >> + results.append(msg) >> + proc = subprocess.Popen(emerge_cmd + tuple(pkg_atoms), >> env=env, >> + stdout=module_output, stderr=sys.stderr) >> + output = proc.communicate()[0] >> > > This seems sort of weird, perhaps: > > output, _ = proc.communicate() > > >> + if output: >> + results.append(output) >> + if proc.returncode != os.EX_OK: >> + emerge_status = "Failed to emerge '%s'" % (' >> '.join(pkg_atoms)) >> + else: >> + emerge_status = "Successfully emerged '%s'" % (' >> '.join(pkg_atoms)) >> + results.append(emerge_status) >> + return results >> + >> + >> + def check(self, **kwargs): >> + """Check for failed merges.""" >> + onProgress = kwargs.get('onProgress', None) >> + failed_pkgs = self._failed_pkgs(onProgress) >> + errors = [] >> + for pkg, mtime in failed_pkgs.items(): >> + mtime_str = time.ctime(int(mtime)) >> + errors.append("'%s' failed to merge on '%s'" % >> (pkg, mtime_str)) >> + return errors >> + >> + >> + def fix(self, **kwargs): >> + """Attempt to fix any failed merges.""" >> + module_output = kwargs.get('module_output', None) >> + failed_pkgs = self._failed_pkgs() >> + if not failed_pkgs: >> + return [ 'No failed merges found. ' ] >> > > Less spacing here: > return ['No failed merges found.'] > > >> + >> + pkg_dne = set() >> + pkg_atoms = set() >> + self._get_pkg_atoms(failed_pkgs, pkg_atoms, pkg_dne) >> + if pkg_dne: >> + return pkg_dne >> + >> + try: >> + self._tracking_file.save(failed_pkgs) >> + except IOError as ex: >> + errors = [ 'Unable to save failed merges to >> tracking file: %s\n' >> + % str(ex) ] >> > > Same here, no spaces before or after [ and ]. > > >> + errors.append(', '.join(sorted(failed_pkgs))) >> + return errors >> + self._remove_failed_dirs(failed_pkgs) >> + results = self._emerge_pkg_atoms(module_output, pkg_atoms) >> + # list any new failed merges >> + for pkg in sorted(self._scan()): >> + results.append("'%s' still found as a failed >> merge." % pkg) >> + # reload config and remove successful packages from >> tracking file >> + emerge_config = load_emerge_config() >> + vardb = emerge_config.target_config.trees['vartree'].dbapi >> + still_failed_pkgs = {} >> + for pkg, mtime in failed_pkgs.items(): >> + pkg_name = '%s' % pkg.replace(MERGING_IDENTIFIER, >> '') >> + if not vardb.cpv_exists(pkg_name): >> + still_failed_pkgs[pkg] = mtime >> + self._tracking_file.save(still_failed_pkgs) >> + return results >> + >> + >> + def purge(self, **kwargs): >> + """Attempt to remove previously saved tracking file.""" >> + if not self._tracking_file.exists(): >> + return ['Tracking file not found.'] >> + self._tracking_file.purge() >> + return ['Removed tracking file.'] >> -- >> 1.8.3.2 >> >> >> > --001a1136b666385f8a04f48aac71 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: base64 PGRpdiBkaXI9Imx0ciI+PGRpdiBjbGFzcz0iZ21haWxfZXh0cmEiPjxkaXYgY2xhc3M9ImdtYWls X3F1b3RlIj5PbiBUaHUsIE1hciAxMywgMjAxNCBhdCAzOjA5IFBNLCBBbGVjIFdhcm5lciA8c3Bh biBkaXI9Imx0ciI+Jmx0OzxhIGhyZWY9Im1haWx0bzphbnRhcnVzQGdlbnRvby5vcmciIHRhcmdl dD0iX2JsYW5rIj5hbnRhcnVzQGdlbnRvby5vcmc8L2E+Jmd0Ozwvc3Bhbj4gd3JvdGU6PGJyPg0K PGJsb2NrcXVvdGUgY2xhc3M9ImdtYWlsX3F1b3RlIiBzdHlsZT0ibWFyZ2luOjAgMCAwIC44ZXg7 Ym9yZGVyLWxlZnQ6MXB4ICNjY2Mgc29saWQ7cGFkZGluZy1sZWZ0OjFleCI+PGRpdiBkaXI9Imx0 ciI+PGJyPjxkaXYgY2xhc3M9ImdtYWlsX2V4dHJhIj48YnI+PGJyPjxkaXYgY2xhc3M9ImdtYWls X3F1b3RlIj48ZGl2PjxkaXYgY2xhc3M9Img1Ij5PbiBXZWQsIE1hciAxMiwgMjAxNCBhdCAxMTox MCBQTSwgUGF2ZWwgS2F6YWtvdiA8c3BhbiBkaXI9Imx0ciI+Jmx0OzxhIGhyZWY9Im1haWx0bzpu dWxsaXNoemVyb0BnZW50b28ub3JnIiB0YXJnZXQ9Il9ibGFuayI+bnVsbGlzaHplcm9AZ2VudG9v Lm9yZzwvYT4mZ3Q7PC9zcGFuPiB3cm90ZTo8YnI+DQoNCjxibG9ja3F1b3RlIGNsYXNzPSJnbWFp bF9xdW90ZSIgc3R5bGU9Im1hcmdpbjowIDAgMCAuOGV4O2JvcmRlci1sZWZ0OjFweCAjY2NjIHNv bGlkO3BhZGRpbmctbGVmdDoxZXgiPi0tLTxicj4NCsKgcHltL3BvcnRhZ2UvZW1haW50L21haW4u cHkgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqB8IMKgIDYgKy08YnI+DQrCoHB5bS9wb3J0 YWdlL2VtYWludC9tb2R1bGVzL21lcmdlcy9fX2luaXRfXy5weSB8IMKgMzAgKysrPGJyPg0KwqBw eW0vcG9ydGFnZS9lbWFpbnQvbW9kdWxlcy9tZXJnZXMvbWVyZ2VzLnB5IMKgIHwgMjgxICsrKysr KysrKysrKysrKysrKysrKysrKysrPGJyPg0KwqAzIGZpbGVzIGNoYW5nZWQsIDMxNSBpbnNlcnRp b25zKCspLCAyIGRlbGV0aW9ucygtKTxicj4NCjxkaXY+wqBjcmVhdGUgbW9kZSAxMDA2NDQgcHlt L3BvcnRhZ2UvZW1haW50L21vZHVsZXMvbWVyZ2VzL19faW5pdF9fLnB5PGJyPg0KwqBjcmVhdGUg bW9kZSAxMDA2NDQgcHltL3BvcnRhZ2UvZW1haW50L21vZHVsZXMvbWVyZ2VzL21lcmdlcy5weTxi cj4NCjxicj4NCmRpZmYgLS1naXQgYS9weW0vcG9ydGFnZS9lbWFpbnQvbWFpbi5weSBiL3B5bS9w b3J0YWdlL2VtYWludC9tYWluLnB5PGJyPg0KPC9kaXY+aW5kZXggNmExNzAyNy4uNjQ2ODgzZCAx MDA2NDQ8YnI+DQotLS0gYS9weW0vcG9ydGFnZS9lbWFpbnQvbWFpbi5weTxicj4NCisrKyBiL3B5 bS9wb3J0YWdlL2VtYWludC9tYWluLnB5PGJyPg0KQEAgLTk4LDEwICs5OCwxMSBAQCBkZWYgbW9k dWxlX29wdHMobW9kdWxlX2NvbnRyb2xsZXIsIG1vZHVsZSk6PGJyPg0KPGRpdj7CoGNsYXNzIFRh c2tIYW5kbGVyKG9iamVjdCk6PGJyPg0KwqAgwqAgwqAgwqAgJnF1b3Q7JnF1b3Q7JnF1b3Q7SGFu ZGxlcyB0aGUgcnVubmluZyBvZiB0aGUgdGFza3MgaXQgaXMgZ2l2ZW4mcXVvdDsmcXVvdDsmcXVv dDs8YnI+DQo8YnI+DQotIMKgIMKgIMKgIGRlZiBfX2luaXRfXyhzZWxmLCBzaG93X3Byb2dyZXNz X2Jhcj1UcnVlLCB2ZXJib3NlPVRydWUsIGNhbGxiYWNrPU5vbmUpOjxicj4NCisgwqAgwqAgwqAg ZGVmIF9faW5pdF9fKHNlbGYsIHNob3dfcHJvZ3Jlc3NfYmFyPVRydWUsIHZlcmJvc2U9VHJ1ZSwg Y2FsbGJhY2s9Tm9uZSwgbW9kdWxlX291dHB1dD1Ob25lKTo8YnI+DQrCoCDCoCDCoCDCoCDCoCDC oCDCoCDCoCBzZWxmLnNob3dfcHJvZ3Jlc3NfYmFyID0gc2hvd19wcm9ncmVzc19iYXI8YnI+DQrC oCDCoCDCoCDCoCDCoCDCoCDCoCDCoCBzZWxmLnZlcmJvc2UgPSB2ZXJib3NlPGJyPg0KwqAgwqAg wqAgwqAgwqAgwqAgwqAgwqAgc2VsZi5jYWxsYmFjayA9IGNhbGxiYWNrPGJyPg0KKyDCoCDCoCDC oCDCoCDCoCDCoCDCoCBzZWxmLm1vZHVsZV9vdXRwdXQgPSBtb2R1bGVfb3V0cHV0PGJyPg0KwqAg wqAgwqAgwqAgwqAgwqAgwqAgwqAgc2VsZi5pc2F0dHkgPSBvcy5lbnZpcm9uLmdldCgmIzM5O1RF Uk0mIzM5OykgIT0gJiMzOTtkdW1iJiMzOTsgYW5kIHN5cy5zdGRvdXQuaXNhdHR5KCk8YnI+DQrC oCDCoCDCoCDCoCDCoCDCoCDCoCDCoCBzZWxmLnByb2dyZXNzX2JhciA9IFByb2dyZXNzQmFyKHNl bGYuaXNhdHR5LCB0aXRsZT0mcXVvdDtFbWFpbnQmcXVvdDssIG1heF9kZXNjX2xlbmd0aD0yNyk8 YnI+DQo8YnI+DQo8L2Rpdj5AQCAtMTI0LDYgKzEyNSw3IEBAIGNsYXNzIFRhc2tIYW5kbGVyKG9i amVjdCk6PGJyPg0KPGRpdj7CoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDC oCDCoCDCoCBvblByb2dyZXNzID0gTm9uZTxicj4NCsKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKg IMKgIMKgIMKgIGt3YXJncyA9IHs8YnI+DQrCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDC oCDCoCDCoCDCoCDCoCDCoCAmIzM5O29uUHJvZ3Jlc3MmIzM5Ozogb25Qcm9ncmVzcyw8YnI+DQor IMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgICYjMzk7bW9kdWxl X291dHB1dCYjMzk7OiBzZWxmLm1vZHVsZV9vdXRwdXQsPGJyPg0KwqAgwqAgwqAgwqAgwqAgwqAg wqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgIyBwYXNzIGluIGEgY29weSBvZiB0aGUgb3B0 aW9ucyBzbyBhIG1vZHVsZSBjYW4gbm90IHBvbGx1dGUgb3IgY2hhbmdlPGJyPg0KwqAgwqAgwqAg wqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgIyB0aGVtIGZvciBvdGhlciB0 YXNrcyBpZiB0aGVyZSBpcyBtb3JlIHRvIGRvLjxicj4NCsKgIMKgIMKgIMKgIMKgIMKgIMKgIMKg IMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgICYjMzk7b3B0aW9ucyYjMzk7OiBvcHRpb25zLmNvcHko KTxicj4NCjwvZGl2PkBAIC0yMTksNSArMjIxLDUgQEAgZGVmIGVtYWludF9tYWluKG15YXJndik6 PGJyPg0KPGRpdj7CoCDCoCDCoCDCoCAjIG5lZWQgdG8gcGFzcyB0aGUgcGFyc2VyIG9wdGlvbnMg ZGljdCB0byB0aGUgbW9kdWxlczxicj4NCsKgIMKgIMKgIMKgICMgc28gdGhleSBhcmUgYXZhaWxh YmxlIGlmIG5lZWRlZC48YnI+DQrCoCDCoCDCoCDCoCB0YXNrX29wdHMgPSBvcHRpb25zLl9fZGlj dF9fPGJyPg0KLSDCoCDCoCDCoCB0YXNrbWFzdGVyID0gVGFza0hhbmRsZXIoY2FsbGJhY2s9cHJp bnRfcmVzdWx0cyk8YnI+DQorIMKgIMKgIMKgIHRhc2ttYXN0ZXIgPSBUYXNrSGFuZGxlcihjYWxs YmFjaz1wcmludF9yZXN1bHRzLCBtb2R1bGVfb3V0cHV0PXN5cy5zdGRvdXQpPGJyPg0KwqAgwqAg wqAgwqAgdGFza21hc3Rlci5ydW5fdGFza3ModGFza3MsIGZ1bmMsIHN0YXR1cywgb3B0aW9ucz10 YXNrX29wdHMpPGJyPg0KZGlmZiAtLWdpdCBhL3B5bS9wb3J0YWdlL2VtYWludC9tb2R1bGVzL21l cmdlcy9fX2luaXRfXy5weSBiL3B5bS9wb3J0YWdlL2VtYWludC9tb2R1bGVzL21lcmdlcy9fX2lu aXRfXy5weTxicj4NCm5ldyBmaWxlIG1vZGUgMTAwNjQ0PGJyPg0KPC9kaXY+aW5kZXggMDAwMDAw MC4uOTZlZTcxYjxicj4NCi0tLSAvZGV2L251bGw8YnI+DQorKysgYi9weW0vcG9ydGFnZS9lbWFp bnQvbW9kdWxlcy9tZXJnZXMvX19pbml0X18ucHk8YnI+DQpAQCAtMCwwICsxLDMwIEBAPGJyPg0K PGRpdj4rIyBDb3B5cmlnaHQgMjAwNS0yMDE0IEdlbnRvbyBGb3VuZGF0aW9uPGJyPg0KKyMgRGlz dHJpYnV0ZWQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5z ZSB2Mjxicj4NCis8YnI+DQorJnF1b3Q7JnF1b3Q7JnF1b3Q7U2NhbiBmb3IgZmFpbGVkIG1lcmdl cyBhbmQgZml4IHRoZW0uJnF1b3Q7JnF1b3Q7JnF1b3Q7PGJyPg0KKzxicj4NCis8YnI+DQorbW9k dWxlX3NwZWMgPSB7PGJyPg0KKyDCoCDCoCDCoCAmIzM5O25hbWUmIzM5OzogJiMzOTttZXJnZXMm IzM5Oyw8YnI+DQorIMKgIMKgIMKgICYjMzk7ZGVzY3JpcHRpb24mIzM5OzogX19kb2NfXyw8YnI+ DQorIMKgIMKgIMKgICYjMzk7cHJvdmlkZXMmIzM5Ozogezxicj4NCisgwqAgwqAgwqAgwqAgwqAg wqAgwqAgJiMzOTttZXJnZXMmIzM5Ozogezxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAg wqAgwqAgwqAgJiMzOTtuYW1lJiMzOTs6ICZxdW90O21lcmdlcyZxdW90Oyw8YnI+DQorIMKgIMKg IMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgICYjMzk7Y2xhc3MmIzM5OzogJnF1b3Q7TWVyZ2Vz SGFuZGxlciZxdW90Oyw8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgICYj Mzk7ZGVzY3JpcHRpb24mIzM5OzogX19kb2NfXyw8YnI+DQo8L2Rpdj4rIMKgIMKgIMKgIMKgIMKg IMKgIMKgIMKgIMKgIMKgIMKgICYjMzk7ZnVuY3Rpb25zJiMzOTs6IFsmIzM5O2NoZWNrJiMzOTss ICYjMzk7Zml4JiMzOTssICYjMzk7cHVyZ2UmIzM5O10sPGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDC oCDCoCDCoCDCoCDCoCDCoCAmIzM5O2Z1bmNfZGVzYyYjMzk7OiB7PGJyPg0KKyDCoCDCoCDCoCDC oCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCAmIzM5O3B1cmdlJiMzOTs6IHs8YnI+ DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKg IMKgICYjMzk7c2hvcnQmIzM5OzogJiMzOTstUCYjMzk7LCAmIzM5O2xvbmcmIzM5OzogJiMzOTst LXB1cmdlLXRyYWNrZXImIzM5Oyw8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKg IMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgICYjMzk7aGVscCYjMzk7OiAmIzM5O1JlbW92ZXMg dGhlIGxpc3Qgb2YgcHJldmlvdXNseSBmYWlsZWQgbWVyZ2VzLiYjMzk7ICs8YnI+DQorIMKgIMKg IMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKg IMKgIMKgIMKgIMKgIMKgIMKgICYjMzk7IFdBUk5JTkc6IE9ubHkgdXNlIHRoaXMgb3B0aW9uIGlm IHlvdSBwbGFuIG9uJiMzOTsgKzxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAg wqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgJiMzOTsg bWFudWFsbHkgZml4aW5nIHRoZW0gb3IgZG8gbm90IHdhbnQgdGhlbSYjMzk7PGJyPg0KKyDCoCDC oCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDC oCDCoCDCoCDCoCDCoCDCoCDCoCAmIzM5OyByZS1pbnN0YWxsZWQuJiMzOTssPGJyPg0KKyDCoCDC oCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCAmIzM5 O3N0YXR1cyYjMzk7OiAmcXVvdDtSZW1vdmluZyAlcyZxdW90Oyw8YnI+DQorIMKgIMKgIMKgIMKg IMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgICYjMzk7YWN0aW9u JiMzOTs6ICYjMzk7c3RvcmVfdHJ1ZSYjMzk7LDxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAg wqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgJiMzOTtmdW5jJiMzOTs6ICYjMzk7 cHVyZ2UmIzM5Ozxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAg wqAgwqAgfTxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgfTxicj4NCjxk aXY+KyDCoCDCoCDCoCDCoCDCoCDCoCDCoCB9PGJyPg0KKyDCoCDCoCDCoCB9PGJyPg0KK308YnI+ DQpkaWZmIC0tZ2l0IGEvcHltL3BvcnRhZ2UvZW1haW50L21vZHVsZXMvbWVyZ2VzL21lcmdlcy5w eSBiL3B5bS9wb3J0YWdlL2VtYWludC9tb2R1bGVzL21lcmdlcy9tZXJnZXMucHk8YnI+DQpuZXcg ZmlsZSBtb2RlIDEwMDY0NDxicj4NCjwvZGl2PmluZGV4IDAwMDAwMDAuLmE5OWRhZDQ8YnI+DQot LS0gL2Rldi9udWxsPGJyPg0KKysrIGIvcHltL3BvcnRhZ2UvZW1haW50L21vZHVsZXMvbWVyZ2Vz L21lcmdlcy5weTxicj4NCkBAIC0wLDAgKzEsMjgxIEBAPGJyPg0KPGRpdj4rIyBDb3B5cmlnaHQg MjAwNS0yMDE0IEdlbnRvbyBGb3VuZGF0aW9uPGJyPg0KKyMgRGlzdHJpYnV0ZWQgdW5kZXIgdGhl IHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSB2Mjxicj4NCis8YnI+DQo8 L2Rpdj4rZnJvbSBfZW1lcmdlLmFjdGlvbnMgaW1wb3J0IGxvYWRfZW1lcmdlX2NvbmZpZzxicj4N CjxkaXY+Kzxicj4NCitpbXBvcnQgcG9ydGFnZTxicj4NCitmcm9tIHBvcnRhZ2UgaW1wb3J0IG9z LCBfdW5pY29kZV9lbmNvZGU8YnI+DQo8L2Rpdj4rZnJvbSBwb3J0YWdlLmNvbnN0IGltcG9ydCBN RVJHSU5HX0lERU5USUZJRVIsIFBPUlRBR0VfQklOX1BBVEgsIFBSSVZBVEVfUEFUSCwgXDxicj4N CisgwqAgwqAgwqAgVkRCX1BBVEg8YnI+DQo8ZGl2Pitmcm9tIHBvcnRhZ2UuZGVwIGltcG9ydCBp c3ZhbGlkYXRvbTxicj4NCis8YnI+PC9kaXY+PC9ibG9ja3F1b3RlPjxkaXY+PGJyPjwvZGl2Pjwv ZGl2PjwvZGl2PjxkaXY+aW1wb3J0IHNodXRpbDwvZGl2PjxkaXY+aW1wb3J0IHN1YnByb2Nlc3M8 L2Rpdj48ZGl2PmltcG9ydCBzeXM8L2Rpdj48ZGl2PmltcG9ydCB0aW1lPC9kaXY+PGRpdj48YnI+ PC9kaXY+PGRpdj5BbHBoYWJldGljYWwgb3JkZXIuPC9kaXY+PGRpdj48ZGl2IGNsYXNzPSJoNSI+ PGRpdj7CoDxicj4NCjwvZGl2PjxibG9ja3F1b3RlIGNsYXNzPSJnbWFpbF9xdW90ZSIgc3R5bGU9 Im1hcmdpbjowIDAgMCAuOGV4O2JvcmRlci1sZWZ0OjFweCAjY2NjIHNvbGlkO3BhZGRpbmctbGVm dDoxZXgiPg0KPGRpdj4NCjwvZGl2PitpbXBvcnQgdGltZTxicj4NCjxkaXY+K2ltcG9ydCBzaHV0 aWw8YnI+DQoraW1wb3J0IHN5czxicj4NCitpbXBvcnQgc3VicHJvY2Vzczxicj4NCis8YnI+DQo8 L2Rpdj4rY2xhc3MgVHJhY2tpbmdGaWxlKG9iamVjdCk6PGJyPg0KKyDCoCDCoCDCoCAmcXVvdDsm cXVvdDsmcXVvdDtGaWxlIGZvciBrZWVwaW5nIHRyYWNrIG9mIGZhaWxlZCBtZXJnZXMuJnF1b3Q7 JnF1b3Q7JnF1b3Q7PGJyPg0KKzxicj4NCis8YnI+DQorIMKgIMKgIMKgIGRlZiBfX2luaXRfXyhz ZWxmLCB0cmFja2luZ19wYXRoKTo8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgICZxdW90OyZx dW90OyZxdW90Ozxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgQ3JlYXRlIGEgVHJhY2tpbmdG aWxlIG9iamVjdC48YnI+DQorPGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCBAcGFyYW0gdHJh Y2tpbmdfcGF0aDogZmlsZSBwYXRoIHVzZWQgdG8ga2VlcCB0cmFjayBvZiBmYWlsZWQgbWVyZ2Vz PGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCBAdHlwZSB0cmFja2luZ19wYXRoOiBTdHJpbmc8 YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgICZxdW90OyZxdW90OyZxdW90Ozxicj4NCisgwqAg wqAgwqAgwqAgwqAgwqAgwqAgc2VsZi5fdHJhY2tpbmdfcGF0aCA9IF91bmljb2RlX2VuY29kZSh0 cmFja2luZ19wYXRoKTxicj4NCis8YnI+DQorPGJyPg0KKyDCoCDCoCDCoCBkZWYgc2F2ZShzZWxm LCBmYWlsZWRfcGtncyk6PGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCAmcXVvdDsmcXVvdDsm cXVvdDs8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgIFNhdmUgdGhlIHNwZWNpZmllZCBwYWNr YWdlcyB0aGF0IGZhaWxlZCB0byBtZXJnZS48YnI+DQorPGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDC oCDCoCBAcGFyYW0gZmFpbGVkX3BrZ3M6IGRpY3Rpb25hcnkgb2YgZmFpbGVkIHBhY2thZ2VzPGJy Pg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCBAdHlwZSBmYWlsZWRfcGtnczogZGljdDxicj4NCisg wqAgwqAgwqAgwqAgwqAgwqAgwqAgJnF1b3Q7JnF1b3Q7JnF1b3Q7PGJyPg0KKyDCoCDCoCDCoCDC oCDCoCDCoCDCoCB0cmFja2luZ19wYXRoID0gc2VsZi5fdHJhY2tpbmdfcGF0aDxicj48L2Jsb2Nr cXVvdGU+PGRpdj48YnI+PC9kaXY+PC9kaXY+PC9kaXY+PGRpdj5Zb3UgZG9uJiMzOTt0IGFwcGVh ciB0byBkbyBhbnkgYXRvbWljIG9wZXJhdGlvbnMgb3IgZmlsZSBsb2NraW5nIGhlcmUsIHdoYXQg aWYgMiBjYWxsZXJzIGFyZSB0cnlpbmcgdG8gd3JpdGUgdG8gdGhlIHNhbWUgZmlsZW5hbWUgYXQg b25jZT88L2Rpdj4NCg0KPGRpdj5Ob3JtYWxseSB3ZSB3cml0ZSB0byBhIHRlbXBvcmFyeSBmaWxl IGFuZCBwZXJmb3JtIGFuIGF0b21pYyByZW5hbWUgKHdoaWNoIHByZXZlbnRzIHRoaXMgc29ydCBv ZiB0aGluZy4pPC9kaXY+PC9kaXY+PC9kaXY+PC9kaXY+PC9ibG9ja3F1b3RlPjxkaXY+PGJyPjwv ZGl2PjxkaXY+cG9ydGFnZS51dGlsLndyaXRlX2F0b21pYyBjYW4gZG8gdGhpcyBmb3IgeW91Ljwv ZGl2PjxkaXY+DQo8YnI+PC9kaXY+PGRpdj4tQTwvZGl2PjxkaXY+wqA8L2Rpdj48YmxvY2txdW90 ZSBjbGFzcz0iZ21haWxfcXVvdGUiIHN0eWxlPSJtYXJnaW46MCAwIDAgLjhleDtib3JkZXItbGVm dDoxcHggI2NjYyBzb2xpZDtwYWRkaW5nLWxlZnQ6MWV4Ij48ZGl2IGRpcj0ibHRyIj48ZGl2IGNs YXNzPSJnbWFpbF9leHRyYSI+PGRpdiBjbGFzcz0iZ21haWxfcXVvdGUiPjxkaXYgY2xhc3M9IiI+ PGRpdj4NCsKgPC9kaXY+PGJsb2NrcXVvdGUgY2xhc3M9ImdtYWlsX3F1b3RlIiBzdHlsZT0ibWFy Z2luOjAgMCAwIC44ZXg7Ym9yZGVyLWxlZnQ6MXB4ICNjY2Mgc29saWQ7cGFkZGluZy1sZWZ0OjFl eCI+DQoNCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgd2l0aCBvcGVuKHRyYWNraW5nX3BhdGgsICYj Mzk7dyYjMzk7KSBhcyB0cmFja2luZ19maWxlOjxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAg wqAgwqAgwqAgwqAgZm9yIHBrZywgbXRpbWUgaW4gZmFpbGVkX3BrZ3MuaXRlbXMoKTo8YnI+DQor IMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIHRyYWNraW5nX2Zp bGUud3JpdGUoJiMzOTslcyAlc1xuJiMzOTsgJSAocGtnLCBtdGltZSkpPGJyPg0KKzxicj4NCis8 YnI+DQorIMKgIMKgIMKgIGRlZiBsb2FkKHNlbGYpOjxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAg wqAgJnF1b3Q7JnF1b3Q7JnF1b3Q7PGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCBMb2FkIHBy ZXZpb3VzbHkgZmFpbGVkIG1lcmdlcy48YnI+DQorPGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDC oCBAcnR5cGU6IGRpY3Q8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgIEByZXR1cm46IGRpY3Rp b25hcnkgb2YgcGFja2FnZXMgdGhhdCBmYWlsZWQgdG8gbWVyZ2U8YnI+DQorIMKgIMKgIMKgIMKg IMKgIMKgIMKgICZxdW90OyZxdW90OyZxdW90Ozxicj48L2Jsb2NrcXVvdGU+PGJsb2NrcXVvdGUg Y2xhc3M9ImdtYWlsX3F1b3RlIiBzdHlsZT0ibWFyZ2luOjAgMCAwIC44ZXg7Ym9yZGVyLWxlZnQ6 MXB4ICNjY2Mgc29saWQ7cGFkZGluZy1sZWZ0OjFleCI+KyDCoCDCoCDCoCDCoCDCoCDCoCDCoCB0 cmFja2luZ19wYXRoID0gc2VsZi5fdHJhY2tpbmdfcGF0aDxicj4NCisgwqAgwqAgwqAgwqAgwqAg wqAgwqAgaWYgbm90IG9zLnBhdGguZXhpc3RzKHRyYWNraW5nX3BhdGgpOjxicj4NCisgwqAgwqAg wqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgcmV0dXJuIHt9PGJyPjwvYmxvY2txdW90ZT48ZGl2 Pjxicj48L2Rpdj48L2Rpdj48ZGl2PmlmIG5vdCBzZWxmLmV4aXN0cygpOjwvZGl2PjxkaXY+PGRp diBjbGFzcz0iaDUiPjxkaXY+wqAgcmV0dXJuIHt9PC9kaXY+PGRpdj7CoDwvZGl2PjxibG9ja3F1 b3RlIGNsYXNzPSJnbWFpbF9xdW90ZSIgc3R5bGU9Im1hcmdpbjowIDAgMCAuOGV4O2JvcmRlci1s ZWZ0OjFweCAjY2NjIHNvbGlkO3BhZGRpbmctbGVmdDoxZXgiPg0KDQoNCisgwqAgwqAgwqAgwqAg wqAgwqAgwqAgZmFpbGVkX3BrZ3MgPSB7fTxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgd2l0 aCBvcGVuKHRyYWNraW5nX3BhdGgsICYjMzk7ciYjMzk7KSBhcyB0cmFja2luZ19maWxlOjxicj4N CisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgZm9yIGZhaWxlZF9tZXJnZSBpbiB0 cmFja2luZ19maWxlOjxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAg wqAgwqAgwqAgcGtnLCBtdGltZSA9IGZhaWxlZF9tZXJnZS5zdHJpcCgpLnNwbGl0KCk8YnI+DQor IMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIGZhaWxlZF9wa2dz W3BrZ10gPSBtdGltZTxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgcmV0dXJuIGZhaWxlZF9w a2dzPGJyPg0KKzxicj4NCis8YnI+DQorIMKgIMKgIMKgIGRlZiBleGlzdHMoc2VsZik6PGJyPg0K KyDCoCDCoCDCoCDCoCDCoCDCoCDCoCAmcXVvdDsmcXVvdDsmcXVvdDs8YnI+DQorIMKgIMKgIMKg IMKgIMKgIMKgIMKgIENoZWNrIGlmIHRyYWNraW5nIGZpbGUgZXhpc3RzLjxicj4NCis8YnI+DQor IMKgIMKgIMKgIMKgIMKgIMKgIMKgIEBydHlwZTogYm9vbDxicj4NCisgwqAgwqAgwqAgwqAgwqAg wqAgwqAgQHJldHVybjogdHJ1ZSBpZiB0cmFja2luZyBmaWxlIGV4aXN0cywgZmFsc2Ugb3RoZXJ3 aXNlPGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCAmcXVvdDsmcXVvdDsmcXVvdDs8YnI+DQor IMKgIMKgIMKgIMKgIMKgIMKgIMKgIHJldHVybiBvcy5wYXRoLmV4aXN0cyhzZWxmLl90cmFja2lu Z19wYXRoKTxicj4NCis8YnI+DQorPGJyPg0KKyDCoCDCoCDCoCBkZWYgcHVyZ2Uoc2VsZik6PGJy Pg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCAmcXVvdDsmcXVvdDsmcXVvdDtEZWxldGUgcHJldmlv dXNseSBzYXZlZCB0cmFja2luZyBmaWxlIGlmIG9uZSBleGlzdHMuJnF1b3Q7JnF1b3Q7JnF1b3Q7 PGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCBpZiBzZWxmLmV4aXN0cygpOjxicj4NCisgwqAg wqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgb3MucmVtb3ZlKHNlbGYuX3RyYWNraW5nX3Bh dGgpPGJyPg0KKzxicj4NCis8YnI+DQorY2xhc3MgTWVyZ2VzSGFuZGxlcihvYmplY3QpOjxicj4N CisgwqAgwqAgwqAgJnF1b3Q7JnF1b3Q7JnF1b3Q7SGFuZGxlIGZhaWxlZCBwYWNrYWdlIG1lcmdl cy4mcXVvdDsmcXVvdDsmcXVvdDs8YnI+DQo8ZGl2Pis8YnI+DQorIMKgIMKgIMKgIHNob3J0X2Rl c2MgPSAmcXVvdDtSZW1vdmUgZmFpbGVkIG1lcmdlcyZxdW90Ozxicj4NCis8YnI+DQorIMKgIMKg IMKgIEBzdGF0aWNtZXRob2Q8YnI+DQorIMKgIMKgIMKgIGRlZiBuYW1lKCk6PGJyPg0KKyDCoCDC oCDCoCDCoCDCoCDCoCDCoCByZXR1cm4gJnF1b3Q7bWVyZ2VzJnF1b3Q7PGJyPg0KKzxicj4NCis8 YnI+DQorIMKgIMKgIMKgIGRlZiBfX2luaXRfXyhzZWxmKTo8YnI+DQo8L2Rpdj4rIMKgIMKgIMKg IMKgIMKgIMKgIMKgICZxdW90OyZxdW90OyZxdW90O0NyZWF0ZSBNZXJnZXNIYW5kbGVyIG9iamVj dC4mcXVvdDsmcXVvdDsmcXVvdDs8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgIGVyb290ID0g cG9ydGFnZS5zZXR0aW5nc1smIzM5O0VST09UJiMzOTtdPGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDC oCDCoCB0cmFja2luZ19wYXRoID0gb3MucGF0aC5qb2luKGVyb290LCBQUklWQVRFX1BBVEgsICYj Mzk7ZmFpbGVkLW1lcmdlcyYjMzk7KTs8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgIHNlbGYu X3RyYWNraW5nX2ZpbGUgPSBUcmFja2luZ0ZpbGUodHJhY2tpbmdfcGF0aCk8YnI+DQorIMKgIMKg IMKgIMKgIMKgIMKgIMKgIHNlbGYuX3ZhcmRiX3BhdGggPSBvcy5wYXRoLmpvaW4oZXJvb3QsIFZE Ql9QQVRIKTxicj4NCjxkaXY+Kzxicj4NCis8YnI+DQorIMKgIMKgIMKgIGRlZiBjYW5fcHJvZ3Jl c3NiYXIoc2VsZiwgZnVuYyk6PGJyPg0KPC9kaXY+KyDCoCDCoCDCoCDCoCDCoCDCoCDCoCByZXR1 cm4gZnVuYyA9PSAmIzM5O2NoZWNrJiMzOTs8YnI+DQorPGJyPg0KKzxicj4NCisgwqAgwqAgwqAg ZGVmIF9zY2FuKHNlbGYsIG9uUHJvZ3Jlc3M9Tm9uZSk6PGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDC oCDCoCAmcXVvdDsmcXVvdDsmcXVvdDs8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgIFNjYW4g dGhlIGZpbGUgc3lzdGVtIGZvciBmYWlsZWQgbWVyZ2VzIGFuZCByZXR1cm4gYW55IGZvdW5kLjxi cj4NCis8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgIEBwYXJhbSBvblByb2dyZXNzOiBmdW5j dGlvbiB0byBjYWxsIGZvciB1cGRhdGluZyBwcm9ncmVzczxicj4NCisgwqAgwqAgwqAgwqAgwqAg wqAgwqAgQHR5cGUgb25Qcm9ncmVzczogRnVuY3Rpb248YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKg IMKgIEBydHlwZTogZGljdDxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgQHJldHVybjogZGlj dGlvbmFyeSBvZiBwYWNrYWdlcyB0aGF0IGZhaWxlZCB0byBtZXJnZXM8YnI+DQorIMKgIMKgIMKg IMKgIMKgIMKgIMKgICZxdW90OyZxdW90OyZxdW90Ozxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAg wqAgZmFpbGVkX3BrZ3MgPSB7fTxicj4NCjxkaXY+KyDCoCDCoCDCoCDCoCDCoCDCoCDCoCBmb3Ig Y2F0IGluIG9zLmxpc3RkaXIoc2VsZi5fdmFyZGJfcGF0aCk6PGJyPg0KPC9kaXY+KyDCoCDCoCDC oCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCBwa2dzX3BhdGggPSBvcy5wYXRoLmpvaW4oc2VsZi5f dmFyZGJfcGF0aCwgY2F0KTxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAg aWYgbm90IG9zLnBhdGguaXNkaXIocGtnc19wYXRoKTo8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKg IMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIGNvbnRpbnVlPGJyPg0KKyDCoCDCoCDCoCDCoCDC oCDCoCDCoCDCoCDCoCDCoCDCoCBwa2dzID0gb3MubGlzdGRpcihwa2dzX3BhdGgpPGJyPg0KPGRp dj4rIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIG1heHZhbCA9IGxlbihwa2dzKTxi cj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgZm9yIGksIHBrZyBpbiBlbnVt ZXJhdGUocGtncyk6PGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDC oCDCoCDCoCBpZiBvblByb2dyZXNzOjxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAg wqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgb25Qcm9ncmVzcyhtYXh2YWwsIGkrMSk8YnI+ DQo8L2Rpdj4rIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIGlm IE1FUkdJTkdfSURFTlRJRklFUiBpbiBwa2c6PGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCDC oCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCBtdGltZSA9IGludChvcy5zdGF0KG9z LnBhdGguam9pbihwa2dzX3BhdGgsIHBrZykpLnN0X210aW1lKTxicj4NCisgwqAgwqAgwqAgwqAg wqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgcGtnID0gb3MucGF0 aC5qb2luKGNhdCwgcGtnKTxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAg wqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgZmFpbGVkX3BrZ3NbcGtnXSA9IG10aW1lPGJyPg0KKyDC oCDCoCDCoCDCoCDCoCDCoCDCoCByZXR1cm4gZmFpbGVkX3BrZ3M8YnI+DQorPGJyPg0KKzxicj4N CisgwqAgwqAgwqAgZGVmIF9mYWlsZWRfcGtncyhzZWxmLCBvblByb2dyZXNzPU5vbmUpOjxicj4N CisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgJnF1b3Q7JnF1b3Q7JnF1b3Q7PGJyPg0KKyDCoCDCoCDC oCDCoCDCoCDCoCDCoCBSZXR1cm4gZmFpbGVkIHBhY2thZ2VzIGZyb20gYm90aCB0aGUgZmlsZSBz eXN0ZW0gYW5kIHRyYWNraW5nIGZpbGUuPGJyPg0KKzxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAg wqAgQHJ0eXBlOiBkaWN0PGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCBAcmV0dXJuOiBkaWN0 aW9uYXJ5IG9mIHBhY2thZ2VzIHRoYXQgZmFpbGVkIHRvIG1lcmdlczxicj4NCisgwqAgwqAgwqAg wqAgwqAgwqAgwqAgJnF1b3Q7JnF1b3Q7JnF1b3Q7PGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDC oCBmYWlsZWRfcGtncyA9IHNlbGYuX3NjYW4ob25Qcm9ncmVzcyk8YnI+PC9ibG9ja3F1b3RlPjxk aXY+PGJyPjwvZGl2PjwvZGl2PjwvZGl2PjxkaXY+UGVyaGFwcyBhZGQgYW4gX19pdGVyX18gZnVu Y3Rpb24gdG8gVHJhY2tpbmdGaWxlLCB0aGF0IGltcGxpY2l0bHkgY2FsbHMgbG9hZD88L2Rpdj48 ZGl2Pjxicj48L2Rpdj48ZGl2PlRoZW4gdGhpcyBjYW4gYmU6PC9kaXY+DQo8ZGl2Pg0KZm9yIHBr ZywgbXRpbWUgaW4gc2VsZi5fdHJhY2tpbmdfZmlsZTo8L2Rpdj48ZGl2Pjxicj48L2Rpdj48ZGl2 Pj88L2Rpdj48ZGl2Pjxicj48L2Rpdj48ZGl2Pkp1c3QgYSB0aG91Z2h0LjwvZGl2PjxkaXYgY2xh c3M9IiI+PGRpdj7CoDwvZGl2PjxibG9ja3F1b3RlIGNsYXNzPSJnbWFpbF9xdW90ZSIgc3R5bGU9 Im1hcmdpbjowIDAgMCAuOGV4O2JvcmRlci1sZWZ0OjFweCAjY2NjIHNvbGlkO3BhZGRpbmctbGVm dDoxZXgiPg0KDQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgIGZvciBwa2csIG10aW1lIGluIHNlbGYu X3RyYWNraW5nX2ZpbGUubG9hZCgpLml0ZW1zKCk6PGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDC oCDCoCDCoCDCoCDCoCBpZiBwa2cgbm90IGluIGZhaWxlZF9wa2dzOjxicj4NCisgwqAgwqAgwqAg wqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgZmFpbGVkX3BrZ3NbcGtnXSA9IG10 aW1lPGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCByZXR1cm4gZmFpbGVkX3BrZ3M8YnI+DQor PGJyPg0KKzxicj4NCisgwqAgwqAgwqAgZGVmIF9yZW1vdmVfZmFpbGVkX2RpcnMoc2VsZiwgZmFp bGVkX3BrZ3MpOjxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgJnF1b3Q7JnF1b3Q7JnF1b3Q7 PGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCBSZW1vdmUgdGhlIGRpcmVjdG9yaWVzIG9mIHBh Y2thZ2VzIHRoYXQgZmFpbGVkIHRvIG1lcmdlLjxicj4NCis8YnI+DQorIMKgIMKgIMKgIMKgIMKg IMKgIMKgIEBwYXJhbSBmYWlsZWRfcGtnczogZmFpbGVkIHBhY2thZ2VzIHdob3NlIGRpcmVjdG9y aWVzIHRvIHJlbW92ZTxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgQHR5cGUgZmFpbGVkX3Br ZzogZGljdDxicj4NCjxkaXY+KyDCoCDCoCDCoCDCoCDCoCDCoCDCoCAmcXVvdDsmcXVvdDsmcXVv dDs8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgIGZvciBmYWlsZWRfcGtnIGluIGZhaWxlZF9w a2dzOjxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgcGtnX3BhdGggPSBv cy5wYXRoLmpvaW4oc2VsZi5fdmFyZGJfcGF0aCwgZmFpbGVkX3BrZyk8YnI+DQo8L2Rpdj4rIMKg IMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgICMgZGVsZXRlIGZhaWxlZCBtZXJnZSBkaXJl Y3RvcnkgaWYgaXQgZXhpc3RzIChpdCBtaWdodCBub3QgZXhpc3Q8YnI+DQorIMKgIMKgIMKgIMKg IMKgIMKgIMKgIMKgIMKgIMKgIMKgICMgaWYgbG9hZGVkIGZyb20gdHJhY2tpbmcgZmlsZSk8YnI+ DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIGlmIG9zLnBhdGguZXhpc3RzKHBr Z19wYXRoKTo8YnI+DQo8ZGl2PisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAg wqAgwqAgwqAgc2h1dGlsLnJtdHJlZShwa2dfcGF0aCk8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKg IMKgIMKgIMKgIMKgIMKgICMgVE9ETzogdHJ5IHJlbW92aW5nIHBhY2thZ2UgY29udGVudHMgdG8g cHJldmVudCBvcnBoYW5lZDxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAg IyBmaWxlczxicj48L2Rpdj48L2Jsb2NrcXVvdGU+PGRpdj48YnI+PC9kaXY+PC9kaXY+PGRpdj5J IGRvbiYjMzk7dCBnZXQgdGhpcyBUT0RPLCBjYW4geW91IGVsYWJvcmF0ZT88L2Rpdj48ZGl2IGNs YXNzPSIiPjxkaXY+wqA8L2Rpdj48YmxvY2txdW90ZSBjbGFzcz0iZ21haWxfcXVvdGUiIHN0eWxl PSJtYXJnaW46MCAwIDAgLjhleDtib3JkZXItbGVmdDoxcHggI2NjYyBzb2xpZDtwYWRkaW5nLWxl ZnQ6MWV4Ij4NCg0KPGRpdj4NCis8YnI+DQorPGJyPg0KPC9kaXY+KyDCoCDCoCDCoCBkZWYgX2dl dF9wa2dfYXRvbXMoc2VsZiwgZmFpbGVkX3BrZ3MsIHBrZ19hdG9tcywgcGtnX2RuZSk6PGJyPg0K KyDCoCDCoCDCoCDCoCDCoCDCoCDCoCAmcXVvdDsmcXVvdDsmcXVvdDs8YnI+DQorIMKgIMKgIMKg IMKgIMKgIMKgIMKgIEdldCB0aGUgcGFja2FnZSBhdG9tcyBmb3IgdGhlIHNwZWNpZmllZCBmYWls ZWQgcGFja2FnZXMuPGJyPg0KKzxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgQHBhcmFtIGZh aWxlZF9wa2dzOiBmYWlsZWQgcGFja2FnZXMgdG8gaXRlcmF0ZTxicj4NCisgwqAgwqAgwqAgwqAg wqAgwqAgwqAgQHR5cGUgZmFpbGVkX3BrZ3M6IGRpY3Q8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKg IMKgIEBwYXJhbSBwa2dfYXRvbXM6IGFwcGVuZCBwYWNrYWdlIGF0b21zIHRvIHRoaXMgc2V0PGJy Pg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCBAdHlwZSBwa2dfYXRvbXM6IHNldDxicj4NCisgwqAg wqAgwqAgwqAgwqAgwqAgwqAgQHBhcmFtIHBrZ19kbmU6IGFwcGVuZCBhbnkgcGFja2FnZXMgYXRv bXMgdGhhdCBhcmUgaW52YWxpZCB0byB0aGlzIHNldDxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAg wqAgQHR5cGUgcGtnX2RuZTogc2V0PGJyPjwvYmxvY2txdW90ZT48ZGl2Pjxicj48L2Rpdj48L2Rp dj48ZGl2Pk5vdCBmb2xsb3dpbmcgd2hhdCBkbmUgbWVhbnMuPC9kaXY+PGRpdj48ZGl2IGNsYXNz PSJoNSI+PGRpdj7CoDwvZGl2PjxibG9ja3F1b3RlIGNsYXNzPSJnbWFpbF9xdW90ZSIgc3R5bGU9 Im1hcmdpbjowIDAgMCAuOGV4O2JvcmRlci1sZWZ0OjFweCAjY2NjIHNvbGlkO3BhZGRpbmctbGVm dDoxZXgiPg0KDQoNCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgJnF1b3Q7JnF1b3Q7JnF1b3Q7PGJy Pg0KKzxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgZW1lcmdlX2NvbmZpZyA9IGxvYWRfZW1l cmdlX2NvbmZpZygpPGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCBwb3J0ZGIgPSBlbWVyZ2Vf Y29uZmlnLnRhcmdldF9jb25maWcudHJlZXNbJiMzOTtwb3J0dHJlZSYjMzk7XS5kYmFwaTxicj4N CjxkaXY+KyDCoCDCoCDCoCDCoCDCoCDCoCDCoCBmb3IgZmFpbGVkX3BrZyBpbiBmYWlsZWRfcGtn czo8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgICMgdmFsaWRhdGUgcGtn IG5hbWU8YnI+DQo8L2Rpdj4rIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIHBrZ19u YW1lID0gJiMzOTslcyYjMzk7ICUgZmFpbGVkX3BrZy5yZXBsYWNlKE1FUkdJTkdfSURFTlRJRklF UiwgJiMzOTsmIzM5Oyk8YnI+DQo8ZGl2PisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAg wqAgcGtnX2F0b20gPSAmIzM5Oz0lcyYjMzk7ICUgcGtnX25hbWU8YnI+DQorPGJyPg0KKyDCoCDC oCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCBpZiBub3QgaXN2YWxpZGF0b20ocGtnX2F0b20p Ojxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgcGtn X2RuZS5hcHBlbmQoICZxdW90OyYjMzk7JXMmIzM5OyBpcyBhbiBpbnZhbGlkIHBhY2thZ2UgYXRv bS4mcXVvdDsgJSBwa2dfYXRvbSk8YnI+DQo8L2Rpdj4rIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKg IMKgIMKgIMKgIGlmIG5vdCBwb3J0ZGIuY3B2X2V4aXN0cyhwa2dfbmFtZSk6PGJyPg0KPGRpdj4r IMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIHBrZ19kbmUuYXBw ZW5kKCAmcXVvdDsmIzM5OyVzJiMzOTsgZG9lcyBub3QgZXhpc3QgaW4gdGhlIHBvcnRhZ2UgdHJl ZS4mcXVvdDs8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKg IMKgIMKgIMKgIMKgIMKgICUgcGtnX25hbWUpPGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCDC oCDCoCDCoCDCoCBwa2dfYXRvbXMuYWRkKHBrZ19hdG9tKTxicj4NCis8YnI+DQo8L2Rpdj4rPGJy Pg0KKyDCoCDCoCDCoCBkZWYgX2VtZXJnZV9wa2dfYXRvbXMoc2VsZiwgbW9kdWxlX291dHB1dCwg cGtnX2F0b21zKTo8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgICZxdW90OyZxdW90OyZxdW90 Ozxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgRW1lcmdlIHRoZSBzcGVjaWZpZWQgcGFja2Fn ZXMgYXRvbXMuPGJyPg0KKzxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgQHBhcmFtIG1vZHVs ZV9vdXRwdXQ6IG91dHB1dCB3aWxsIGJlIHdyaXR0ZW4gdG88YnI+DQorIMKgIMKgIMKgIMKgIMKg IMKgIMKgIEB0eXBlIG1vZHVsZV9vdXRwdXQ6IENsYXNzPGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDC oCDCoCBAcGFyYW0gcGtnX2F0b21zOiBwYWNrYWdlcyBhdG9tcyB0byBlbWVyZ2U8YnI+DQorIMKg IMKgIMKgIMKgIMKgIMKgIMKgIEB0eXBlIHBrZ19hdG9tczogc2V0PGJyPg0KKyDCoCDCoCDCoCDC oCDCoCDCoCDCoCBAcnR5cGU6IGxpc3Q8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgIEByZXR1 cm46IExpc3Qgb2YgcmVzdWx0czxicj4NCjxkaXY+PGRpdj4rIMKgIMKgIMKgIMKgIMKgIMKgIMKg ICZxdW90OyZxdW90OyZxdW90Ozxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgIyBUT0RPOiBy ZXdyaXRlIGNvZGUgdG8gdXNlIHBvcnRhZ2UmIzM5O3MgQVBJcyBpbnN0ZWFkIG9mIGEgc3VicHJv Y2Vzczxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgZW52ID0gezxicj4NCisgwqAgwqAgwqAg wqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgJnF1b3Q7RkVBVFVSRVMmcXVvdDsgOiAmcXVvdDstY29s bGlzaW9uLWRldGVjdCAtcHJvdGVjdC1vd25lZCZxdW90Oyw8YnI+DQorIMKgIMKgIMKgIMKgIMKg IMKgIMKgIMKgIMKgIMKgIMKgICZxdW90O1BBVEgmcXVvdDsgOiBvcy5lbnZpcm9uWyZxdW90O1BB VEgmcXVvdDtdPGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCB9PGJyPg0KKyDCoCDCoCDCoCDC oCDCoCDCoCDCoCBlbWVyZ2VfY21kID0gKDxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAg wqAgwqAgwqAgcG9ydGFnZS5fcHl0aG9uX2ludGVycHJldGVyLDxicj4NCisgwqAgwqAgwqAgwqAg wqAgwqAgwqAgwqAgwqAgwqAgwqAgJiMzOTstYiYjMzk7LDxicj4NCisgwqAgwqAgwqAgwqAgwqAg wqAgwqAgwqAgwqAgwqAgwqAgb3MucGF0aC5qb2luKFBPUlRBR0VfQklOX1BBVEgsICYjMzk7ZW1l cmdlJiMzOTspLDxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgJiMzOTst LXF1aWV0JiMzOTssPGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCAmIzM5 Oy0tb25lc2hvdCYjMzk7LDxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAg JiMzOTstLWNvbXBsZXRlLWdyYXBoPXkmIzM5Ozxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAg KTxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgcmVzdWx0cyA9IFtdPGJyPg0KKyDCoCDCoCDC oCDCoCDCoCDCoCDCoCBtc2cgPSAmIzM5O1JlLUVtZXJnaW5nIHBhY2thZ2VzIHRoYXQgZmFpbGVk IHRvIG1lcmdlLi4uXG4mIzM5Ozxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgaWYgbW9kdWxl X291dHB1dDo8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIG1vZHVsZV9v dXRwdXQud3JpdGUobXNnKTxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgZWxzZTo8YnI+DQor IMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIG1vZHVsZV9vdXRwdXQgPSBzdWJwcm9j ZXNzLlBJUEU8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIHJlc3VsdHMu YXBwZW5kKG1zZyk8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgIHByb2MgPSBzdWJwcm9jZXNz LlBvcGVuKGVtZXJnZV9jbWQgKyB0dXBsZShwa2dfYXRvbXMpLCBlbnY9ZW52LDxicj4NCisgwqAg wqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgc3Rkb3V0PW1vZHVsZV9vdXRwdXQsIHN0ZGVy cj1zeXMuc3RkZXJyKTxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgb3V0cHV0ID0gcHJvYy5j b21tdW5pY2F0ZSgpWzBdPGJyPjwvZGl2PjwvZGl2PjwvYmxvY2txdW90ZT48ZGl2Pjxicj48L2Rp dj48L2Rpdj48L2Rpdj48ZGl2PlRoaXMgc2VlbXMgc29ydCBvZiB3ZWlyZCwgcGVyaGFwczo8L2Rp dj48ZGl2Pjxicj48L2Rpdj48ZGl2Pm91dHB1dCwgXyA9IHByb2MuY29tbXVuaWNhdGUoKTwvZGl2 PjxkaXY+PGRpdiBjbGFzcz0iaDUiPg0KPGRpdj7CoDwvZGl2PjxibG9ja3F1b3RlIGNsYXNzPSJn bWFpbF9xdW90ZSIgc3R5bGU9Im1hcmdpbjowIDAgMCAuOGV4O2JvcmRlci1sZWZ0OjFweCAjY2Nj IHNvbGlkO3BhZGRpbmctbGVmdDoxZXgiPg0KPGRpdj48ZGl2Pg0KKyDCoCDCoCDCoCDCoCDCoCDC oCDCoCBpZiBvdXRwdXQ6PGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCBy ZXN1bHRzLmFwcGVuZChvdXRwdXQpPGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCBpZiBwcm9j LnJldHVybmNvZGUgIT0gb3MuRVhfT0s6PGJyPg0KPC9kaXY+PC9kaXY+KyDCoCDCoCDCoCDCoCDC oCDCoCDCoCDCoCDCoCDCoCDCoCDCoGVtZXJnZV9zdGF0dXMgPSAmcXVvdDtGYWlsZWQgdG8gZW1l cmdlICYjMzk7JXMmIzM5OyZxdW90OyAlICgmIzM5OyAmIzM5Oy5qb2luKHBrZ19hdG9tcykpPGJy Pg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCBlbHNlOjxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAg wqAgwqAgwqAgwqAgwqAgwqBlbWVyZ2Vfc3RhdHVzID0gJnF1b3Q7U3VjY2Vzc2Z1bGx5IGVtZXJn ZWQgJiMzOTslcyYjMzk7JnF1b3Q7ICUgKCYjMzk7ICYjMzk7LmpvaW4ocGtnX2F0b21zKSk8YnI+ DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgIHJlc3VsdHMuYXBwZW5kKGVtZXJnZV9zdGF0dXMpPGJy Pg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCByZXR1cm4gcmVzdWx0czxicj4NCjxkaXY+Kzxicj4N Cis8YnI+DQorIMKgIMKgIMKgIGRlZiBjaGVjayhzZWxmLCAqKmt3YXJncyk6PGJyPg0KPC9kaXY+ KyDCoCDCoCDCoCDCoCDCoCDCoCDCoCAmcXVvdDsmcXVvdDsmcXVvdDtDaGVjayBmb3IgZmFpbGVk IG1lcmdlcy4mcXVvdDsmcXVvdDsmcXVvdDs8YnI+DQo8ZGl2PisgwqAgwqAgwqAgwqAgwqAgwqAg wqAgb25Qcm9ncmVzcyA9IGt3YXJncy5nZXQoJiMzOTtvblByb2dyZXNzJiMzOTssIE5vbmUpPGJy Pg0KPC9kaXY+KyDCoCDCoCDCoCDCoCDCoCDCoCDCoCBmYWlsZWRfcGtncyA9IHNlbGYuX2ZhaWxl ZF9wa2dzKG9uUHJvZ3Jlc3MpPGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCBlcnJvcnMgPSBb XTxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgZm9yIHBrZywgbXRpbWUgaW4gZmFpbGVkX3Br Z3MuaXRlbXMoKTo8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIG10aW1l X3N0ciA9IHRpbWUuY3RpbWUoaW50KG10aW1lKSk8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKg IMKgIMKgIMKgIMKgIGVycm9ycy5hcHBlbmQoJnF1b3Q7JiMzOTslcyYjMzk7IGZhaWxlZCB0byBt ZXJnZSBvbiAmIzM5OyVzJiMzOTsmcXVvdDsgJSAocGtnLCBtdGltZV9zdHIpKTxicj4NCjxkaXY+ KyDCoCDCoCDCoCDCoCDCoCDCoCDCoCByZXR1cm4gZXJyb3JzPGJyPg0KKzxicj4NCis8YnI+DQor IMKgIMKgIMKgIGRlZiBmaXgoc2VsZiwgKiprd2FyZ3MpOjxicj4NCjwvZGl2PisgwqAgwqAgwqAg wqAgwqAgwqAgwqAgJnF1b3Q7JnF1b3Q7JnF1b3Q7QXR0ZW1wdCB0byBmaXggYW55IGZhaWxlZCBt ZXJnZXMuJnF1b3Q7JnF1b3Q7JnF1b3Q7PGJyPg0KPGRpdj4rIMKgIMKgIMKgIMKgIMKgIMKgIMKg IG1vZHVsZV9vdXRwdXQgPSBrd2FyZ3MuZ2V0KCYjMzk7bW9kdWxlX291dHB1dCYjMzk7LCBOb25l KTxicj4NCjwvZGl2PisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgZmFpbGVkX3BrZ3MgPSBzZWxmLl9m YWlsZWRfcGtncygpPGJyPg0KPGRpdj4rIMKgIMKgIMKgIMKgIMKgIMKgIMKgIGlmIG5vdCBmYWls ZWRfcGtnczo8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIHJldHVybiBb ICYjMzk7Tm8gZmFpbGVkIG1lcmdlcyBmb3VuZC4gJiMzOTsgXTxicj48L2Rpdj48L2Jsb2NrcXVv dGU+PGRpdj48YnI+PC9kaXY+PC9kaXY+PC9kaXY+PGRpdj5MZXNzIHNwYWNpbmcgaGVyZTo8L2Rp dj48ZGl2IGNsYXNzPSIiPjxkaXY+cmV0dXJuIFsmIzM5O05vIGZhaWxlZCBtZXJnZXMgZm91bmQu JiMzOTtdPC9kaXY+PGRpdj4NCsKgPC9kaXY+PGJsb2NrcXVvdGUgY2xhc3M9ImdtYWlsX3F1b3Rl IiBzdHlsZT0ibWFyZ2luOjAgMCAwIC44ZXg7Ym9yZGVyLWxlZnQ6MXB4ICNjY2Mgc29saWQ7cGFk ZGluZy1sZWZ0OjFleCI+DQo8ZGl2Pg0KKzxicj4NCjwvZGl2PisgwqAgwqAgwqAgwqAgwqAgwqAg wqAgcGtnX2RuZSA9IHNldCgpPGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCBwa2dfYXRvbXMg PSBzZXQoKTxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgc2VsZi5fZ2V0X3BrZ19hdG9tcyhm YWlsZWRfcGtncywgcGtnX2F0b21zLCBwa2dfZG5lKTxicj4NCjxkaXY+KyDCoCDCoCDCoCDCoCDC oCDCoCDCoCBpZiBwa2dfZG5lOjxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAg wqAgcmV0dXJuIHBrZ19kbmU8YnI+DQorPGJyPg0KPC9kaXY+KyDCoCDCoCDCoCDCoCDCoCDCoCDC oCB0cnk6PGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCBzZWxmLl90cmFj a2luZ19maWxlLnNhdmUoZmFpbGVkX3BrZ3MpPGJyPg0KPGRpdj4rIMKgIMKgIMKgIMKgIMKgIMKg IMKgIGV4Y2VwdCBJT0Vycm9yIGFzIGV4Ojxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAg wqAgwqAgwqAgZXJyb3JzID0gWyAmIzM5O1VuYWJsZSB0byBzYXZlIGZhaWxlZCBtZXJnZXMgdG8g dHJhY2tpbmcgZmlsZTogJXNcbiYjMzk7PGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDC oCDCoCDCoCDCoCDCoCDCoCDCoCAlIHN0cihleCkgXTxicj48L2Rpdj48L2Jsb2NrcXVvdGU+PGRp dj48YnI+PC9kaXY+PC9kaXY+PGRpdj5TYW1lIGhlcmUsIG5vIHNwYWNlcyBiZWZvcmUgb3IgYWZ0 ZXIgWyBhbmQgXS48L2Rpdj48ZGl2PjxkaXYgY2xhc3M9Img1Ij48ZGl2PsKgPGJyPjwvZGl2Pjxi bG9ja3F1b3RlIGNsYXNzPSJnbWFpbF9xdW90ZSIgc3R5bGU9Im1hcmdpbjowIDAgMCAuOGV4O2Jv cmRlci1sZWZ0OjFweCAjY2NjIHNvbGlkO3BhZGRpbmctbGVmdDoxZXgiPg0KDQo8ZGl2Pg0KPC9k aXY+KyDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCBlcnJvcnMuYXBwZW5kKCYjMzk7 LCAmIzM5Oy5qb2luKHNvcnRlZChmYWlsZWRfcGtncykpKTxicj4NCisgwqAgwqAgwqAgwqAgwqAg wqAgwqAgwqAgwqAgwqAgwqAgcmV0dXJuIGVycm9yczxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAg wqAgc2VsZi5fcmVtb3ZlX2ZhaWxlZF9kaXJzKGZhaWxlZF9wa2dzKTxicj4NCisgwqAgwqAgwqAg wqAgwqAgwqAgwqAgcmVzdWx0cyA9IHNlbGYuX2VtZXJnZV9wa2dfYXRvbXMobW9kdWxlX291dHB1 dCwgcGtnX2F0b21zKTxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgIyBsaXN0IGFueSBuZXcg ZmFpbGVkIG1lcmdlczxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgZm9yIHBrZyBpbiBzb3J0 ZWQoc2VsZi5fc2NhbigpKTo8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKg IHJlc3VsdHMuYXBwZW5kKCZxdW90OyYjMzk7JXMmIzM5OyBzdGlsbCBmb3VuZCBhcyBhIGZhaWxl ZCBtZXJnZS4mcXVvdDsgJSBwa2cpPGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCAjIHJlbG9h ZCBjb25maWcgYW5kIHJlbW92ZSBzdWNjZXNzZnVsIHBhY2thZ2VzIGZyb20gdHJhY2tpbmcgZmls ZTxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgZW1lcmdlX2NvbmZpZyA9IGxvYWRfZW1lcmdl X2NvbmZpZygpPGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCB2YXJkYiA9IGVtZXJnZV9jb25m aWcudGFyZ2V0X2NvbmZpZy50cmVlc1smIzM5O3ZhcnRyZWUmIzM5O10uZGJhcGk8YnI+DQorIMKg IMKgIMKgIMKgIMKgIMKgIMKgIHN0aWxsX2ZhaWxlZF9wa2dzID0ge308YnI+DQorIMKgIMKgIMKg IMKgIMKgIMKgIMKgIGZvciBwa2csIG10aW1lIGluIGZhaWxlZF9wa2dzLml0ZW1zKCk6PGJyPg0K KyDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCBwa2dfbmFtZSA9ICYjMzk7JXMmIzM5 OyAlIHBrZy5yZXBsYWNlKE1FUkdJTkdfSURFTlRJRklFUiwgJiMzOTsmIzM5Oyk8YnI+DQorIMKg IMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIGlmIG5vdCB2YXJkYi5jcHZfZXhpc3RzKHBr Z19uYW1lKTo8YnI+DQorIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKg IMKgIHN0aWxsX2ZhaWxlZF9wa2dzW3BrZ10gPSBtdGltZTxicj4NCisgwqAgwqAgwqAgwqAgwqAg wqAgwqAgc2VsZi5fdHJhY2tpbmdfZmlsZS5zYXZlKHN0aWxsX2ZhaWxlZF9wa2dzKTxicj4NCisg wqAgwqAgwqAgwqAgwqAgwqAgwqAgcmV0dXJuIHJlc3VsdHM8YnI+DQorPGJyPg0KKzxicj4NCisg wqAgwqAgwqAgZGVmIHB1cmdlKHNlbGYsICoqa3dhcmdzKTo8YnI+DQorIMKgIMKgIMKgIMKgIMKg IMKgIMKgICZxdW90OyZxdW90OyZxdW90O0F0dGVtcHQgdG8gcmVtb3ZlIHByZXZpb3VzbHkgc2F2 ZWQgdHJhY2tpbmcgZmlsZS4mcXVvdDsmcXVvdDsmcXVvdDs8YnI+DQorIMKgIMKgIMKgIMKgIMKg IMKgIMKgIGlmIG5vdCBzZWxmLl90cmFja2luZ19maWxlLmV4aXN0cygpOjxicj4NCisgwqAgwqAg wqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgcmV0dXJuIFsmIzM5O1RyYWNraW5nIGZpbGUgbm90 IGZvdW5kLiYjMzk7XTxicj4NCisgwqAgwqAgwqAgwqAgwqAgwqAgwqAgc2VsZi5fdHJhY2tpbmdf ZmlsZS5wdXJnZSgpPGJyPg0KKyDCoCDCoCDCoCDCoCDCoCDCoCDCoCByZXR1cm4gWyYjMzk7UmVt b3ZlZCB0cmFja2luZyBmaWxlLiYjMzk7XTxicj4NCjxzcGFuPjxmb250IGNvbG9yPSIjODg4ODg4 Ij4tLTxicj4NCjEuOC4zLjI8YnI+DQo8YnI+DQo8YnI+DQo8L2ZvbnQ+PC9zcGFuPjwvYmxvY2tx dW90ZT48L2Rpdj48L2Rpdj48L2Rpdj48YnI+PC9kaXY+PC9kaXY+DQo8L2Jsb2NrcXVvdGU+PC9k aXY+PGJyPjwvZGl2PjwvZGl2Pg0K --001a1136b666385f8a04f48aac71--