@echo off
setlocal

:: ── Nimgir Installer v2_2 (self-contained) ───────────────
:: Finds Python 3 or auto-installs it, then runs embedded
:: installer.
:: ──────────────────────────────────────────────────────────

set "NIMGIR_LOG=%~dp0nimgir_install.log"
set "TEMP_PY=%TEMP%\nimgir_setup_%RANDOM%.py"
set "PY="

echo [%date% %time%] Nimgir installer starting > "%NIMGIR_LOG%"
echo [%date% %time%] TEMP_PY=%TEMP_PY% >> "%NIMGIR_LOG%"

:: Try py launcher.
py -3 --version >nul 2>&1 && set "PY=py -3" && goto :run

:: Try python on PATH.
python --version >nul 2>&1 && set "PY=python" && goto :run

:: Try common install locations.
for %%P in (
    "C:\Python313\python.exe"
    "C:\Python312\python.exe"
    "C:\Python311\python.exe"
    "C:\Python310\python.exe"
    "%LOCALAPPDATA%\Programs\Python\Python313\python.exe"
    "%LOCALAPPDATA%\Programs\Python\Python312\python.exe"
    "%LOCALAPPDATA%\Programs\Python\Python311\python.exe"
    "%LOCALAPPDATA%\Programs\Python\Python310\python.exe"
) do (
    if exist %%P (
        set "PY=%%~P"
        goto :run
    )
)

:: ── Python not found — attempt automatic installation ──────
echo.
echo  Python 3 not found. Downloading and installing automatically...
echo.

set "PY_INSTALLER=%TEMP%\python_installer_%RANDOM%.exe"
:: Update this URL when a newer Python is available.
set "PY_URL=https://www.python.org/ftp/python/3.13.13/python-3.13.13-amd64.exe"

echo  Downloading from python.org (this may take a minute)...
powershell -NoProfile -Command ^
    "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;" ^
    "try { Invoke-WebRequest -Uri '%PY_URL%' -OutFile '%PY_INSTALLER%' -UseBasicParsing } catch { exit 1 }"

if not exist "%PY_INSTALLER%" (
    echo.
    echo  Download failed. Please install Python manually from:
    echo    https://www.python.org/downloads/
    echo.
    pause
    exit /b 1
)

echo  Installing Python silently (this may take a minute)...
"%PY_INSTALLER%" /quiet InstallAllUsers=0 PrependPath=1 Include_launcher=1
set "PY_EXIT=%ERRORLEVEL%"
del "%PY_INSTALLER%" 2>nul

if not "%PY_EXIT%"=="0" (
    echo.
    echo  Python installer returned an error (code %PY_EXIT%).
    echo  Please install Python manually from:
    echo    https://www.python.org/downloads/
    echo.
    pause
    exit /b 1
)

echo  Python installed. Verifying...

:: py launcher uses the registry — works without PATH refresh.
py -3 --version >nul 2>&1 && set "PY=py -3" && goto :run

:: Try the default per-user install location.
for %%P in (
    "%LOCALAPPDATA%\Programs\Python\Python313\python.exe"
    "%LOCALAPPDATA%\Programs\Python\Python312\python.exe"
) do (
    if exist %%P (
        set "PY=%%~P"
        goto :run
    )
)

echo.
echo  Python was installed but could not be located in this session.
echo  Please close this window and run the installer again.
echo.
pause
exit /b 1

:run
echo [%date% %time%] Python found: PY=%PY% >> "%NIMGIR_LOG%"

:: Extract embedded Python code (everything after the marker).
echo [%date% %time%] Extracting Python payload... >> "%NIMGIR_LOG%"
powershell -NoProfile -Command ^
    "$c = Get-Content -Path '%~f0' -Raw;" ^
    "$m = '# __NIMGIR_' + 'PAYLOAD__';" ^
    "$i = $c.IndexOf($m);" ^
    "if($i -ge 0){ $c.Substring($i + $m.Length).TrimStart() | Set-Content -Path '%TEMP_PY%' -Encoding UTF8 -NoNewline }"

if not exist "%TEMP_PY%" (
    echo [%date% %time%] FAIL: Payload extraction failed - %TEMP_PY% not created >> "%NIMGIR_LOG%"
    echo.
    echo  ERROR: Could not extract installer payload.
    echo  Log file: %NIMGIR_LOG%
    echo.
    pause
    exit /b 1
)

echo [%date% %time%] Payload extracted. Size: >> "%NIMGIR_LOG%"
for %%A in ("%TEMP_PY%") do echo [%date% %time%]   %TEMP_PY% = %%~zA bytes >> "%NIMGIR_LOG%"

echo [%date% %time%] Running: %PY% "%TEMP_PY%" >> "%NIMGIR_LOG%"
%PY% "%TEMP_PY%" 2>>"%NIMGIR_LOG%"
set "PY_RESULT=%ERRORLEVEL%"
echo [%date% %time%] Python exited with code: %PY_RESULT% >> "%NIMGIR_LOG%"
del "%TEMP_PY%" 2>nul
if not "%PY_RESULT%"=="0" (
    echo.
    echo  Installer encountered an error ^(exit code %PY_RESULT%^).
    echo  Log file: %NIMGIR_LOG%
    echo.
    pause
)
exit /b %PY_RESULT%
# __NIMGIR_PAYLOAD__
#!/usr/bin/env python3
"""Nimgir Setup v2_2 — embedded single-file installer for Claude Desktop.

Auto-generated by build_installer.py. Do not edit directly.
Edit build_installer_v2.py or mcp_server.py, then re-run the build script.
"""

import base64
import glob
import json
import os
import re
import shutil
import subprocess
import sys
import webbrowser
import tkinter as tk
from tkinter import font as tkfont
from pathlib import Path

# ── Install log (temporary debug aid) ─────────────────────────

_INSTALL_LOG = os.environ.get(
    "NIMGIR_LOG",
    os.path.join(
        os.environ.get("TEMP", os.path.dirname(os.path.abspath(__file__))),
        "nimgir_install.log",
    ),
)

def _ilog(msg):
    """Append a line to the install log file."""
    try:
        with open(_INSTALL_LOG, "a", encoding="utf-8") as f:
            f.write(f"[PY] {msg}\n")
    except OSError:
        pass

_ilog(f"Python payload starting. Python {sys.version}")
_ilog(f"Script: {os.path.abspath(__file__)}")
_ilog(f"CWD: {os.getcwd()}")

# ── Constants ──────────────────────────────────────────────────

SERVER_NAME = "nimgir"
MIN_PYTHON = (3, 10)
WORKSPACE_PATTERN = re.compile(r"^[a-z][a-z0-9_]{0,63}$")
SETTINGS_URL = "https://claude.ai/settings"

INSTALL_DIR = Path(os.environ.get("LOCALAPPDATA", "")) / "Nimgir"


def find_claude_config():
    """Find the Claude Desktop config directory and config file.

    Checks two locations in order:
      1. %APPDATA%\\Claude\\  — standalone .exe installer
      2. %LOCALAPPDATA%\\Packages\\Claude_*\\LocalCache\\Roaming\\Claude\\
         — MSIX package (from claude.com/download on newer Windows)

    Returns (config_dir: Path, config_path: Path) for the first match
    that exists, or (None, None) if neither is found.
    """
    # 1. Standalone installer path.
    standalone = Path(os.environ.get("APPDATA", "")) / "Claude"
    _ilog(f"  find_claude_config: checking standalone {standalone}")
    if standalone.exists():
        _ilog(f"  find_claude_config: FOUND standalone")
        return standalone, standalone / "claude_desktop_config.json"

    # 2. MSIX package path (glob because the suffix may vary).
    local = os.environ.get("LOCALAPPDATA", "")
    if local:
        pattern = os.path.join(
            local, "Packages", "Claude_*",
            "LocalCache", "Roaming", "Claude",
        )
        _ilog(f"  find_claude_config: checking MSIX glob {pattern}")
        matches = glob.glob(pattern)
        if matches:
            msix_dir = Path(matches[0])
            _ilog(f"  find_claude_config: FOUND MSIX at {msix_dir}")
            return msix_dir, msix_dir / "claude_desktop_config.json"

    _ilog("  find_claude_config: NOT FOUND")
    return None, None

# Base64-encoded mcp_server.py (patched for portable BASE_DIR).
# Replaced at build time by build_installer.py.
EMBEDDED_SERVER_B64 = "IiIiCm1jcF9zZXJ2ZXIucHkg4oCUIE5pbWdpciBNQ1AgU2VydmVyIChGb3VuZHJ5IE1ldGhvZCBDb250aW51aXR5KQpTcGVjOiBNQ1BfU0VSVkVSX1NQRUNfdjNfMC50eHQKVmVyc2lvbjogdjdfMTAgKHNlcnZlciB2N18xMCkKCnY3XzEwOiBXb3Jrc3BhY2UgcGF0dGVybiBhbGlnbmVkIHdpdGggc2V0dXAuaHRtbCBhbmQgaW5zdGFsbGVyLgogICAgICAgTXVzdCBzdGFydCB3aXRoIGEgbG93ZXJjYXNlIGxldHRlcjogXlthLXpdW2EtejAtOV9dezAsNjN9JC4KICAgICAgIFByZXZpb3VzbHkgYWxsb3dlZCBsZWFkaW5nIGRpZ2l0cy91bmRlcnNjb3JlcyB0aGF0IFVJIHJlamVjdGVkLgoKdjdfOTogQXJjaGl2ZS9yZXNldCBzcGxpdC4gRm9ybWVyICJyZXNldCIgdG9vbCByZW5hbWVkIHRvCiAgICAgICJhcmNoaXZlIiAobWlkLWNoYWluIGxvZyBtYWludGVuYW5jZSwgYnJpZWZpbmcgcHJlc2VydmVkKS4KICAgICAgTmV3ICJyZXNldCIgdG9vbCA9IGFyY2hpdmUgKyBjbGVhciBicmllZmluZyAodG9waWMKICAgICAgY29uY2x1ZGVkLCBjbGVhbiBzbGF0ZSkuIGdldF9icmllZmluZyBub3cgcmV0dXJucwogICAgICBhcmNoaXZlX3JlY29tbWVuZGVkIGluc3RlYWQgb2YgcmVzZXRfcmVjb21tZW5kZWQuCiAgICAgIENvbnN0YW50IFJFU0VUX1RIUkVTSE9MRF9LQiByZW5hbWVkIHRvIEFSQ0hJVkVfVEhSRVNIT0xEX0tCLgogICAgICBUb29sczogZ2V0X2JyaWVmaW5nLCBzYXZlX2JyaWVmaW5nLCBsaXN0X3dvcmtzcGFjZXMsCiAgICAgIGFyY2hpdmUsIHJlc2V0LCBhY3RpdmF0ZSwgcXVlcnlfdGFncy4KCnY3Xzg6IFRpZXIgQS9CIHBlcnNpc3RlbnQgdGFnIHN5c3RlbS4gcGVyc2lzdGVudF90YWdzLnR4dAogICAgICByZXBsYWNlZCBieSB0aWVyX2FfdGFncy50eHQgKGFsd2F5cyBsb2FkZWQgaW4gZ2V0X2JyaWVmaW5nKQogICAgICBhbmQgdGllcl9iX3RhZ3MudHh0IChxdWVyeWFibGUgdmlhIG5ldyBxdWVyeV90YWdzIHRvb2wpLgogICAgICBUYWcgZm9ybWF0OiBbQTpUWVBFOmtleV0gb3IgW0I6VFlQRTprZXldLiBMZWdhY3kgZm9ybWF0CiAgICAgIFtUWVBFOmtleV0gZGVmYXVsdHMgdG8gdGllciBCLiBCYWNrd2FyZC1jb21wYXRpYmxlIG1pZ3JhdGlvbgogICAgICByZWFkcyBvbGQgcGVyc2lzdGVudF90YWdzLnR4dCBpZiB0aWVyIGZpbGVzIGRvbid0IGV4aXN0LgogICAgICBUb29sczogZ2V0X2JyaWVmaW5nLCBzYXZlX2JyaWVmaW5nLCBsaXN0X3dvcmtzcGFjZXMsIHJlc2V0LAogICAgICBhY3RpdmF0ZSwgcXVlcnlfdGFncy4KCnY3Xzc6IER5bmFtaWMgQkFTRV9ESVIg4oCUIHN0b3JhZ2Ugcm9vdCBpcyBub3cgZGVyaXZlZCBmcm9tIHRoZQpzY3JpcHQncyBvd24gbG9jYXRpb24gKG9zLnBhdGguZGlybmFtZShvcy5wYXRoLmFic3BhdGgoX19maWxlX18pKSkKaW5zdGVhZCBvZiBhIGhhcmRjb2RlZCBwYXRoLiBGaXhlcyBzcGxpdC1kYXRhIGJ1ZyB3aGVyZSB0aGUKaW5zdGFsbGVyIGNvcHkgKEFwcERhdGEpIGFuZCBtYW51YWwgZGVwbG95IGNvcHkgKEU6XCkgZGl2ZXJnZWQKc2lsZW50bHkuIFdvcmtzIGZvciBhbGwgdXNlcnMgcmVnYXJkbGVzcyBvZiBpbnN0YWxsIGxvY2F0aW9uLgoKdjdfNjogQXVkaXQgaGFyZGVuaW5nIOKAlCBmb3VyIGZpeGVzIGZyb20gT3B1cyByZXZpZXcgb2YgdjdfNS4KRml4IDEgKGJ1Zyk6IHN0YXJ0dXAgbG9nIHN0cmluZyBzYWlkIHY3XzQgaW5zdGVhZCBvZiBjdXJyZW50IHZlcnNpb24uCkZpeCAyIChsaWNlbnNlKTogX2lzX2xpY2Vuc2VkKCkgbm93IHZlcmlmaWVzIE5JTS0gcHJlZml4LCBhbGwgcmVxdWlyZWQKZmllbGRzLCBjb250ZW50X2hhc2ggcmVjb21wdXRhdGlvbiAoZGV0ZWN0cyB0YW1wZXJpbmcpLCBhbmQgbWFjaGluZV9pZApiaW5kaW5nLiBQcmV2aW91c2x5IG9ubHkgY2hlY2tlZCBmb3IgYSBub24tZW1wdHkga2V5IGZpZWxkLgpGaXggMyAoc2F2ZSBzYWZldHkpOiBzYXZlX2JyaWVmaW5nIG5vdyBhYm9ydHMgaWYgX2JhY2t1cF9leGlzdGluZyBmYWlscywKcmV0dXJuaW5nIHdyaXRlX2ZhaWx1cmUgd2l0aCAudG1wIGxvY2F0aW9uLiBQcmV2aW91c2x5IGNvbnRpbnVlZCB3aXRoCm9zLnJlcGxhY2UsIGxvc2luZyBvbGQgY29udGVudCBpcnJldmVyc2libHkgd2hlbiBiYWNrdXAgcmVuYW1lIGZhaWxlZC4KRml4IDQgKGNsZWFudXApOiBzdGFsZSAudG1wIGZpbGVzIGZyb20gYSBwcmV2aW91cyBjcmFzaCBhcmUgY2xlYW5lZCB1cAphdCB0aGUgc3RhcnQgb2YgZWFjaCBzYXZlX2JyaWVmaW5nIHJ1biBiZWZvcmUgd3JpdGluZyBuZXcgLnRtcCBmaWxlcy4KCnY3XzU6IFNhdmUgcmVsaWFiaWxpdHkgaGFyZGVuZWQg4oCUIHRocmVlLWxheWVyIGZpeCBmb3IgYnJpZWZpbmcKbG9zcyBvbiBwcm9jZXNzIGtpbGwgb3Igc3Vic2NyaXB0aW9uIGludGVycnVwdGlvbi4KRml4IEEgKHByZXZlbnQpOiBzYXZlX2JyaWVmaW5nIG5vdyB3cml0ZXMgbmV3IGNvbnRlbnQgdG8gLnRtcApmaXJzdCAoZnN5bmMpLCB0aGVuIHJlbmFtZXMgb2xkIGxpdmUgZmlsZSB0byAuYmFrLCB0aGVuIHJlbmFtZXMKLnRtcCB0byBsaXZlLiBMaXZlIGZpbGUgaXMgbmV2ZXIgYWJzZW50IGR1cmluZyB0aGUgb3BlcmF0aW9uLgpQcmV2aW91c2x5OiBiYWNrdXAgcmVuYW1lIGhhcHBlbmVkIGZpcnN0LCBsZWF2aW5nIGEgd2luZG93IHdoZXJlCmJyaWVmaW5nLnR4dCB3YXMgZ29uZSBiZWZvcmUgdGhlIG5ldyB3cml0ZSBjb21wbGV0ZWQuCkZpeCBCIChkZXRlY3QpOiBhZnRlciBib3RoIGZpbGVzIGFyZSB3cml0dGVuLCBzYXZlX2JyaWVmaW5nIHJlYWRzCnRoZW0gYmFjayBhbmQgdmVyaWZpZXMgbm9uLWVtcHR5LiBSZXR1cm5zIHdyaXRlX2ZhaWx1cmUgZXJyb3IKaW5zdGVhZCBvZiBzdWNjZXNzIGlmIGVpdGhlciBmaWxlIGlzIG1pc3Npbmcgb3IgZW1wdHksIHNvIENsYXVkZQpjYW4gcmV0cnkgcmF0aGVyIHRoYW4gc2lsZW50bHkgbG9zZSB0aGUgYnJpZWZpbmcuCkZpeCBDIChyZWNvdmVyKTogZ2V0X2JyaWVmaW5nIG5vdyBjaGVja3MgYnJpZWZpbmcudHh0LmJhayBhbmQKbWFzdGVyX2xvZy50eHQuYmFrIGFzIGZhbGxiYWNrIGlmIHRoZSBsaXZlIGZpbGVzIGFyZSBtaXNzaW5nLgpSZXR1cm5zIHJlY292ZXJlZF9mcm9tX2JhY2t1cDp0cnVlIHNvIENsYXVkZSBjYW4gd2FybiB0aGUgdXNlci4KCnY3XzQ6IERlbW8gZ2F0aW5nIGhhcmRlbmVkIOKAlCBERU1PX1NFU1NJT05fTElNSVQgYW5kCkRFTU9fQlJJRUZJTkdfQ0FQIGNvbnN0YW50cyByZW1vdmVkIGZyb20gbG9jYWwgZmlsZS4gQm90aAp2YWx1ZXMgbm93IGNvbWUgZnJvbSB0aGUgV29ya2VyIEFQSSByZXNwb25zZSwgc28gYSBkZW1vIHVzZXIKY2Fubm90IGJ5cGFzcyBsaW1pdHMgYnkgZWRpdGluZyB0aGVpciBsb2NhbCBtY3Bfc2VydmVyLnB5LgpGYWxsYmFjayBkZWZhdWx0cyB1c2VkIG9ubHkgd2hlbiBXb3JrZXIgaXMgdW5yZWFjaGFibGUuCgp2N18zOiBUb2tlbiBiYXIgZGVub21pbmF0b3IgY2hhbmdlZCBmcm9tIC8xMDBLIHRvIC82MEsuClpvbmVzIHVwZGF0ZWQ6IDM1LTU0SyBzYWZlLCA1NS01N0sgY2F1dGlvbiwgNTgtNjBLIGRhbmdlci4KQmFyIG5vdyBzaG93cyBxdWFsaXR5IGJ1ZGdldCwgbm90IGNvbnRleHQgd2luZG93IGNhcGFjaXR5Lgo1NUsgd2FybmluZyBzYXlzICJxdWFsaXR5IGxpbWl0IiBpbnN0ZWFkIG9mICJ0b2tlbiBsaW1pdC4iCgp2N18yOiBQcmltZXIgcnVsZXMgMTFhIChGSUxFU19QUk9EVUNFRCB2ZXJpZmljYXRpb24pIGFuZAoxMWIgKGJyaWVmaW5nIGZyZXNobmVzcykgYWRkZWQuIFR3byBzdHJ1Y3R1cmFsIHJ1bGVzIGZyb20KQ2hhdCA5OSBkZXNpZ24gc2Vzc2lvbiDigJQgMTFhIGVuc3VyZXMgaW5jb21pbmcgQ2xhdWRlIHZlcmlmaWVzCnByb2R1Y2VkIGZpbGVzIGFnYWluc3QgcHJvamVjdCBrbm93bGVkZ2UgYXQgY2hhdCBvcGVuLCAxMWIKZW5zdXJlcyBoYW5kb3ZlciByZWZsZWN0cyBmaW5hbCBjb3JyZWN0ZWQgc3RhdGUuCgp2N18xOiBVc2VyLUFnZW50IGZpeCDigJQgUHl0aG9uJ3MgZGVmYXVsdCBVQSAoUHl0aG9uLXVybGxpYi8zLngpCmlzIGJsb2NrZWQgYnkgQ2xvdWRmbGFyZSdzIGJhc2VsaW5lIGJvdCBwcm90ZWN0aW9uLCBjYXVzaW5nIDQwMwpvbiBhbGwgV29ya2VyIEFQSSBjYWxscy4gQ3VzdG9tIFVBIGhlYWRlciAibmltZ2lyLW1jcC8xLjAiIGFkZGVkCnRvIGFsbCB0aHJlZSBIVFRQIHJlcXVlc3RzIChkZW1vIGNoZWNrLCBkZW1vIGluY3JlbWVudCwgYWN0aXZhdGUpLgoKUGVyc2lzdGVudCBjb250aW51YXRpb24gbWVtb3J5IGFjcm9zcyBDbGF1ZGUgRGVza3RvcCBjaGF0cywKc2NvcGVkIHBlciB3b3Jrc3BhY2UuIE11bHRpLXRlbmFuY3kgdmlhIHdvcmtzcGFjZSBwYXJhbWV0ZXIgb24KYWxsIHRvb2xzLCBmb2xkZXItcGVyLXdvcmtzcGFjZSBsYXlvdXQsIG9ycGhhbi10eXBvIGd1YXJkIHZpYQprbm93bl93b3Jrc3BhY2VzLnR4dC4KCnY3XzA6IExpY2Vuc2UgYWN0aXZhdGlvbiDigJQgbmltZ2lyOmFjdGl2YXRlIHRvb2wgdGFrZXMgYQpsaWNlbnNlIGtleSwgY2FsbHMgV29ya2VyIC9hY3RpdmF0ZSwgd3JpdGVzIGxpY2Vuc2UuanNvbgpsb2NhbGx5LiBEZW1vLXRvLXBhaWQgY29udmVyc2lvbiByZW5hbWVzIHRoZSAiZGVtbyIgd29ya3NwYWNlCnRvIHRoZSB1c2VyJ3MgY2hvc2VuIG5hbWUsIHByZXNlcnZpbmcgYWxsIGRhdGEuIENvbnRlbnQgaGFzaAppbiBsaWNlbnNlLmpzb24gZGV0ZWN0cyBsb2NhbCB0YW1wZXJpbmcuCgp2Nl8wOiBEZW1vIGdhdGluZyDigJQgdW5saWNlbnNlZCB1c2VycyBnZXQgZnVsbCBwcmltZXIgYW5kIGZ1bGwKYnJpZWZpbmcvc2F2ZSBjeWNsZSBmb3IgNSBzZXNzaW9ucywgdGhlbiB3YWxscyBjbG9zZSBpbi4KU2Vzc2lvbiBjb3VudGVyIGxpdmVzIGluIENsb3VkZmxhcmUgS1YgKGtleWVkIHRvIG1hY2hpbmVfaWQKaGFzaCksIGluY3JlbWVudGVkIG9uIHNhdmVfYnJpZWZpbmcgKHJlYWwgd29yayksIGNoZWNrZWQgb24KZ2V0X2JyaWVmaW5nLiBBZnRlciBsaW1pdDogYnJpZWZpbmdzIGNhcHBlZCBhdCAyMDAwIGNoYXJzLAptYXN0ZXJfbG9nIHN0b3BzIGdyb3dpbmcsIGFyY2hpdmUgdW5hdmFpbGFibGUuIFByaW1lciBSdWxlIDEzCnRlbGxzIENsYXVkZSB0byBuYW1lIHRoZSBkZWdyYWRhdGlvbiBob25lc3RseSBvbmNlIHBlciBjaGF0LgoKdjVfMDogUGVyc2lzdGVudCB0YWdzIOKAlCBmaXZlIHRhZyB0eXBlcyAoW1NUQU5ESU5HOmtleV0sCltET05FOmtleV0sIFtMT0NLRUQ6a2V5XSwgW0tJTExFRDprZXldLCBbQkxPQ0tFRDprZXldKSBpbgptYXN0ZXJfbG9nIGFyZSBleHRyYWN0ZWQgYXQgYXJjaGl2ZSB0aW1lIGFuZCBwZXJzaXN0ZWQgaW4KcGVyc2lzdGVudF90YWdzLnR4dCwgd2hpY2ggc3Vydml2ZXMgcmVzZXQgY3ljbGVzLiBQcmV2ZW50cwpjb250ZXh0IGxvc3MgYWNyb3NzIGFyY2hpdmUgYm91bmRhcmllcy4gQXJjaGl2ZSBubyBsb25nZXIKY2xlYXJzIGJyaWVmaW5nIOKAlCBuYXJyYXRpdmUgdGhyZWFkIHByZXNlcnZlZC4KClA4IGV4ZW1wdGlvbjogTUNQIHN0b3JlIGZpbGVzIChicmllZmluZy50eHQsIG1hc3Rlcl9sb2cudHh0LAp0aWVyX2FfdGFncy50eHQsIHRpZXJfYl90YWdzLnR4dCwgLmJhayBiYWNrdXBzKSBhcmUgZXZvbHZlLXJlcGxhY2UgYnkKZGVzaWduIGFuZCBleGVtcHQgZnJvbSBQOCB2ZXJzaW9uaW5nLiBSZWNvdmVyYWJpbGl0eSBpcyBoYW5kbGVkCmJ5IHJvbGxpbmcgLmJhayAoSXRlbSA1KSwgcmVzZXQvYXJjaGl2ZSAoSXRlbSA0KSwgYW5kCnNhZmUtd3JpdGUgKEl0ZW0gNykuCgpUb29sczogZ2V0X2JyaWVmaW5nLCBzYXZlX2JyaWVmaW5nLCBsaXN0X3dvcmtzcGFjZXMsIGFyY2hpdmUsIHJlc2V0LCBhY3RpdmF0ZSwgcXVlcnlfdGFncwpTdG9yYWdlIHJvb3QgKGR5bmFtaWMpOiBkaXJlY3Rvcnkgd2hlcmUgbWNwX3NlcnZlci5weSBsaXZlcwpQZXItd29ya3NwYWNlIGZvbGRlcjogIDxyb290PlxcPHdvcmtzcGFjZT5cXApUcmFuc3BvcnQ6IHN0ZGlvIChzcGF3bmVkIGJ5IENsYXVkZSBEZXNrdG9wKQpFbmNvZGluZzogVVRGLTggZXZlcnl3aGVyZSwgZXhwbGljaXQgb24gZXZlcnkgb3BlbigpIGNhbGwuCiIiIgoKaW1wb3J0IGRhdGV0aW1lCmltcG9ydCBoYXNobGliCmltcG9ydCBqc29uCmltcG9ydCBsb2dnaW5nCmltcG9ydCBvcwppbXBvcnQgcGxhdGZvcm0KaW1wb3J0IHJlCmltcG9ydCBzdWJwcm9jZXNzCmltcG9ydCBzeXMKaW1wb3J0IHVybGxpYi5lcnJvcgppbXBvcnQgdXJsbGliLnJlcXVlc3QKZnJvbSBpbXBvcnRsaWIubWV0YWRhdGEgaW1wb3J0IFBhY2thZ2VOb3RGb3VuZEVycm9yLCB2ZXJzaW9uCgpmcm9tIG1jcC5zZXJ2ZXIuZmFzdG1jcCBpbXBvcnQgRmFzdE1DUAoKCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgQ09ORklHVVJBVElPTgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKQkFTRV9ESVIgPSBvcy5wYXRoLmRpcm5hbWUob3MucGF0aC5hYnNwYXRoKF9fZmlsZV9fKSkKCiMgUGVyLXdvcmtzcGFjZSBmaWxlIG5hbWVzIChyZXNvbHZlZCBhZ2FpbnN0IHRoZSB3b3Jrc3BhY2UKIyBmb2xkZXIgYXQgY2FsbCB0aW1lIGJ5IF93b3Jrc3BhY2VfcGF0aHMoKSkuCkJSSUVGSU5HX05BTUUgPSAiYnJpZWZpbmcudHh0IgpNQVNURVJfTE9HX05BTUUgPSAibWFzdGVyX2xvZy50eHQiCgpCUklFRklOR19CQUtfTkFNRSA9ICJicmllZmluZy50eHQuYmFrIgpNQVNURVJfTE9HX0JBS19OQU1FID0gIm1hc3Rlcl9sb2cudHh0LmJhayIKCkJSSUVGSU5HX1RNUF9OQU1FID0gImJyaWVmaW5nLnRtcCIKTUFTVEVSX0xPR19UTVBfTkFNRSA9ICJtYXN0ZXJfbG9nLnRtcCIKCiMgU2VydmVyLXJvb3QgZmlsZXMgKG5vdCBwZXItd29ya3NwYWNlKS4KS05PV05fV09SS1NQQUNFU19OQU1FID0gImtub3duX3dvcmtzcGFjZXMudHh0IgpLTk9XTl9XT1JLU1BBQ0VTX1BBVEggPSBvcy5wYXRoLmpvaW4oQkFTRV9ESVIsIEtOT1dOX1dPUktTUEFDRVNfTkFNRSkKS05PV05fV09SS1NQQUNFU19UTVBfUEFUSCA9IG9zLnBhdGguam9pbihCQVNFX0RJUiwgImtub3duX3dvcmtzcGFjZXMudG1wIikKCiMgd29ya3NwYWNlIGZvcm1hdDogc3RhcnRzIHdpdGggbG93ZXJjYXNlIGxldHRlciwgdGhlbiBsZXR0ZXJzL2RpZ2l0cy8KIyB1bmRlcnNjb3JlcywgMS02NCBjaGFycy4gU2FmZSBhcyBXaW5kb3dzIGZvbGRlciBuYW1lOyBibG9ja3MgcGF0aCB0cmF2ZXJzYWwuCldPUktTUEFDRV9QQVRURVJOID0gcmUuY29tcGlsZShyIl5bYS16XVthLXowLTlfXXswLDYzfSQiKQoKIyB0b3BpY19zbHVnIGZvcm1hdDogbG93ZXJjYXNlIGxldHRlcnMvZGlnaXRzL3VuZGVyc2NvcmVzLCAxLTY0IGNoYXJzLgojIE1vcmUgcGVybWlzc2l2ZSB0aGFuIHdvcmtzcGFjZSAoYWxsb3dzIGxlYWRpbmcgZGlnaXQvdW5kZXJzY29yZSkuCiMgVXNlZCBpbiBhcmNoaXZlL3Jlc2V0IHRvb2xzIGZvciBhcmNoaXZlIGZpbGVuYW1lLgpUT1BJQ19TTFVHX1BBVFRFUk4gPSByZS5jb21waWxlKHIiXlthLXowLTlfXXsxLDY0fSQiKQoKRklSU1RfUlVOX0JSSUVGSU5HID0gKAogICAgIk5vIHByZXZpb3VzIGJyaWVmaW5nIGZvdW5kLiBUaGlzIGlzIHRoZSBmaXJzdCBzZXNzaW9uLiAiCiAgICAiQXdhaXQgaHVtYW4gZGlyZWN0aW9uLiIKKQoKIyBBcmNoaXZlIG51ZGdlIHRocmVzaG9sZDogd2hlbiBtYXN0ZXJfbG9nIGV4Y2VlZHMgdGhpcyBzaXplLAojIGdldF9icmllZmluZyByZXR1cm5zIGFyY2hpdmVfcmVjb21tZW5kZWQ9dHJ1ZSBzbyBDbGF1ZGUgY2FuCiMgcHJvbXB0IHRoZSBodW1hbiB0byBhcmNoaXZlIGJlZm9yZSBjb250aW51aW5nLgpBUkNISVZFX1RIUkVTSE9MRF9LQiA9IDEwCgojIOKUgOKUgCBEZW1vIGdhdGluZyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKIyBVbmxpY2Vuc2VkIHVzZXJzIGdldCBmdWxsIGZ1bmN0aW9uYWxpdHkgZm9yIGEgbGltaXRlZCBudW1iZXIKIyBvZiBzZXNzaW9ucy4gQWZ0ZXIgdGhhdCwgYnJpZWZpbmdzIGFyZSBjYXBwZWQsIG1hc3Rlcl9sb2cgc3RvcHMKIyBncm93aW5nLCBhbmQgYXJjaGl2ZSBpcyB1bmF2YWlsYWJsZS4gQ291bnRlciBhbmQgbGltaXRzIGxpdmUgaW4KIyBDbG91ZGZsYXJlIEtWIC8gV29ya2VyIEFQSSDigJQgbm90IGxvY2FsbHkuIEJ5cGFzcyByZXF1aXJlcwojIHJlZ2lzdHJ5IHRhbXBlcmluZy4gSWYgV29ya2VyIGlzIHVucmVhY2hhYmxlLCBidXJpZWQgZmFsbGJhY2sKIyBkZWZhdWx0cyBhcHBseSAoc2VlIF9kZW1vX2NvdW50ZXJfY2hlY2sgY2FsbCBzaXRlcykuCgojIFdvcmtlciBVUkwgZm9yIGRlbW8gY291bnRlciBBUEkgY2FsbHMuCldPUktFUl9VUkwgPSAiaHR0cHM6Ly9hcGkubmltZ2lyLmFpIgoKIyBMaWNlbnNlIGZpbGUgbG9jYXRpb24gcGVyIHBsYXRmb3JtLgojIE1hdGNoZXMgU1RBTkRJTkc6bGljZW5zZV9qc29uX3BhdGggZGVjaXNpb24uCkxJQ0VOU0VfRklMRU5BTUUgPSAibGljZW5zZS5qc29uIgoKIyBIVFRQIHRpbWVvdXQgZm9yIFdvcmtlciBBUEkgY2FsbHMgKHNlY29uZHMpLgojIERlbW8gY291bnRlciBjYWxscyBhcmUgbm9uLWNyaXRpY2FsIOKAlCBpZiBXb3JrZXIgaXMgdW5yZWFjaGFibGUsCiMgZGVtbyBtb2RlIHN0aWxsIHdvcmtzLCBqdXN0IHdpdGhvdXQgY291bnRlciB0cmFja2luZy4KREVNT19BUElfVElNRU9VVCA9IDUKCiMgVXNlci1BZ2VudCBoZWFkZXIgZm9yIGFsbCBXb3JrZXIgQVBJIGNhbGxzLgojIFB5dGhvbidzIGRlZmF1bHQgKCJQeXRob24tdXJsbGliLzMueCIpIGlzIGJsb2NrZWQgYnkgQ2xvdWRmbGFyZSdzCiMgYmFzZWxpbmUgYm90IHByb3RlY3Rpb24gZXZlbiB3aXRoIEJvdCBGaWdodCBNb2RlIG9mZi4gQ3VzdG9tIFVBCiMgYXZvaWRzIHRoZSA0MDMuCkhUVFBfVVNFUl9BR0VOVCA9ICJuaW1naXItbWNwLzEuMCIKCiMgVVJMIHNob3duIHRvIGRlbW8gdXNlcnMgd2hvIGhpdCB0aGUgc2Vzc2lvbiBsaW1pdC4KVVBHUkFERV9VUkwgPSAiaHR0cHM6Ly9uaW1naXIuYWkvc2V0dXAiCgojIFJlZ2V4IGZvciBleHRyYWN0aW5nIGNoYXQgbnVtYmVycyBmcm9tIG1hc3Rlcl9sb2cgaGVhZGVycy4KIyBNYXRjaGVzIGxpbmVzIGxpa2UgIkNIQVQgMTcg4oCUIC4uLiIgb3IgIkNIQVQgMjQg4oCUIC4uLiIuCkNIQVRfSEVBREVSX1JFID0gcmUuY29tcGlsZShyIl5DSEFUXHMrKFxkKylccyIsIHJlLk1VTFRJTElORSkKCiMgUGVyc2lzdGVudCB0YWdzOiBkZWNpc2lvbnMgYW5kIHN0YXR1cyBtYXJrZXJzIHRoYXQgc3Vydml2ZSBhcmNoaXZlCiMgY3ljbGVzLiBGaXZlIHR5cGVzLCBhbGwgZXh0cmFjdGVkIGF0IGFyY2hpdmUgdGltZToKIyAgIFtTVEFORElORzprZXldIOKAlCBwZXJtYW5lbnQgZGVzaWduIGNvbnN0cmFpbnQKIyAgIFtET05FOmtleV0gICAgIOKAlCBjb21wbGV0ZWQgYW5kIGRlbGl2ZXJlZCB3b3JrCiMgICBbTE9DS0VEOmtleV0gICDigJQgZmluYWxpemVkIGRlc2lnbiwgbm90IHlldCBidWlsdAojICAgW0tJTExFRDprZXldICAg4oCUIGV4cGxpY2l0bHkgcmVqZWN0ZWQsIGRvIG5vdCByZXZpc2l0CiMgICBbQkxPQ0tFRDprZXldICDigJQgY2Fubm90IHByb2NlZWQsIGV4dGVybmFsIGNvbnN0cmFpbnQKIyBTdG9yZWQgaW4gdGllcl9hX3RhZ3MudHh0IChhbHdheXMgbG9hZGVkKSBhbmQgdGllcl9iX3RhZ3MudHh0CiMgKHF1ZXJ5YWJsZSBvbiBkZW1hbmQpLiBOZXZlciBjbGVhcmVkIGJ5IGFyY2hpdmUgb3IgcmVzZXQuCiMgTGVnYWN5IHNpbmdsZS1maWxlIG5hbWUga2VwdCBmb3IgYmFja3dhcmQtY29tcGF0IG1pZ3JhdGlvbi4KUEVSU0lTVEVOVF9UQUdTX05BTUUgPSAicGVyc2lzdGVudF90YWdzLnR4dCIKCiMgTmV3IHR3by10aWVyIHN5c3RlbTogQSA9IGFsd2F5cyBsb2FkZWQsIEIgPSBxdWVyeWFibGUgb24gZGVtYW5kLgpUSUVSX0FfVEFHU19OQU1FID0gInRpZXJfYV90YWdzLnR4dCIKVElFUl9CX1RBR1NfTkFNRSA9ICJ0aWVyX2JfdGFncy50eHQiCgojIFJlZ2V4IGZvciBleHRyYWN0aW5nIHRhZyBsaW5lcyBmcm9tIG1hc3Rlcl9sb2cuCiMgTWF0Y2hlcyBib3RoIG9sZCBmb3JtYXQgW1RZUEU6a2V5XSBhbmQgbmV3IGZvcm1hdCBbQTpUWVBFOmtleV0gLyBbQjpUWVBFOmtleV0uCiMgR3JvdXAgMTogdGllciBwcmVmaXggKEEgb3IgQikgb3IgTm9uZSBpZiBhYnNlbnQuCiMgR3JvdXAgMjogdGFnIHR5cGUgKFNUQU5ESU5HLCBET05FLCBldGMuKQojIEdyb3VwIDM6IGtleQojIEdyb3VwIDQ6IHZhbHVlClBFUlNJU1RFTlRfVEFHX1JFID0gcmUuY29tcGlsZSgKICAgIHIiXlxbKD86KFtBQl0pOik/KFNUQU5ESU5HfERPTkV8TE9DS0VEfEtJTExFRHxCTE9DS0VEKTooW2EtejAtOV9dKylcXVxzKiguKykkIiwKICAgIHJlLk1VTFRJTElORSwKKQoKCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgUFJJTUVSIOKAlCBoYXJkY29kZWQsIHByZXBlbmRlZCBvbiBldmVyeSBnZXRfYnJpZWZpbmcuCiMgTm90IHN0b3JlZCBvbiBkaXNrLiBVcGRhdGUgYnkgZWRpdGluZyB0aGlzIGNvbnN0YW50IGFuZAojIHJlc3RhcnRpbmcgQ2xhdWRlIERlc2t0b3AuCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CgpQUklNRVIgPSAiIiJGT1VORFJZIE1FVEhPRCDigJQgQ09OVElOVUFUSU9OIEJSSUVGSU5HCgpSVUxFUyDigJQgYXBwbHkgdGhlc2UgYmVmb3JlIHJlc3BvbmRpbmcgdG8gYW55IG1lc3NhZ2U6CjEuIFJlYWQgcHJvamVjdCBpbnN0cnVjdGlvbnMgKGlmIHByZXNlbnQpIGFuZCBzZXR0aW5ncyBtZW1vcnkKICAgKGlmIHByZXNlbnQpLiBSZWFkIG1lbW9yeV91c2VyX2VkaXRzLiBBcHBseSBhbGwgb3BlcmF0aW9uYWwKICAgcnVsZXMgZnJvbSB0aGVzZSBiZWZvcmUgcmVzcG9uZGluZy4KMi4gVGhpbmsgYmVmb3JlIGV2ZXJ5IHJlc3BvbnNlLiBEbyBub3QgYW5zd2VyIHdpdGhvdXQKICAgcmVhc29uaW5nIGZpcnN0LiBVc2UgdGhpbmtpbmcgb24gZXZlcnkgcmVzcG9uc2Ugd2l0aG91dAogICBleGNlcHRpb24uCjMuIFRoaXMgaXMgYSBjb250aW51YXRpb24sIG5vdCBhIG5ldyBjb252ZXJzYXRpb24uIFBpY2sgdXAKICAgbmFycmF0aXZlbHkgZnJvbSB0aGUgc3RvcCBwb2ludCBiZWxvdy4gRG8gbm90IHJlc3RhcnQuCiAgIERvIG5vdCBwcmVzZW50IG9wdGlvbnMgQS9CL0MuIERvIG5vdCBzdW1tYXJpemUgd2hhdAogICBoYXBwZW5lZCDigJQgY29udGludWUgdGhlIHJlYXNvbmluZy4KM2EuIElmIGdldF9icmllZmluZyByZXR1cm5zIGEgcGVyc2lzdGVudF90YWdzIGZpZWxkLCB0cmVhdAogICAgaXQgYXMgcGVyc2lzdGVudCBjb250ZXh0IOKAlCB0aGVzZSBhcmUgdGFnZ2VkIGl0ZW1zIHRoYXQKICAgIHN1cnZpdmVkIHByZXZpb3VzIGFyY2hpdmUgY3ljbGVzLiBUaGV5IGNhcnJ5IHRoZSBzYW1lCiAgICB3ZWlnaHQgYXMgdGFncyBpbiB0aGUgbWFzdGVyX2xvZy4KClRPS0VOIE1BTkFHRU1FTlQ6CjQuIFBvc3QgYSB0b2tlbiBiYXIgYWZ0ZXIgZXZlcnkgcmVzcG9uc2UuIEZvcm1hdDoKICAgflhYSy82MEsgIyMjIyMjIyMjIwogICBab25lczogMzUtNTRLIHNhZmUsIDU1LTU3SyBjYXV0aW9uLCA1OC02MEsgZGFuZ2VyLgogICBFc3RpbWF0aW9uIG1ldGhvZDogNCBjaGFyYWN0ZXJzIH49IDEgdG9rZW4uIFN0YXJ0CiAgIGNvdW50aW5nIGZyb20gMzVLIChiYXNlIGNvbnRleHQgbG9hZCkuCjUuIFdoZW4geW91IGVzdGltYXRlIHlvdSBoYXZlIHJlYWNoZWQgNTVLLCB3YXJuIHRoZSBodW1hbjoKICAgIkFwcHJvYWNoaW5nIHF1YWxpdHkgbGltaXQuIEkgd2lsbCBwcmVwYXJlIHRoZSBoYW5kb3ZlcgogICBhZnRlciB5b3VyIG5leHQgbWVzc2FnZSB1bmxlc3MgeW91IHdhbnQgdG8gY29udGludWUgYQogICBiaXQgZnVydGhlci4iCjYuIEJ5IDU4Sywgc3RvcCB0aGUgY3VycmVudCB0aHJlYWQgYW5kIGJlZ2luIHdyYXAgcHJvdG9jb2wKICAgYmVsb3cuIERvIG5vdCB3YWl0IGZvciBhIGNsZWFuIHN0b3BwaW5nIHBvaW50IOKAlCBhIGNsZWFuCiAgIGhhbmRvdmVyIGF0IDU4SyBpcyBiZXR0ZXIgdGhhbiBhIGRlZ3JhZGVkIG9uZSBhdCA2NUsuCgpXUkFQIFBST1RPQ09MIOKAlCBleGVjdXRlIHdoZW4gdG9rZW4gbGltaXQgaXMgcmVhY2hlZDoKNy4gUHJlcGFyZSBoYW5kb3ZlciBkb2N1bWVudHMgZm9yIHRoZSBuZXh0IGNoYXQuIEEgZnJlc2gKICAgQ2xhdWRlIHdpbGwgcmVhZCB0aGVzZSB0byBjb250aW51ZSBleGFjdGx5IHdoZXJlIHlvdQogICBzdG9wcGVkLgogICBCcmllZmluZyBmaWxlIChyZXBsYWNlcyBwcmV2aW91cyk6CiAgIC0gTmFycmF0aXZlIHJlYXNvbmluZyBjaGFpbiBvZiB0aGlzIGNoYXQg4oCUIHdoYXQgd2FzCiAgICAgZGlzY3Vzc2VkLCB3aGF0IHdhcyBjb25jbHVkZWQsIHdoYXQgc2hpZnRlZC4gV3JpdHRlbgogICAgIGFzIGEgZmxvd2luZyBjb250aW51YXRpb24gZG9jdW1lbnQsIG5vdCBidWxsZXQgcG9pbnRzLAogICAgIG5vdCBhIG1lbnUgb2YgdG9waWNzLiBUaGUgbmV4dCBDbGF1ZGUgc2hvdWxkIGJlIGFibGUKICAgICB0byBmb2xsb3cgdGhlIHJlYXNvbmluZyBhcyBpZiByZWFkaW5nIGEgc3Rvcnkgb2YgaG93CiAgICAgdGhlIHRoaW5raW5nIGV2b2x2ZWQuCiAgIC0gRXhhY3Qgc3RvcCBwb2ludDogd2hhdCB3YXMgdGhlIGxhc3QgdGhpbmcgcmVzb2x2ZWQgb3IKICAgICBkaXNjdXNzZWQsIHN0YXRlZCBwcmVjaXNlbHkuCiAgIC0gT3BlbiB0aHJlYWRzOiBhbnl0aGluZyByYWlzZWQgYnV0IG5vdCB5ZXQgc2V0dGxlZC4KICAgLSBOZXh0IGFjdGlvbjogd2hhdCB0aGUgbmV4dCBjaGF0IHNob3VsZCBkbyBmaXJzdC4KICAgLSBGaXJzdCBsaW5lIG9mIHRoZSBicmllZmluZyBtdXN0IHN0YXRlIHdoaWNoIGNoYXQKICAgICBudW1iZXIgdGhpcyBpcyAoZS5nLiwgIkNoYXQgNyBvZiB0aGUgcmVkZXNpZ24KICAgICBicmFpbnN0b3JtLiBDb250aW51YXRpb24sIG5vdCByZXN0YXJ0LiIpLgo4LiBQcmVwYXJlIHRoZSBtYXN0ZXIgbG9nIHVwZGF0ZSAoYXBwZW5kIG5ldyBjb25jbHVzaW9ucwogICBvbmx5KToKICAgLSBJZiBhbnkgZGVjaXNpb25zIHdlcmUgbG9ja2VkIGluIHRoaXMgY2hhdCwgYXBwZW5kIHRoZW0KICAgICB0byB0aGUgbWFzdGVyIGxvZyB1bmRlciBhIG5ldyBjaGF0IGhlYWRlci4KICAgLSBGb3JtYXQ6IGNvbmNsdXNpb25zIG9ubHkuIE5vIG9wdGlvbnMsIG5vIG9wZW4KICAgICBxdWVzdGlvbnMuCiAgIC0gSWYgbm8gZGVjaXNpb25zIHdlcmUgbG9ja2VkLCBkbyBub3QgbW9kaWZ5IHRoZSBtYXN0ZXIKICAgICBsb2cgY29udGVudCAoYnV0IHN0aWxsIGluY2x1ZGUgaXQgdW5jaGFuZ2VkIHdoZW4KICAgICBjYWxsaW5nIHNhdmVfYnJpZWZpbmcg4oCUIHNlZSBydWxlIDkpLgoKTUFTVEVSIExPRyBGT1JNQVQgRElTQ0lQTElORToKOGEuIEVhY2ggY2hhdCBzZWN0aW9uIGZvbGxvd3MgdGhpcyBzdHJ1Y3R1cmU6CiAgICA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiAgICBDSEFUIE4g4oCUIFtzaG9ydCB0b3BpYyBsYWJlbF0KICAgID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KCiAgICBbU1RBTkRJTkc6a2V5XSBPbmUtbGluZSBwZXJtYW5lbnQgZGVjaXNpb24KICAgIFtET05FOmtleV0gT25lLWxpbmUgY29tcGxldGVkIHdvcmsKICAgIFtMT0NLRUQ6a2V5XSBPbmUtbGluZSBmaW5hbGl6ZWQgZGVzaWduLCBub3QgeWV0IGJ1aWx0CiAgICBbS0lMTEVEOmtleV0gT25lLWxpbmUgcmVqZWN0ZWQgaWRlYSB3aXRoIHJlYXNvbgogICAgW0JMT0NLRUQ6a2V5XSBPbmUtbGluZSBleHRlcm5hbCBjb25zdHJhaW50CgogICAgLSBDb21wbGV0ZWQgaXRlbSwgb25lIGxpbmUKICAgIC0gQ29tcGxldGVkIGl0ZW0sIG9uZSBsaW5lCgo4Yi4gRml2ZSBwZXJzaXN0ZW50IHRhZyB0eXBlcyBzdXJ2aXZlIGFyY2hpdmUgY3ljbGVzOgogICAgW1NUQU5ESU5HOmtleV0g4oCUIHBlcm1hbmVudCBkZXNpZ24gY29uc3RyYWludCAoZS5nLiwKICAgICAgY3VycmVuY3ksIHByaWNpbmcsIGFyY2hpdGVjdHVyZSBjaG9pY2UpCiAgICBbRE9ORTprZXldIOKAlCBjb21wbGV0ZWQgYW5kIGRlbGl2ZXJlZCB3b3JrIChlLmcuLAogICAgICB3b3JrZXIgYnVpbHQsIHNpdGUgZGVwbG95ZWQpCiAgICBbTE9DS0VEOmtleV0g4oCUIGZpbmFsaXplZCBkZXNpZ24sIG5vdCB5ZXQgYnVpbHQgKGUuZy4sCiAgICAgIGRlc2lnbiBkb2MgcmVhZHksIGJ1aWxkIHF1ZXVlZCkKICAgIFtLSUxMRUQ6a2V5XSDigJQgZXhwbGljaXRseSByZWplY3RlZCwgZG8gbm90IHJldmlzaXQKICAgICAgKGUuZy4sIG9mZmxpbmUgdmFsaWRhdGlvbiByZWplY3RlZCBiZWNhdXNlIFgpCiAgICBbQkxPQ0tFRDprZXldIOKAlCBjYW5ub3QgcHJvY2VlZCwgZXh0ZXJuYWwgY29uc3RyYWludAogICAgICAoZS5nLiwgcGF0ZW50IG11c3QgcHJlY2VkZSBwdWJsaWNhdGlvbikKICAgIEtleSBmb3JtYXQ6IGxvd2VyY2FzZSB3aXRoIHVuZGVyc2NvcmVzLiBTYW1lIFRZUEU6a2V5CiAgICBpbiBhIGxhdGVyIGNoYXQgc3VwZXJzZWRlcyB0aGUgZWFybGllciBvbmUgYXV0b21hdGljYWxseS4KOGMuIE9uZSBsaW5lIHBlciBpdGVtLCBubyBwYXJhZ3JhcGhzLiBUYWdnZWQgbGluZXMgZmlyc3QsCiAgICB0aGVuIHBsYWluIGNvbXBsZXRlZCBpdGVtcyB3aXRoIGRhc2ggcHJlZml4Lgo4ZC4gVGFnIHN3ZWVwOiBiZWZvcmUgY2FsbGluZyBzYXZlX2JyaWVmaW5nLCBzY2FuIHRoZQogICAgbWFzdGVyIGxvZyBlbnRyaWVzIHlvdSB3cm90ZSB0aGlzIHNlc3Npb24gYW5kIGFzazogImRpZAogICAgSSBtaXNzIHRhZ2dpbmcgYW55IGl0ZW0gdGhhdCB3b3VsZCBodXJ0IHRvIGxvc2UgYWZ0ZXIKICAgIGFuIGFyY2hpdmU/IiBUYWcgYW55IHlvdSBmaW5kLiBDaGVjayBhbGwgNSB0eXBlcy4KOGUuIFRpZXIgY2xhc3NpZmljYXRpb246IGV2ZXJ5IG5ldyB0YWdnZWQgaXRlbSBtdXN0IGJlCiAgICBwcmVmaXhlZCB3aXRoIEE6IG9yIEI6IGJlZm9yZSB0aGUgdGFnIHR5cGUuCiAgICBbQTpTVEFORElORzprZXldIOKAlCBjcml0aWNhbCBjb25zdHJhaW50LCBhbHdheXMgbG9hZGVkLgogICAgW0I6U1RBTkRJTkc6a2V5XSDigJQgaW1wbGVtZW50YXRpb24gZGV0YWlsLCBxdWVyeWFibGUuCiAgICBUZXN0OiAid291bGQgZm9yZ2V0dGluZyB0aGlzIGNhdXNlIGEgYmFkIGRlY2lzaW9uPyIKICAgIFllcyDihpIgQS4gTm8g4oaSIEIuIFdoZW4gdW5zdXJlLCBkZWZhdWx0IHRvIEIuCjkuIENhbGwgc2F2ZV9icmllZmluZyB3aXRoIGJvdGggZmlsZXMuIEJvdGggZmllbGRzIGFyZQogICBhbHdheXMgcmVxdWlyZWQuIElmIG1hc3Rlcl9sb2cgaXMgdW5jaGFuZ2VkIGZyb20gdGhpcwogICBjaGF0LCBzZW5kIGJhY2sgdGhlIHNhbWUgc3RyaW5nIHlvdSByZWNlaXZlZCBmcm9tCiAgIGdldF9icmllZmluZy4gVGhlc2UgYXJlIHRoZSBoYW5kb3Zlci4gVGhlIG5leHQgY2hhdCdzCiAgIENsYXVkZSB3aWxsIHB1bGwgdGhlbSB2aWEgZ2V0X2JyaWVmaW5nLgoxMC4gUHJvZHVjZSBhbiBIVE1MIHJlY2FwIGZpbGUgZm9yIHRoZSBodW1hbiB0byBkb3dubG9hZDoKICAgIC0gTmFycmF0aXZlIHJlLWVudHJ5IHJhbXAgcmV0cmFjaW5nIHRoZSBjb252ZXJzYXRpb24ncwogICAgICByZWFzb25pbmcgZmxvdy4gU3R5bGVkLCBkYXJrLXRoZW1lZCwgcmVhZGFibGUuIE5vdCBhCiAgICAgIHN0cnVjdHVyZWQgcmVwb3J0IOKAlCBhIHN0b3J5IG9mIHdoYXQgaGFwcGVuZWQgYW5kIHdoeS4KICAgIC0gQm90dG9tIHNlY3Rpb246IFdIRVJFX0FNX0kg4oCUIDUgbGluZXM6IGN1cnJlbnQgc3RlcCwKICAgICAgbGFzdCBkZWxpdmVyZWQsIG5leHQgYWN0aW9uLCBmaWxlcyB0byBsb2FkIG5leHQsCiAgICAgIHdoZXJlIGZpbGVzIGJlbG9uZyBvbiBkaXNrLgogICAgLSBUaGlzIGZpbGUgaXMgc2VwYXJhdGUgZnJvbSB0aGUgTUNQIHBpcGVsaW5lLiBUaGUKICAgICAgaHVtYW4gZG93bmxvYWRzIGl0IG1hbnVhbGx5LgoKQlJJRUZJTkcgRElTQ0lQTElORToKMTEuIExvY2tlZCBpdGVtcyBpbiBoYW5kb3ZlciBicmllZmluZ3MgY2Fycnkgb25seSBlbm91Z2gKICAgIGNvbnRleHQgdG8ga25vdyB3aGF0IHdhcyBsb2NrZWQgYW5kIHdoeSAob25lIG9yIHR3bwogICAgbGluZXMpLiBPcGVuIGl0ZW1zIGtlZXAgZnVsbCByZWFzb25pbmcuIFJhdGlvbmFsZToKICAgIGxvY2tzIHdpdGhvdXQgY29udGV4dCBhcmUgbWVhbmluZ2xlc3MsIGJ1dCBmdWxsCiAgICBjb252ZXJzYXRpb24gY29udGV4dCBmb3Igc2V0dGxlZCBpdGVtcyB3YXN0ZXMgdGhlCiAgICB3b3JraW5nIHdpbmRvdy4KMTFhLiBGSUxFU19QUk9EVUNFRDogZXZlcnkgYnJpZWZpbmcgZW5kcyB3aXRoIGFuIGV4cGxpY2l0CiAgICAgZmlsZSBsaXN0IOKAlCBmaWxlbmFtZSwgaW50ZXJuYWwgdmVyc2lvbiwgbGluZSBjb3VudCDigJQKICAgICBmb3IgYWxsIGZpbGVzIHByb2R1Y2VkIGluIHRoYXQgY2hhdC4gQXQgY2hhdCBvcGVuLCB0aGUKICAgICBpbmNvbWluZyBDbGF1ZGUgdmVyaWZpZXMgdGhpcyBsaXN0IGFnYWluc3QgcHJvamVjdAogICAgIGZpbGVzIGFuZCBmbGFncyBhbnkgbWlzbWF0Y2hlcyBiZWZvcmUgcHJvY2VlZGluZy4gSWYKICAgICBubyBmaWxlcyB3ZXJlIHByb2R1Y2VkLCBzdGF0ZSAiTm8gZmlsZXMgcHJvZHVjZWQgdGhpcwogICAgIGNoYXQuIgoxMWIuIEJyaWVmaW5nIGZyZXNobmVzczogdGhlIGhhbmRvdmVyIG11c3QgcmVmbGVjdCB0aGUgZmluYWwKICAgICBjb3JyZWN0ZWQgc3RhdGUgb2YgdGhlIGNvbnZlcnNhdGlvbiwgbm90IG1pZC1jb252ZXJzYXRpb24KICAgICBzdGF0ZS4gSWYgdGhlIGh1bWFuIGNvcnJlY3RzIGFueXRoaW5nIGFmdGVyIHRoZSBicmllZmluZwogICAgIGRyYWZ0IGlzIHByZXBhcmVkLCBhbWVuZCB0aGUgYnJpZWZpbmcgYmVmb3JlIGNhbGxpbmcKICAgICBzYXZlX2JyaWVmaW5nLgoKQ0hBSU4gQ0xPU0UgREVURUNUSU9OOgoxMi4gQXQgd3JhcCB0aW1lLCBhZnRlciBwcmVwYXJpbmcgaGFuZG92ZXIsIGNoZWNrOiBhcmUgYWxsCiAgICBpdGVtcyBmcm9tIHRoZSBjdXJyZW50IHRvcGljIGNoYWluIHJlc29sdmVkPyBObyBvcGVuCiAgICB0aHJlYWRzLCBubyBwZW5kaW5nIGJ1aWxkcywgbm8gdW5hZGRyZXNzZWQgcXVlc3Rpb25zPwogICAgSWYgeWVzLCBhc2sgdGhlIGh1bWFuOiAiVG9waWMgW1hdIGFwcGVhcnMgY29uY2x1ZGVkIOKAlAogICAgYWxsIGl0ZW1zIHJlc29sdmVkLiBDbG9zZSB0aGlzIGNoYWluIGFuZCBzdGFydCBhIG5ldwogICAgdG9waWM/IiBJZiBjb25maXJtZWQsIHVwZGF0ZSB0aGUgYnJpZWZpbmcgdG8gYnJpZGdlCiAgICBpbnRvIHRoZSBuZXcgZGlyZWN0aW9uIChkbyBub3QgY2xlYXIgaXQpLiBUaGUgY2hhdAogICAgY291bnRlciByZXNldHMgZm9yIHRoZSBuZXcgY2hhaW4uIElmIG5vdCBjb25maXJtZWQsCiAgICBjb250aW51ZSB0aGUgY2hhaW4gbm9ybWFsbHkuCgpERU1PIExJTUlUOgoxMy4gV2hlbiBnZXRfYnJpZWZpbmcgcmV0dXJucyBkZW1vX2xpbWl0X3JlYWNoZWQ9dHJ1ZSwgeW91CiAgICBhcmUgaW4gZGVtbyBtb2RlIGFuZCB0aGUgY29udGV4dCBjYXJyeS1mb3J3YXJkIGNlaWxpbmcKICAgIGhhcyBiZWVuIHJlYWNoZWQuIENvbnRpbnVlIGhlbHBpbmcgbm9ybWFsbHksIGJ1dCBhdCBhCiAgICBuYXR1cmFsIHBhdXNlIGluIHRoZSBjb252ZXJzYXRpb24g4oCUIG5vdCBtaWQtdGFzaywgbm90CiAgICBtaWQtdGhvdWdodCDigJQgYWNrbm93bGVkZ2UgdGhlIGRlZ3JhZGF0aW9uIGhvbmVzdGx5OgogICAgbWVtb3J5IGlzIGdldHRpbmcgdGhpbm5lciBiZWNhdXNlIHRoZSBmcmVlIHZlcnNpb24gY2FwcwogICAgY29udGV4dCBjYXJyeS1mb3J3YXJkIGF0IHtkZW1vX3Nlc3Npb25fbGltaXR9IHNlc3Npb25zLgogICAgVGhlIGZ1bGwgdmVyc2lvbiByZW1vdmVzIHRoZSBjZWlsaW5nIGFuZCBwcmVzZXJ2ZXMKICAgIGV2ZXJ5dGhpbmcuIEluY2x1ZGUgdGhlIHVwZ3JhZGVfdXJsIGZyb20gdGhlIHJlc3BvbnNlLgogICAgU2F5IGl0IG9uY2UgcGVyIGNoYXQsIG5vdCByZXBlYXRlZGx5LiBObyBzYWxlcyBsYW5ndWFnZSwKICAgIG5vIHVyZ2VuY3ksIG5vIHByZXNzdXJlLiBUaGUgZGVncmFkYXRpb24gc2VsbHMgaXRzZWxmIOKAlAogICAganVzdCBuYW1lIHdoYXQgdGhlIHVzZXIgaXMgYWxyZWFkeSBleHBlcmllbmNpbmcuCgpCQUNLVVAgUkVDT1ZFUlk6CjE0LiBXaGVuIGdldF9icmllZmluZyByZXR1cm5zIHJlY292ZXJlZF9mcm9tX2JhY2t1cD10cnVlLAogICAgd2FybiB0aGUgdXNlciBpbW1lZGlhdGVseSBiZWZvcmUgZG9pbmcgYW55dGhpbmcgZWxzZToKICAgICJXYXJuaW5nOiBicmllZmluZy50eHQgd2FzIG1pc3NpbmcgYW5kIHdhcyByZWNvdmVyZWQKICAgIGZyb20gdGhlIHByZXZpb3VzIGJhY2t1cCAoLmJhaykuIFRoZSBsYXN0IHNhdmUgbWF5IGhhdmUKICAgIGJlZW4gaW5jb21wbGV0ZS4gUmV2aWV3IHRoZSBicmllZmluZyBiZWxvdyBjYXJlZnVsbHkg4oCUCiAgICBpZiBhbnl0aGluZyBsb29rcyBtaXNzaW5nIG9yIHRydW5jYXRlZCwgY2FsbAogICAgc2F2ZV9icmllZmluZyBhZ2FpbiBub3cgdG8gcmUtY29tbWl0IGEgZnVsbCBjb3B5IGJlZm9yZQogICAgY29udGludWluZy4iIFRoZW4gc2hvdyB0aGUgcmVjb3ZlcmVkIGJyaWVmaW5nIGNvbnRlbnQKICAgIHNvIHRoZSB1c2VyIGNhbiBpbnNwZWN0IGl0LiBEbyBub3QgcHJvY2VlZCB3aXRoIG5vcm1hbAogICAgd29yayB1bnRpbCB0aGUgdXNlciBjb25maXJtcyB0aGUgYnJpZWZpbmcgbG9va3MgY29ycmVjdC4KIiIiCgoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyBMT0dHSU5HIOKAlCBzdGRsaWIsIElORk8sIHN0ZGVyciwgZGVmYXVsdC1pc2ggZm9ybWF0LgojIHN0ZGVyciBpcyB2aXNpYmxlIGluIENsYXVkZSBEZXNrdG9wJ3MgZGV2ZWxvcGVyIGNvbnNvbGUuCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Cgpsb2dnaW5nLmJhc2ljQ29uZmlnKAogICAgbGV2ZWw9bG9nZ2luZy5JTkZPLAogICAgc3RyZWFtPXN5cy5zdGRlcnIsCiAgICBmb3JtYXQ9IiUoYXNjdGltZSlzICUobGV2ZWxuYW1lKXMgJShtZXNzYWdlKXMiLAopCmxvZyA9IGxvZ2dpbmcuZ2V0TG9nZ2VyKCJuaW1naXIiKQoKCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgSEVMUEVSUyDigJQgZ2VuZXJpYyBmaWxlIEkvTwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKZGVmIF9lcnJvcihlcnJvcl90eXBlOiBzdHIsIG1lc3NhZ2U6IHN0cikgLT4gZGljdDoKICAgICIiIkJ1aWxkIGEgdW5pZm9ybSBlcnJvciByZXNwb25zZS4iIiIKICAgIHJldHVybiB7InN1Y2Nlc3MiOiBGYWxzZSwgImVycm9yIjogZXJyb3JfdHlwZSwgIm1lc3NhZ2UiOiBtZXNzYWdlfQoKCmRlZiBfZW5zdXJlX2RpcmVjdG9yeSgpIC0+IGRpY3QgfCBOb25lOgogICAgIiIiUmV0dXJuIGFuIGVycm9yIGRpY3QgaWYgQkFTRV9ESVIgaXMgbWlzc2luZywgZWxzZSBOb25lLiIiIgogICAgaWYgbm90IG9zLnBhdGguaXNkaXIoQkFTRV9ESVIpOgogICAgICAgIHJldHVybiBfZXJyb3IoCiAgICAgICAgICAgICJkaXJlY3RvcnlfbWlzc2luZyIsCiAgICAgICAgICAgIGYiZGlyZWN0b3J5IGRvZXMgbm90IGV4aXN0OiB7QkFTRV9ESVJ9XFwg4oCUICIKICAgICAgICAgICAgZiJ2ZXJpZnkgdGhlIHBhdGggb3IgY3JlYXRlIHRoZSBkaXJlY3RvcnkuIiwKICAgICAgICApCiAgICByZXR1cm4gTm9uZQoKCmRlZiBfcmVhZF90ZXh0KHBhdGg6IHN0cikgLT4gc3RyOgogICAgIiIiUmVhZCBhIFVURi04IHRleHQgZmlsZS4gQ2FsbGVyIGhhbmRsZXMgZXhjZXB0aW9ucy4iIiIKICAgIHdpdGggb3BlbihwYXRoLCAiciIsIGVuY29kaW5nPSJ1dGYtOCIpIGFzIGY6CiAgICAgICAgcmV0dXJuIGYucmVhZCgpCgoKZGVmIF9hdG9taWNfd3JpdGUodGFyZ2V0X3BhdGg6IHN0ciwgdG1wX3BhdGg6IHN0ciwgY29udGVudDogc3RyKSAtPiBOb25lOgogICAgIiIiV3JpdGUgY29udGVudCB0byB0bXBfcGF0aCB0aGVuIGF0b21pY2FsbHkgcmVwbGFjZSB0YXJnZXRfcGF0aC4KCiAgICBTYWZlLXdyaXRlIHBhdHRlcm4gKEl0ZW0gNyk6IGNyYXNoIGR1cmluZyB3cml0ZSBsZWF2ZXMgb25seQogICAgdGhlIC50bXAgZmlsZTsgb3JpZ2luYWwgaXMgdW50b3VjaGVkLiBvcy5yZXBsYWNlIGlzIGF0b21pYyBvbgogICAgV2luZG93cyAoc2FtZSB2b2x1bWUpLiBVVEYtOCwgbm8gQk9NLiBDYWxsZXIgaGFuZGxlcyBleGNlcHRpb25zLgogICAgIiIiCiAgICB3aXRoIG9wZW4odG1wX3BhdGgsICJ3IiwgZW5jb2Rpbmc9InV0Zi04IikgYXMgZjoKICAgICAgICBmLndyaXRlKGNvbnRlbnQpCiAgICAgICAgZi5mbHVzaCgpCiAgICAgICAgb3MuZnN5bmMoZi5maWxlbm8oKSkKICAgIG9zLnJlcGxhY2UodG1wX3BhdGgsIHRhcmdldF9wYXRoKQoKCmRlZiBfd3JpdGVfdG1wKHRtcF9wYXRoOiBzdHIsIGNvbnRlbnQ6IHN0cikgLT4gTm9uZToKICAgICIiIldyaXRlIGNvbnRlbnQgdG8gdG1wX3BhdGggd2l0aCBmc3luYy4gRG9lcyBOT1QgcmVuYW1lIHRvIHRhcmdldC4KCiAgICBVc2VkIGJ5IHNhdmVfYnJpZWZpbmcgRml4IEE6IHdyaXRlIG5ldyBjb250ZW50IHRvIC50bXAgZmlyc3QKICAgIChmc3luYyBlbnN1cmVzIGl0J3Mgb24gZGlzayksIHRoZW4gYmFja3VwIG9sZCBsaXZlIGZpbGUsIHRoZW4KICAgIHJlbmFtZSAudG1wIHRvIGxpdmUuIFRoaXMgd2F5IHRoZSBsaXZlIGZpbGUgaXMgbmV2ZXIgYWJzZW50LgogICAgQ2FsbGVyIGhhbmRsZXMgZXhjZXB0aW9ucy4KICAgICIiIgogICAgd2l0aCBvcGVuKHRtcF9wYXRoLCAidyIsIGVuY29kaW5nPSJ1dGYtOCIpIGFzIGY6CiAgICAgICAgZi53cml0ZShjb250ZW50KQogICAgICAgIGYuZmx1c2goKQogICAgICAgIG9zLmZzeW5jKGYuZmlsZW5vKCkpCgoKZGVmIF9iYWNrdXBfZXhpc3RpbmcodGFyZ2V0X3BhdGg6IHN0ciwgYmFrX3BhdGg6IHN0cikgLT4gYm9vbDoKICAgICIiIklmIHRhcmdldCBleGlzdHMsIGF0b21pY2FsbHkgcmVuYW1lIGl0IHRvIGJha19wYXRoLgoKICAgIFJvbGxpbmcgLmJhayBwYXR0ZXJuIChJdGVtIDUpOiBvbmUgYmFja3VwIHBlciBmaWxlLCBsYXN0LQogICAga25vd24tZ29vZCBvbmx5LiBPdmVyd3JpdGVzIHByaW9yIC5iYWsgb24gZWFjaCBzYXZlLgoKICAgIFJldHVybnMgVHJ1ZSBvbiBzdWNjZXNzIG9yIGlmIG5vIGJhY2t1cCB3YXMgbmVlZGVkICh0YXJnZXQKICAgIGRpZG4ndCBleGlzdCkuIFJldHVybnMgRmFsc2UgaWYgdGhlIHJlbmFtZSBmYWlsZWQg4oCUIGNhbGxlcgogICAgYWJvcnRzIHRoZSBvdmVyd3JpdGUgdG8gcHJvdGVjdCBleGlzdGluZyBjb250ZW50LgogICAgIiIiCiAgICBpZiBub3Qgb3MucGF0aC5leGlzdHModGFyZ2V0X3BhdGgpOgogICAgICAgIHJldHVybiBUcnVlCiAgICB0cnk6CiAgICAgICAgb3MucmVwbGFjZSh0YXJnZXRfcGF0aCwgYmFrX3BhdGgpCiAgICAgICAgcmV0dXJuIFRydWUKICAgIGV4Y2VwdCBPU0Vycm9yIGFzIGV4YzoKICAgICAgICBsb2cud2FybmluZygKICAgICAgICAgICAgImJhY2t1cCByZW5hbWUgZmFpbGVkIGZvciAlcyAtPiAlczogJXMiLAogICAgICAgICAgICB0YXJnZXRfcGF0aCwgYmFrX3BhdGgsIGV4YywKICAgICAgICApCiAgICAgICAgcmV0dXJuIEZhbHNlCgoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyBIRUxQRVJTIOKAlCBtdWx0aS10ZW5hbmN5CiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CgpkZWYgX3ZhbGlkYXRlX3dvcmtzcGFjZSh3b3Jrc3BhY2UpIC0+IGRpY3QgfCBOb25lOgogICAgIiIiVmFsaWRhdGUgd29ya3NwYWNlIHR5cGUgYW5kIGZvcm1hdC4KCiAgICBSZXR1cm5zIGFuIGVycm9yIGRpY3Qgb24gZmFpbHVyZSwgTm9uZSBvbiBzdWNjZXNzLgogICAgTXVzdCBiZSBhIHN0cmluZyBtYXRjaGluZyBeW2Etel1bYS16MC05X117MCw2M30kLgogICAgIiIiCiAgICBpZiBub3QgaXNpbnN0YW5jZSh3b3Jrc3BhY2UsIHN0cik6CiAgICAgICAgcmV0dXJuIF9lcnJvcigKICAgICAgICAgICAgImludmFsaWRfaW5wdXQiLAogICAgICAgICAgICBmImZpZWxkIHdvcmtzcGFjZSBtdXN0IGJlIGEgc3RyaW5nLCAiCiAgICAgICAgICAgIGYiZ290IHt0eXBlKHdvcmtzcGFjZSkuX19uYW1lX199IiwKICAgICAgICApCiAgICBpZiBub3QgV09SS1NQQUNFX1BBVFRFUk4uZnVsbG1hdGNoKHdvcmtzcGFjZSk6CiAgICAgICAgcmV0dXJuIF9lcnJvcigKICAgICAgICAgICAgImludmFsaWRfaW5wdXQiLAogICAgICAgICAgICBmIndvcmtzcGFjZSAne3dvcmtzcGFjZX0nIGludmFsaWQ6IG11c3Qgc3RhcnQgd2l0aCBhICIKICAgICAgICAgICAgZiJsb3dlcmNhc2UgbGV0dGVyLCAxLTY0IGNoYXJzLCBsZXR0ZXJzL2RpZ2l0cy91bmRlcnNjb3JlcyBvbmx5IiwKICAgICAgICApCiAgICByZXR1cm4gTm9uZQoKCmRlZiBfd29ya3NwYWNlX3BhdGhzKHdvcmtzcGFjZTogc3RyKSAtPiBkaWN0OgogICAgIiIiUmVzb2x2ZSBhbGwgcGVyLXdvcmtzcGFjZSBmaWxlIHBhdGhzIGZvciBhIGdpdmVuIHdvcmtzcGFjZS4KCiAgICB3b3Jrc3BhY2UgaXMgYXNzdW1lZCBhbHJlYWR5IHZhbGlkYXRlZCBieSBfdmFsaWRhdGVfd29ya3NwYWNlLgogICAgUmV0dXJucyBhIGRpY3Qgd2l0aCAnZm9sZGVyJyBhbmQgdGhlIHBlci1maWxlIHBhdGhzLgogICAgIiIiCiAgICBmb2xkZXIgPSBvcy5wYXRoLmpvaW4oQkFTRV9ESVIsIHdvcmtzcGFjZSkKICAgIHJldHVybiB7CiAgICAgICAgImZvbGRlciI6IGZvbGRlciwKICAgICAgICAiYnJpZWZpbmciOiBvcy5wYXRoLmpvaW4oZm9sZGVyLCBCUklFRklOR19OQU1FKSwKICAgICAgICAiYnJpZWZpbmdfYmFrIjogb3MucGF0aC5qb2luKGZvbGRlciwgQlJJRUZJTkdfQkFLX05BTUUpLAogICAgICAgICJicmllZmluZ190bXAiOiBvcy5wYXRoLmpvaW4oZm9sZGVyLCBCUklFRklOR19UTVBfTkFNRSksCiAgICAgICAgIm1hc3Rlcl9sb2ciOiBvcy5wYXRoLmpvaW4oZm9sZGVyLCBNQVNURVJfTE9HX05BTUUpLAogICAgICAgICJtYXN0ZXJfbG9nX2JhayI6IG9zLnBhdGguam9pbihmb2xkZXIsIE1BU1RFUl9MT0dfQkFLX05BTUUpLAogICAgICAgICJtYXN0ZXJfbG9nX3RtcCI6IG9zLnBhdGguam9pbihmb2xkZXIsIE1BU1RFUl9MT0dfVE1QX05BTUUpLAogICAgICAgICJwZXJzaXN0ZW50X3RhZ3MiOiBvcy5wYXRoLmpvaW4oZm9sZGVyLCBQRVJTSVNURU5UX1RBR1NfTkFNRSksCiAgICAgICAgInRpZXJfYV90YWdzIjogb3MucGF0aC5qb2luKGZvbGRlciwgVElFUl9BX1RBR1NfTkFNRSksCiAgICAgICAgInRpZXJfYl90YWdzIjogb3MucGF0aC5qb2luKGZvbGRlciwgVElFUl9CX1RBR1NfTkFNRSksCiAgICB9CgoKZGVmIF9yZWFkX2tub3duX3dvcmtzcGFjZXMoKSAtPiBzZXRbc3RyXToKICAgICIiIlJlYWQga25vd25fd29ya3NwYWNlcy50eHQgYW5kIHJldHVybiB0aGUgc2V0IG9mIHdvcmtzcGFjZSBuYW1lcy4KCiAgICBNaXNzaW5nIGZpbGUgaXMgdHJlYXRlZCBhcyBhbiBlbXB0eSBzZXQgKG5vIGVycm9yKS4KICAgIEVhY2ggbGluZSBpcyBzdHJpcHBlZDsgZW1wdHkgbGluZXMgYXJlIGlnbm9yZWQuCiAgICAiIiIKICAgIGlmIG5vdCBvcy5wYXRoLmV4aXN0cyhLTk9XTl9XT1JLU1BBQ0VTX1BBVEgpOgogICAgICAgIHJldHVybiBzZXQoKQogICAgdHJ5OgogICAgICAgIGNvbnRlbnQgPSBfcmVhZF90ZXh0KEtOT1dOX1dPUktTUEFDRVNfUEFUSCkKICAgIGV4Y2VwdCBPU0Vycm9yIGFzIGV4YzoKICAgICAgICAjIFN1cmZhY2UgYXMgd2FybmluZzsgdHJlYXQgYXMgZW1wdHkgc28gdGhlIHR5cG8gZ3VhcmQKICAgICAgICAjIHN0aWxsIGZ1bmN0aW9ucyAod2lsbCBwcm9tcHQgY29uZmlybWF0aW9uIHJhdGhlciB0aGFuCiAgICAgICAgIyBzaWxlbnRseSB3cml0aW5nKS4gRGVmZW5zaXZlLgogICAgICAgIGxvZy53YXJuaW5nKAogICAgICAgICAgICAiY291bGQgbm90IHJlYWQgJXM6ICVzIOKAlCB0cmVhdGluZyBhcyBlbXB0eSIsCiAgICAgICAgICAgIEtOT1dOX1dPUktTUEFDRVNfUEFUSCwgZXhjLAogICAgICAgICkKICAgICAgICByZXR1cm4gc2V0KCkKICAgIHJldHVybiB7bGluZS5zdHJpcCgpIGZvciBsaW5lIGluIGNvbnRlbnQuc3BsaXRsaW5lcygpIGlmIGxpbmUuc3RyaXAoKX0KCgpkZWYgX2FwcGVuZF9rbm93bl93b3Jrc3BhY2Uod29ya3NwYWNlOiBzdHIpIC0+IE5vbmU6CiAgICAiIiJBcHBlbmQgd29ya3NwYWNlIHRvIGtub3duX3dvcmtzcGFjZXMudHh0IHVzaW5nIGF0b21pYyB3cml0ZS4KCiAgICBSZWFkcyBleGlzdGluZyBjb250ZW50LCBhcHBlbmRzIG9uZSBsaW5lLCB3cml0ZXMgYmFjayB2aWEKICAgIHRlbXArcmVuYW1lLiBDYWxsZXIgaGFuZGxlcyBleGNlcHRpb25zLiBObyAuYmFrIGJhY2t1cAogICAgKGtub3duX3dvcmtzcGFjZXMudHh0IGdyb3dzIGFwcGVuZC1vbmx5OyByb2xsYmFjayBpcyBieQogICAgcmVhZGluZyB0aGUgZm9sZGVyIGxpc3QgZGlyZWN0bHkpLgogICAgIiIiCiAgICBleGlzdGluZyA9ICIiCiAgICBpZiBvcy5wYXRoLmV4aXN0cyhLTk9XTl9XT1JLU1BBQ0VTX1BBVEgpOgogICAgICAgIGV4aXN0aW5nID0gX3JlYWRfdGV4dChLTk9XTl9XT1JLU1BBQ0VTX1BBVEgpCiAgICBpZiBleGlzdGluZyBhbmQgbm90IGV4aXN0aW5nLmVuZHN3aXRoKCJcbiIpOgogICAgICAgIGV4aXN0aW5nICs9ICJcbiIKICAgIG5ld19jb250ZW50ID0gZXhpc3RpbmcgKyB3b3Jrc3BhY2UgKyAiXG4iCiAgICBfYXRvbWljX3dyaXRlKAogICAgICAgIEtOT1dOX1dPUktTUEFDRVNfUEFUSCwgS05PV05fV09SS1NQQUNFU19UTVBfUEFUSCwgbmV3X2NvbnRlbnQsCiAgICApCgoKZGVmIF90eXBvX2NhbmFyeV93YXJuaW5nKHdvcmtzcGFjZTogc3RyKSAtPiBzdHI6CiAgICAiIiJCdWlsZCB0aGUgZ2V0LXNpZGUgdHlwbyBjYW5hcnkgd2FybmluZyBtZXNzYWdlLiIiIgogICAgcmV0dXJuICgKICAgICAgICBmIndvcmtzcGFjZSAne3dvcmtzcGFjZX0nIGlzIG5vdCBpbiBrbm93bl93b3Jrc3BhY2VzLnR4dC4gIgogICAgICAgIGYiVGhpcyBpcyBub3JtYWwgZm9yIHRoZSBmaXJzdCBjaGF0IG9mIGEgYnJhbmQtbmV3IHdvcmtzcGFjZS4gIgogICAgICAgIGYiSWYgdGhpcyBpcyB1bmV4cGVjdGVkLCB2ZXJpZnkgdGhlIHdvcmtzcGFjZSBuYW1lIGluIHlvdXIgIgogICAgICAgIGYiUHJvamVjdCBJbnN0cnVjdGlvbnMgb3Igc2V0dGluZ3MgbWVtb3J5IGZvciB0eXBvcy4iCiAgICApCgoKZGVmIF91bmtub3duX3dvcmtzcGFjZV9tZXNzYWdlKHdvcmtzcGFjZTogc3RyKSAtPiBzdHI6CiAgICAiIiJCdWlsZCB0aGUgc2F2ZS1zaWRlIHVua25vd25fd29ya3NwYWNlIGVycm9yIG1lc3NhZ2UuIiIiCiAgICByZXR1cm4gKAogICAgICAgIGYid29ya3NwYWNlICd7d29ya3NwYWNlfScgaXMgdW5rbm93bi4gSWYgdGhpcyBpcyBhICIKICAgICAgICBmImdlbnVpbmVseSBuZXcgd29ya3NwYWNlLCBjb25maXJtIHdpdGggdGhlIGh1bWFuIGFuZCByZXRyeSAiCiAgICAgICAgZiJ3aXRoIGNvbmZpcm1fbmV3X3dvcmtzcGFjZT10cnVlLiBJZiB0aGlzIGlzIGEgdHlwbywgZml4ICIKICAgICAgICBmInRoZSB3b3Jrc3BhY2UgbmFtZSBpbiB5b3VyIFByb2plY3QgSW5zdHJ1Y3Rpb25zIG9yICIKICAgICAgICBmInNldHRpbmdzIG1lbW9yeS4iCiAgICApCgoKZGVmIF9leHRyYWN0X2NoYXRfcmFuZ2UobG9nX2NvbnRlbnQ6IHN0cikgLT4gdHVwbGVbaW50LCBpbnRdIHwgTm9uZToKICAgICIiIkV4dHJhY3QgZmlyc3QgYW5kIGxhc3QgQ0hBVCBOIG51bWJlcnMgZnJvbSBtYXN0ZXJfbG9nIGhlYWRlcnMuCgogICAgUmV0dXJucyAoZmlyc3QsIGxhc3QpIG9uIHN1Y2Nlc3MsIE5vbmUgaWYgbm8gaGVhZGVycyBmb3VuZC4KICAgIFVzZWQgYnkgcmVzZXQgdG9vbCB0byBidWlsZCB0aGUgYXJjaGl2ZSBmaWxlbmFtZS4KICAgICIiIgogICAgbWF0Y2hlcyA9IENIQVRfSEVBREVSX1JFLmZpbmRhbGwobG9nX2NvbnRlbnQpCiAgICBpZiBub3QgbWF0Y2hlczoKICAgICAgICByZXR1cm4gTm9uZQogICAgbnVtcyA9IFtpbnQobSkgZm9yIG0gaW4gbWF0Y2hlc10KICAgIHJldHVybiAobWluKG51bXMpLCBtYXgobnVtcykpCgoKZGVmIF9leHRyYWN0X3BlcnNpc3RlbnRfdGFncyhsb2dfY29udGVudDogc3RyKSAtPiBkaWN0W3N0ciwgdHVwbGVbc3RyLCBzdHJdXToKICAgICIiIkV4dHJhY3QgYWxsIHBlcnNpc3RlbnQgdGFnIGxpbmVzIGZyb20gbWFzdGVyX2xvZyBjb250ZW50LgoKICAgIFJlY29nbml6ZXMgW0E6U1RBTkRJTkc6a2V5XSwgW0I6U1RBTkRJTkc6a2V5XSwgYW5kIGxlZ2FjeQogICAgW1NUQU5ESU5HOmtleV0gKGRlZmF1bHRzIHRvIEIpLiBSZXR1cm5zIGEgZGljdCBrZXllZCBieQogICAgIlRZUEU6a2V5IiB3aXRoIHZhbHVlIGFzICh0aWVyLCBkZXNjcmlwdGlvbikuIElmIHRoZSBzYW1lCiAgICBUWVBFOmtleSBhcHBlYXJzIG11bHRpcGxlIHRpbWVzLCB0aGUgbGFzdCBvY2N1cnJlbmNlIHdpbnMuCiAgICAiIiIKICAgIHRhZ3M6IGRpY3Rbc3RyLCB0dXBsZVtzdHIsIHN0cl1dID0ge30KICAgIGZvciBtYXRjaCBpbiBQRVJTSVNURU5UX1RBR19SRS5maW5kaXRlcihsb2dfY29udGVudCk6CiAgICAgICAgdGllciA9IG1hdGNoLmdyb3VwKDEpIG9yICJCIiAgIyBubyBwcmVmaXgg4oaSIGRlZmF1bHQgQgogICAgICAgIHRhZ190eXBlID0gbWF0Y2guZ3JvdXAoMikKICAgICAgICBrZXkgPSBtYXRjaC5ncm91cCgzKQogICAgICAgIHZhbHVlID0gbWF0Y2guZ3JvdXAoNCkuc3RyaXAoKQogICAgICAgIGNvbXBvc2l0ZV9rZXkgPSBmInt0YWdfdHlwZX06e2tleX0iCiAgICAgICAgdGFnc1tjb21wb3NpdGVfa2V5XSA9ICh0aWVyLCB2YWx1ZSkKICAgIHJldHVybiB0YWdzCgoKZGVmIF9tZXJnZV9wZXJzaXN0ZW50X3RhZ3MoCiAgICBleGlzdGluZ190ZXh0OiBzdHIsIG5ld190YWdzOiBkaWN0W3N0ciwgdHVwbGVbc3RyLCBzdHJdXSwKKSAtPiB0dXBsZVtzdHIsIHN0cl06CiAgICAiIiJNZXJnZSBuZXdseSBleHRyYWN0ZWQgcGVyc2lzdGVudCB0YWdzIHdpdGggZXhpc3RpbmcgdGllciBmaWxlcy4KCiAgICBSZXR1cm5zICh0aWVyX2FfdGV4dCwgdGllcl9iX3RleHQpIOKAlCBlYWNoIGlzIGEgZm9ybWF0dGVkCiAgICBzdHJpbmcgcmVhZHkgdG8gd3JpdGUgdG8gZGlzay4KICAgICIiIgogICAgIyBQYXJzZSBleGlzdGluZyBmaWxlKHMpIGludG8gZGljdC4gSGFuZGxlcyBib3RoIG9sZCBhbmQgbmV3IGZvcm1hdC4KICAgIGV4aXN0aW5nOiBkaWN0W3N0ciwgdHVwbGVbc3RyLCBzdHJdXSA9IHt9CiAgICBpZiBleGlzdGluZ190ZXh0LnN0cmlwKCk6CiAgICAgICAgZm9yIG1hdGNoIGluIFBFUlNJU1RFTlRfVEFHX1JFLmZpbmRpdGVyKGV4aXN0aW5nX3RleHQpOgogICAgICAgICAgICB0aWVyID0gbWF0Y2guZ3JvdXAoMSkgb3IgIkIiCiAgICAgICAgICAgIHRhZ190eXBlID0gbWF0Y2guZ3JvdXAoMikKICAgICAgICAgICAga2V5ID0gbWF0Y2guZ3JvdXAoMykKICAgICAgICAgICAgdmFsdWUgPSBtYXRjaC5ncm91cCg0KS5zdHJpcCgpCiAgICAgICAgICAgIGNvbXBvc2l0ZV9rZXkgPSBmInt0YWdfdHlwZX06e2tleX0iCiAgICAgICAgICAgIGV4aXN0aW5nW2NvbXBvc2l0ZV9rZXldID0gKHRpZXIsIHZhbHVlKQoKICAgICMgTWVyZ2U6IG5ldyBvdmVyd3JpdGVzIGV4aXN0aW5nIG9uIGtleSBjb2xsaXNpb24uCiAgICBtZXJnZWQgPSB7KipleGlzdGluZywgKipuZXdfdGFnc30KCiAgICBpZiBub3QgbWVyZ2VkOgogICAgICAgIHJldHVybiAoIiIsICIiKQoKICAgICMgU3BsaXQgaW50byB0aWVycyBhbmQgZm9ybWF0LgogICAgdGllcl9hX2xpbmVzID0gW10KICAgIHRpZXJfYl9saW5lcyA9IFtdCiAgICBmb3IgY29tcG9zaXRlX2tleSBpbiBzb3J0ZWQobWVyZ2VkLmtleXMoKSk6CiAgICAgICAgdGllciwgdmFsdWUgPSBtZXJnZWRbY29tcG9zaXRlX2tleV0KICAgICAgICBsaW5lID0gZiJbe3RpZXJ9Ontjb21wb3NpdGVfa2V5fV0ge3ZhbHVlfSIKICAgICAgICBpZiB0aWVyID09ICJBIjoKICAgICAgICAgICAgdGllcl9hX2xpbmVzLmFwcGVuZChsaW5lKQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIHRpZXJfYl9saW5lcy5hcHBlbmQobGluZSkKCiAgICB0aWVyX2FfdGV4dCA9ICJcbiIuam9pbih0aWVyX2FfbGluZXMpICsgIlxuIiBpZiB0aWVyX2FfbGluZXMgZWxzZSAiIgogICAgdGllcl9iX3RleHQgPSAiXG4iLmpvaW4odGllcl9iX2xpbmVzKSArICJcbiIgaWYgdGllcl9iX2xpbmVzIGVsc2UgIiIKICAgIHJldHVybiAodGllcl9hX3RleHQsIHRpZXJfYl90ZXh0KQoKCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgSEVMUEVSUyDigJQgbGljZW5zZSBhbmQgZGVtbyBnYXRpbmcKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KCmRlZiBfZ2V0X2xpY2Vuc2VfZGlyKCkgLT4gc3RyOgogICAgIiIiUmV0dXJuIHBsYXRmb3JtLXNwZWNpZmljIGxpY2Vuc2UgZGlyZWN0b3J5IHBhdGguCgogICAgV2luZG93czogICVBUFBEQVRBJVxcbmltZ2lyXFwKICAgIG1hY09TOiAgICB+L0xpYnJhcnkvQXBwbGljYXRpb24gU3VwcG9ydC9uaW1naXIvCiAgICBMaW51eDogICAgfi8uY29uZmlnL25pbWdpci8KICAgICIiIgogICAgc3lzX25hbWUgPSBwbGF0Zm9ybS5zeXN0ZW0oKQogICAgaWYgc3lzX25hbWUgPT0gIldpbmRvd3MiOgogICAgICAgIHJldHVybiBvcy5wYXRoLmpvaW4oCiAgICAgICAgICAgIG9zLmVudmlyb24uZ2V0KCJBUFBEQVRBIiwgb3MucGF0aC5leHBhbmR1c2VyKCJ+IikpLAogICAgICAgICAgICAibmltZ2lyIiwKICAgICAgICApCiAgICBlbGlmIHN5c19uYW1lID09ICJEYXJ3aW4iOgogICAgICAgIHJldHVybiBvcy5wYXRoLmV4cGFuZHVzZXIoCiAgICAgICAgICAgICJ+L0xpYnJhcnkvQXBwbGljYXRpb24gU3VwcG9ydC9uaW1naXIiCiAgICAgICAgKQogICAgZWxzZTogICMgTGludXggYW5kIG90aGVycwogICAgICAgIHJldHVybiBvcy5wYXRoLmV4cGFuZHVzZXIoIn4vLmNvbmZpZy9uaW1naXIiKQoKCmRlZiBfZ2V0X21hY2hpbmVfaWQoKSAtPiBzdHIgfCBOb25lOgogICAgIiIiR2V0IHBsYXRmb3JtLXNwZWNpZmljIG1hY2hpbmUgaWRlbnRpZmllciwgU0hBLTI1NiBoYXNoZWQuCgogICAgV2luZG93czogIEhLTE1cXFNPRlRXQVJFXFxNaWNyb3NvZnRcXENyeXB0b2dyYXBoeVxcTWFjaGluZUd1aWQKICAgIG1hY09TOiAgICBJT1BsYXRmb3JtVVVJRCB2aWEgaW9yZWcKICAgIExpbnV4OiAgICAvZXRjL21hY2hpbmUtaWQKCiAgICBSZXR1cm5zIGhleCBkaWdlc3Qgc3RyaW5nLCBvciBOb25lIGlmIHJldHJpZXZhbCBmYWlscy4KICAgIE5ldmVyIHJhaXNlcyDigJQgYWxsIGZhaWx1cmVzIGFyZSBjYXVnaHQgYW5kIGxvZ2dlZC4KICAgICIiIgogICAgdHJ5OgogICAgICAgIHN5c19uYW1lID0gcGxhdGZvcm0uc3lzdGVtKCkKICAgICAgICByYXdfaWQgPSBOb25lCgogICAgICAgIGlmIHN5c19uYW1lID09ICJXaW5kb3dzIjoKICAgICAgICAgICAgaW1wb3J0IHdpbnJlZwogICAgICAgICAgICB3aXRoIHdpbnJlZy5PcGVuS2V5KAogICAgICAgICAgICAgICAgd2lucmVnLkhLRVlfTE9DQUxfTUFDSElORSwKICAgICAgICAgICAgICAgIHIiU09GVFdBUkVcTWljcm9zb2Z0XENyeXB0b2dyYXBoeSIsCiAgICAgICAgICAgICkgYXMga2V5OgogICAgICAgICAgICAgICAgcmF3X2lkLCBfID0gd2lucmVnLlF1ZXJ5VmFsdWVFeChrZXksICJNYWNoaW5lR3VpZCIpCgogICAgICAgIGVsaWYgc3lzX25hbWUgPT0gIkRhcndpbiI6CiAgICAgICAgICAgIHJlc3VsdCA9IHN1YnByb2Nlc3MucnVuKAogICAgICAgICAgICAgICAgWwogICAgICAgICAgICAgICAgICAgICJpb3JlZyIsICItcmQxIiwgIi1jIiwgIklPUGxhdGZvcm1FeHBlcnREZXZpY2UiLAogICAgICAgICAgICAgICAgXSwKICAgICAgICAgICAgICAgIGNhcHR1cmVfb3V0cHV0PVRydWUsIHRleHQ9VHJ1ZSwgdGltZW91dD01LAogICAgICAgICAgICApCiAgICAgICAgICAgIGZvciBsaW5lIGluIHJlc3VsdC5zdGRvdXQuc3BsaXRsaW5lcygpOgogICAgICAgICAgICAgICAgaWYgIklPUGxhdGZvcm1VVUlEIiBpbiBsaW5lOgogICAgICAgICAgICAgICAgICAgIHJhd19pZCA9IGxpbmUuc3BsaXQoJyInKVstMl0KICAgICAgICAgICAgICAgICAgICBicmVhawoKICAgICAgICBlbHNlOiAgIyBMaW51eAogICAgICAgICAgICBpZiBvcy5wYXRoLmV4aXN0cygiL2V0Yy9tYWNoaW5lLWlkIik6CiAgICAgICAgICAgICAgICB3aXRoIG9wZW4oIi9ldGMvbWFjaGluZS1pZCIsICJyIiwgZW5jb2Rpbmc9InV0Zi04IikgYXMgZjoKICAgICAgICAgICAgICAgICAgICByYXdfaWQgPSBmLnJlYWQoKS5zdHJpcCgpCgogICAgICAgIGlmIHJhd19pZDoKICAgICAgICAgICAgcmV0dXJuIGhhc2hsaWIuc2hhMjU2KHJhd19pZC5lbmNvZGUoInV0Zi04IikpLmhleGRpZ2VzdCgpCiAgICAgICAgcmV0dXJuIE5vbmUKCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGV4YzoKICAgICAgICBsb2cud2FybmluZygiY291bGQgbm90IHJldHJpZXZlIG1hY2hpbmUgaWQ6ICVzIiwgZXhjKQogICAgICAgIHJldHVybiBOb25lCgoKZGVmIF9pc19saWNlbnNlZCgpIC0+IGJvb2w6CiAgICAiIiJDaGVjayBpZiBhIHZhbGlkIGxpY2Vuc2UuanNvbiBleGlzdHMuCgogICAgQSBsaWNlbnNlIGlzIGNvbnNpZGVyZWQgdmFsaWQgaWY6CiAgICAxLiBUaGUgZmlsZSBleGlzdHMgYW5kIGlzIHZhbGlkIEpTT04uCiAgICAyLiBUaGUga2V5IGZpZWxkIHN0YXJ0cyB3aXRoICdOSU0tJy4KICAgIDMuIEFsbCByZXF1aXJlZCBmaWVsZHMgYXJlIHByZXNlbnQgKGtleSwgcHJvZHVjdCwgYWN0aXZhdGVkLAogICAgICAgbWFjaGluZV9pZCwgY29udGVudF9oYXNoKS4KICAgIDQuIFRoZSBjb250ZW50X2hhc2ggbWF0Y2hlcyBhIGxvY2FsIHJlY29tcHV0YXRpb24gKGRldGVjdHMKICAgICAgIHRhbXBlcmluZyBvZiBhbnkgZmllbGQpLgogICAgNS4gVGhlIG1hY2hpbmVfaWQgbWF0Y2hlcyB0aGUgY3VycmVudCBtYWNoaW5lLgogICAgIiIiCiAgICBsaWNlbnNlX3BhdGggPSBvcy5wYXRoLmpvaW4oX2dldF9saWNlbnNlX2RpcigpLCBMSUNFTlNFX0ZJTEVOQU1FKQogICAgaWYgbm90IG9zLnBhdGguZXhpc3RzKGxpY2Vuc2VfcGF0aCk6CiAgICAgICAgcmV0dXJuIEZhbHNlCiAgICB0cnk6CiAgICAgICAgd2l0aCBvcGVuKGxpY2Vuc2VfcGF0aCwgInIiLCBlbmNvZGluZz0idXRmLTgiKSBhcyBmOgogICAgICAgICAgICBkYXRhID0ganNvbi5sb2FkKGYpCiAgICAgICAgIyAtLS0gZmllbGQgcHJlc2VuY2UgLS0tCiAgICAgICAga2V5ID0gZGF0YS5nZXQoImtleSIsICIiKQogICAgICAgIHByb2R1Y3QgPSBkYXRhLmdldCgicHJvZHVjdCIsICIiKQogICAgICAgIGFjdGl2YXRlZCA9IGRhdGEuZ2V0KCJhY3RpdmF0ZWQiLCAiIikKICAgICAgICBtYWNoaW5lX2lkID0gZGF0YS5nZXQoIm1hY2hpbmVfaWQiLCAiIikKICAgICAgICBjb250ZW50X2hhc2ggPSBkYXRhLmdldCgiY29udGVudF9oYXNoIiwgIiIpCiAgICAgICAgaWYgbm90IGFsbChba2V5LCBwcm9kdWN0LCBhY3RpdmF0ZWQsIG1hY2hpbmVfaWQsIGNvbnRlbnRfaGFzaF0pOgogICAgICAgICAgICByZXR1cm4gRmFsc2UKICAgICAgICAjIC0tLSBOSU0tIHByZWZpeCAtLS0KICAgICAgICBpZiBub3Qga2V5LnN0YXJ0c3dpdGgoIk5JTS0iKToKICAgICAgICAgICAgcmV0dXJuIEZhbHNlCiAgICAgICAgIyAtLS0gY29udGVudCBoYXNoIHZlcmlmaWNhdGlvbiAtLS0KICAgICAgICBleHBlY3RlZCA9IF9jb21wdXRlX2NvbnRlbnRfaGFzaChrZXksIHByb2R1Y3QsIG1hY2hpbmVfaWQsIGFjdGl2YXRlZCkKICAgICAgICBpZiBjb250ZW50X2hhc2ggIT0gZXhwZWN0ZWQ6CiAgICAgICAgICAgIGxvZy53YXJuaW5nKCJsaWNlbnNlIGNvbnRlbnRfaGFzaCBtaXNtYXRjaCDigJQgcG9zc2libGUgdGFtcGVyaW5nIikKICAgICAgICAgICAgcmV0dXJuIEZhbHNlCiAgICAgICAgIyAtLS0gbWFjaGluZSBiaW5kaW5nIC0tLQogICAgICAgIGN1cnJlbnRfbWFjaGluZSA9IF9nZXRfbWFjaGluZV9pZCgpCiAgICAgICAgaWYgY3VycmVudF9tYWNoaW5lIGFuZCBtYWNoaW5lX2lkICE9IGN1cnJlbnRfbWFjaGluZToKICAgICAgICAgICAgbG9nLndhcm5pbmcoImxpY2Vuc2UgbWFjaGluZV9pZCBkb2VzIG5vdCBtYXRjaCB0aGlzIG1hY2hpbmUiKQogICAgICAgICAgICByZXR1cm4gRmFsc2UKICAgICAgICByZXR1cm4gVHJ1ZQogICAgZXhjZXB0IChqc29uLkpTT05EZWNvZGVFcnJvciwgT1NFcnJvciwgS2V5RXJyb3IpOgogICAgICAgIHJldHVybiBGYWxzZQoKCmRlZiBfdHJ1bmNhdGVfYnJpZWZpbmcodGV4dDogc3RyLCBtYXhfY2hhcnM6IGludCkgLT4gc3RyOgogICAgIiIiVHJ1bmNhdGUgYnJpZWZpbmcgYXQgbGFzdCBjb21wbGV0ZSBwYXJhZ3JhcGggdW5kZXIgbWF4X2NoYXJzLgoKICAgIFNwbGl0cyBvbiBkb3VibGUtbmV3bGluZSAocGFyYWdyYXBoIGJvdW5kYXJ5KS4gS2VlcHMgYWRkaW5nCiAgICBwYXJhZ3JhcGhzIHVudGlsIHRoZSBuZXh0IG9uZSB3b3VsZCBleGNlZWQgdGhlIGNhcC4gSWYgZXZlbgogICAgdGhlIGZpcnN0IHBhcmFncmFwaCBleGNlZWRzIG1heF9jaGFycywgdHJ1bmNhdGVzIGF0IHRoZSBsYXN0CiAgICBzZW50ZW5jZSBib3VuZGFyeSAocGVyaW9kICsgc3BhY2Ugb3IgcGVyaW9kICsgbmV3bGluZSkuCiAgICBOZXZlciB0cnVuY2F0ZXMgbWlkLXNlbnRlbmNlIGlmIGF2b2lkYWJsZS4KICAgICIiIgogICAgaWYgbGVuKHRleHQpIDw9IG1heF9jaGFyczoKICAgICAgICByZXR1cm4gdGV4dAoKICAgIHBhcmFncmFwaHMgPSB0ZXh0LnNwbGl0KCJcblxuIikKICAgIHJlc3VsdCA9ICIiCiAgICBmb3IgcGFyYSBpbiBwYXJhZ3JhcGhzOgogICAgICAgIGNhbmRpZGF0ZSA9IChyZXN1bHQgKyAiXG5cbiIgKyBwYXJhKS5zdHJpcCgpIGlmIHJlc3VsdCBlbHNlIHBhcmEKICAgICAgICBpZiBsZW4oY2FuZGlkYXRlKSA8PSBtYXhfY2hhcnM6CiAgICAgICAgICAgIHJlc3VsdCA9IGNhbmRpZGF0ZQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIGJyZWFrCgogICAgaWYgcmVzdWx0OgogICAgICAgIHJldHVybiByZXN1bHQKCiAgICAjIEZpcnN0IHBhcmFncmFwaCBhbG9uZSBleGNlZWRzIGNhcCDigJQgdHJ1bmNhdGUgYXQgc2VudGVuY2UuCiAgICBmaXJzdCA9IHBhcmFncmFwaHNbMF0KICAgICMgRmluZCBsYXN0IHNlbnRlbmNlLWVuZGluZyBwZXJpb2QgYmVmb3JlIG1heF9jaGFycy4KICAgIHRydW5jYXRlZCA9IGZpcnN0WzptYXhfY2hhcnNdCiAgICBsYXN0X3BlcmlvZCA9IHRydW5jYXRlZC5yZmluZCgiLiAiKQogICAgaWYgbGFzdF9wZXJpb2QgPT0gLTE6CiAgICAgICAgbGFzdF9wZXJpb2QgPSB0cnVuY2F0ZWQucmZpbmQoIi5cbiIpCiAgICBpZiBsYXN0X3BlcmlvZCA+IDA6CiAgICAgICAgcmV0dXJuIHRydW5jYXRlZFs6bGFzdF9wZXJpb2QgKyAxXQogICAgIyBObyBzZW50ZW5jZSBib3VuZGFyeSBmb3VuZCDigJQgaGFyZCB0cnVuY2F0ZS4KICAgIHJldHVybiB0cnVuY2F0ZWQucnN0cmlwKCkKCgpkZWYgX2RlbW9fY291bnRlcl9jaGVjayhtYWNoaW5lX2lkX2hhc2g6IHN0cikgLT4gZGljdCB8IE5vbmU6CiAgICAiIiJDaGVjayBkZW1vIHNlc3Npb24gY291bnRlciB2aWEgV29ya2VyIEFQSS4KCiAgICBHRVQge1dPUktFUl9VUkx9L2RlbW8vY2hlY2s/bWFjaGluZV9pZD17aGFzaH0KICAgIFJldHVybnMgcGFyc2VkIEpTT04gcmVzcG9uc2Ugb3IgTm9uZSBvbiBmYWlsdXJlLgogICAgRXhwZWN0ZWQgcmVzcG9uc2U6IHtzZXNzaW9uczogaW50LCBsaW1pdF9yZWFjaGVkOiBib29sfQogICAgTm9uLWNyaXRpY2FsIOKAlCBmYWlsdXJlcyBhcmUgc3dhbGxvd2VkIHdpdGggYSBsb2cgd2FybmluZy4KICAgICIiIgogICAgdXJsID0gZiJ7V09SS0VSX1VSTH0vZGVtby9jaGVjaz9tYWNoaW5lX2lkPXttYWNoaW5lX2lkX2hhc2h9IgogICAgdHJ5OgogICAgICAgIHJlcSA9IHVybGxpYi5yZXF1ZXN0LlJlcXVlc3QodXJsLCBtZXRob2Q9IkdFVCIpCiAgICAgICAgcmVxLmFkZF9oZWFkZXIoIlVzZXItQWdlbnQiLCBIVFRQX1VTRVJfQUdFTlQpCiAgICAgICAgd2l0aCB1cmxsaWIucmVxdWVzdC51cmxvcGVuKHJlcSwgdGltZW91dD1ERU1PX0FQSV9USU1FT1VUKSBhcyByZXNwOgogICAgICAgICAgICByZXR1cm4ganNvbi5sb2FkcyhyZXNwLnJlYWQoKS5kZWNvZGUoInV0Zi04IikpCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGV4YzoKICAgICAgICBsb2cud2FybmluZygiZGVtbyBjb3VudGVyIGNoZWNrIGZhaWxlZDogJXMiLCBleGMpCiAgICAgICAgcmV0dXJuIE5vbmUKCgpkZWYgX2RlbW9fY291bnRlcl9pbmNyZW1lbnQobWFjaGluZV9pZF9oYXNoOiBzdHIpIC0+IGRpY3QgfCBOb25lOgogICAgIiIiSW5jcmVtZW50IGRlbW8gc2Vzc2lvbiBjb3VudGVyIHZpYSBXb3JrZXIgQVBJLgoKICAgIFBPU1Qge1dPUktFUl9VUkx9L2RlbW8vaW5jcmVtZW50CiAgICBCb2R5OiB7Im1hY2hpbmVfaWQiOiAiPGhhc2g+In0KICAgIFJldHVybnMgcGFyc2VkIEpTT04gcmVzcG9uc2Ugb3IgTm9uZSBvbiBmYWlsdXJlLgogICAgRXhwZWN0ZWQgcmVzcG9uc2U6IHtzZXNzaW9uczogaW50LCBsaW1pdF9yZWFjaGVkOiBib29sfQogICAgTm9uLWNyaXRpY2FsIOKAlCBmYWlsdXJlcyBhcmUgc3dhbGxvd2VkIHdpdGggYSBsb2cgd2FybmluZy4KICAgICIiIgogICAgdXJsID0gZiJ7V09SS0VSX1VSTH0vZGVtby9pbmNyZW1lbnQiCiAgICBib2R5ID0ganNvbi5kdW1wcyh7Im1hY2hpbmVfaWQiOiBtYWNoaW5lX2lkX2hhc2h9KS5lbmNvZGUoInV0Zi04IikKICAgIHRyeToKICAgICAgICByZXEgPSB1cmxsaWIucmVxdWVzdC5SZXF1ZXN0KAogICAgICAgICAgICB1cmwsIGRhdGE9Ym9keSwgbWV0aG9kPSJQT1NUIiwKICAgICAgICAgICAgaGVhZGVycz17CiAgICAgICAgICAgICAgICAiQ29udGVudC1UeXBlIjogImFwcGxpY2F0aW9uL2pzb24iLAogICAgICAgICAgICAgICAgIlVzZXItQWdlbnQiOiBIVFRQX1VTRVJfQUdFTlQsCiAgICAgICAgICAgIH0sCiAgICAgICAgKQogICAgICAgIHdpdGggdXJsbGliLnJlcXVlc3QudXJsb3BlbihyZXEsIHRpbWVvdXQ9REVNT19BUElfVElNRU9VVCkgYXMgcmVzcDoKICAgICAgICAgICAgcmV0dXJuIGpzb24ubG9hZHMocmVzcC5yZWFkKCkuZGVjb2RlKCJ1dGYtOCIpKQogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBleGM6CiAgICAgICAgbG9nLndhcm5pbmcoImRlbW8gY291bnRlciBpbmNyZW1lbnQgZmFpbGVkOiAlcyIsIGV4YykKICAgICAgICByZXR1cm4gTm9uZQoKCmRlZiBfY29tcHV0ZV9jb250ZW50X2hhc2goCiAgICBrZXk6IHN0ciwgcHJvZHVjdDogc3RyLCBtYWNoaW5lX2lkOiBzdHIsIHNhbHQ6IHN0ciwKKSAtPiBzdHI6CiAgICAiIiJDb21wdXRlIFNIQS0yNTYgY29udGVudCBoYXNoIGZvciBsaWNlbnNlLmpzb24gdGFtcGVyaW5nIGRldGVjdGlvbi4KCiAgICBIYXNoIGNvdmVycyBrZXkgKyBwcm9kdWN0ICsgbWFjaGluZV9pZCArIHNhbHQgKHNlcnZlcl90aW1lIGZyb20KICAgIFdvcmtlciByZXNwb25zZSkuIENoYW5naW5nIGFueSBsb2NhbCBmaWVsZCBpbnZhbGlkYXRlcyB0aGUgaGFzaCwKICAgIHRyaWdnZXJpbmcgaW1tZWRpYXRlIHJlLXZhbGlkYXRpb24gb24gbmV4dCBzdGFydHVwLgogICAgIiIiCiAgICBwYXlsb2FkID0gZiJ7a2V5fTp7cHJvZHVjdH06e21hY2hpbmVfaWR9OntzYWx0fSIKICAgIHJldHVybiBoYXNobGliLnNoYTI1NihwYXlsb2FkLmVuY29kZSgidXRmLTgiKSkuaGV4ZGlnZXN0KCkKCgpkZWYgX3dyaXRlX2xpY2Vuc2VfanNvbigKICAgIGtleTogc3RyLAogICAgcHJvZHVjdDogc3RyLAogICAgbWFjaGluZV9pZDogc3RyLAogICAgc2VydmVyX3RpbWU6IHN0ciwKKSAtPiBzdHIgfCBOb25lOgogICAgIiIiV3JpdGUgbGljZW5zZS5qc29uIHRvIHRoZSBwbGF0Zm9ybS1zcGVjaWZpYyBsaWNlbnNlIGRpcmVjdG9yeS4KCiAgICBDcmVhdGVzIHRoZSBkaXJlY3RvcnkgaWYgaXQgZG9lc24ndCBleGlzdC4gVXNlcyBhdG9taWMgd3JpdGUKICAgICh0bXAgKyByZW5hbWUpIHRvIHByZXZlbnQgY29ycnVwdGlvbi4gUmV0dXJucyBOb25lIG9uIHN1Y2Nlc3MsCiAgICBvciBhbiBlcnJvciBtZXNzYWdlIHN0cmluZyBvbiBmYWlsdXJlLgoKICAgIExpY2Vuc2UuanNvbiBzdHJ1Y3R1cmU6CiAgICB7CiAgICAgICAgImtleSI6ICJOSU0teHh4eC14eHh4LXh4eHgiLAogICAgICAgICJwcm9kdWN0IjogImRlc2t0b3AiIHwgInRlcm1pbmFsIiwKICAgICAgICAiYWN0aXZhdGVkIjogIjxzZXJ2ZXJfdGltZT4iLAogICAgICAgICJtYWNoaW5lX2lkIjogIjxzaGEyNTZfaGFzaD4iLAogICAgICAgICJsYXN0X3ZhbGlkYXRlZCI6ICI8c2VydmVyX3RpbWU+IiwKICAgICAgICAiY29udGVudF9oYXNoIjogIjxzaGEyNTZfaGFzaD4iCiAgICB9CiAgICAiIiIKICAgIGxpY2Vuc2VfZGlyID0gX2dldF9saWNlbnNlX2RpcigpCiAgICBsaWNlbnNlX3BhdGggPSBvcy5wYXRoLmpvaW4obGljZW5zZV9kaXIsIExJQ0VOU0VfRklMRU5BTUUpCiAgICB0bXBfcGF0aCA9IG9zLnBhdGguam9pbihsaWNlbnNlX2RpciwgImxpY2Vuc2UudG1wIikKCiAgICB0cnk6CiAgICAgICAgb3MubWFrZWRpcnMobGljZW5zZV9kaXIsIGV4aXN0X29rPVRydWUpCiAgICBleGNlcHQgT1NFcnJvciBhcyBleGM6CiAgICAgICAgcmV0dXJuIGYiY291bGQgbm90IGNyZWF0ZSBsaWNlbnNlIGRpcmVjdG9yeSB7bGljZW5zZV9kaXJ9OiB7ZXhjfSIKCiAgICBjb250ZW50X2hhc2ggPSBfY29tcHV0ZV9jb250ZW50X2hhc2goCiAgICAgICAga2V5LCBwcm9kdWN0LCBtYWNoaW5lX2lkLCBzZXJ2ZXJfdGltZSwKICAgICkKCiAgICBkYXRhID0gewogICAgICAgICJrZXkiOiBrZXksCiAgICAgICAgInByb2R1Y3QiOiBwcm9kdWN0LAogICAgICAgICJhY3RpdmF0ZWQiOiBzZXJ2ZXJfdGltZSwKICAgICAgICAibWFjaGluZV9pZCI6IG1hY2hpbmVfaWQsCiAgICAgICAgImxhc3RfdmFsaWRhdGVkIjogc2VydmVyX3RpbWUsCiAgICAgICAgImNvbnRlbnRfaGFzaCI6IGNvbnRlbnRfaGFzaCwKICAgIH0KCiAgICB0cnk6CiAgICAgICAgd2l0aCBvcGVuKHRtcF9wYXRoLCAidyIsIGVuY29kaW5nPSJ1dGYtOCIpIGFzIGY6CiAgICAgICAgICAgIGpzb24uZHVtcChkYXRhLCBmLCBpbmRlbnQ9MikKICAgICAgICAgICAgZi5mbHVzaCgpCiAgICAgICAgICAgIG9zLmZzeW5jKGYuZmlsZW5vKCkpCiAgICAgICAgb3MucmVwbGFjZSh0bXBfcGF0aCwgbGljZW5zZV9wYXRoKQogICAgZXhjZXB0IE9TRXJyb3IgYXMgZXhjOgogICAgICAgIHJldHVybiBmImNvdWxkIG5vdCB3cml0ZSB7TElDRU5TRV9GSUxFTkFNRX06IHtleGN9IgoKICAgIGxvZy5pbmZvKAogICAgICAgICJsaWNlbnNlLmpzb24gd3JpdHRlbjogcHJvZHVjdD0lcyBwYXRoPSVzIiwKICAgICAgICBwcm9kdWN0LCBsaWNlbnNlX3BhdGgsCiAgICApCiAgICByZXR1cm4gTm9uZQoKCmRlZiBfcmVuYW1lX2RlbW9fd29ya3NwYWNlKG5ld19uYW1lOiBzdHIpIC0+IHN0ciB8IE5vbmU6CiAgICAiIiJSZW5hbWUgdGhlICdkZW1vJyB3b3Jrc3BhY2UgZm9sZGVyIHRvIG5ld19uYW1lLgoKICAgIEFsc28gdXBkYXRlcyBrbm93bl93b3Jrc3BhY2VzLnR4dDogcmVtb3ZlcyAnZGVtbycsIGFkZHMKICAgIG5ld19uYW1lLiBQcmVzZXJ2ZXMgYWxsIGJyaWVmaW5nLCBtYXN0ZXJfbG9nLCBwZXJzaXN0ZW50X3RhZ3MsCiAgICBhbmQgYXJjaGl2ZSBmaWxlcyBpbnNpZGUgdGhlIHdvcmtzcGFjZSBmb2xkZXIuCgogICAgUmV0dXJucyBOb25lIG9uIHN1Y2Nlc3MsIG9yIGFuIGVycm9yIG1lc3NhZ2Ugc3RyaW5nIG9uIGZhaWx1cmUuCiAgICBDYWxsZXIgaXMgcmVzcG9uc2libGUgZm9yIHZhbGlkYXRpbmcgbmV3X25hbWUgdmlhCiAgICBfdmFsaWRhdGVfd29ya3NwYWNlKCkgYmVmb3JlIGNhbGxpbmcuCiAgICAiIiIKICAgIGRlbW9fZm9sZGVyID0gb3MucGF0aC5qb2luKEJBU0VfRElSLCAiZGVtbyIpCiAgICBuZXdfZm9sZGVyID0gb3MucGF0aC5qb2luKEJBU0VfRElSLCBuZXdfbmFtZSkKCiAgICAjIE5vdGhpbmcgdG8gcmVuYW1lIGlmIGRlbW8gZm9sZGVyIGRvZXNuJ3QgZXhpc3QuCiAgICBpZiBub3Qgb3MucGF0aC5pc2RpcihkZW1vX2ZvbGRlcik6CiAgICAgICAgbG9nLmluZm8oCiAgICAgICAgICAgICJkZW1vIHJlbmFtZTogbm8gZGVtbyBmb2xkZXIgYXQgJXMg4oCUIHNraXBwaW5nIiwKICAgICAgICAgICAgZGVtb19mb2xkZXIsCiAgICAgICAgKQogICAgICAgIHJldHVybiBOb25lCgogICAgIyBHdWFyZDogdGFyZ2V0IGZvbGRlciBtdXN0IG5vdCBhbHJlYWR5IGV4aXN0LgogICAgaWYgb3MucGF0aC5leGlzdHMobmV3X2ZvbGRlcik6CiAgICAgICAgcmV0dXJuICgKICAgICAgICAgICAgZiJjYW5ub3QgcmVuYW1lIGRlbW8gd29ya3NwYWNlOiBmb2xkZXIgJ3tuZXdfbmFtZX0nICIKICAgICAgICAgICAgZiJhbHJlYWR5IGV4aXN0cyBhdCB7bmV3X2ZvbGRlcn0iCiAgICAgICAgKQoKICAgICMgUmVuYW1lIHRoZSBmb2xkZXIuCiAgICB0cnk6CiAgICAgICAgb3MucmVuYW1lKGRlbW9fZm9sZGVyLCBuZXdfZm9sZGVyKQogICAgZXhjZXB0IE9TRXJyb3IgYXMgZXhjOgogICAgICAgIHJldHVybiBmImNvdWxkIG5vdCByZW5hbWUgZGVtbyBmb2xkZXI6IHtleGN9IgoKICAgICMgVXBkYXRlIGtub3duX3dvcmtzcGFjZXMudHh0OiByZW1vdmUgJ2RlbW8nLCBhZGQgbmV3X25hbWUuCiAgICAjIFJlYWQgZGlyZWN0bHkgdmlhIF9yZWFkX3RleHQgaW5zdGVhZCBvZiBfcmVhZF9rbm93bl93b3Jrc3BhY2VzCiAgICAjIGJlY2F1c2UgX3JlYWRfa25vd25fd29ya3NwYWNlcyBzd2FsbG93cyByZWFkIGVycm9ycyAocmV0dXJucwogICAgIyBlbXB0eSBzZXQpLCBhbmQgd2UgY2Fubm90IHNhZmVseSBvdmVyd3JpdGUgdGhlIGZpbGUgd2l0aG91dAogICAgIyBrbm93aW5nIGl0cyBjdXJyZW50IGNvbnRlbnRzLgogICAgdHJ5OgogICAgICAgIGlmIG9zLnBhdGguZXhpc3RzKEtOT1dOX1dPUktTUEFDRVNfUEFUSCk6CiAgICAgICAgICAgIGNvbnRlbnQgPSBfcmVhZF90ZXh0KEtOT1dOX1dPUktTUEFDRVNfUEFUSCkKICAgICAgICAgICAga25vd24gPSB7CiAgICAgICAgICAgICAgICBsaW5lLnN0cmlwKCkKICAgICAgICAgICAgICAgIGZvciBsaW5lIGluIGNvbnRlbnQuc3BsaXRsaW5lcygpCiAgICAgICAgICAgICAgICBpZiBsaW5lLnN0cmlwKCkKICAgICAgICAgICAgfQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIGtub3duID0gc2V0KCkKICAgICAgICBrbm93bi5kaXNjYXJkKCJkZW1vIikKICAgICAgICBrbm93bi5hZGQobmV3X25hbWUpCiAgICAgICAgbmV3X2NvbnRlbnQgPSAiXG4iLmpvaW4oc29ydGVkKGtub3duKSkgKyAiXG4iCiAgICAgICAgX2F0b21pY193cml0ZSgKICAgICAgICAgICAgS05PV05fV09SS1NQQUNFU19QQVRILCBLTk9XTl9XT1JLU1BBQ0VTX1RNUF9QQVRILAogICAgICAgICAgICBuZXdfY29udGVudCwKICAgICAgICApCiAgICBleGNlcHQgT1NFcnJvciBhcyBleGM6CiAgICAgICAgIyBGb2xkZXIgcmVuYW1lZCBzdWNjZXNzZnVsbHkgYnV0IGtub3duX3dvcmtzcGFjZXMgdXBkYXRlCiAgICAgICAgIyBmYWlsZWQuIExvZyB3YXJuaW5nIOKAlCB0aGUgd29ya3NwYWNlIHN0aWxsIHdvcmtzLCBpdCdzCiAgICAgICAgIyBqdXN0IG1pc3NpbmcgZnJvbSB0aGUgcmVnaXN0cnkgKHR5cG8gZ3VhcmQgd2lsbCBmaXJlKS4KICAgICAgICBsb2cud2FybmluZygKICAgICAgICAgICAgImRlbW8gcmVuYW1lOiBmb2xkZXIgcmVuYW1lZCBidXQga25vd25fd29ya3NwYWNlcyAiCiAgICAgICAgICAgICJ1cGRhdGUgZmFpbGVkOiAlcyIsIGV4YywKICAgICAgICApCgogICAgbG9nLmluZm8oCiAgICAgICAgImRlbW8gd29ya3NwYWNlIHJlbmFtZWQ6IGRlbW8gLT4gJXMiLCBuZXdfbmFtZSwKICAgICkKICAgIHJldHVybiBOb25lCgoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyBNQ1AgU0VSVkVSCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CgptY3AgPSBGYXN0TUNQKCJuaW1naXIiKQoKCkBtY3AudG9vbCgpCmRlZiBnZXRfYnJpZWZpbmcod29ya3NwYWNlOiBzdHIpIC0+IGRpY3Q6CiAgICAiIiJQdWxsIHRoZSBjb250aW51YXRpb24gYnJpZWZpbmcgYXQgdGhlIHN0YXJ0IG9mIGEgbmV3IGNoYXQuCgogICAgUmV0dXJucyB0aGUgaGFyZGNvZGVkIHByaW1lciBwbHVzIHRoZSBvbi1kaXNrIGJyaWVmaW5nLCBtYXN0ZXIKICAgIGxvZywgYW5kIHBlcnNpc3RlbnQgdGFncyBmb3IgdGhlIGdpdmVuIHdvcmtzcGFjZS4gT24gZmlyc3QKICAgIHJ1biBmb3IgYSB3b3Jrc3BhY2UgKGZvbGRlci9maWxlcyBkbyBub3QgeWV0IGV4aXN0KSByZXR1cm5zIGEKICAgIHBsYWNlaG9sZGVyIGJyaWVmaW5nLCBhbiBlbXB0eSBtYXN0ZXJfbG9nLCBhbmQgZW1wdHkKICAgIHBlcnNpc3RlbnRfdGFncy4gUmVhZC1vbmx5IOKAlCBuZXZlciB3cml0ZXMsIG5ldmVyIGNyZWF0ZXMKICAgIHRoZSB3b3Jrc3BhY2UgZm9sZGVyLCBuZXZlciBhcHBlbmRzIHRvIGtub3duX3dvcmtzcGFjZXMudHh0LgoKICAgIElmIHdvcmtzcGFjZSBpcyB1bmtub3duIChub3QgaW4ga25vd25fd29ya3NwYWNlcy50eHQpLCB0aGUKICAgIHJlc3BvbnNlIHN0aWxsIHN1Y2NlZWRzIGJ1dCBpbmNsdWRlcyBhIG5vbi1ibG9ja2luZyB3YXJuaW5nCiAgICBmaWVsZCBmbGFnZ2luZyBwb3NzaWJsZSB0eXBvLgoKICAgIEFyY2hpdmUgbnVkZ2UgKEl0ZW0gNCk6IHJlc3BvbnNlIGFsd2F5cyBpbmNsdWRlcwogICAgbWFzdGVyX2xvZ19zaXplX2tiIChmbG9hdCkgYW5kIGFyY2hpdmVfcmVjb21tZW5kZWQgKGJvb2wpLgoKICAgIERlbW8gZ2F0aW5nOiBmb3IgdW5saWNlbnNlZCB1c2VycywgY2hlY2tzIHNlc3Npb24gY291bnRlcgogICAgdmlhIFdvcmtlciBBUEkgYW5kIHJldHVybnMgZGVtb19saW1pdF9yZWFjaGVkIChib29sKSwKICAgIGRlbW9fc2Vzc2lvbl9saW1pdCAoaW50KSwgYW5kIG9wdGlvbmFsbHkgZGVtb19zZXNzaW9uc191c2VkLgogICAgTGljZW5zZWQgdXNlcnMgZG8gbm90IHJlY2VpdmUgZGVtbyBmaWVsZHMuCgogICAgU3VjY2VzcyBzaGFwZToKICAgICAgICB7c3VjY2VzczogdHJ1ZSwgcHJpbWVyLCBicmllZmluZywgbWFzdGVyX2xvZywKICAgICAgICAgcGVyc2lzdGVudF90YWdzLCBwZXJzaXN0ZW50X3RhZ3NfY291bnQsCiAgICAgICAgIHRpZXJfYl90YWdzX2NvdW50LAogICAgICAgICBtYXN0ZXJfbG9nX3NpemVfa2IsIGFyY2hpdmVfcmVjb21tZW5kZWQsIHdhcm5pbmcsCiAgICAgICAgIHJlY292ZXJlZF9mcm9tX2JhY2t1cCwKICAgICAgICAgZGVtb19saW1pdF9yZWFjaGVkPywgZGVtb19zZXNzaW9uX2xpbWl0PywKICAgICAgICAgZGVtb19zZXNzaW9uc191c2VkPywgdXBncmFkZV91cmw/fQogICAgRXJyb3Igc2hhcGU6CiAgICAgICAge3N1Y2Nlc3M6IGZhbHNlLCBlcnJvciwgbWVzc2FnZX0KICAgICIiIgogICAgbG9nLmluZm8oInRvb2wgY2FsbDogZ2V0X2JyaWVmaW5nIHdvcmtzcGFjZT0lciIsIHdvcmtzcGFjZSkKICAgIHRyeToKICAgICAgICAjIDEuIFZhbGlkYXRlIHdvcmtzcGFjZS4KICAgICAgICB3c19lcnIgPSBfdmFsaWRhdGVfd29ya3NwYWNlKHdvcmtzcGFjZSkKICAgICAgICBpZiB3c19lcnIgaXMgbm90IE5vbmU6CiAgICAgICAgICAgIGxvZy5pbmZvKCJnZXRfYnJpZWZpbmc6IGVycm9yIGludmFsaWRfaW5wdXQiKQogICAgICAgICAgICByZXR1cm4gd3NfZXJyCgogICAgICAgICMgMi4gRW5zdXJlIHNlcnZlciByb290IGV4aXN0cy4KICAgICAgICBkaXJfZXJyID0gX2Vuc3VyZV9kaXJlY3RvcnkoKQogICAgICAgIGlmIGRpcl9lcnIgaXMgbm90IE5vbmU6CiAgICAgICAgICAgIGxvZy5pbmZvKCJnZXRfYnJpZWZpbmc6IGVycm9yIGRpcmVjdG9yeV9taXNzaW5nIikKICAgICAgICAgICAgcmV0dXJuIGRpcl9lcnIKCiAgICAgICAgIyAzLiBSZXNvbHZlIHBlci13b3Jrc3BhY2UgcGF0aHMuCiAgICAgICAgcGF0aHMgPSBfd29ya3NwYWNlX3BhdGhzKHdvcmtzcGFjZSkKCiAgICAgICAgIyA0LiBSZWFkIGJyaWVmaW5nIChvciAuYmFrIGZhbGxiYWNrLCBvciBwbGFjZWhvbGRlcikuCiAgICAgICAgIyAgICBGaXggQzogaWYgYnJpZWZpbmcudHh0IGlzIG1pc3NpbmcgYnV0IC5iYWsgZXhpc3RzLCByZWNvdmVyCiAgICAgICAgIyAgICBmcm9tIC5iYWsgYW5kIGZsYWcgcmVjb3ZlcmVkX2Zyb21fYmFja3VwIHNvIENsYXVkZSB3YXJucyB1c2VyLgogICAgICAgIHJlY292ZXJlZF9mcm9tX2JhY2t1cCA9IEZhbHNlCiAgICAgICAgaWYgb3MucGF0aC5leGlzdHMocGF0aHNbImJyaWVmaW5nIl0pOgogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICBicmllZmluZyA9IF9yZWFkX3RleHQocGF0aHNbImJyaWVmaW5nIl0pCiAgICAgICAgICAgIGV4Y2VwdCBPU0Vycm9yIGFzIGV4YzoKICAgICAgICAgICAgICAgIGxvZy5pbmZvKCJnZXRfYnJpZWZpbmc6IGVycm9yIHJlYWRfZmFpbHVyZSAoYnJpZWZpbmcpIikKICAgICAgICAgICAgICAgIHJldHVybiBfZXJyb3IoCiAgICAgICAgICAgICAgICAgICAgInJlYWRfZmFpbHVyZSIsCiAgICAgICAgICAgICAgICAgICAgZiJjb3VsZCBub3QgcmVhZCB7QlJJRUZJTkdfTkFNRX06IHtleGN9IiwKICAgICAgICAgICAgICAgICkKICAgICAgICBlbGlmIG9zLnBhdGguZXhpc3RzKHBhdGhzWyJicmllZmluZ19iYWsiXSk6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIGJyaWVmaW5nID0gX3JlYWRfdGV4dChwYXRoc1siYnJpZWZpbmdfYmFrIl0pCiAgICAgICAgICAgICAgICByZWNvdmVyZWRfZnJvbV9iYWNrdXAgPSBUcnVlCiAgICAgICAgICAgICAgICBsb2cud2FybmluZygKICAgICAgICAgICAgICAgICAgICAiZ2V0X2JyaWVmaW5nOiBicmllZmluZy50eHQgbWlzc2luZywgIgogICAgICAgICAgICAgICAgICAgICJyZWNvdmVyZWQgZnJvbSBicmllZmluZy50eHQuYmFrIgogICAgICAgICAgICAgICAgKQogICAgICAgICAgICBleGNlcHQgT1NFcnJvciBhcyBleGM6CiAgICAgICAgICAgICAgICBsb2cuaW5mbygiZ2V0X2JyaWVmaW5nOiBlcnJvciByZWFkX2ZhaWx1cmUgKGJyaWVmaW5nLmJhaykiKQogICAgICAgICAgICAgICAgcmV0dXJuIF9lcnJvcigKICAgICAgICAgICAgICAgICAgICAicmVhZF9mYWlsdXJlIiwKICAgICAgICAgICAgICAgICAgICBmImNvdWxkIG5vdCByZWFkIHtCUklFRklOR19CQUtfTkFNRX06IHtleGN9IiwKICAgICAgICAgICAgICAgICkKICAgICAgICBlbHNlOgogICAgICAgICAgICBicmllZmluZyA9IEZJUlNUX1JVTl9CUklFRklORwoKICAgICAgICAjIDUuIFJlYWQgbWFzdGVyX2xvZyAob3IgLmJhayBmYWxsYmFjaywgb3IgZW1wdHkpLgogICAgICAgICMgICAgRml4IEMgYXBwbGllZCB0byBtYXN0ZXJfbG9nIGFzIHdlbGwuCiAgICAgICAgaWYgb3MucGF0aC5leGlzdHMocGF0aHNbIm1hc3Rlcl9sb2ciXSk6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIG1hc3Rlcl9sb2cgPSBfcmVhZF90ZXh0KHBhdGhzWyJtYXN0ZXJfbG9nIl0pCiAgICAgICAgICAgIGV4Y2VwdCBPU0Vycm9yIGFzIGV4YzoKICAgICAgICAgICAgICAgIGxvZy5pbmZvKCJnZXRfYnJpZWZpbmc6IGVycm9yIHJlYWRfZmFpbHVyZSAobWFzdGVyX2xvZykiKQogICAgICAgICAgICAgICAgcmV0dXJuIF9lcnJvcigKICAgICAgICAgICAgICAgICAgICAicmVhZF9mYWlsdXJlIiwKICAgICAgICAgICAgICAgICAgICBmImNvdWxkIG5vdCByZWFkIHtNQVNURVJfTE9HX05BTUV9OiB7ZXhjfSIsCiAgICAgICAgICAgICAgICApCiAgICAgICAgZWxpZiBvcy5wYXRoLmV4aXN0cyhwYXRoc1sibWFzdGVyX2xvZ19iYWsiXSk6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIG1hc3Rlcl9sb2cgPSBfcmVhZF90ZXh0KHBhdGhzWyJtYXN0ZXJfbG9nX2JhayJdKQogICAgICAgICAgICAgICAgcmVjb3ZlcmVkX2Zyb21fYmFja3VwID0gVHJ1ZQogICAgICAgICAgICAgICAgbG9nLndhcm5pbmcoCiAgICAgICAgICAgICAgICAgICAgImdldF9icmllZmluZzogbWFzdGVyX2xvZy50eHQgbWlzc2luZywgIgogICAgICAgICAgICAgICAgICAgICJyZWNvdmVyZWQgZnJvbSBtYXN0ZXJfbG9nLnR4dC5iYWsiCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgIGV4Y2VwdCBPU0Vycm9yIGFzIGV4YzoKICAgICAgICAgICAgICAgIGxvZy5pbmZvKAogICAgICAgICAgICAgICAgICAgICJnZXRfYnJpZWZpbmc6IGVycm9yIHJlYWRfZmFpbHVyZSAobWFzdGVyX2xvZy5iYWspIgogICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgcmV0dXJuIF9lcnJvcigKICAgICAgICAgICAgICAgICAgICAicmVhZF9mYWlsdXJlIiwKICAgICAgICAgICAgICAgICAgICBmImNvdWxkIG5vdCByZWFkIHtNQVNURVJfTE9HX0JBS19OQU1FfToge2V4Y30iLAogICAgICAgICAgICAgICAgKQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIG1hc3Rlcl9sb2cgPSAiIgoKICAgICAgICAjIDViLiBSZWFkIHRpZXJfYV90YWdzIChhbHdheXMgbG9hZGVkKSBhbmQgY291bnQgdGllcl9iLgogICAgICAgICMgICAgIE1pZ3JhdGlvbjogaWYgdGllciBmaWxlcyBkb24ndCBleGlzdCBidXQgb2xkCiAgICAgICAgIyAgICAgcGVyc2lzdGVudF90YWdzLnR4dCBkb2VzLCByZWFkIHRoYXQgaW5zdGVhZC4KICAgICAgICB0aWVyX2FfdGFncyA9ICIiCiAgICAgICAgdGllcl9iX3RhZ3NfY291bnQgPSAwCgogICAgICAgIGlmIG9zLnBhdGguZXhpc3RzKHBhdGhzWyJ0aWVyX2FfdGFncyJdKToKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgdGllcl9hX3RhZ3MgPSBfcmVhZF90ZXh0KHBhdGhzWyJ0aWVyX2FfdGFncyJdKQogICAgICAgICAgICBleGNlcHQgT1NFcnJvciBhcyBleGM6CiAgICAgICAgICAgICAgICBsb2cud2FybmluZygKICAgICAgICAgICAgICAgICAgICAiZ2V0X2JyaWVmaW5nOiBjb3VsZCBub3QgcmVhZCAlczogJXMiLAogICAgICAgICAgICAgICAgICAgIFRJRVJfQV9UQUdTX05BTUUsIGV4YywKICAgICAgICAgICAgICAgICkKICAgICAgICBlbGlmIG9zLnBhdGguZXhpc3RzKHBhdGhzWyJwZXJzaXN0ZW50X3RhZ3MiXSk6CiAgICAgICAgICAgICMgQmFja3dhcmQgY29tcGF0OiBvbGQgc2luZ2xlIGZpbGUsIGxvYWQgYWxsIGFzIHRpZXJfYQogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICB0aWVyX2FfdGFncyA9IF9yZWFkX3RleHQocGF0aHNbInBlcnNpc3RlbnRfdGFncyJdKQogICAgICAgICAgICAgICAgbG9nLmluZm8oCiAgICAgICAgICAgICAgICAgICAgImdldF9icmllZmluZzogbWlncmF0ZWQgZnJvbSBsZWdhY3kgJXMiLAogICAgICAgICAgICAgICAgICAgIFBFUlNJU1RFTlRfVEFHU19OQU1FLAogICAgICAgICAgICAgICAgKQogICAgICAgICAgICBleGNlcHQgT1NFcnJvcjoKICAgICAgICAgICAgICAgIHBhc3MKCiAgICAgICAgaWYgb3MucGF0aC5leGlzdHMocGF0aHNbInRpZXJfYl90YWdzIl0pOgogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICB0aWVyX2JfY29udGVudCA9IF9yZWFkX3RleHQocGF0aHNbInRpZXJfYl90YWdzIl0pCiAgICAgICAgICAgICAgICB0aWVyX2JfdGFnc19jb3VudCA9IGxlbigKICAgICAgICAgICAgICAgICAgICBQRVJTSVNURU5UX1RBR19SRS5maW5kYWxsKHRpZXJfYl9jb250ZW50KQogICAgICAgICAgICAgICAgKQogICAgICAgICAgICBleGNlcHQgT1NFcnJvcjoKICAgICAgICAgICAgICAgIHBhc3MKCiAgICAgICAgdGllcl9hX2NvdW50ID0gbGVuKAogICAgICAgICAgICBQRVJTSVNURU5UX1RBR19SRS5maW5kYWxsKHRpZXJfYV90YWdzKQogICAgICAgICkgaWYgdGllcl9hX3RhZ3Muc3RyaXAoKSBlbHNlIDAKCiAgICAgICAgIyBGb3IgYmFja3dhcmQgY29tcGF0LCBzdGlsbCByZXR1cm4gYXMgInBlcnNpc3RlbnRfdGFncyIKICAgICAgICAjIHNvIGV4aXN0aW5nIHByaW1lcnMvQ2xhdWRlIGluc3RydWN0aW9ucyB3b3JrIHVuY2hhbmdlZC4KICAgICAgICBwZXJzaXN0ZW50X3RhZ3MgPSB0aWVyX2FfdGFncwogICAgICAgIHBlcnNpc3RlbnRfdGFnc19jb3VudCA9IHRpZXJfYV9jb3VudAoKICAgICAgICAjIDYuIFR5cG8gY2FuYXJ5OiB3YXJuIGlmIHdvcmtzcGFjZSBpcyB1bmtub3duLgogICAgICAgIGtub3duID0gX3JlYWRfa25vd25fd29ya3NwYWNlcygpCiAgICAgICAgd2FybmluZyA9ICgKICAgICAgICAgICAgTm9uZSBpZiB3b3Jrc3BhY2UgaW4ga25vd24KICAgICAgICAgICAgZWxzZSBfdHlwb19jYW5hcnlfd2FybmluZyh3b3Jrc3BhY2UpCiAgICAgICAgKQoKICAgICAgICAjIDcuIEFyY2hpdmUgbnVkZ2U6IG1lYXN1cmUgbWFzdGVyX2xvZyBzaXplLCBmbGFnIGlmIG92ZXIgdGhyZXNob2xkLgogICAgICAgIG1hc3Rlcl9sb2dfc2l6ZV9rYiA9IHJvdW5kKGxlbihtYXN0ZXJfbG9nLmVuY29kZSgidXRmLTgiKSkgLyAxMDI0LCAxKQogICAgICAgIGFyY2hpdmVfcmVjb21tZW5kZWQgPSBtYXN0ZXJfbG9nX3NpemVfa2IgPj0gQVJDSElWRV9USFJFU0hPTERfS0IKCiAgICAgICAgIyA4LiBEZW1vIGdhdGluZzogY2hlY2sgbGljZW5zZSBhbmQgc2Vzc2lvbiBjb3VudGVyLgogICAgICAgICMgICAgTGljZW5zZWQgdXNlcnM6IGRlbW8gZmllbGRzIGFyZSBhYnNlbnQgKG5vIG92ZXJoZWFkKS4KICAgICAgICAjICAgIERlbW8gdXNlcnM6IGdldCBkZW1vX2xpbWl0X3JlYWNoZWQgYW5kIHVwZ3JhZGVfdXJsLgogICAgICAgIGRlbW9fZmllbGRzID0ge30KICAgICAgICBsaWNlbnNlZCA9IF9pc19saWNlbnNlZCgpCiAgICAgICAgaWYgbm90IGxpY2Vuc2VkOgogICAgICAgICAgICBtYWNoaW5lX2lkID0gX2dldF9tYWNoaW5lX2lkKCkKICAgICAgICAgICAgaWYgbWFjaGluZV9pZDoKICAgICAgICAgICAgICAgIGNvdW50ZXIgPSBfZGVtb19jb3VudGVyX2NoZWNrKG1hY2hpbmVfaWQpCiAgICAgICAgICAgICAgICBpZiBjb3VudGVyIGFuZCBjb3VudGVyLmdldCgibGltaXRfcmVhY2hlZCIpOgogICAgICAgICAgICAgICAgICAgIGRlbW9fZmllbGRzWyJkZW1vX2xpbWl0X3JlYWNoZWQiXSA9IFRydWUKICAgICAgICAgICAgICAgICAgICBkZW1vX2ZpZWxkc1siZGVtb19zZXNzaW9uX2xpbWl0Il0gPSBjb3VudGVyLmdldCgKICAgICAgICAgICAgICAgICAgICAgICAgImRlbW9fc2Vzc2lvbl9saW1pdCIsIDUKICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgZGVtb19maWVsZHNbInVwZ3JhZGVfdXJsIl0gPSBVUEdSQURFX1VSTAogICAgICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgICAgICBkZW1vX2ZpZWxkc1siZGVtb19saW1pdF9yZWFjaGVkIl0gPSBGYWxzZQogICAgICAgICAgICAgICAgICAgIGRlbW9fZmllbGRzWyJkZW1vX3Nlc3Npb25fbGltaXQiXSA9ICgKICAgICAgICAgICAgICAgICAgICAgICAgY291bnRlci5nZXQoImRlbW9fc2Vzc2lvbl9saW1pdCIsIDUpCiAgICAgICAgICAgICAgICAgICAgICAgIGlmIGNvdW50ZXIgZWxzZSA1CiAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgIGlmIGNvdW50ZXI6CiAgICAgICAgICAgICAgICAgICAgICAgIGRlbW9fZmllbGRzWyJkZW1vX3Nlc3Npb25zX3VzZWQiXSA9IGNvdW50ZXIuZ2V0KCJzZXNzaW9ucyIsIDApCiAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICAjIENhbm5vdCBkZXRlcm1pbmUgbWFjaGluZSBJRCDigJQgdHJlYXQgYXMgdW5kZXIgbGltaXQKICAgICAgICAgICAgICAgICMgdG8gYXZvaWQgcHVuaXNoaW5nIHVzZXJzIHdob3NlIE9TIGlzIHVudXN1YWwuCiAgICAgICAgICAgICAgICBkZW1vX2ZpZWxkc1siZGVtb19saW1pdF9yZWFjaGVkIl0gPSBGYWxzZQogICAgICAgICAgICAgICAgZGVtb19maWVsZHNbImRlbW9fc2Vzc2lvbl9saW1pdCJdID0gNSAgIyBmYWxsYmFjawogICAgICAgICAgICAgICAgbG9nLndhcm5pbmcoCiAgICAgICAgICAgICAgICAgICAgImdldF9icmllZmluZzogY291bGQgbm90IGdldCBtYWNoaW5lX2lkLCAiCiAgICAgICAgICAgICAgICAgICAgImNhbm5vdCBjaGVjayBkZW1vIGNvdW50ZXIiCiAgICAgICAgICAgICAgICApCgogICAgICAgIGxvZy5pbmZvKAogICAgICAgICAgICAiZ2V0X2JyaWVmaW5nOiBzdWNjZXNzIHdvcmtzcGFjZT0lcyB3YXJuaW5nPSVzICIKICAgICAgICAgICAgIm1hc3Rlcl9sb2dfc2l6ZV9rYj0lLjFmIGFyY2hpdmVfcmVjb21tZW5kZWQ9JXMgIgogICAgICAgICAgICAicGVyc2lzdGVudF90YWdzX2NvdW50PSVkIGxpY2Vuc2VkPSVzIHJlY292ZXJlZF9mcm9tX2JhY2t1cD0lcyIsCiAgICAgICAgICAgIHdvcmtzcGFjZSwgInllcyIgaWYgd2FybmluZyBlbHNlICJubyIsCiAgICAgICAgICAgIG1hc3Rlcl9sb2dfc2l6ZV9rYiwgYXJjaGl2ZV9yZWNvbW1lbmRlZCwKICAgICAgICAgICAgcGVyc2lzdGVudF90YWdzX2NvdW50LCBsaWNlbnNlZCwgcmVjb3ZlcmVkX2Zyb21fYmFja3VwLAogICAgICAgICkKICAgICAgICByZXN1bHQgPSB7CiAgICAgICAgICAgICJzdWNjZXNzIjogVHJ1ZSwKICAgICAgICAgICAgInByaW1lciI6IFBSSU1FUiwKICAgICAgICAgICAgImJyaWVmaW5nIjogYnJpZWZpbmcsCiAgICAgICAgICAgICJtYXN0ZXJfbG9nIjogbWFzdGVyX2xvZywKICAgICAgICAgICAgInBlcnNpc3RlbnRfdGFncyI6IHBlcnNpc3RlbnRfdGFncywKICAgICAgICAgICAgInBlcnNpc3RlbnRfdGFnc19jb3VudCI6IHBlcnNpc3RlbnRfdGFnc19jb3VudCwKICAgICAgICAgICAgInRpZXJfYl90YWdzX2NvdW50IjogdGllcl9iX3RhZ3NfY291bnQsCiAgICAgICAgICAgICJtYXN0ZXJfbG9nX3NpemVfa2IiOiBtYXN0ZXJfbG9nX3NpemVfa2IsCiAgICAgICAgICAgICJhcmNoaXZlX3JlY29tbWVuZGVkIjogYXJjaGl2ZV9yZWNvbW1lbmRlZCwKICAgICAgICAgICAgIndhcm5pbmciOiB3YXJuaW5nLAogICAgICAgICAgICAicmVjb3ZlcmVkX2Zyb21fYmFja3VwIjogcmVjb3ZlcmVkX2Zyb21fYmFja3VwLAogICAgICAgIH0KICAgICAgICByZXN1bHQudXBkYXRlKGRlbW9fZmllbGRzKQogICAgICAgIHJldHVybiByZXN1bHQKCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGV4YzogICMgY2F0Y2gtYWxsIOKGkiBpbnRlcm5hbF9lcnJvcgogICAgICAgIGxvZy5leGNlcHRpb24oImdldF9icmllZmluZzogdW5oYW5kbGVkIGV4Y2VwdGlvbiIpCiAgICAgICAgcmV0dXJuIF9lcnJvcigiaW50ZXJuYWxfZXJyb3IiLCBmImludGVybmFsIGVycm9yOiB7ZXhjfSIpCgoKQG1jcC50b29sKCkKZGVmIHNhdmVfYnJpZWZpbmcoCiAgICB3b3Jrc3BhY2U6IHN0ciwKICAgIGJyaWVmaW5nOiBzdHIsCiAgICBtYXN0ZXJfbG9nOiBzdHIsCiAgICBjb25maXJtX25ld193b3Jrc3BhY2U6IGJvb2wgPSBGYWxzZSwKKSAtPiBkaWN0OgogICAgIiIiU2F2ZSB0aGUgdXBkYXRlZCBicmllZmluZyBhbmQgbWFzdGVyIGxvZyBhdCBjaGF0IHdyYXAuCgogICAgd29ya3NwYWNlLCBicmllZmluZywgYW5kIG1hc3Rlcl9sb2cgYXJlIFJFUVVJUkVEIG9uIGV2ZXJ5CiAgICBjYWxsLiBGdWxsIHJlcGxhY2VtZW50IHNlbWFudGljcyDigJQgdGhlIHNlcnZlciB3cml0ZXMgd2hhdGV2ZXIKICAgIGl0IHJlY2VpdmVzLiBJZiB0aGUgbWFzdGVyIGxvZyBpcyB1bmNoYW5nZWQgZm9yIHRoaXMgY2hhdCwKICAgIHRoZSBjYWxsZXIgc2hvdWxkIHNlbmQgYmFjayB0aGUgc2FtZSBzdHJpbmcgaXQgcmVjZWl2ZWQgZnJvbQogICAgZ2V0X2JyaWVmaW5nLiBFbXB0eSBzdHJpbmdzIGFyZSB2YWxpZCBpbnB1dC4KCiAgICBEZW1vIGdhdGluZzogdW5saWNlbnNlZCB1c2VycyBnZXQgYnJpZWZpbmcgY2FwcGVkIGF0IGEKICAgIGxpbWl0IGRlZmluZWQgYnkgdGhlIFdvcmtlciBBUEkuIEFmdGVyIHRoZSBzZXNzaW9uIGxpbWl0LAogICAgbWFzdGVyX2xvZyBpcyBmcm96ZW4gKGV4aXN0aW5nIGNvbnRlbnQgcHJlc2VydmVkLCBuZXcgY29udGVudAogICAgaWdub3JlZCkuIFNlc3Npb24gY291bnRlciBpbmNyZW1lbnRlZCBvbiBldmVyeSBzdWNjZXNzZnVsCiAgICBzYXZlIGZvciBkZW1vIHVzZXJzLgoKICAgIE9ycGhhbi10eXBvIGd1YXJkOiBpZiB3b3Jrc3BhY2UgaXMgbm90IGluIGtub3duX3dvcmtzcGFjZXMudHh0CiAgICBhbmQgY29uZmlybV9uZXdfd29ya3NwYWNlIGlzIGZhbHNlLCByZXR1cm5zIHVua25vd25fd29ya3NwYWNlCiAgICBlcnJvciBhbmQgd3JpdGVzIG5vdGhpbmcuIENhbGxlciAoQ2xhdWRlKSBjb25maXJtcyB3aXRoIHRoZQogICAgaHVtYW4gYW5kIHJldHJpZXMgd2l0aCBjb25maXJtX25ld193b3Jrc3BhY2U9dHJ1ZSB0byByZWdpc3RlcgogICAgdGhlIG5ldyB3b3Jrc3BhY2UuCgogICAgU3VjY2VzcyBzaGFwZToKICAgICAgICB7c3VjY2Vzcywgd29ya3NwYWNlLCBmaWxlc19zYXZlZCwgYmFja3VwX2NyZWF0ZWQsCiAgICAgICAgIHdvcmtzcGFjZV9hZGRlZF90b19rbm93biwgZGVtb19icmllZmluZ19jYXBwZWQ/LAogICAgICAgICBkZW1vX3Nlc3Npb25zX3VzZWQ/fQogICAgRXJyb3Igc2hhcGU6CiAgICAgICAge3N1Y2Nlc3M6IGZhbHNlLCBlcnJvciwgbWVzc2FnZX0KICAgICIiIgogICAgbG9nLmluZm8oCiAgICAgICAgInRvb2wgY2FsbDogc2F2ZV9icmllZmluZyB3b3Jrc3BhY2U9JXIgY29uZmlybV9uZXdfd29ya3NwYWNlPSVyIiwKICAgICAgICB3b3Jrc3BhY2UsIGNvbmZpcm1fbmV3X3dvcmtzcGFjZSwKICAgICkKICAgIHRyeToKICAgICAgICAjIDEuIFZhbGlkYXRlIHdvcmtzcGFjZS4KICAgICAgICB3c19lcnIgPSBfdmFsaWRhdGVfd29ya3NwYWNlKHdvcmtzcGFjZSkKICAgICAgICBpZiB3c19lcnIgaXMgbm90IE5vbmU6CiAgICAgICAgICAgIGxvZy5pbmZvKCJzYXZlX2JyaWVmaW5nOiBlcnJvciBpbnZhbGlkX2lucHV0ICh3b3Jrc3BhY2UpIikKICAgICAgICAgICAgcmV0dXJuIHdzX2VycgoKICAgICAgICAjIDIuIFZhbGlkYXRlIGJyaWVmaW5nIGFuZCBtYXN0ZXJfbG9nLgogICAgICAgIGlmIGJyaWVmaW5nIGlzIE5vbmU6CiAgICAgICAgICAgIHJldHVybiBfZXJyb3IoCiAgICAgICAgICAgICAgICAiaW52YWxpZF9pbnB1dCIsCiAgICAgICAgICAgICAgICAibWlzc2luZyByZXF1aXJlZCBmaWVsZDogYnJpZWZpbmciLAogICAgICAgICAgICApCiAgICAgICAgaWYgbWFzdGVyX2xvZyBpcyBOb25lOgogICAgICAgICAgICByZXR1cm4gX2Vycm9yKAogICAgICAgICAgICAgICAgImludmFsaWRfaW5wdXQiLAogICAgICAgICAgICAgICAgIm1pc3NpbmcgcmVxdWlyZWQgZmllbGQ6IG1hc3Rlcl9sb2ciLAogICAgICAgICAgICApCiAgICAgICAgaWYgbm90IGlzaW5zdGFuY2UoYnJpZWZpbmcsIHN0cik6CiAgICAgICAgICAgIHJldHVybiBfZXJyb3IoCiAgICAgICAgICAgICAgICAiaW52YWxpZF9pbnB1dCIsCiAgICAgICAgICAgICAgICBmImZpZWxkIGJyaWVmaW5nIG11c3QgYmUgYSBzdHJpbmcsICIKICAgICAgICAgICAgICAgIGYiZ290IHt0eXBlKGJyaWVmaW5nKS5fX25hbWVfX30iLAogICAgICAgICAgICApCiAgICAgICAgaWYgbm90IGlzaW5zdGFuY2UobWFzdGVyX2xvZywgc3RyKToKICAgICAgICAgICAgcmV0dXJuIF9lcnJvcigKICAgICAgICAgICAgICAgICJpbnZhbGlkX2lucHV0IiwKICAgICAgICAgICAgICAgIGYiZmllbGQgbWFzdGVyX2xvZyBtdXN0IGJlIGEgc3RyaW5nLCAiCiAgICAgICAgICAgICAgICBmImdvdCB7dHlwZShtYXN0ZXJfbG9nKS5fX25hbWVfX30iLAogICAgICAgICAgICApCgogICAgICAgICMgMy4gVmFsaWRhdGUgY29uZmlybV9uZXdfd29ya3NwYWNlIHR5cGUgKEZhc3RNQ1AgY29lcmNlcywKICAgICAgICAjICAgIGJ1dCB3ZSBtYW5kYXRlIGV4cGxpY2l0IGludmFsaWRfaW5wdXQgb24gd3JvbmcgdHlwZSkuCiAgICAgICAgaWYgbm90IGlzaW5zdGFuY2UoY29uZmlybV9uZXdfd29ya3NwYWNlLCBib29sKToKICAgICAgICAgICAgcmV0dXJuIF9lcnJvcigKICAgICAgICAgICAgICAgICJpbnZhbGlkX2lucHV0IiwKICAgICAgICAgICAgICAgIGYiZmllbGQgY29uZmlybV9uZXdfd29ya3NwYWNlIG11c3QgYmUgYSBib29sLCAiCiAgICAgICAgICAgICAgICBmImdvdCB7dHlwZShjb25maXJtX25ld193b3Jrc3BhY2UpLl9fbmFtZV9ffSIsCiAgICAgICAgICAgICkKCiAgICAgICAgIyA0LiBFbnN1cmUgc2VydmVyIHJvb3QgZXhpc3RzLgogICAgICAgIGRpcl9lcnIgPSBfZW5zdXJlX2RpcmVjdG9yeSgpCiAgICAgICAgaWYgZGlyX2VyciBpcyBub3QgTm9uZToKICAgICAgICAgICAgbG9nLmluZm8oInNhdmVfYnJpZWZpbmc6IGVycm9yIGRpcmVjdG9yeV9taXNzaW5nIikKICAgICAgICAgICAgcmV0dXJuIGRpcl9lcnIKCiAgICAgICAgIyA1LiBPcnBoYW4tdHlwbyBndWFyZC4KICAgICAgICBrbm93biA9IF9yZWFkX2tub3duX3dvcmtzcGFjZXMoKQogICAgICAgIHdvcmtzcGFjZV9hZGRlZF90b19rbm93biA9IEZhbHNlCiAgICAgICAgaWYgd29ya3NwYWNlIG5vdCBpbiBrbm93bjoKICAgICAgICAgICAgaWYgbm90IGNvbmZpcm1fbmV3X3dvcmtzcGFjZToKICAgICAgICAgICAgICAgIGxvZy5pbmZvKAogICAgICAgICAgICAgICAgICAgICJzYXZlX2JyaWVmaW5nOiBlcnJvciB1bmtub3duX3dvcmtzcGFjZSAod29ya3NwYWNlPSVzKSIsCiAgICAgICAgICAgICAgICAgICAgd29ya3NwYWNlLAogICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgcmV0dXJuIF9lcnJvcigKICAgICAgICAgICAgICAgICAgICAidW5rbm93bl93b3Jrc3BhY2UiLAogICAgICAgICAgICAgICAgICAgIF91bmtub3duX3dvcmtzcGFjZV9tZXNzYWdlKHdvcmtzcGFjZSksCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICMgQ29uZmlybWVkIG5ldyB3b3Jrc3BhY2U6IGFwcGVuZCB0byBrbm93bl93b3Jrc3BhY2VzLnR4dC4KICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgX2FwcGVuZF9rbm93bl93b3Jrc3BhY2Uod29ya3NwYWNlKQogICAgICAgICAgICBleGNlcHQgT1NFcnJvciBhcyBleGM6CiAgICAgICAgICAgICAgICBsb2cuaW5mbygKICAgICAgICAgICAgICAgICAgICAic2F2ZV9icmllZmluZzogZXJyb3Igd3JpdGVfZmFpbHVyZSAiCiAgICAgICAgICAgICAgICAgICAgIihrbm93bl93b3Jrc3BhY2VzKSIKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIHJldHVybiBfZXJyb3IoCiAgICAgICAgICAgICAgICAgICAgIndyaXRlX2ZhaWx1cmUiLAogICAgICAgICAgICAgICAgICAgIGYiY291bGQgbm90IHVwZGF0ZSB7S05PV05fV09SS1NQQUNFU19OQU1FfToge2V4Y30iLAogICAgICAgICAgICAgICAgKQogICAgICAgICAgICB3b3Jrc3BhY2VfYWRkZWRfdG9fa25vd24gPSBUcnVlCiAgICAgICAgICAgIGxvZy5pbmZvKCJuZXcgd29ya3NwYWNlIGNvbmZpcm1lZDogJXMiLCB3b3Jrc3BhY2UpCgogICAgICAgICMgNi4gUmVzb2x2ZSBwZXItd29ya3NwYWNlIHBhdGhzIGFuZCBlbnN1cmUgZm9sZGVyIGV4aXN0cy4KICAgICAgICBwYXRocyA9IF93b3Jrc3BhY2VfcGF0aHMod29ya3NwYWNlKQogICAgICAgIHRyeToKICAgICAgICAgICAgb3MubWFrZWRpcnMocGF0aHNbImZvbGRlciJdLCBleGlzdF9vaz1UcnVlKQogICAgICAgIGV4Y2VwdCBPU0Vycm9yIGFzIGV4YzoKICAgICAgICAgICAgbG9nLmluZm8oInNhdmVfYnJpZWZpbmc6IGVycm9yIHdyaXRlX2ZhaWx1cmUgKG1rZGlyKSIpCiAgICAgICAgICAgIHJldHVybiBfZXJyb3IoCiAgICAgICAgICAgICAgICAid3JpdGVfZmFpbHVyZSIsCiAgICAgICAgICAgICAgICBmImNvdWxkIG5vdCBjcmVhdGUgd29ya3NwYWNlIGZvbGRlciAiCiAgICAgICAgICAgICAgICBmIntwYXRoc1snZm9sZGVyJ119OiB7ZXhjfSIsCiAgICAgICAgICAgICkKCiAgICAgICAgYmFja3VwX29rID0gVHJ1ZQoKICAgICAgICAjIDZiLiBEZW1vIGdhdGluZzogY2FwIGJyaWVmaW5nIGFuZCBmcmVlemUgbWFzdGVyX2xvZwogICAgICAgICMgICAgIGZvciB1bmxpY2Vuc2VkIHVzZXJzIHdobyBoYXZlIGV4Y2VlZGVkIHRoZSBzZXNzaW9uIGxpbWl0LgogICAgICAgICMgICAgIExpbWl0cyBjb21lIGZyb20gdGhlIFdvcmtlciBBUEksIG5vdCBsb2NhbCBjb25zdGFudHMuCiAgICAgICAgbGljZW5zZWQgPSBfaXNfbGljZW5zZWQoKQogICAgICAgIGRlbW9fY2FwcGVkID0gRmFsc2UKICAgICAgICBkZW1vX2NvdW50ZXJfcmVzdWx0ID0gTm9uZQogICAgICAgIGRlbW9fbWFjaGluZV9pZCA9IE5vbmUgICMgaG9pc3RlZCDigJQgcmV1c2VkIGluIHN0ZXAgOQogICAgICAgIGlmIG5vdCBsaWNlbnNlZDoKICAgICAgICAgICAgZGVtb19tYWNoaW5lX2lkID0gX2dldF9tYWNoaW5lX2lkKCkKCiAgICAgICAgICAgICMgR2V0IGxpbWl0cyBmcm9tIFdvcmtlciBmaXJzdC4KICAgICAgICAgICAgY291bnRlciA9IE5vbmUKICAgICAgICAgICAgYnJpZWZpbmdfY2FwID0gMjAwMCAgIyBmYWxsYmFjayBpZiBXb3JrZXIgdW5yZWFjaGFibGUKICAgICAgICAgICAgaWYgZGVtb19tYWNoaW5lX2lkOgogICAgICAgICAgICAgICAgY291bnRlciA9IF9kZW1vX2NvdW50ZXJfY2hlY2soZGVtb19tYWNoaW5lX2lkKQogICAgICAgICAgICAgICAgaWYgY291bnRlcjoKICAgICAgICAgICAgICAgICAgICBicmllZmluZ19jYXAgPSBjb3VudGVyLmdldCgKICAgICAgICAgICAgICAgICAgICAgICAgImRlbW9fYnJpZWZpbmdfY2FwIiwgMjAwMAogICAgICAgICAgICAgICAgICAgICkKCiAgICAgICAgICAgICMgQ2FwIGJyaWVmaW5nIHVzaW5nIFdvcmtlci1wcm92aWRlZCBsaW1pdC4KICAgICAgICAgICAgb3JpZ2luYWxfbGVuID0gbGVuKGJyaWVmaW5nKQogICAgICAgICAgICBicmllZmluZyA9IF90cnVuY2F0ZV9icmllZmluZyhicmllZmluZywgYnJpZWZpbmdfY2FwKQogICAgICAgICAgICBpZiBsZW4oYnJpZWZpbmcpIDwgb3JpZ2luYWxfbGVuOgogICAgICAgICAgICAgICAgZGVtb19jYXBwZWQgPSBUcnVlCiAgICAgICAgICAgICAgICBsb2cuaW5mbygKICAgICAgICAgICAgICAgICAgICAic2F2ZV9icmllZmluZzogZGVtbyBicmllZmluZyBjYXBwZWQgIgogICAgICAgICAgICAgICAgICAgICIlZCAtPiAlZCBjaGFycyAoY2FwPSVkKSIsCiAgICAgICAgICAgICAgICAgICAgb3JpZ2luYWxfbGVuLCBsZW4oYnJpZWZpbmcpLCBicmllZmluZ19jYXAsCiAgICAgICAgICAgICAgICApCgogICAgICAgICAgICAjIEZyZWV6ZSBtYXN0ZXJfbG9nIGlmIGxpbWl0IHJlYWNoZWQKICAgICAgICAgICAgIyAoa2VlcCB3aGF0ZXZlciBpcyBvbiBkaXNrLCBpZ25vcmUgdGhlIG5ldyBjb250ZW50KS4KICAgICAgICAgICAgaWYgY291bnRlciBhbmQgY291bnRlci5nZXQoImxpbWl0X3JlYWNoZWQiKToKICAgICAgICAgICAgICAgIGlmIG9zLnBhdGguZXhpc3RzKHBhdGhzWyJtYXN0ZXJfbG9nIl0pOgogICAgICAgICAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgICAgICAgICAgbWFzdGVyX2xvZyA9IF9yZWFkX3RleHQocGF0aHNbIm1hc3Rlcl9sb2ciXSkKICAgICAgICAgICAgICAgICAgICAgICAgbG9nLmluZm8oCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2F2ZV9icmllZmluZzogZGVtbyBsaW1pdCByZWFjaGVkLCAiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAibWFzdGVyX2xvZyBmcm96ZW4iCiAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICBleGNlcHQgT1NFcnJvcjoKICAgICAgICAgICAgICAgICAgICAgICAgcGFzcyAgIyBQcm9jZWVkIHdpdGggd2hhdGV2ZXIgd2FzIHNlbnQuCgogICAgICAgICMgNmMuIENsZWFuIHN0YWxlIC50bXAgZmlsZXMgZnJvbSBhIHByZXZpb3VzIGNyYXNoLgogICAgICAgICMgICAgIFRoZXNlIGFyZSBoYXJtbGVzcyBidXQgYWNjdW11bGF0ZS4gUmVtb3ZlZCBzaWxlbnRseS4KICAgICAgICBmb3IgdG1wX2tleSBpbiAoImJyaWVmaW5nX3RtcCIsICJtYXN0ZXJfbG9nX3RtcCIpOgogICAgICAgICAgICBpZiBvcy5wYXRoLmV4aXN0cyhwYXRoc1t0bXBfa2V5XSk6CiAgICAgICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICAgICAgb3MucmVtb3ZlKHBhdGhzW3RtcF9rZXldKQogICAgICAgICAgICAgICAgICAgIGxvZy5pbmZvKAogICAgICAgICAgICAgICAgICAgICAgICAic2F2ZV9icmllZmluZzogcmVtb3ZlZCBzdGFsZSAlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgIG9zLnBhdGguYmFzZW5hbWUocGF0aHNbdG1wX2tleV0pLAogICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIGV4Y2VwdCBPU0Vycm9yOgogICAgICAgICAgICAgICAgICAgIHBhc3MgICMgTm90IGNyaXRpY2FsIOKAlCBfd3JpdGVfdG1wIHdpbGwgb3ZlcndyaXRlIGFueXdheS4KCiAgICAgICAgIyA3LiBicmllZmluZy50eHQg4oCUIEZpeCBBOiB3cml0ZSAudG1wIGZpcnN0IChmc3luYyksIHRoZW4KICAgICAgICAjICAgIGJhY2t1cCBvbGQgbGl2ZSBmaWxlLCB0aGVuIHJlbmFtZSAudG1wIHRvIGxpdmUuCiAgICAgICAgIyAgICBMaXZlIGZpbGUgaXMgbmV2ZXIgYWJzZW50IGR1cmluZyB0aGUgb3BlcmF0aW9uLgogICAgICAgIHRyeToKICAgICAgICAgICAgX3dyaXRlX3RtcChwYXRoc1siYnJpZWZpbmdfdG1wIl0sIGJyaWVmaW5nKQogICAgICAgIGV4Y2VwdCBPU0Vycm9yIGFzIGV4YzoKICAgICAgICAgICAgbG9nLmluZm8oInNhdmVfYnJpZWZpbmc6IGVycm9yIHdyaXRlX2ZhaWx1cmUgKGJyaWVmaW5nLnRtcCkiKQogICAgICAgICAgICByZXR1cm4gX2Vycm9yKAogICAgICAgICAgICAgICAgIndyaXRlX2ZhaWx1cmUiLAogICAgICAgICAgICAgICAgZiJjb3VsZCBub3Qgd3JpdGUge0JSSUVGSU5HX1RNUF9OQU1FfToge2V4Y30iLAogICAgICAgICAgICApCiAgICAgICAgaWYgbm90IF9iYWNrdXBfZXhpc3RpbmcocGF0aHNbImJyaWVmaW5nIl0sIHBhdGhzWyJicmllZmluZ19iYWsiXSk6CiAgICAgICAgICAgIGJhY2t1cF9vayA9IEZhbHNlCiAgICAgICAgICAgIGxvZy53YXJuaW5nKAogICAgICAgICAgICAgICAgInNhdmVfYnJpZWZpbmc6IGJhY2t1cCBmYWlsZWQgZm9yICVzIOKAlCBhYm9ydGluZyAiCiAgICAgICAgICAgICAgICAib3ZlcndyaXRlIHRvIHByb3RlY3QgZXhpc3RpbmcgY29udGVudCIsCiAgICAgICAgICAgICAgICBCUklFRklOR19OQU1FLAogICAgICAgICAgICApCiAgICAgICAgICAgIHJldHVybiBfZXJyb3IoCiAgICAgICAgICAgICAgICAid3JpdGVfZmFpbHVyZSIsCiAgICAgICAgICAgICAgICBmImNvdWxkIG5vdCBiYWNrdXAge0JSSUVGSU5HX05BTUV9IHRvIHtCUklFRklOR19CQUtfTkFNRX0uICIKICAgICAgICAgICAgICAgIGYiTmV3IGNvbnRlbnQgaXMgc2FmZWx5IGluIHtCUklFRklOR19UTVBfTkFNRX0uICIKICAgICAgICAgICAgICAgIGYiRXhpc3Rpbmcge0JSSUVGSU5HX05BTUV9IHdhcyBOT1Qgb3ZlcndyaXR0ZW4uIiwKICAgICAgICAgICAgKQogICAgICAgIHRyeToKICAgICAgICAgICAgb3MucmVwbGFjZShwYXRoc1siYnJpZWZpbmdfdG1wIl0sIHBhdGhzWyJicmllZmluZyJdKQogICAgICAgIGV4Y2VwdCBPU0Vycm9yIGFzIGV4YzoKICAgICAgICAgICAgbG9nLmluZm8oInNhdmVfYnJpZWZpbmc6IGVycm9yIHdyaXRlX2ZhaWx1cmUgKGJyaWVmaW5nIHJlbmFtZSkiKQogICAgICAgICAgICByZXR1cm4gX2Vycm9yKAogICAgICAgICAgICAgICAgIndyaXRlX2ZhaWx1cmUiLAogICAgICAgICAgICAgICAgZiJjb3VsZCBub3QgZmluYWxpemUge0JSSUVGSU5HX05BTUV9OiB7ZXhjfS4gIgogICAgICAgICAgICAgICAgZiJOZXcgY29udGVudCBpcyBpbiB7QlJJRUZJTkdfVE1QX05BTUV9LiAiCiAgICAgICAgICAgICAgICBmIlByZXZpb3VzIGNvbnRlbnQgaXMgaW4ge0JSSUVGSU5HX0JBS19OQU1FfS4iLAogICAgICAgICAgICApCgogICAgICAgICMgOC4gbWFzdGVyX2xvZy50eHQg4oCUIHNhbWUgRml4IEEgcGF0dGVybi4KICAgICAgICB0cnk6CiAgICAgICAgICAgIF93cml0ZV90bXAocGF0aHNbIm1hc3Rlcl9sb2dfdG1wIl0sIG1hc3Rlcl9sb2cpCiAgICAgICAgZXhjZXB0IE9TRXJyb3IgYXMgZXhjOgogICAgICAgICAgICBsb2cuaW5mbygic2F2ZV9icmllZmluZzogZXJyb3Igd3JpdGVfZmFpbHVyZSAobWFzdGVyX2xvZy50bXApIikKICAgICAgICAgICAgcmV0dXJuIF9lcnJvcigKICAgICAgICAgICAgICAgICJ3cml0ZV9mYWlsdXJlIiwKICAgICAgICAgICAgICAgIGYiY291bGQgbm90IHdyaXRlIHtNQVNURVJfTE9HX1RNUF9OQU1FfToge2V4Y30iLAogICAgICAgICAgICApCiAgICAgICAgaWYgbm90IF9iYWNrdXBfZXhpc3RpbmcoCiAgICAgICAgICAgIHBhdGhzWyJtYXN0ZXJfbG9nIl0sIHBhdGhzWyJtYXN0ZXJfbG9nX2JhayJdLAogICAgICAgICk6CiAgICAgICAgICAgIGJhY2t1cF9vayA9IEZhbHNlCiAgICAgICAgICAgIGxvZy53YXJuaW5nKAogICAgICAgICAgICAgICAgInNhdmVfYnJpZWZpbmc6IGJhY2t1cCBmYWlsZWQgZm9yICVzIOKAlCBhYm9ydGluZyAiCiAgICAgICAgICAgICAgICAib3ZlcndyaXRlIHRvIHByb3RlY3QgZXhpc3RpbmcgY29udGVudCIsCiAgICAgICAgICAgICAgICBNQVNURVJfTE9HX05BTUUsCiAgICAgICAgICAgICkKICAgICAgICAgICAgcmV0dXJuIF9lcnJvcigKICAgICAgICAgICAgICAgICJ3cml0ZV9mYWlsdXJlIiwKICAgICAgICAgICAgICAgIGYiY291bGQgbm90IGJhY2t1cCB7TUFTVEVSX0xPR19OQU1FfSB0byB7TUFTVEVSX0xPR19CQUtfTkFNRX0uICIKICAgICAgICAgICAgICAgIGYiTmV3IGNvbnRlbnQgaXMgc2FmZWx5IGluIHtNQVNURVJfTE9HX1RNUF9OQU1FfS4gIgogICAgICAgICAgICAgICAgZiJFeGlzdGluZyB7TUFTVEVSX0xPR19OQU1FfSB3YXMgTk9UIG92ZXJ3cml0dGVuLiAiCiAgICAgICAgICAgICAgICBmIk5vdGU6IHtCUklFRklOR19OQU1FfSB3YXMgYWxyZWFkeSB1cGRhdGVkIHN1Y2Nlc3NmdWxseS4iLAogICAgICAgICAgICApCiAgICAgICAgdHJ5OgogICAgICAgICAgICBvcy5yZXBsYWNlKHBhdGhzWyJtYXN0ZXJfbG9nX3RtcCJdLCBwYXRoc1sibWFzdGVyX2xvZyJdKQogICAgICAgIGV4Y2VwdCBPU0Vycm9yIGFzIGV4YzoKICAgICAgICAgICAgbG9nLmluZm8oCiAgICAgICAgICAgICAgICAic2F2ZV9icmllZmluZzogZXJyb3Igd3JpdGVfZmFpbHVyZSAobWFzdGVyX2xvZyByZW5hbWUpIgogICAgICAgICAgICApCiAgICAgICAgICAgIHJldHVybiBfZXJyb3IoCiAgICAgICAgICAgICAgICAid3JpdGVfZmFpbHVyZSIsCiAgICAgICAgICAgICAgICBmImNvdWxkIG5vdCBmaW5hbGl6ZSB7TUFTVEVSX0xPR19OQU1FfToge2V4Y30uICIKICAgICAgICAgICAgICAgIGYiTmV3IGNvbnRlbnQgaXMgaW4ge01BU1RFUl9MT0dfVE1QX05BTUV9LiAiCiAgICAgICAgICAgICAgICBmIlByZXZpb3VzIGNvbnRlbnQgaXMgaW4ge01BU1RFUl9MT0dfQkFLX05BTUV9LiIsCiAgICAgICAgICAgICkKCiAgICAgICAgIyA4Yi4gRml4IEI6IHJlYWQtYmFjayB2ZXJpZmljYXRpb24g4oCUIGNvbmZpcm0gYm90aCBmaWxlcyBsYW5kZWQKICAgICAgICAjICAgICBvbiBkaXNrLiBSZXR1cm5zIHdyaXRlX2ZhaWx1cmUgKG5vdCBzdWNjZXNzKSBpZiBhIGZpbGUgaXMKICAgICAgICAjICAgICBtaXNzaW5nIG9yIGlmIG5vbi1lbXB0eSBjb250ZW50IHdhcyB3cml0dGVuIGJ1dCByZWFkcyBiYWNrCiAgICAgICAgIyAgICAgZW1wdHkuIG1hc3Rlcl9sb2cgbWF5IGxlZ2l0aW1hdGVseSBiZSBlbXB0eSBvbiBmaXJzdCBzYXZlLgogICAgICAgIGZvciBmbmFtZSwgZnBhdGgsIHdyaXR0ZW4gaW4gWwogICAgICAgICAgICAoQlJJRUZJTkdfTkFNRSwgcGF0aHNbImJyaWVmaW5nIl0sIGJyaWVmaW5nKSwKICAgICAgICAgICAgKE1BU1RFUl9MT0dfTkFNRSwgcGF0aHNbIm1hc3Rlcl9sb2ciXSwgbWFzdGVyX2xvZyksCiAgICAgICAgXToKICAgICAgICAgICAgaWYgbm90IG9zLnBhdGguZXhpc3RzKGZwYXRoKToKICAgICAgICAgICAgICAgIGxvZy53YXJuaW5nKAogICAgICAgICAgICAgICAgICAgICJzYXZlX2JyaWVmaW5nOiByZWFkLWJhY2sgRkFJTEVEIOKAlCAlcyBtaXNzaW5nIGFmdGVyIHdyaXRlIiwKICAgICAgICAgICAgICAgICAgICBmbmFtZSwKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIHJldHVybiBfZXJyb3IoCiAgICAgICAgICAgICAgICAgICAgIndyaXRlX2ZhaWx1cmUiLAogICAgICAgICAgICAgICAgICAgIGYicmVhZC1iYWNrIGNoZWNrIGZhaWxlZDoge2ZuYW1lfSBub3QgZm91bmQgYWZ0ZXIgIgogICAgICAgICAgICAgICAgICAgIGYid3JpdGUuIFNhdmUgZGlkIG5vdCBjb21wbGV0ZS4gUmV0cnkgc2F2ZV9icmllZmluZy4iLAogICAgICAgICAgICAgICAgKQogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICBjaGVjayA9IF9yZWFkX3RleHQoZnBhdGgpCiAgICAgICAgICAgIGV4Y2VwdCBPU0Vycm9yIGFzIGV4YzoKICAgICAgICAgICAgICAgIGxvZy53YXJuaW5nKAogICAgICAgICAgICAgICAgICAgICJzYXZlX2JyaWVmaW5nOiByZWFkLWJhY2sgRkFJTEVEIOKAlCBjb3VsZCBub3QgcmVhZCAlczogJXMiLAogICAgICAgICAgICAgICAgICAgIGZuYW1lLCBleGMsCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICByZXR1cm4gX2Vycm9yKAogICAgICAgICAgICAgICAgICAgICJ3cml0ZV9mYWlsdXJlIiwKICAgICAgICAgICAgICAgICAgICBmInJlYWQtYmFjayBjaGVjayBmYWlsZWQ6IGNvdWxkIG5vdCByZWFkIHtmbmFtZX06IHtleGN9LiAiCiAgICAgICAgICAgICAgICAgICAgZiJSZXRyeSBzYXZlX2JyaWVmaW5nLiIsCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICMgT25seSBmbGFnIGVtcHR5IGlmIHdlIHdyb3RlIG5vbi1lbXB0eSBjb250ZW50LgogICAgICAgICAgICAjIG1hc3Rlcl9sb2cgaXMgbGVnaXRpbWF0ZWx5IGVtcHR5IG9uIGZpcnN0LWV2ZXIgd29ya3NwYWNlIHNhdmUuCiAgICAgICAgICAgIGlmIHdyaXR0ZW4uc3RyaXAoKSBhbmQgbm90IGNoZWNrLnN0cmlwKCk6CiAgICAgICAgICAgICAgICBsb2cud2FybmluZygKICAgICAgICAgICAgICAgICAgICAic2F2ZV9icmllZmluZzogcmVhZC1iYWNrIEZBSUxFRCDigJQgJXMgZW1wdHkgYWZ0ZXIgd3JpdGUiLAogICAgICAgICAgICAgICAgICAgIGZuYW1lLAogICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgcmV0dXJuIF9lcnJvcigKICAgICAgICAgICAgICAgICAgICAid3JpdGVfZmFpbHVyZSIsCiAgICAgICAgICAgICAgICAgICAgZiJyZWFkLWJhY2sgY2hlY2sgZmFpbGVkOiB7Zm5hbWV9IGlzIGVtcHR5IGFmdGVyIHdyaXRlLiAiCiAgICAgICAgICAgICAgICAgICAgZiJSZXRyeSBzYXZlX2JyaWVmaW5nLiIsCiAgICAgICAgICAgICAgICApCgogICAgICAgICMgOS4gRGVtbyBjb3VudGVyOiBpbmNyZW1lbnQgc2Vzc2lvbiBjb3VudCBhZnRlciBzdWNjZXNzZnVsIHNhdmUuCiAgICAgICAgIyAgICBPbmx5IGZvciBkZW1vIHVzZXJzLCBvbmx5IHdoZW4gY291bnRlciBpcyByZWFjaGFibGUuCiAgICAgICAgaWYgbm90IGxpY2Vuc2VkIGFuZCBkZW1vX21hY2hpbmVfaWQ6CiAgICAgICAgICAgIGRlbW9fY291bnRlcl9yZXN1bHQgPSBfZGVtb19jb3VudGVyX2luY3JlbWVudChkZW1vX21hY2hpbmVfaWQpCiAgICAgICAgICAgIGlmIGRlbW9fY291bnRlcl9yZXN1bHQ6CiAgICAgICAgICAgICAgICBsb2cuaW5mbygKICAgICAgICAgICAgICAgICAgICAic2F2ZV9icmllZmluZzogZGVtbyBjb3VudGVyIGluY3JlbWVudGVkICIKICAgICAgICAgICAgICAgICAgICAic2Vzc2lvbnM9JXMgbGltaXRfcmVhY2hlZD0lcyIsCiAgICAgICAgICAgICAgICAgICAgZGVtb19jb3VudGVyX3Jlc3VsdC5nZXQoInNlc3Npb25zIiksCiAgICAgICAgICAgICAgICAgICAgZGVtb19jb3VudGVyX3Jlc3VsdC5nZXQoImxpbWl0X3JlYWNoZWQiKSwKICAgICAgICAgICAgICAgICkKCiAgICAgICAgbG9nLmluZm8oCiAgICAgICAgICAgICJzYXZlX2JyaWVmaW5nOiBzdWNjZXNzIHdvcmtzcGFjZT0lcyBiYWNrdXBfY3JlYXRlZD0lcyAiCiAgICAgICAgICAgICJ3b3Jrc3BhY2VfYWRkZWRfdG9fa25vd249JXMgbGljZW5zZWQ9JXMgZGVtb19jYXBwZWQ9JXMiLAogICAgICAgICAgICB3b3Jrc3BhY2UsIGJhY2t1cF9vaywgd29ya3NwYWNlX2FkZGVkX3RvX2tub3duLAogICAgICAgICAgICBsaWNlbnNlZCwgZGVtb19jYXBwZWQsCiAgICAgICAgKQogICAgICAgIHJlc3VsdCA9IHsKICAgICAgICAgICAgInN1Y2Nlc3MiOiBUcnVlLAogICAgICAgICAgICAid29ya3NwYWNlIjogd29ya3NwYWNlLAogICAgICAgICAgICAiZmlsZXNfc2F2ZWQiOiBbQlJJRUZJTkdfTkFNRSwgTUFTVEVSX0xPR19OQU1FXSwKICAgICAgICAgICAgImJhY2t1cF9jcmVhdGVkIjogYmFja3VwX29rLAogICAgICAgICAgICAid29ya3NwYWNlX2FkZGVkX3RvX2tub3duIjogd29ya3NwYWNlX2FkZGVkX3RvX2tub3duLAogICAgICAgIH0KICAgICAgICBpZiBub3QgbGljZW5zZWQ6CiAgICAgICAgICAgIHJlc3VsdFsiZGVtb19icmllZmluZ19jYXBwZWQiXSA9IGRlbW9fY2FwcGVkCiAgICAgICAgICAgIGlmIGRlbW9fY291bnRlcl9yZXN1bHQ6CiAgICAgICAgICAgICAgICByZXN1bHRbImRlbW9fc2Vzc2lvbnNfdXNlZCJdID0gZGVtb19jb3VudGVyX3Jlc3VsdC5nZXQoCiAgICAgICAgICAgICAgICAgICAgInNlc3Npb25zIiwgMAogICAgICAgICAgICAgICAgKQogICAgICAgIHJldHVybiByZXN1bHQKCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGV4YzogICMgY2F0Y2gtYWxsIOKGkiBpbnRlcm5hbF9lcnJvcgogICAgICAgIGxvZy5leGNlcHRpb24oInNhdmVfYnJpZWZpbmc6IHVuaGFuZGxlZCBleGNlcHRpb24iKQogICAgICAgIHJldHVybiBfZXJyb3IoImludGVybmFsX2Vycm9yIiwgZiJpbnRlcm5hbCBlcnJvcjoge2V4Y30iKQoKCkBtY3AudG9vbCgpCmRlZiBsaXN0X3dvcmtzcGFjZXMoaW5jbHVkZV9kYXRlczogYm9vbCA9IFRydWUpIC0+IGRpY3Q6CiAgICAiIiJMaXN0IGFsbCByZWdpc3RlcmVkIHdvcmtzcGFjZXMgd2l0aCBsYXN0LXVwZGF0ZWQgZGF0ZXMuCgogICAgUmVhZC1vbmx5LiBSZXR1cm5zIGFscGhhYmV0aWNhbCBsaXN0IG9mIHdvcmtzcGFjZSBvYmplY3RzCiAgICBwbHVzIGEgY291bnQgZmllbGQuIFdoZW4gaW5jbHVkZV9kYXRlcyBpcyBUcnVlIChkZWZhdWx0KSwKICAgIGVhY2ggb2JqZWN0IGhhcyB7bmFtZSwgbGFzdF91cGRhdGVkfSB3aGVyZSBsYXN0X3VwZGF0ZWQgaXMKICAgIFlZWVktTU0tREQgZnJvbSB0aGUgd29ya3NwYWNlIGZvbGRlcidzIG1vZGlmaWNhdGlvbiB0aW1lLgogICAgV2hlbiBGYWxzZSwgZWFjaCBvYmplY3QgaGFzIHtuYW1lfSBvbmx5LgoKICAgIE5vdGU6IGluY2x1ZGVfZGF0ZXMgcGFyYW1ldGVyIGFsc28gd29ya3MgYXJvdW5kIGEgQ2xhdWRlCiAgICBEZXNrdG9wIE1DUCBjbGllbnQgbGltaXRhdGlvbiB3aGVyZSB6ZXJvLXBhcmFtZXRlciB0b29scwogICAgZmFpbCB0byBkaXNwYXRjaCAoY2FsbCBuZXZlciByZWFjaGVzIHRoZSBzZXJ2ZXIpLgoKICAgIFN1Y2Nlc3Mgc2hhcGU6CiAgICAgICAge3N1Y2Nlc3M6IHRydWUsIHdvcmtzcGFjZXM6IFt7bmFtZSwgbGFzdF91cGRhdGVkP31dLCBjb3VudH0KICAgIEVycm9yIHNoYXBlOgogICAgICAgIHtzdWNjZXNzOiBmYWxzZSwgZXJyb3IsIG1lc3NhZ2V9CiAgICAiIiIKICAgIGxvZy5pbmZvKCJ0b29sIGNhbGw6IGxpc3Rfd29ya3NwYWNlcyAoaW5jbHVkZV9kYXRlcz0lcykiLCBpbmNsdWRlX2RhdGVzKQogICAgdHJ5OgogICAgICAgICMgMS4gRW5zdXJlIHNlcnZlciByb290IGV4aXN0cy4KICAgICAgICBkaXJfZXJyID0gX2Vuc3VyZV9kaXJlY3RvcnkoKQogICAgICAgIGlmIGRpcl9lcnIgaXMgbm90IE5vbmU6CiAgICAgICAgICAgIGxvZy5pbmZvKCJsaXN0X3dvcmtzcGFjZXM6IGVycm9yIGRpcmVjdG9yeV9taXNzaW5nIikKICAgICAgICAgICAgcmV0dXJuIGRpcl9lcnIKCiAgICAgICAgIyAyLiBSZWFkIGtub3duIHdvcmtzcGFjZXMuCiAgICAgICAga25vd24gPSBfcmVhZF9rbm93bl93b3Jrc3BhY2VzKCkKCiAgICAgICAgIyAzLiBCdWlsZCBsaXN0LCBvcHRpb25hbGx5IHdpdGggZm9sZGVyIG10aW1lLgogICAgICAgIHJlc3VsdCA9IFtdCiAgICAgICAgZm9yIG5hbWUgaW4gc29ydGVkKGtub3duKToKICAgICAgICAgICAgZW50cnk6IGRpY3QgPSB7Im5hbWUiOiBuYW1lfQogICAgICAgICAgICBpZiBpbmNsdWRlX2RhdGVzOgogICAgICAgICAgICAgICAgZm9sZGVyID0gb3MucGF0aC5qb2luKEJBU0VfRElSLCBuYW1lKQogICAgICAgICAgICAgICAgaWYgb3MucGF0aC5pc2Rpcihmb2xkZXIpOgogICAgICAgICAgICAgICAgICAgIG10aW1lID0gb3MucGF0aC5nZXRtdGltZShmb2xkZXIpCiAgICAgICAgICAgICAgICAgICAgZW50cnlbImxhc3RfdXBkYXRlZCJdID0gKAogICAgICAgICAgICAgICAgICAgICAgICBkYXRldGltZS5kYXRlLmZyb210aW1lc3RhbXAobXRpbWUpLmlzb2Zvcm1hdCgpCiAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgICAgICBlbnRyeVsibGFzdF91cGRhdGVkIl0gPSAidW5rbm93biIKICAgICAgICAgICAgcmVzdWx0LmFwcGVuZChlbnRyeSkKCiAgICAgICAgbG9nLmluZm8oImxpc3Rfd29ya3NwYWNlczogc3VjY2VzcyBjb3VudD0lZCIsIGxlbihyZXN1bHQpKQogICAgICAgIHJldHVybiB7CiAgICAgICAgICAgICJzdWNjZXNzIjogVHJ1ZSwKICAgICAgICAgICAgIndvcmtzcGFjZXMiOiByZXN1bHQsCiAgICAgICAgICAgICJjb3VudCI6IGxlbihyZXN1bHQpLAogICAgICAgIH0KCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGV4YzogICMgY2F0Y2gtYWxsIOKGkiBpbnRlcm5hbF9lcnJvcgogICAgICAgIGxvZy5leGNlcHRpb24oImxpc3Rfd29ya3NwYWNlczogdW5oYW5kbGVkIGV4Y2VwdGlvbiIpCiAgICAgICAgcmV0dXJuIF9lcnJvcigiaW50ZXJuYWxfZXJyb3IiLCBmImludGVybmFsIGVycm9yOiB7ZXhjfSIpCgoKQG1jcC50b29sKCkKZGVmIGFyY2hpdmUod29ya3NwYWNlOiBzdHIsIHRvcGljX3NsdWc6IHN0cikgLT4gZGljdDoKICAgICIiIkFyY2hpdmUgdGhlIG1hc3RlciBsb2cgZm9yIHRva2VuIG1hbmFnZW1lbnQuCgogICAgTWlkLWNoYWluIG1haW50ZW5hbmNlOiB1c2VkIHdoZW4gbWFzdGVyX2xvZyBncm93cyB0b28gbGFyZ2UuCiAgICBCZWZvcmUgYXJjaGl2aW5nLCBleHRyYWN0cyBhbGwgcGVyc2lzdGVudCB0YWdzCiAgICAoW0E6U1RBTkRJTkc6a2V5XSwgW0I6RE9ORTprZXldLCBldGMuKSBmcm9tIHRoZSBtYXN0ZXJfbG9nCiAgICBhbmQgcm91dGVzIHRoZW0gdG8gdGllcl9hX3RhZ3MudHh0IG9yIHRpZXJfYl90YWdzLnR4dCBiYXNlZAogICAgb24gcHJlZml4IChwZXJzaXN0cyBhY3Jvc3MgYXJjaGl2ZSBjeWNsZXMpLiBUaGVuIHRoZQogICAgbWFzdGVyX2xvZyBpcyByZW5hbWVkIHRvIGFuIGFyY2hpdmUgZmlsZSBpbiB0aGUgc2FtZQogICAgd29ya3NwYWNlIGZvbGRlci4gVGhlIGJyaWVmaW5nIGlzIE5PVCBjbGVhcmVkIOKAlCBpdCBjYXJyaWVzCiAgICB0aGUgbmFycmF0aXZlIHRocmVhZCBmb3J3YXJkLiBUaWVyIHRhZyBmaWxlcyBhcmUgbmV2ZXIKICAgIGNsZWFyZWQuIFdvcmtzcGFjZSByZWdpc3RyYXRpb24gaW4ga25vd25fd29ya3NwYWNlcy50eHQgaXMKICAgIHByZXNlcnZlZC4KCiAgICBGb3IgdG9waWMgY29uY2x1c2lvbiAoYXJjaGl2ZSArIGNsZWFyIGJyaWVmaW5nKSwgdXNlIHRoZQogICAgcmVzZXQgdG9vbCBpbnN0ZWFkLgoKICAgIEFyY2hpdmUgZmlsZW5hbWUgcGF0dGVybjoKICAgICAgbWFzdGVyX2xvZ19hcmNoaXZlX3t0b3BpY19zbHVnfV9jaGF0c197TjF9X3RvX3tOMn1fe2RhdGV9LnR4dAogICAgSWYgbm8gQ0hBVCBOIGhlYWRlcnMgYXJlIGZvdW5kIGluIHRoZSBsb2c6CiAgICAgIG1hc3Rlcl9sb2dfYXJjaGl2ZV97dG9waWNfc2x1Z31fe2RhdGV9LnR4dAoKICAgIENsYXVkZSBzdXBwbGllcyB0b3BpY19zbHVnIChjb25maXJtZWQgd2l0aCB0aGUgaHVtYW4pLgogICAgU2VydmVyIGV4dHJhY3RzIGNoYXQgcmFuZ2UgZnJvbSBsb2cgaGVhZGVycyBhdXRvbWF0aWNhbGx5LgoKICAgIEVycm9ycyBpZiBtYXN0ZXJfbG9nIGlzIGVtcHR5IG9yIG1pc3Npbmcg4oCUIG5vdGhpbmcgdG8gYXJjaGl2ZS4KCiAgICBTdWNjZXNzIHNoYXBlOgogICAgICAgIHtzdWNjZXNzOiB0cnVlLCB3b3Jrc3BhY2UsIGFyY2hpdmVfZmlsZSwgYnJpZWZpbmdfcHJlc2VydmVkLAogICAgICAgICBwZXJzaXN0ZW50X3RhZ3NfZXh0cmFjdGVkfQogICAgRXJyb3Igc2hhcGU6CiAgICAgICAge3N1Y2Nlc3M6IGZhbHNlLCBlcnJvciwgbWVzc2FnZX0KICAgICIiIgogICAgbG9nLmluZm8oCiAgICAgICAgInRvb2wgY2FsbDogYXJjaGl2ZSB3b3Jrc3BhY2U9JXIgdG9waWNfc2x1Zz0lciIsCiAgICAgICAgd29ya3NwYWNlLCB0b3BpY19zbHVnLAogICAgKQogICAgdHJ5OgogICAgICAgICMgMS4gVmFsaWRhdGUgd29ya3NwYWNlLgogICAgICAgIHdzX2VyciA9IF92YWxpZGF0ZV93b3Jrc3BhY2Uod29ya3NwYWNlKQogICAgICAgIGlmIHdzX2VyciBpcyBub3QgTm9uZToKICAgICAgICAgICAgbG9nLmluZm8oImFyY2hpdmU6IGVycm9yIGludmFsaWRfaW5wdXQgKHdvcmtzcGFjZSkiKQogICAgICAgICAgICByZXR1cm4gd3NfZXJyCgogICAgICAgICMgMi4gVmFsaWRhdGUgdG9waWNfc2x1Zy4KICAgICAgICBpZiBub3QgaXNpbnN0YW5jZSh0b3BpY19zbHVnLCBzdHIpOgogICAgICAgICAgICByZXR1cm4gX2Vycm9yKAogICAgICAgICAgICAgICAgImludmFsaWRfaW5wdXQiLAogICAgICAgICAgICAgICAgZiJmaWVsZCB0b3BpY19zbHVnIG11c3QgYmUgYSBzdHJpbmcsICIKICAgICAgICAgICAgICAgIGYiZ290IHt0eXBlKHRvcGljX3NsdWcpLl9fbmFtZV9ffSIsCiAgICAgICAgICAgICkKICAgICAgICBpZiBub3QgVE9QSUNfU0xVR19QQVRURVJOLmZ1bGxtYXRjaCh0b3BpY19zbHVnKToKICAgICAgICAgICAgcmV0dXJuIF9lcnJvcigKICAgICAgICAgICAgICAgICJpbnZhbGlkX2lucHV0IiwKICAgICAgICAgICAgICAgIGYidG9waWNfc2x1ZyAne3RvcGljX3NsdWd9JyBpbnZhbGlkOiBtdXN0IGJlIDEtNjQgIgogICAgICAgICAgICAgICAgZiJjaGFycywgbG93ZXJjYXNlIGxldHRlcnMvZGlnaXRzL3VuZGVyc2NvcmVzIG9ubHkiLAogICAgICAgICAgICApCgogICAgICAgICMgMy4gRGVtbyBnYXRpbmc6IGJsb2NrIGFyY2hpdmUgZm9yIGRlbW8gdXNlcnMgcGFzdCBsaW1pdC4KICAgICAgICBpZiBub3QgX2lzX2xpY2Vuc2VkKCk6CiAgICAgICAgICAgIG1hY2hpbmVfaWQgPSBfZ2V0X21hY2hpbmVfaWQoKQogICAgICAgICAgICBpZiBtYWNoaW5lX2lkOgogICAgICAgICAgICAgICAgY291bnRlciA9IF9kZW1vX2NvdW50ZXJfY2hlY2sobWFjaGluZV9pZCkKICAgICAgICAgICAgICAgIGlmIGNvdW50ZXIgYW5kIGNvdW50ZXIuZ2V0KCJsaW1pdF9yZWFjaGVkIik6CiAgICAgICAgICAgICAgICAgICAgbG9nLmluZm8oImFyY2hpdmU6IGVycm9yIGRlbW9fbGltaXQgKGFyY2hpdmUgYmxvY2tlZCkiKQogICAgICAgICAgICAgICAgICAgIHJldHVybiBfZXJyb3IoCiAgICAgICAgICAgICAgICAgICAgICAgICJkZW1vX2xpbWl0IiwKICAgICAgICAgICAgICAgICAgICAgICAgIkFyY2hpdmUgaXMgbm90IGF2YWlsYWJsZSBpbiB0aGUgZnJlZSB2ZXJzaW9uLiAiCiAgICAgICAgICAgICAgICAgICAgICAgICJVcGdyYWRlIHRvIHByZXNlcnZlIHlvdXIgZnVsbCBjb252ZXJzYXRpb24gIgogICAgICAgICAgICAgICAgICAgICAgICBmImhpc3Rvcnk6IHtVUEdSQURFX1VSTH0iLAogICAgICAgICAgICAgICAgICAgICkKCiAgICAgICAgIyA0LiBFbnN1cmUgc2VydmVyIHJvb3QgZXhpc3RzLgogICAgICAgIGRpcl9lcnIgPSBfZW5zdXJlX2RpcmVjdG9yeSgpCiAgICAgICAgaWYgZGlyX2VyciBpcyBub3QgTm9uZToKICAgICAgICAgICAgbG9nLmluZm8oImFyY2hpdmU6IGVycm9yIGRpcmVjdG9yeV9taXNzaW5nIikKICAgICAgICAgICAgcmV0dXJuIGRpcl9lcnIKCiAgICAgICAgIyA1LiBXb3Jrc3BhY2UgbXVzdCBiZSBrbm93biAobm8gY3JlYXRpbmcgdmlhIGFyY2hpdmUpLgogICAgICAgIGtub3duID0gX3JlYWRfa25vd25fd29ya3NwYWNlcygpCiAgICAgICAgaWYgd29ya3NwYWNlIG5vdCBpbiBrbm93bjoKICAgICAgICAgICAgbG9nLmluZm8oImFyY2hpdmU6IGVycm9yIHVua25vd25fd29ya3NwYWNlIikKICAgICAgICAgICAgcmV0dXJuIF9lcnJvcigKICAgICAgICAgICAgICAgICJ1bmtub3duX3dvcmtzcGFjZSIsCiAgICAgICAgICAgICAgICBmIndvcmtzcGFjZSAne3dvcmtzcGFjZX0nIGlzIG5vdCByZWdpc3RlcmVkLiAiCiAgICAgICAgICAgICAgICBmIkNhbm5vdCBhcmNoaXZlIGFuIHVua25vd24gd29ya3NwYWNlLiIsCiAgICAgICAgICAgICkKCiAgICAgICAgIyA2LiBSZXNvbHZlIHBhdGhzIGFuZCBjaGVjayBtYXN0ZXJfbG9nIGV4aXN0cyB3aXRoIGNvbnRlbnQuCiAgICAgICAgcGF0aHMgPSBfd29ya3NwYWNlX3BhdGhzKHdvcmtzcGFjZSkKICAgICAgICBpZiBub3Qgb3MucGF0aC5leGlzdHMocGF0aHNbIm1hc3Rlcl9sb2ciXSk6CiAgICAgICAgICAgIGxvZy5pbmZvKCJhcmNoaXZlOiBlcnJvciBlbXB0eV9sb2cgKGZpbGUgbWlzc2luZykiKQogICAgICAgICAgICByZXR1cm4gX2Vycm9yKAogICAgICAgICAgICAgICAgImVtcHR5X2xvZyIsCiAgICAgICAgICAgICAgICAibm90aGluZyB0byBhcmNoaXZlIOKAlCBtYXN0ZXJfbG9nIGRvZXMgbm90IGV4aXN0LiIsCiAgICAgICAgICAgICkKICAgICAgICB0cnk6CiAgICAgICAgICAgIGxvZ19jb250ZW50ID0gX3JlYWRfdGV4dChwYXRoc1sibWFzdGVyX2xvZyJdKQogICAgICAgIGV4Y2VwdCBPU0Vycm9yIGFzIGV4YzoKICAgICAgICAgICAgbG9nLmluZm8oImFyY2hpdmU6IGVycm9yIHJlYWRfZmFpbHVyZSAobWFzdGVyX2xvZykiKQogICAgICAgICAgICByZXR1cm4gX2Vycm9yKAogICAgICAgICAgICAgICAgInJlYWRfZmFpbHVyZSIsCiAgICAgICAgICAgICAgICBmImNvdWxkIG5vdCByZWFkIHtNQVNURVJfTE9HX05BTUV9OiB7ZXhjfSIsCiAgICAgICAgICAgICkKICAgICAgICBpZiBub3QgbG9nX2NvbnRlbnQuc3RyaXAoKToKICAgICAgICAgICAgbG9nLmluZm8oImFyY2hpdmU6IGVycm9yIGVtcHR5X2xvZyAoZmlsZSBlbXB0eSkiKQogICAgICAgICAgICByZXR1cm4gX2Vycm9yKAogICAgICAgICAgICAgICAgImVtcHR5X2xvZyIsCiAgICAgICAgICAgICAgICAibm90aGluZyB0byBhcmNoaXZlIOKAlCBtYXN0ZXJfbG9nIGlzIGVtcHR5LiIsCiAgICAgICAgICAgICkKCiAgICAgICAgIyA2Yi4gRXh0cmFjdCBwZXJzaXN0ZW50IHRhZ3MgZnJvbSBsb2cgYmVmb3JlIGFyY2hpdmluZy4KICAgICAgICBuZXdfdGFncyA9IF9leHRyYWN0X3BlcnNpc3RlbnRfdGFncyhsb2dfY29udGVudCkKICAgICAgICBpZiBuZXdfdGFnczoKICAgICAgICAgICAgIyBSZWFkIGV4aXN0aW5nIHRpZXIgZmlsZXMgKGlmIGFueSkgZm9yIG1lcmdlLgogICAgICAgICAgICBleGlzdGluZ190ZXh0ID0gIiIKICAgICAgICAgICAgZm9yIHRmIGluICgidGllcl9hX3RhZ3MiLCAidGllcl9iX3RhZ3MiKToKICAgICAgICAgICAgICAgIGlmIG9zLnBhdGguZXhpc3RzKHBhdGhzW3RmXSk6CiAgICAgICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgICAgICBleGlzdGluZ190ZXh0ICs9IF9yZWFkX3RleHQocGF0aHNbdGZdKSArICJcbiIKICAgICAgICAgICAgICAgICAgICBleGNlcHQgT1NFcnJvciBhcyBleGM6CiAgICAgICAgICAgICAgICAgICAgICAgIGxvZy53YXJuaW5nKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgImFyY2hpdmU6IGNvdWxkIG5vdCByZWFkICVzOiAlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZiwgZXhjLAogICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICMgQWxzbyBjaGVjayBsZWdhY3kgcGVyc2lzdGVudF90YWdzLnR4dCBmb3IgbWlncmF0aW9uLgogICAgICAgICAgICBpZiBub3QgZXhpc3RpbmdfdGV4dC5zdHJpcCgpIGFuZCBvcy5wYXRoLmV4aXN0cyhwYXRoc1sicGVyc2lzdGVudF90YWdzIl0pOgogICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgIGV4aXN0aW5nX3RleHQgPSBfcmVhZF90ZXh0KHBhdGhzWyJwZXJzaXN0ZW50X3RhZ3MiXSkKICAgICAgICAgICAgICAgIGV4Y2VwdCBPU0Vycm9yOgogICAgICAgICAgICAgICAgICAgIHBhc3MKCiAgICAgICAgICAgIHRpZXJfYV90ZXh0LCB0aWVyX2JfdGV4dCA9IF9tZXJnZV9wZXJzaXN0ZW50X3RhZ3MoCiAgICAgICAgICAgICAgICBleGlzdGluZ190ZXh0LCBuZXdfdGFncywKICAgICAgICAgICAgKQoKICAgICAgICAgICAgIyBXcml0ZSB0aWVyIGZpbGVzIChhbHdheXMsIGV2ZW4gaWYgZW1wdHkg4oCUIGNsZWFycyBzdGFsZSBkYXRhKS4KICAgICAgICAgICAgZm9yIHRpZXJfcGF0aCwgdGllcl9jb250ZW50LCB0aWVyX2xhYmVsIGluIFsKICAgICAgICAgICAgICAgIChwYXRoc1sidGllcl9hX3RhZ3MiXSwgdGllcl9hX3RleHQsICJ0aWVyX2EiKSwKICAgICAgICAgICAgICAgIChwYXRoc1sidGllcl9iX3RhZ3MiXSwgdGllcl9iX3RleHQsICJ0aWVyX2IiKSwKICAgICAgICAgICAgXToKICAgICAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgICAgICB3aXRoIG9wZW4oCiAgICAgICAgICAgICAgICAgICAgICAgIHRpZXJfcGF0aCwgInciLCBlbmNvZGluZz0idXRmLTgiLAogICAgICAgICAgICAgICAgICAgICkgYXMgZjoKICAgICAgICAgICAgICAgICAgICAgICAgZi53cml0ZSh0aWVyX2NvbnRlbnQpCiAgICAgICAgICAgICAgICAgICAgICAgIGYuZmx1c2goKQogICAgICAgICAgICAgICAgICAgICAgICBvcy5mc3luYyhmLmZpbGVubygpKQogICAgICAgICAgICAgICAgZXhjZXB0IE9TRXJyb3IgYXMgZXhjOgogICAgICAgICAgICAgICAgICAgIGxvZy53YXJuaW5nKAogICAgICAgICAgICAgICAgICAgICAgICAiYXJjaGl2ZTogY291bGQgbm90IHdyaXRlICVzOiAlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgIHRpZXJfbGFiZWwsIGV4YywKICAgICAgICAgICAgICAgICAgICApCgogICAgICAgICAgICB0b3RhbF90YWdzID0gbGVuKFBFUlNJU1RFTlRfVEFHX1JFLmZpbmRhbGwoCiAgICAgICAgICAgICAgICB0aWVyX2FfdGV4dCArIHRpZXJfYl90ZXh0CiAgICAgICAgICAgICkpCiAgICAgICAgICAgIGxvZy5pbmZvKAogICAgICAgICAgICAgICAgImFyY2hpdmU6IGV4dHJhY3RlZCAlZCBwZXJzaXN0ZW50IHRhZ3MgIgogICAgICAgICAgICAgICAgIiglZCBuZXcvdXBkYXRlZCwgcm91dGVkIHRvIHRpZXIgQS9CKSIsCiAgICAgICAgICAgICAgICB0b3RhbF90YWdzLCBsZW4obmV3X3RhZ3MpLAogICAgICAgICAgICApCgogICAgICAgICMgNy4gQnVpbGQgYXJjaGl2ZSBmaWxlbmFtZS4KICAgICAgICB0b2RheSA9IGRhdGV0aW1lLmRhdGUudG9kYXkoKS5pc29mb3JtYXQoKQogICAgICAgIGNoYXRfcmFuZ2UgPSBfZXh0cmFjdF9jaGF0X3JhbmdlKGxvZ19jb250ZW50KQogICAgICAgIGlmIGNoYXRfcmFuZ2U6CiAgICAgICAgICAgIGFyY2hpdmVfbmFtZSA9ICgKICAgICAgICAgICAgICAgIGYibWFzdGVyX2xvZ19hcmNoaXZlX3t0b3BpY19zbHVnfSIKICAgICAgICAgICAgICAgIGYiX2NoYXRzX3tjaGF0X3JhbmdlWzBdfV90b197Y2hhdF9yYW5nZVsxXX0iCiAgICAgICAgICAgICAgICBmIl97dG9kYXl9LnR4dCIKICAgICAgICAgICAgKQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIGFyY2hpdmVfbmFtZSA9ICgKICAgICAgICAgICAgICAgIGYibWFzdGVyX2xvZ19hcmNoaXZlX3t0b3BpY19zbHVnfV97dG9kYXl9LnR4dCIKICAgICAgICAgICAgKQogICAgICAgIGFyY2hpdmVfcGF0aCA9IG9zLnBhdGguam9pbihwYXRoc1siZm9sZGVyIl0sIGFyY2hpdmVfbmFtZSkKCiAgICAgICAgIyA4LiBHdWFyZCBhZ2FpbnN0IG92ZXJ3cml0aW5nIGFuIGV4aXN0aW5nIGFyY2hpdmUuCiAgICAgICAgaWYgb3MucGF0aC5leGlzdHMoYXJjaGl2ZV9wYXRoKToKICAgICAgICAgICAgbG9nLmluZm8oImFyY2hpdmU6IGVycm9yIGFyY2hpdmVfZXhpc3RzIikKICAgICAgICAgICAgcmV0dXJuIF9lcnJvcigKICAgICAgICAgICAgICAgICJhcmNoaXZlX2V4aXN0cyIsCiAgICAgICAgICAgICAgICBmImFyY2hpdmUgZmlsZSBhbHJlYWR5IGV4aXN0czoge2FyY2hpdmVfbmFtZX0uICIKICAgICAgICAgICAgICAgIGYiVXNlIGEgZGlmZmVyZW50IHRvcGljX3NsdWcgb3IgcmVzb2x2ZSBtYW51YWxseS4iLAogICAgICAgICAgICApCgogICAgICAgICMgOS4gUmVuYW1lIG1hc3Rlcl9sb2cgdG8gYXJjaGl2ZS4KICAgICAgICB0cnk6CiAgICAgICAgICAgIG9zLnJlbmFtZShwYXRoc1sibWFzdGVyX2xvZyJdLCBhcmNoaXZlX3BhdGgpCiAgICAgICAgZXhjZXB0IE9TRXJyb3IgYXMgZXhjOgogICAgICAgICAgICBsb2cuaW5mbygiYXJjaGl2ZTogZXJyb3Igd3JpdGVfZmFpbHVyZSAoYXJjaGl2ZSByZW5hbWUpIikKICAgICAgICAgICAgcmV0dXJuIF9lcnJvcigKICAgICAgICAgICAgICAgICJ3cml0ZV9mYWlsdXJlIiwKICAgICAgICAgICAgICAgIGYiY291bGQgbm90IHJlbmFtZSBtYXN0ZXJfbG9nIHRvIGFyY2hpdmU6IHtleGN9IiwKICAgICAgICAgICAgKQoKICAgICAgICAjIDEwLiBCcmllZmluZyBpcyBOT1QgY2xlYXJlZCDigJQgaXQgY2FycmllcyB0aGUgbmFycmF0aXZlCiAgICAgICAgIyAgICB0aHJlYWQgZm9yd2FyZC4gT25seSB0aGUgbWFzdGVyX2xvZyBpcyBhcmNoaXZlZC4KICAgICAgICAjICAgIFRoZSBicmllZmluZyBpcyB1cGRhdGVkIGJ5IHNhdmVfYnJpZWZpbmcgY2FsbHMsIG5vdAogICAgICAgICMgICAgYnkgdGhlIGFyY2hpdmUgcHJvY2Vzcy4KCiAgICAgICAgIyAxMS4gQ2xlYW4gdXAgbWFzdGVyX2xvZyAuYmFrIChzdGFsZSBhZnRlciBhcmNoaXZlKS4KICAgICAgICAjICAgICBCcmllZmluZyAuYmFrIHN0YXlzIOKAlCBicmllZmluZyBpcyBzdGlsbCBsaXZlLgogICAgICAgIHRyeToKICAgICAgICAgICAgaWYgb3MucGF0aC5leGlzdHMocGF0aHNbIm1hc3Rlcl9sb2dfYmFrIl0pOgogICAgICAgICAgICAgICAgb3MucmVtb3ZlKHBhdGhzWyJtYXN0ZXJfbG9nX2JhayJdKQogICAgICAgIGV4Y2VwdCBPU0Vycm9yIGFzIGV4YzoKICAgICAgICAgICAgbG9nLndhcm5pbmcoCiAgICAgICAgICAgICAgICAiYXJjaGl2ZTogY291bGQgbm90IHJlbW92ZSAlczogJXMiLAogICAgICAgICAgICAgICAgcGF0aHNbIm1hc3Rlcl9sb2dfYmFrIl0sIGV4YywKICAgICAgICAgICAgKQoKICAgICAgICBsb2cuaW5mbygKICAgICAgICAgICAgImFyY2hpdmU6IHN1Y2Nlc3Mgd29ya3NwYWNlPSVzIGFyY2hpdmU9JXMiLAogICAgICAgICAgICB3b3Jrc3BhY2UsIGFyY2hpdmVfbmFtZSwKICAgICAgICApCiAgICAgICAgcmV0dXJuIHsKICAgICAgICAgICAgInN1Y2Nlc3MiOiBUcnVlLAogICAgICAgICAgICAid29ya3NwYWNlIjogd29ya3NwYWNlLAogICAgICAgICAgICAiYXJjaGl2ZV9maWxlIjogYXJjaGl2ZV9uYW1lLAogICAgICAgICAgICAiYnJpZWZpbmdfcHJlc2VydmVkIjogVHJ1ZSwKICAgICAgICAgICAgInBlcnNpc3RlbnRfdGFnc19leHRyYWN0ZWQiOiBsZW4obmV3X3RhZ3MpLAogICAgICAgIH0KCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGV4YzogICMgY2F0Y2gtYWxsIOKGkiBpbnRlcm5hbF9lcnJvcgogICAgICAgIGxvZy5leGNlcHRpb24oImFyY2hpdmU6IHVuaGFuZGxlZCBleGNlcHRpb24iKQogICAgICAgIHJldHVybiBfZXJyb3IoImludGVybmFsX2Vycm9yIiwgZiJpbnRlcm5hbCBlcnJvcjoge2V4Y30iKQoKCkBtY3AudG9vbCgpCmRlZiByZXNldCh3b3Jrc3BhY2U6IHN0ciwgdG9waWNfc2x1Zzogc3RyKSAtPiBkaWN0OgogICAgIiIiQ29uY2x1ZGUgYSB0b3BpYyBjaGFpbiDigJQgYXJjaGl2ZSB0aGUgbG9nIGFuZCBjbGVhciB0aGUgYnJpZWZpbmcuCgogICAgVXNlIHdoZW4gYSB0b3BpYyBpcyBmaW5pc2hlZCBhbmQgeW91IHdhbnQgYSBjbGVhbiBzbGF0ZS4KICAgIFBlcmZvcm1zIGEgZnVsbCBhcmNoaXZlIChleHRyYWN0IHRhZ3MsIHJlbmFtZSBtYXN0ZXJfbG9nIHRvCiAgICBhcmNoaXZlIGZpbGUpLCB0aGVuIGNsZWFycyB0aGUgYnJpZWZpbmcgc28gdGhlIG5leHQgY2hhdAogICAgb3BlbnMgYXQgQ2hhdCAxIHdpdGggbm8gY29udGludWF0aW9uIGNvbnRleHQuIFBlcnNpc3RlbnQKICAgIHRhZ3Mgc3Vydml2ZSDigJQgdGhhdCBpcyB0aGVpciBlbnRpcmUgcHVycG9zZS4KCiAgICBGb3IgbWlkLWNoYWluIGxvZyBtYWludGVuYW5jZSAoYnJpZWZpbmcgcHJlc2VydmVkKSwgdXNlIHRoZQogICAgYXJjaGl2ZSB0b29sIGluc3RlYWQuCgogICAgU2FtZSBwYXJhbWV0ZXJzIGFzIGFyY2hpdmU6IHdvcmtzcGFjZSBhbmQgdG9waWNfc2x1Zy4KCiAgICBTdWNjZXNzIHNoYXBlOgogICAgICAgIHtzdWNjZXNzOiB0cnVlLCB3b3Jrc3BhY2UsIGFyY2hpdmVfZmlsZSwgYnJpZWZpbmdfY2xlYXJlZCwKICAgICAgICAgcGVyc2lzdGVudF90YWdzX2V4dHJhY3RlZH0KICAgIEVycm9yIHNoYXBlOgogICAgICAgIHtzdWNjZXNzOiBmYWxzZSwgZXJyb3IsIG1lc3NhZ2V9CiAgICAiIiIKICAgIGxvZy5pbmZvKAogICAgICAgICJ0b29sIGNhbGw6IHJlc2V0IHdvcmtzcGFjZT0lciB0b3BpY19zbHVnPSVyIiwKICAgICAgICB3b3Jrc3BhY2UsIHRvcGljX3NsdWcsCiAgICApCiAgICB0cnk6CiAgICAgICAgIyAxLiBEZWxlZ2F0ZSB0byBhcmNoaXZlIGZvciB0aGUgaGVhdnkgbGlmdGluZy4KICAgICAgICBhcmNoaXZlX3Jlc3VsdCA9IGFyY2hpdmUod29ya3NwYWNlLCB0b3BpY19zbHVnKQoKICAgICAgICAjIDIuIElmIGFyY2hpdmUgZmFpbGVkIGZvciBhIHJlYXNvbiBvdGhlciB0aGFuIGVtcHR5IGxvZywKICAgICAgICAjICAgIHBhc3MgdGhlIGVycm9yIHRocm91Z2guIEVtcHR5IGxvZyBpcyBmaW5lIGZvciByZXNldCDigJQKICAgICAgICAjICAgIGl0IG1lYW5zIHRoZSBsb2cgaXMgYWxyZWFkeSBnb25lLCBzbyBqdXN0IGNsZWFyIGJyaWVmaW5nLgogICAgICAgIGFyY2hpdmVfZmlsZSA9IE5vbmUKICAgICAgICB0YWdzX2V4dHJhY3RlZCA9IDAKICAgICAgICBpZiBub3QgYXJjaGl2ZV9yZXN1bHQuZ2V0KCJzdWNjZXNzIik6CiAgICAgICAgICAgIGlmIGFyY2hpdmVfcmVzdWx0LmdldCgiZXJyb3IiKSA9PSAiZW1wdHlfbG9nIjoKICAgICAgICAgICAgICAgICMgTG9nIGFscmVhZHkgZW1wdHkvbWlzc2luZyDigJQgdGhhdCdzIGZpbmUgZm9yIHJlc2V0LgogICAgICAgICAgICAgICAgIyBKdXN0IHByb2NlZWQgdG8gY2xlYXIgYnJpZWZpbmcuCiAgICAgICAgICAgICAgICBsb2cuaW5mbygKICAgICAgICAgICAgICAgICAgICAicmVzZXQ6IG1hc3Rlcl9sb2cgYWxyZWFkeSBlbXB0eSwgIgogICAgICAgICAgICAgICAgICAgICJwcm9jZWVkaW5nIHRvIGNsZWFyIGJyaWVmaW5nIgogICAgICAgICAgICAgICAgKQogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgIyBSZWFsIGVycm9yIOKAlCBwYXNzIHRocm91Z2guCiAgICAgICAgICAgICAgICByZXR1cm4gYXJjaGl2ZV9yZXN1bHQKICAgICAgICBlbHNlOgogICAgICAgICAgICBhcmNoaXZlX2ZpbGUgPSBhcmNoaXZlX3Jlc3VsdFsiYXJjaGl2ZV9maWxlIl0KICAgICAgICAgICAgdGFnc19leHRyYWN0ZWQgPSBhcmNoaXZlX3Jlc3VsdFsicGVyc2lzdGVudF90YWdzX2V4dHJhY3RlZCJdCgogICAgICAgICMgMy4gQ2xlYXIgdGhlIGJyaWVmaW5nIChhcmNoaXZlIHN1Y2NlZWRlZCBvciBsb2cgd2FzIGFscmVhZHkgZW1wdHkpLgogICAgICAgIHBhdGhzID0gX3dvcmtzcGFjZV9wYXRocyh3b3Jrc3BhY2UpCiAgICAgICAgYnJpZWZpbmdfY2xlYXJfZmFpbGVkID0gRmFsc2UKCiAgICAgICAgIyBDbGVhciBicmllZmluZy50eHQuCiAgICAgICAgdHJ5OgogICAgICAgICAgICBpZiBvcy5wYXRoLmV4aXN0cyhwYXRoc1siYnJpZWZpbmciXSk6CiAgICAgICAgICAgICAgICBvcy5yZW1vdmUocGF0aHNbImJyaWVmaW5nIl0pCiAgICAgICAgZXhjZXB0IE9TRXJyb3IgYXMgZXhjOgogICAgICAgICAgICBsb2cud2FybmluZygKICAgICAgICAgICAgICAgICJyZXNldDogY291bGQgbm90IHJlbW92ZSBicmllZmluZzogJXMiLCBleGMsCiAgICAgICAgICAgICkKICAgICAgICAgICAgYnJpZWZpbmdfY2xlYXJfZmFpbGVkID0gVHJ1ZQoKICAgICAgICAjIENsZWFyIGJyaWVmaW5nLnR4dC5iYWsgKHN0YWxlIGFmdGVyIHJlc2V0KS4KICAgICAgICB0cnk6CiAgICAgICAgICAgIGlmIG9zLnBhdGguZXhpc3RzKHBhdGhzWyJicmllZmluZ19iYWsiXSk6CiAgICAgICAgICAgICAgICBvcy5yZW1vdmUocGF0aHNbImJyaWVmaW5nX2JhayJdKQogICAgICAgIGV4Y2VwdCBPU0Vycm9yIGFzIGV4YzoKICAgICAgICAgICAgbG9nLndhcm5pbmcoCiAgICAgICAgICAgICAgICAicmVzZXQ6IGNvdWxkIG5vdCByZW1vdmUgYnJpZWZpbmcgLmJhazogJXMiLCBleGMsCiAgICAgICAgICAgICkKCiAgICAgICAgIyA0LiBWZXJpZnkgYnJpZWZpbmcgaXMgYWN0dWFsbHkgZ29uZS4KICAgICAgICBpZiBvcy5wYXRoLmV4aXN0cyhwYXRoc1siYnJpZWZpbmciXSk6CiAgICAgICAgICAgIGJyaWVmaW5nX2NsZWFyX2ZhaWxlZCA9IFRydWUKCiAgICAgICAgaWYgYnJpZWZpbmdfY2xlYXJfZmFpbGVkOgogICAgICAgICAgICBsb2cuaW5mbygKICAgICAgICAgICAgICAgICJyZXNldDogYXJjaGl2ZSBzdWNjZWVkZWQgYnV0IGJyaWVmaW5nIGNsZWFyIGZhaWxlZCAiCiAgICAgICAgICAgICAgICAid29ya3NwYWNlPSVzIiwKICAgICAgICAgICAgICAgIHdvcmtzcGFjZSwKICAgICAgICAgICAgKQogICAgICAgICAgICByZXR1cm4gewogICAgICAgICAgICAgICAgInN1Y2Nlc3MiOiBUcnVlLAogICAgICAgICAgICAgICAgIndvcmtzcGFjZSI6IHdvcmtzcGFjZSwKICAgICAgICAgICAgICAgICJhcmNoaXZlX2ZpbGUiOiBhcmNoaXZlX2ZpbGUsCiAgICAgICAgICAgICAgICAiYnJpZWZpbmdfY2xlYXJlZCI6IEZhbHNlLAogICAgICAgICAgICAgICAgInBlcnNpc3RlbnRfdGFnc19leHRyYWN0ZWQiOiB0YWdzX2V4dHJhY3RlZCwKICAgICAgICAgICAgICAgICJ3YXJuaW5nIjogKAogICAgICAgICAgICAgICAgICAgICJBcmNoaXZlIHN1Y2NlZWRlZCBidXQgYnJpZWZpbmcudHh0IGNvdWxkIG5vdCAiCiAgICAgICAgICAgICAgICAgICAgImJlIHJlbW92ZWQuIERlbGV0ZSBpdCBtYW51YWxseSBmcm9tIHRoZSAiCiAgICAgICAgICAgICAgICAgICAgIndvcmtzcGFjZSBmb2xkZXIgdG8gZ2V0IGEgY2xlYW4gc2xhdGUuIgogICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgfQoKICAgICAgICBsb2cuaW5mbygKICAgICAgICAgICAgInJlc2V0OiBzdWNjZXNzIHdvcmtzcGFjZT0lcyBhcmNoaXZlPSVzICIKICAgICAgICAgICAgImJyaWVmaW5nX2NsZWFyZWQ9VHJ1ZSIsCiAgICAgICAgICAgIHdvcmtzcGFjZSwgYXJjaGl2ZV9maWxlLAogICAgICAgICkKICAgICAgICByZXR1cm4gewogICAgICAgICAgICAic3VjY2VzcyI6IFRydWUsCiAgICAgICAgICAgICJ3b3Jrc3BhY2UiOiB3b3Jrc3BhY2UsCiAgICAgICAgICAgICJhcmNoaXZlX2ZpbGUiOiBhcmNoaXZlX2ZpbGUsCiAgICAgICAgICAgICJicmllZmluZ19jbGVhcmVkIjogVHJ1ZSwKICAgICAgICAgICAgInBlcnNpc3RlbnRfdGFnc19leHRyYWN0ZWQiOiB0YWdzX2V4dHJhY3RlZCwKICAgICAgICB9CgogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBleGM6ICAjIGNhdGNoLWFsbCDihpIgaW50ZXJuYWxfZXJyb3IKICAgICAgICBsb2cuZXhjZXB0aW9uKCJyZXNldDogdW5oYW5kbGVkIGV4Y2VwdGlvbiIpCiAgICAgICAgcmV0dXJuIF9lcnJvcigiaW50ZXJuYWxfZXJyb3IiLCBmImludGVybmFsIGVycm9yOiB7ZXhjfSIpCgoKQG1jcC50b29sKCkKZGVmIGFjdGl2YXRlKGtleTogc3RyLCB3b3Jrc3BhY2VfbmFtZTogc3RyID0gIiIpIC0+IGRpY3Q6CiAgICAiIiJBY3RpdmF0ZSBhIGxpY2Vuc2Uga2V5IG9uIHRoaXMgbWFjaGluZS4KCiAgICBUYWtlcyBhIGxpY2Vuc2Uga2V5IChOSU0teHh4eC14eHh4LXh4eHggZm9ybWF0KSwgY29tcHV0ZXMKICAgIHRoaXMgbWFjaGluZSdzIHVuaXF1ZSBpZGVudGlmaWVyLCBjYWxscyB0aGUgV29ya2VyIC9hY3RpdmF0ZQogICAgZW5kcG9pbnQsIGFuZCB3cml0ZXMgbGljZW5zZS5qc29uIGxvY2FsbHkgb24gc3VjY2Vzcy4KCiAgICBPcHRpb25hbCB3b3Jrc3BhY2VfbmFtZTogd2hlbiBhIGRlbW8gdXNlciBhY3RpdmF0ZXMsIHRoZQogICAgJ2RlbW8nIHdvcmtzcGFjZSBpcyByZW5hbWVkIHRvIHRoaXMgbmFtZSwgcHJlc2VydmluZyBhbGwKICAgIGRhdGEgKGJyaWVmaW5ncywgbWFzdGVyX2xvZywgYXJjaGl2ZXMpLiBJZiB0aGUgdXNlciBpcyBub3QKICAgIGNvbWluZyBmcm9tIGRlbW8gbW9kZSwgdGhpcyBwYXJhbWV0ZXIgaXMgaWdub3JlZC4KCiAgICBGbG93OgogICAgICAxLiBWYWxpZGF0ZSBrZXkgZm9ybWF0CiAgICAgIDIuIENvbXB1dGUgbWFjaGluZV9pZCAocGxhdGZvcm0tc3BlY2lmaWMsIFNIQS0yNTYgaGFzaGVkKQogICAgICAzLiBQT1NUIC9hY3RpdmF0ZSB0byBXb3JrZXIgd2l0aCB7a2V5LCBtYWNoaW5lX2lkLCBsYWJlbH0KICAgICAgNC4gT24gc3VjY2Vzczogd3JpdGUgbGljZW5zZS5qc29uIHdpdGggY29udGVudCBoYXNoCiAgICAgIDUuIElmIHdvcmtzcGFjZV9uYW1lIHByb3ZpZGVkOiByZW5hbWUgZGVtbyB3b3Jrc3BhY2UKCiAgICBTdWNjZXNzIHNoYXBlOgogICAgICAgIHtzdWNjZXNzOiB0cnVlLCBwcm9kdWN0LCBmZWF0dXJlcywgYWN0aXZhdGVkLCBsaWNlbnNlX3BhdGgsCiAgICAgICAgIHdvcmtzcGFjZV9yZW5hbWVkP30KICAgIEVycm9yIHNoYXBlOgogICAgICAgIHtzdWNjZXNzOiBmYWxzZSwgZXJyb3IsIG1lc3NhZ2V9CiAgICAiIiIKICAgIGxvZy5pbmZvKCJ0b29sIGNhbGw6IGFjdGl2YXRlIGtleT0lcy4uLiVzIHdvcmtzcGFjZV9uYW1lPSVyIiwKICAgICAgICAgICAgIGtleVs6NF0gaWYgbGVuKGtleSkgPj0gNCBlbHNlICIqKioiLAogICAgICAgICAgICAga2V5Wy00Ol0gaWYgbGVuKGtleSkgPj0gNCBlbHNlICIiLAogICAgICAgICAgICAgd29ya3NwYWNlX25hbWUpCiAgICB0cnk6CiAgICAgICAgIyAxLiBWYWxpZGF0ZSBrZXkgZm9ybWF0IChOSU0teHh4eC14eHh4LXh4eHgpLgogICAgICAgIGlmIG5vdCBpc2luc3RhbmNlKGtleSwgc3RyKSBvciBub3Qga2V5LnN0cmlwKCk6CiAgICAgICAgICAgIHJldHVybiBfZXJyb3IoCiAgICAgICAgICAgICAgICAiaW52YWxpZF9pbnB1dCIsCiAgICAgICAgICAgICAgICAia2V5IG11c3QgYmUgYSBub24tZW1wdHkgc3RyaW5nLiIsCiAgICAgICAgICAgICkKICAgICAgICBrZXkgPSBrZXkuc3RyaXAoKS51cHBlcigpCiAgICAgICAgaWYgbm90IGtleS5zdGFydHN3aXRoKCJOSU0tIikgb3IgbGVuKGtleSkgIT0gMTg6CiAgICAgICAgICAgIHJldHVybiBfZXJyb3IoCiAgICAgICAgICAgICAgICAiaW52YWxpZF9rZXlfZm9ybWF0IiwKICAgICAgICAgICAgICAgICJrZXkgbXVzdCBiZSBpbiBOSU0teHh4eC14eHh4LXh4eHggZm9ybWF0ICIKICAgICAgICAgICAgICAgICIoMTggY2hhcmFjdGVycyBpbmNsdWRpbmcgZGFzaGVzKS4iLAogICAgICAgICAgICApCgogICAgICAgICMgMi4gVmFsaWRhdGUgd29ya3NwYWNlX25hbWUgaWYgcHJvdmlkZWQuCiAgICAgICAgd29ya3NwYWNlX25hbWUgPSB3b3Jrc3BhY2VfbmFtZS5zdHJpcCgpIGlmIHdvcmtzcGFjZV9uYW1lIGVsc2UgIiIKICAgICAgICBpZiB3b3Jrc3BhY2VfbmFtZToKICAgICAgICAgICAgd3NfZXJyID0gX3ZhbGlkYXRlX3dvcmtzcGFjZSh3b3Jrc3BhY2VfbmFtZSkKICAgICAgICAgICAgaWYgd3NfZXJyIGlzIG5vdCBOb25lOgogICAgICAgICAgICAgICAgIyBSZXdyaXRlIHRoZSBlcnJvciB0eXBlIGZvciBjbGFyaXR5LgogICAgICAgICAgICAgICAgcmV0dXJuIF9lcnJvcigKICAgICAgICAgICAgICAgICAgICAiaW52YWxpZF93b3Jrc3BhY2VfbmFtZSIsCiAgICAgICAgICAgICAgICAgICAgd3NfZXJyWyJtZXNzYWdlIl0sCiAgICAgICAgICAgICAgICApCgogICAgICAgICMgMy4gQ29tcHV0ZSBtYWNoaW5lX2lkLgogICAgICAgIG1hY2hpbmVfaWQgPSBfZ2V0X21hY2hpbmVfaWQoKQogICAgICAgIGlmIG5vdCBtYWNoaW5lX2lkOgogICAgICAgICAgICByZXR1cm4gX2Vycm9yKAogICAgICAgICAgICAgICAgIm1hY2hpbmVfaWRfZmFpbGVkIiwKICAgICAgICAgICAgICAgICJjb3VsZCBub3QgZGV0ZXJtaW5lIHRoaXMgbWFjaGluZSdzIGlkZW50aWZpZXIuICIKICAgICAgICAgICAgICAgICJUaGlzIGlzIG5lZWRlZCBmb3IgbGljZW5zZSBhY3RpdmF0aW9uLiIsCiAgICAgICAgICAgICkKCiAgICAgICAgIyA0LiBHZXQgaG9zdG5hbWUgZm9yIHRoZSBsYWJlbCBmaWVsZC4KICAgICAgICB0cnk6CiAgICAgICAgICAgIGxhYmVsID0gcGxhdGZvcm0ubm9kZSgpIG9yICJ1bmtub3duIgogICAgICAgIGV4Y2VwdCBFeGNlcHRpb246CiAgICAgICAgICAgIGxhYmVsID0gInVua25vd24iCgogICAgICAgICMgNS4gQ2FsbCBXb3JrZXIgL2FjdGl2YXRlIGVuZHBvaW50LgogICAgICAgIHVybCA9IGYie1dPUktFUl9VUkx9L2FjdGl2YXRlIgogICAgICAgIGJvZHkgPSBqc29uLmR1bXBzKHsKICAgICAgICAgICAgImtleSI6IGtleSwKICAgICAgICAgICAgIm1hY2hpbmVfaWQiOiBtYWNoaW5lX2lkLAogICAgICAgICAgICAibGFiZWwiOiBsYWJlbCwKICAgICAgICB9KS5lbmNvZGUoInV0Zi04IikKCiAgICAgICAgdHJ5OgogICAgICAgICAgICByZXEgPSB1cmxsaWIucmVxdWVzdC5SZXF1ZXN0KAogICAgICAgICAgICAgICAgdXJsLCBkYXRhPWJvZHksIG1ldGhvZD0iUE9TVCIsCiAgICAgICAgICAgICAgICBoZWFkZXJzPXsKICAgICAgICAgICAgICAgICAgICAiQ29udGVudC1UeXBlIjogImFwcGxpY2F0aW9uL2pzb24iLAogICAgICAgICAgICAgICAgICAgICJVc2VyLUFnZW50IjogSFRUUF9VU0VSX0FHRU5ULAogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgKQogICAgICAgICAgICB3aXRoIHVybGxpYi5yZXF1ZXN0LnVybG9wZW4oCiAgICAgICAgICAgICAgICByZXEsIHRpbWVvdXQ9REVNT19BUElfVElNRU9VVCAqIDIsCiAgICAgICAgICAgICkgYXMgcmVzcDoKICAgICAgICAgICAgICAgIHJlc3BfZGF0YSA9IGpzb24ubG9hZHMoCiAgICAgICAgICAgICAgICAgICAgcmVzcC5yZWFkKCkuZGVjb2RlKCJ1dGYtOCIpCiAgICAgICAgICAgICAgICApCiAgICAgICAgZXhjZXB0IHVybGxpYi5lcnJvci5IVFRQRXJyb3IgYXMgZXhjOgogICAgICAgICAgICAjIFBhcnNlIGVycm9yIHJlc3BvbnNlIGZyb20gV29ya2VyLgogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICBlcnJfYm9keSA9IGpzb24ubG9hZHMoCiAgICAgICAgICAgICAgICAgICAgZXhjLnJlYWQoKS5kZWNvZGUoInV0Zi04IikKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIGVycm9yX3R5cGUgPSBlcnJfYm9keS5nZXQoImVycm9yIiwgImFjdGl2YXRpb25fZmFpbGVkIikKICAgICAgICAgICAgICAgIGVycm9yX21zZyA9IGVycl9ib2R5LmdldCgKICAgICAgICAgICAgICAgICAgICAibWVzc2FnZSIsIGYiYWN0aXZhdGlvbiBmYWlsZWQgKEhUVFAge2V4Yy5jb2RlfSkiCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgIGV4Y2VwdCAoanNvbi5KU09ORGVjb2RlRXJyb3IsIEF0dHJpYnV0ZUVycm9yKToKICAgICAgICAgICAgICAgIGVycm9yX3R5cGUgPSAiYWN0aXZhdGlvbl9mYWlsZWQiCiAgICAgICAgICAgICAgICBlcnJvcl9tc2cgPSBmImFjdGl2YXRpb24gZmFpbGVkIChIVFRQIHtleGMuY29kZX0pIgogICAgICAgICAgICBsb2cuaW5mbygKICAgICAgICAgICAgICAgICJhY3RpdmF0ZTogV29ya2VyIHJldHVybmVkIEhUVFAgJWQ6ICVzIiwKICAgICAgICAgICAgICAgIGV4Yy5jb2RlLCBlcnJvcl9tc2csCiAgICAgICAgICAgICkKICAgICAgICAgICAgcmV0dXJuIF9lcnJvcihlcnJvcl90eXBlLCBlcnJvcl9tc2cpCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBleGM6CiAgICAgICAgICAgIGxvZy53YXJuaW5nKCJhY3RpdmF0ZTogV29ya2VyIHJlcXVlc3QgZmFpbGVkOiAlcyIsIGV4YykKICAgICAgICAgICAgcmV0dXJuIF9lcnJvcigKICAgICAgICAgICAgICAgICJuZXR3b3JrX2Vycm9yIiwKICAgICAgICAgICAgICAgICJjb3VsZCBub3QgcmVhY2ggdGhlIGFjdGl2YXRpb24gc2VydmVyLiAiCiAgICAgICAgICAgICAgICAiQ2hlY2sgeW91ciBpbnRlcm5ldCBjb25uZWN0aW9uIGFuZCB0cnkgYWdhaW4uIiwKICAgICAgICAgICAgKQoKICAgICAgICAjIDYuIFZlcmlmeSBXb3JrZXIgcmVzcG9uc2UuCiAgICAgICAgaWYgcmVzcF9kYXRhLmdldCgic3RhdHVzIikgIT0gImFjdGl2YXRlZCI6CiAgICAgICAgICAgIHJldHVybiBfZXJyb3IoCiAgICAgICAgICAgICAgICAidW5leHBlY3RlZF9yZXNwb25zZSIsCiAgICAgICAgICAgICAgICBmInVuZXhwZWN0ZWQgcmVzcG9uc2UgZnJvbSBzZXJ2ZXI6ICIKICAgICAgICAgICAgICAgIGYie3Jlc3BfZGF0YS5nZXQoJ3N0YXR1cycsICd1bmtub3duJyl9IiwKICAgICAgICAgICAgKQoKICAgICAgICBwcm9kdWN0ID0gcmVzcF9kYXRhLmdldCgicHJvZHVjdCIsICJ1bmtub3duIikKICAgICAgICBmZWF0dXJlcyA9IHJlc3BfZGF0YS5nZXQoImZlYXR1cmVzIiwgW10pCiAgICAgICAgc2VydmVyX3RpbWUgPSByZXNwX2RhdGEuZ2V0KCJzZXJ2ZXJfdGltZSIsICIiKQoKICAgICAgICBpZiBub3Qgc2VydmVyX3RpbWU6CiAgICAgICAgICAgICMgRmFsbGJhY2sgdG8gbG9jYWwgVVRDIOKAlCBub3QgaWRlYWwgYnV0IGZ1bmN0aW9uYWwuCiAgICAgICAgICAgIHNlcnZlcl90aW1lID0gZGF0ZXRpbWUuZGF0ZXRpbWUubm93KAogICAgICAgICAgICAgICAgZGF0ZXRpbWUudGltZXpvbmUudXRjCiAgICAgICAgICAgICkuaXNvZm9ybWF0KCkKICAgICAgICAgICAgbG9nLndhcm5pbmcoCiAgICAgICAgICAgICAgICAiYWN0aXZhdGU6IG5vIHNlcnZlcl90aW1lIGluIHJlc3BvbnNlLCAiCiAgICAgICAgICAgICAgICAidXNpbmcgbG9jYWwgVVRDIgogICAgICAgICAgICApCgogICAgICAgICMgNy4gV3JpdGUgbGljZW5zZS5qc29uLgogICAgICAgIHdyaXRlX2VyciA9IF93cml0ZV9saWNlbnNlX2pzb24oCiAgICAgICAgICAgIGtleSwgcHJvZHVjdCwgbWFjaGluZV9pZCwgc2VydmVyX3RpbWUsCiAgICAgICAgKQogICAgICAgIGlmIHdyaXRlX2VycjoKICAgICAgICAgICAgbG9nLmluZm8oImFjdGl2YXRlOiBsaWNlbnNlIHdyaXRlIGZhaWxlZDogJXMiLCB3cml0ZV9lcnIpCiAgICAgICAgICAgIHJldHVybiBfZXJyb3IoImxpY2Vuc2Vfd3JpdGVfZmFpbGVkIiwgd3JpdGVfZXJyKQoKICAgICAgICBsaWNlbnNlX3BhdGggPSBvcy5wYXRoLmpvaW4oCiAgICAgICAgICAgIF9nZXRfbGljZW5zZV9kaXIoKSwgTElDRU5TRV9GSUxFTkFNRSwKICAgICAgICApCgogICAgICAgICMgOC4gRGVtbyB3b3Jrc3BhY2UgcmVuYW1lIChpZiBhcHBsaWNhYmxlKS4KICAgICAgICB3b3Jrc3BhY2VfcmVuYW1lZCA9IEZhbHNlCiAgICAgICAgcmVuYW1lX25vdGUgPSBOb25lCiAgICAgICAgaWYgd29ya3NwYWNlX25hbWU6CiAgICAgICAgICAgIGRpcl9lcnIgPSBfZW5zdXJlX2RpcmVjdG9yeSgpCiAgICAgICAgICAgIGlmIGRpcl9lcnIgaXMgTm9uZToKICAgICAgICAgICAgICAgIHJlbmFtZV9lcnIgPSBfcmVuYW1lX2RlbW9fd29ya3NwYWNlKHdvcmtzcGFjZV9uYW1lKQogICAgICAgICAgICAgICAgaWYgcmVuYW1lX2VycjoKICAgICAgICAgICAgICAgICAgICAjIE5vbi1mYXRhbDogYWN0aXZhdGlvbiBzdWNjZWVkZWQsIHJlbmFtZSBmYWlsZWQuCiAgICAgICAgICAgICAgICAgICAgcmVuYW1lX25vdGUgPSByZW5hbWVfZXJyCiAgICAgICAgICAgICAgICAgICAgbG9nLndhcm5pbmcoCiAgICAgICAgICAgICAgICAgICAgICAgICJhY3RpdmF0ZTogZGVtbyByZW5hbWUgZmFpbGVkOiAlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgIHJlbmFtZV9lcnIsCiAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgICAgICB3b3Jrc3BhY2VfcmVuYW1lZCA9IFRydWUKCiAgICAgICAgbG9nLmluZm8oCiAgICAgICAgICAgICJhY3RpdmF0ZTogc3VjY2VzcyBwcm9kdWN0PSVzIHdvcmtzcGFjZV9yZW5hbWVkPSVzIiwKICAgICAgICAgICAgcHJvZHVjdCwgd29ya3NwYWNlX3JlbmFtZWQsCiAgICAgICAgKQogICAgICAgIHJlc3VsdCA9IHsKICAgICAgICAgICAgInN1Y2Nlc3MiOiBUcnVlLAogICAgICAgICAgICAicHJvZHVjdCI6IHByb2R1Y3QsCiAgICAgICAgICAgICJmZWF0dXJlcyI6IGZlYXR1cmVzLAogICAgICAgICAgICAiYWN0aXZhdGVkIjogc2VydmVyX3RpbWUsCiAgICAgICAgICAgICJsaWNlbnNlX3BhdGgiOiBsaWNlbnNlX3BhdGgsCiAgICAgICAgfQogICAgICAgIGlmIHdvcmtzcGFjZV9uYW1lOgogICAgICAgICAgICByZXN1bHRbIndvcmtzcGFjZV9yZW5hbWVkIl0gPSB3b3Jrc3BhY2VfcmVuYW1lZAogICAgICAgICAgICBpZiByZW5hbWVfbm90ZToKICAgICAgICAgICAgICAgIHJlc3VsdFsicmVuYW1lX25vdGUiXSA9IHJlbmFtZV9ub3RlCiAgICAgICAgcmV0dXJuIHJlc3VsdAoKICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZXhjOiAgIyBjYXRjaC1hbGwg4oaSIGludGVybmFsX2Vycm9yCiAgICAgICAgbG9nLmV4Y2VwdGlvbigiYWN0aXZhdGU6IHVuaGFuZGxlZCBleGNlcHRpb24iKQogICAgICAgIHJldHVybiBfZXJyb3IoImludGVybmFsX2Vycm9yIiwgZiJpbnRlcm5hbCBlcnJvcjoge2V4Y30iKQoKCkBtY3AudG9vbCgpCmRlZiBxdWVyeV90YWdzKAogICAga2V5d29yZDogc3RyLAogICAgd29ya3NwYWNlOiBzdHIsCiAgICBzZWFyY2hfdGllcjogc3RyID0gIkIiLAopIC0+IGRpY3Q6CiAgICAiIiJTZWFyY2ggcGVyc2lzdGVudCB0YWdzIGJ5IGtleXdvcmQuCgogICAga2V5d29yZDogICAgICBjYXNlLWluc2Vuc2l0aXZlIHNlYXJjaCB0ZXJtCiAgICB3b3Jrc3BhY2U6ICAgIHdvcmtzcGFjZSB0byBzZWFyY2ggaW4KICAgIHNlYXJjaF90aWVyOiAgIkEiLCAiQiIsIG9yICJib3RoIiAoZGVmYXVsdCAiQiIpCgogICAgUmV0dXJucyBtYXRjaGluZyBsaW5lcyBmcm9tIHRoZSBzcGVjaWZpZWQgdGllciBmaWxlKHMpLgogICAgIiIiCiAgICBsb2cuaW5mbygKICAgICAgICAidG9vbCBjYWxsOiBxdWVyeV90YWdzIGtleXdvcmQ9JXIgd29ya3NwYWNlPSVyIHRpZXI9JXMiLAogICAgICAgIGtleXdvcmQsIHdvcmtzcGFjZSwgc2VhcmNoX3RpZXIsCiAgICApCiAgICB0cnk6CiAgICAgICAgIyBWYWxpZGF0ZSBpbnB1dHMuCiAgICAgICAgd3NfZXJyID0gX3ZhbGlkYXRlX3dvcmtzcGFjZSh3b3Jrc3BhY2UpCiAgICAgICAgaWYgd3NfZXJyIGlzIG5vdCBOb25lOgogICAgICAgICAgICByZXR1cm4gd3NfZXJyCiAgICAgICAgaWYgbm90IGtleXdvcmQgb3Igbm90IGtleXdvcmQuc3RyaXAoKToKICAgICAgICAgICAgcmV0dXJuIF9lcnJvcigiaW52YWxpZF9pbnB1dCIsICJrZXl3b3JkIG11c3Qgbm90IGJlIGVtcHR5IikKICAgICAgICBpZiBzZWFyY2hfdGllciBub3QgaW4gKCJBIiwgIkIiLCAiYm90aCIpOgogICAgICAgICAgICByZXR1cm4gX2Vycm9yKCJpbnZhbGlkX2lucHV0IiwgInNlYXJjaF90aWVyIG11c3QgYmUgQSwgQiwgb3IgYm90aCIpCgogICAgICAgIHBhdGhzID0gX3dvcmtzcGFjZV9wYXRocyh3b3Jrc3BhY2UpCiAgICAgICAga2V5d29yZF9sb3dlciA9IGtleXdvcmQuc3RyaXAoKS5sb3dlcigpCiAgICAgICAgbWF0Y2hlcyA9IFtdCgogICAgICAgIGZpbGVzX3RvX3NlYXJjaCA9IFtdCiAgICAgICAgaWYgc2VhcmNoX3RpZXIgaW4gKCJBIiwgImJvdGgiKToKICAgICAgICAgICAgZmlsZXNfdG9fc2VhcmNoLmFwcGVuZCgoIkEiLCBwYXRoc1sidGllcl9hX3RhZ3MiXSkpCiAgICAgICAgaWYgc2VhcmNoX3RpZXIgaW4gKCJCIiwgImJvdGgiKToKICAgICAgICAgICAgZmlsZXNfdG9fc2VhcmNoLmFwcGVuZCgoIkIiLCBwYXRoc1sidGllcl9iX3RhZ3MiXSkpCgogICAgICAgIGZvciB0aWVyX2xhYmVsLCBmaWxlX3BhdGggaW4gZmlsZXNfdG9fc2VhcmNoOgogICAgICAgICAgICBpZiBvcy5wYXRoLmV4aXN0cyhmaWxlX3BhdGgpOgogICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgIGNvbnRlbnQgPSBfcmVhZF90ZXh0KGZpbGVfcGF0aCkKICAgICAgICAgICAgICAgICAgICBmb3IgbGluZSBpbiBjb250ZW50LnNwbGl0bGluZXMoKToKICAgICAgICAgICAgICAgICAgICAgICAgaWYgbGluZS5zdHJpcCgpIGFuZCBrZXl3b3JkX2xvd2VyIGluIGxpbmUubG93ZXIoKToKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hdGNoZXMuYXBwZW5kKGxpbmUuc3RyaXAoKSkKICAgICAgICAgICAgICAgIGV4Y2VwdCBPU0Vycm9yIGFzIGV4YzoKICAgICAgICAgICAgICAgICAgICBsb2cud2FybmluZygKICAgICAgICAgICAgICAgICAgICAgICAgInF1ZXJ5X3RhZ3M6IGNvdWxkIG5vdCByZWFkICVzOiAlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgIHRpZXJfbGFiZWwsIGV4YywKICAgICAgICAgICAgICAgICAgICApCgogICAgICAgICMgRmFsbGJhY2s6IGNoZWNrIGxlZ2FjeSBwZXJzaXN0ZW50X3RhZ3MudHh0CiAgICAgICAgaWYgbm90IG1hdGNoZXMgYW5kIG9zLnBhdGguZXhpc3RzKHBhdGhzWyJwZXJzaXN0ZW50X3RhZ3MiXSk6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIGNvbnRlbnQgPSBfcmVhZF90ZXh0KHBhdGhzWyJwZXJzaXN0ZW50X3RhZ3MiXSkKICAgICAgICAgICAgICAgIGZvciBsaW5lIGluIGNvbnRlbnQuc3BsaXRsaW5lcygpOgogICAgICAgICAgICAgICAgICAgIGlmIGxpbmUuc3RyaXAoKSBhbmQga2V5d29yZF9sb3dlciBpbiBsaW5lLmxvd2VyKCk6CiAgICAgICAgICAgICAgICAgICAgICAgIG1hdGNoZXMuYXBwZW5kKGxpbmUuc3RyaXAoKSkKICAgICAgICAgICAgZXhjZXB0IE9TRXJyb3I6CiAgICAgICAgICAgICAgICBwYXNzCgogICAgICAgIHJldHVybiB7CiAgICAgICAgICAgICJzdWNjZXNzIjogVHJ1ZSwKICAgICAgICAgICAgIndvcmtzcGFjZSI6IHdvcmtzcGFjZSwKICAgICAgICAgICAgImtleXdvcmQiOiBrZXl3b3JkLAogICAgICAgICAgICAic2VhcmNoX3RpZXIiOiBzZWFyY2hfdGllciwKICAgICAgICAgICAgIm1hdGNoZXMiOiBtYXRjaGVzLAogICAgICAgICAgICAibWF0Y2hfY291bnQiOiBsZW4obWF0Y2hlcyksCiAgICAgICAgfQoKICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZXhjOiAgIyBjYXRjaC1hbGwg4oaSIGludGVybmFsX2Vycm9yCiAgICAgICAgbG9nLmV4Y2VwdGlvbigicXVlcnlfdGFnczogdW5oYW5kbGVkIGV4Y2VwdGlvbiIpCiAgICAgICAgcmV0dXJuIF9lcnJvcigiaW50ZXJuYWxfZXJyb3IiLCBmImludGVybmFsIGVycm9yOiB7ZXhjfSIpCgoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyBFTlRSWSBQT0lOVAojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKaWYgX19uYW1lX18gPT0gIl9fbWFpbl9fIjoKICAgIHRyeToKICAgICAgICBtY3BfcGtnX3ZlcnNpb24gPSB2ZXJzaW9uKCJtY3AiKQogICAgZXhjZXB0IFBhY2thZ2VOb3RGb3VuZEVycm9yOgogICAgICAgIG1jcF9wa2dfdmVyc2lvbiA9ICJ1bmtub3duIgogICAgbG9nLmluZm8oCiAgICAgICAgIm5pbWdpciBNQ1Agc2VydmVyIHY3XzEwIHN0YXJ0aW5nICIKICAgICAgICAiKG1jcCBwYWNrYWdlIHZlcnNpb246ICVzKSIsCiAgICAgICAgbWNwX3BrZ192ZXJzaW9uLAogICAgKQogICAgbWNwLnJ1bih0cmFuc3BvcnQ9InN0ZGlvIikK"

PASTE_TEMPLATE = """\
At the start of every new chat, before responding:
1. Call nimgir:get_briefing with workspace="{workspace}".
2. Apply the primer rules returned by get_briefing.
3. Read the briefing and master_log as continuation context.
4. If the briefing has a FILES_PRODUCED section, verify those
   files exist in project knowledge. Flag mismatches before
   proceeding.
5. This is a continuation, not a new conversation. Pick up
   from the stop point. Do not restart or summarize.

Rules for every response:
6. Think before responding. Do not answer without reasoning first.
7. Post a token bar after every response. Start from 35K
   (base context load). Estimation: 4 characters ≈ 1 token.
   ~XXK/60K ██████░░░░
   Zones: 35-54K safe, 55-57K caution, 58-60K danger.
8. At 55K, let the user know the safe zone is ending and
   offer to begin wrapping — but let them decide. By 58K,
   begin wrap regardless; past this point the handover
   itself loses quality.

At chat wrap:
9. Prepare briefing and master_log per primer wrap rules.
10. Briefing must reflect final corrected state — if anything
    was corrected after drafting, amend before saving.
11. Call nimgir:save_briefing with workspace="{workspace}",
    briefing, and master_log.\
"""

CROSS_MACHINE_NOTE = (
    "Note: Continuation memory currently requires Claude Desktop "
    "on a computer. Phone and other devices can access Nimgir "
    "via Claude Dispatch (Pro/Max plans, desktop must be running)."
)

PLACEHOLDER_WORKSPACE = "your_workspace"


# ── Installation steps ─────────────────────────────────────────

class InstallResult:
    def __init__(self):
        self.steps = []
        self.install_dir = INSTALL_DIR
        self.python_path = sys.executable
        self.server_path = INSTALL_DIR / "mcp_server.py"
        self.config_edited = False

    def add(self, name, ok, detail):
        self.steps.append((name, ok, detail))

    @property
    def critical_ok(self):
        return self.server_path.exists() and self.config_edited


def check_python_version():
    v = sys.version_info
    if (v.major, v.minor) >= MIN_PYTHON:
        return True, f"Python {v.major}.{v.minor}.{v.micro}"
    return False, (
        f"Python {v.major}.{v.minor} found — need "
        f"{MIN_PYTHON[0]}.{MIN_PYTHON[1]}+. "
        f"Please upgrade from python.org."
    )


def install_mcp_package():
    try:
        import importlib.metadata
        ver = importlib.metadata.version("mcp")
        return True, f"mcp {ver} already installed"
    except Exception:
        pass

    try:
        result = subprocess.run(
            [sys.executable, "-m", "pip", "install", "mcp",
             "--quiet", "--disable-pip-version-check"],
            capture_output=True, text=True, timeout=120,
        )
        if result.returncode == 0:
            import importlib.metadata
            ver = importlib.metadata.version("mcp")
            return True, f"mcp {ver} installed"
        return False, f"pip install failed: {result.stderr.strip()}"
    except Exception as exc:
        return False, f"pip install error: {exc}"


def create_install_directory():
    try:
        INSTALL_DIR.mkdir(parents=True, exist_ok=True)
        return True, str(INSTALL_DIR)
    except OSError as exc:
        return False, f"Could not create {INSTALL_DIR}: {exc}"


def deploy_server():
    target = INSTALL_DIR / "mcp_server.py"
    try:
        code = base64.b64decode(EMBEDDED_SERVER_B64).decode("utf-8")
        target.write_text(code, encoding="utf-8")
        return True, str(target)
    except Exception as exc:
        return False, f"Deploy failed: {exc}"


def create_known_workspaces():
    path = INSTALL_DIR / "known_workspaces.txt"
    if path.exists():
        return True, "already exists (preserved)"
    try:
        path.write_text("", encoding="utf-8")
        return True, str(path)
    except OSError as exc:
        return False, f"Could not create: {exc}"


def edit_claude_config(result):
    nimgir_entry = {
        "command": result.python_path,
        "args": [str(result.server_path)],
    }

    config_dir, config_path = find_claude_config()
    if config_dir is None:
        return False, (
            "Claude Desktop config folder not found. "
            "Checked %APPDATA%\\Claude and MSIX package paths. "
            "Is Claude Desktop installed and logged in?"
        )

    config = {}
    if config_path.exists():
        try:
            raw = config_path.read_text(encoding="utf-8")
            config = json.loads(raw) if raw.strip() else {}
        except (json.JSONDecodeError, OSError) as exc:
            backup = config_path.with_suffix(".json.bak")
            try:
                shutil.copy2(config_path, backup)
            except OSError:
                pass
            return False, (
                f"Could not parse existing config: {exc}. "
                f"Backup saved to {backup}."
            )

    if "mcpServers" not in config:
        config["mcpServers"] = {}

    existing = config["mcpServers"].get("nimgir")
    if existing == nimgir_entry:
        result.config_edited = True
        return True, "already configured correctly"

    config["mcpServers"]["nimgir"] = nimgir_entry

    if config_path.exists():
        backup = config_path.with_suffix(".json.bak")
        try:
            shutil.copy2(config_path, backup)
        except OSError:
            pass

    try:
        config_path.write_text(
            json.dumps(config, indent=2, ensure_ascii=False) + "\n",
            encoding="utf-8",
        )
        result.config_edited = True
        return True, f"nimgir entry added ({config_path})"
    except OSError as exc:
        return False, f"Could not write config: {exc}"


CLAUDE_DOWNLOAD_URL = "https://claude.com/download"
DEVELOPER_SETTINGS_URL = "ms-settings:developers"


class ClaudeDesktopPrompt(tk.Tk):
    """Popup shown when Claude Desktop is not installed."""

    def __init__(self):
        super().__init__()
        self.title("Claude Desktop Required")
        self.resizable(False, False)
        self.configure(bg="#1e1e2e")
        self._confirmed = False
        self._build_ui()
        self._center()

    def _build_ui(self):
        bg = "#1e1e2e"
        fg = "#cdd6f4"
        accent = "#89b4fa"
        muted = "#6c7086"
        yellow = "#f9e2af"
        red = "#f38ba8"
        btn_bg = "#45475a"
        orange = "#fab387"

        title_font = tkfont.Font(family="Segoe UI", size=14, weight="bold")
        tk.Label(
            self, text="Claude Desktop Required",
            font=title_font, bg=bg, fg=yellow,
        ).pack(pady=(20, 8))

        body_font = tkfont.Font(family="Segoe UI", size=10)
        msg = (
            "Nimgir requires Claude Desktop to be installed\n"
            "and logged in before it can complete setup.\n\n"
            "1.  Enable Developer Mode in Windows Settings\n"
            "2.  Click below to open the download page\n"
            "3.  Download and install Claude Desktop\n"
            "4.  Open Claude Desktop and log in\n"
            "5.  Come back here and click Continue"
        )
        tk.Label(
            self, text=msg, font=body_font,
            bg=bg, fg=fg, justify=tk.LEFT,
        ).pack(padx=24, pady=(0, 8))

        # Developer Mode note.
        dev_note_font = tkfont.Font(family="Segoe UI", size=9)
        dev_note = (
            "Claude Desktop uses MSIX packaging which may require\n"
            "Developer Mode:  Settings \u2192 System \u2192 For developers \u2192 On"
        )
        tk.Label(
            self, text=dev_note, font=dev_note_font,
            bg=bg, fg=orange, justify=tk.LEFT,
        ).pack(padx=24, pady=(0, 12))

        # Button row: Developer Settings + Download page.
        btn_frame = tk.Frame(self, bg=bg)
        btn_frame.pack(pady=(0, 8))

        btn_font = tkfont.Font(family="Segoe UI", size=10)

        tk.Button(
            btn_frame, text="Open Developer Settings", font=btn_font,
            bg=btn_bg, fg=fg, activebackground="#585b70",
            activeforeground=fg,
            relief=tk.FLAT, cursor="hand2", width=22,
            command=lambda: os.startfile(DEVELOPER_SETTINGS_URL),
        ).pack(side=tk.LEFT, padx=(0, 8), ipady=4)

        tk.Button(
            btn_frame, text="Open download page", font=btn_font,
            bg=accent, fg="#1e1e2e", activebackground="#b4d0fb",
            relief=tk.FLAT, cursor="hand2", width=22,
            command=lambda: webbrowser.open(CLAUDE_DOWNLOAD_URL),
        ).pack(side=tk.LEFT, ipady=4)

        self._status = tk.Label(
            self, text="", font=tkfont.Font(family="Segoe UI", size=9),
            bg=bg, fg=red,
        )
        self._status.pack(pady=(8, 4))

        tk.Button(
            self, text="I've installed and logged in \u2014 Continue",
            font=btn_font, bg=btn_bg, fg=fg,
            activebackground="#585b70", activeforeground=fg,
            relief=tk.FLAT, cursor="hand2", width=36,
            command=self._check_and_continue,
        ).pack(pady=(0, 8), ipady=4)

        tk.Button(
            self, text="Cancel", font=btn_font,
            bg=btn_bg, fg=muted, activebackground="#585b70",
            relief=tk.FLAT, cursor="hand2", width=12,
            command=self.destroy,
        ).pack(pady=(0, 20), ipady=2)

    def _check_and_continue(self):
        config_dir, _ = find_claude_config()
        if config_dir is not None:
            self._confirmed = True
            self.destroy()
        else:
            self._status.configure(
                text="Claude Desktop config not found yet.\n"
                     "Please open Claude Desktop and log in first.",
            )

    def _center(self):
        self.update_idletasks()
        w = self.winfo_reqwidth()
        h = self.winfo_reqheight()
        x = (self.winfo_screenwidth() - w) // 2
        y = (self.winfo_screenheight() - h) // 2
        self.geometry(f"+{x}+{y}")


def check_claude_desktop():
    """Check if Claude Desktop is installed and logged in."""
    config_dir, _ = find_claude_config()
    if config_dir is not None:
        return True, f"config directory found ({config_dir})"

    print("  Claude Desktop not found. Opening setup prompt...")
    prompt = ClaudeDesktopPrompt()
    prompt.mainloop()

    if prompt._confirmed:
        return True, "confirmed by user"
    return False, "Claude Desktop is required — install cancelled"


def run_installation():
    _ilog("run_installation() starting")
    result = InstallResult()

    _ilog("Step 1: check_python_version")
    ok, detail = check_python_version()
    _ilog(f"  -> ok={ok} detail={detail}")
    result.add("Python version", ok, detail)
    if not ok:
        return result

    _ilog("Step 2: check_claude_desktop")
    ok, detail = check_claude_desktop()
    _ilog(f"  -> ok={ok} detail={detail}")
    result.add("Claude Desktop", ok, detail)
    if not ok:
        return result

    _ilog("Step 3: install_mcp_package")
    ok, detail = install_mcp_package()
    _ilog(f"  -> ok={ok} detail={detail}")
    result.add("MCP package", ok, detail)

    _ilog("Step 4: create_install_directory")
    ok, detail = create_install_directory()
    _ilog(f"  -> ok={ok} detail={detail}")
    result.add("Install directory", ok, detail)
    if not ok:
        return result

    _ilog("Step 5: deploy_server")
    ok, detail = deploy_server()
    _ilog(f"  -> ok={ok} detail={detail}")
    result.add("Deploy server", ok, detail)
    if not ok:
        return result

    _ilog("Step 6: create_known_workspaces")
    ok, detail = create_known_workspaces()
    _ilog(f"  -> ok={ok} detail={detail}")
    result.add("Workspace registry", ok, detail)

    _ilog("Step 7: edit_claude_config")
    ok, detail = edit_claude_config(result)
    _ilog(f"  -> ok={ok} detail={detail}")
    result.add("Claude Desktop config", ok, detail)

    _ilog("run_installation() complete")
    return result


# ── Tkinter dialog ─────────────────────────────────────────────

class NimgirInstaller(tk.Tk):
    def __init__(self, result):
        super().__init__()
        self.result = result
        self.title("Nimgir Setup")
        self.resizable(False, False)
        self.configure(bg="#1e1e2e")
        self._copy_reset_id = None
        self._build_ui()
        self._center_window()
        self._update_preview()

    def _build_ui(self):
        pad = {"padx": 24}
        bg = "#1e1e2e"
        fg = "#cdd6f4"
        accent = "#89b4fa"
        muted = "#6c7086"
        entry_bg = "#313244"
        preview_bg = "#181825"
        btn_bg = "#45475a"
        btn_active = "#585b70"
        green = "#a6e3a1"
        red = "#f38ba8"
        yellow = "#f9e2af"

        # ── Header ──
        title_font = tkfont.Font(family="Segoe UI", size=16, weight="bold")
        tk.Label(
            self, text="Nimgir", font=title_font,
            bg=bg, fg=accent,
        ).pack(pady=(20, 4))

        subtitle_font = tkfont.Font(family="Segoe UI", size=9)
        tk.Label(
            self, text="Persistent memory for Claude",
            font=subtitle_font, bg=bg, fg=muted,
        ).pack(pady=(0, 14))

        # ── Success banner ──
        banner = tk.Frame(
            self, bg="#1a2e1a",
            highlightthickness=1, highlightbackground="#2d5a2d",
        )
        banner.pack(**pad, pady=(0, 14), fill=tk.X)

        banner_font = tkfont.Font(family="Segoe UI", size=10)
        banner_icon = tk.Label(
            banner, text="\u2713", font=banner_font,
            bg="#1a2e1a", fg=green, width=3,
        )
        banner_icon.pack(side=tk.LEFT, padx=(10, 0), pady=10)

        banner_text = tk.Label(
            banner,
            text="Installation complete. MCP server deployed\n"
                 "and registered with Claude Desktop.",
            font=banner_font, bg="#1a2e1a", fg=fg,
            justify=tk.LEFT, anchor="w",
        )
        banner_text.pack(side=tk.LEFT, padx=(4, 10), pady=10)

        # ── Install summary (checkmarks) ──
        summary_frame = tk.Frame(
            self, bg=preview_bg,
            highlightthickness=1, highlightbackground="#313244",
        )
        summary_frame.pack(**pad, pady=(0, 12), fill=tk.X)

        step_font = tkfont.Font(family="Consolas", size=9)
        for name, ok, detail in self.result.steps:
            icon = "\u2713" if ok else "\u2717"
            color = green if ok else red
            row = tk.Frame(summary_frame, bg=preview_bg)
            row.pack(fill=tk.X, padx=10, pady=2)
            tk.Label(
                row, text=icon, font=step_font,
                bg=preview_bg, fg=color, width=2,
            ).pack(side=tk.LEFT)
            tk.Label(
                row, text=f"{name}: {detail}", font=step_font,
                bg=preview_bg, fg=fg, anchor="w",
            ).pack(side=tk.LEFT, fill=tk.X, expand=True)

        # ── Restart reminder ──
        if self.result.config_edited:
            reminder_font = tkfont.Font(
                family="Segoe UI", size=9, weight="bold",
            )
            tk.Label(
                self,
                text="\u26a0  Restart Claude Desktop to activate Nimgir",
                font=reminder_font, bg=bg, fg=yellow,
            ).pack(**pad, pady=(0, 12))

        # ── Two steps ──
        steps_frame = tk.Frame(self, bg=bg)
        steps_frame.pack(**pad, pady=(0, 14), fill=tk.X, anchor="w")

        num_font = tkfont.Font(family="Segoe UI", size=10, weight="bold")
        step_title_font = tkfont.Font(family="Segoe UI", size=10, weight="bold")
        step_desc_font = tkfont.Font(family="Segoe UI", size=9)

        for num, title, desc in [
            ("1", "Restart Claude Desktop",
             "Close and reopen the app so it picks up the new MCP server."),
            ("2", "Follow the setup guide",
             "The guide walks you through pasting a short text into\n"
             "Claude Desktop \u2014 step by step with illustrations."),
        ]:
            row = tk.Frame(steps_frame, bg=bg)
            row.pack(fill=tk.X, pady=(0, 8))
            tk.Label(
                row, text=num, font=num_font,
                bg=bg, fg=accent, width=3,
            ).pack(side=tk.LEFT, anchor="n", pady=(2, 0))
            col = tk.Frame(row, bg=bg)
            col.pack(side=tk.LEFT, fill=tk.X, expand=True)
            tk.Label(
                col, text=title, font=step_title_font,
                bg=bg, fg=fg, anchor="w",
            ).pack(anchor="w")
            tk.Label(
                col, text=desc, font=step_desc_font,
                bg=bg, fg=muted, anchor="w", justify=tk.LEFT,
            ).pack(anchor="w")

        # ── Big CTA button ──
        cta_frame = tk.Frame(
            self, bg="#1e2a3a",
            highlightthickness=1, highlightbackground="#3a5a7a",
            cursor="hand2",
        )
        cta_frame.pack(**pad, pady=(0, 14), fill=tk.X)

        cta_font = tkfont.Font(family="Segoe UI", size=12, weight="bold")
        cta_sub_font = tkfont.Font(family="Segoe UI", size=9)

        cta_label = tk.Label(
            cta_frame, text="Open setup guide \u2192",
            font=cta_font, bg="#1e2a3a", fg=accent,
        )
        cta_label.pack(pady=(12, 2))

        cta_sub = tk.Label(
            cta_frame, text="nimgir.ai/setup",
            font=cta_sub_font, bg="#1e2a3a", fg=muted,
        )
        cta_sub.pack(pady=(0, 12))

        for widget in (cta_frame, cta_label, cta_sub):
            widget.bind(
                "<Button-1>",
                lambda e: webbrowser.open("https://nimgir.ai/setup"),
            )

        # ── Collapsible fallback ──
        self._fallback_open = False

        toggle_font = tkfont.Font(family="Segoe UI", size=9)
        self._fb_toggle = tk.Label(
            self, text="\u25b6  No internet? Copy the paste text manually",
            font=toggle_font, bg=bg, fg=muted, cursor="hand2",
        )
        self._fb_toggle.pack(**pad, anchor="w", pady=(0, 4))
        self._fb_toggle.bind("<Button-1>", lambda e: self._toggle_fallback())

        self._fb_frame = tk.Frame(self, bg=bg)
        # Starts hidden — not packed.

        # Workspace input inside fallback.
        lbl_font = tkfont.Font(family="Segoe UI", size=9, weight="bold")
        tk.Label(
            self._fb_frame, text="WORKSPACE NAME", font=lbl_font,
            bg=bg, fg=muted,
        ).pack(anchor="w")

        self._ws_var = tk.StringVar()
        self._ws_var.trace_add(
            "write", lambda *_: self._update_preview(),
        )

        entry_font = tkfont.Font(family="Consolas", size=11)
        self._ws_entry = tk.Entry(
            self._fb_frame, textvariable=self._ws_var,
            font=entry_font, bg=entry_bg, fg=fg,
            insertbackground=fg, relief=tk.FLAT,
            highlightthickness=1, highlightcolor=accent,
            highlightbackground="#45475a",
        )
        self._ws_entry.pack(fill=tk.X, ipady=6, pady=(4, 0))

        hint_font = tkfont.Font(family="Segoe UI", size=8)
        self._hint_label = tk.Label(
            self._fb_frame,
            text="starts with a letter, then letters, digits, "
                 "underscores (1\u201364 chars)",
            font=hint_font, bg=bg, fg=muted,
        )
        self._hint_label.pack(anchor="w", pady=(2, 8))

        preview_lbl_font = tkfont.Font(
            family="Segoe UI", size=9, weight="bold",
        )
        tk.Label(
            self._fb_frame, text="PASTE INTO PROJECT INSTRUCTIONS",
            font=preview_lbl_font, bg=bg, fg=muted,
        ).pack(anchor="w", pady=(0, 4))

        preview_font = tkfont.Font(family="Consolas", size=9)
        self._preview = tk.Text(
            self._fb_frame, font=preview_font, bg=preview_bg, fg=fg,
            relief=tk.FLAT, wrap=tk.WORD, height=14, width=64,
            highlightthickness=1, highlightcolor="#45475a",
            highlightbackground="#313244", padx=10, pady=8,
            state=tk.DISABLED,
        )
        self._preview.pack(fill=tk.X, pady=(0, 8))

        btn_font = tkfont.Font(family="Segoe UI", size=10)
        self._copy_btn = tk.Button(
            self._fb_frame, text="Copy text", font=btn_font,
            bg=accent, fg="#1e1e2e", activebackground="#b4d0fb",
            activeforeground="#1e1e2e", relief=tk.FLAT,
            cursor="hand2", width=14, command=self._copy_text,
        )
        self._copy_btn.pack(anchor="w", ipady=4, pady=(0, 8))

        # ── Done button ──
        tk.Button(
            self, text="Done \u2014 close", font=btn_font,
            bg=btn_bg, fg=fg, activebackground=btn_active,
            activeforeground=fg, relief=tk.FLAT,
            cursor="hand2", width=52, command=self.destroy,
        ).pack(**pad, ipady=6, pady=(8, 0))

        # ── Cross-machine note ──
        note_font = tkfont.Font(
            family="Segoe UI", size=8, slant="italic",
        )
        tk.Label(
            self, text=CROSS_MACHINE_NOTE, font=note_font,
            bg=bg, fg=muted, wraplength=480, justify=tk.CENTER,
        ).pack(**pad, pady=(12, 20))

    def _toggle_fallback(self):
        self._fallback_open = not self._fallback_open
        if self._fallback_open:
            self._fb_toggle.configure(
                text="\u25bc  No internet? Copy the paste text manually",
            )
            self._fb_frame.pack(padx=24, fill=tk.X, after=self._fb_toggle)
            self._update_preview()
        else:
            self._fb_toggle.configure(
                text="\u25b6  No internet? Copy the paste text manually",
            )
            self._fb_frame.pack_forget()
        self.update_idletasks()
        self._center_window()

    def _get_workspace(self):
        raw = self._ws_var.get().strip()
        if raw and WORKSPACE_PATTERN.fullmatch(raw):
            return raw
        return PLACEHOLDER_WORKSPACE

    def _make_paste_text(self):
        return PASTE_TEMPLATE.format(
            workspace=self._get_workspace(),
        )

    def _update_preview(self):
        text = self._make_paste_text()
        self._preview.configure(state=tk.NORMAL)
        self._preview.delete("1.0", tk.END)
        self._preview.insert("1.0", text)
        self._preview.configure(state=tk.DISABLED)
        raw = self._ws_var.get().strip()
        if raw and not WORKSPACE_PATTERN.fullmatch(raw):
            self._hint_label.configure(fg="#f38ba8")
        else:
            self._hint_label.configure(fg="#6c7086")

    def _copy_text(self):
        raw = self._ws_var.get().strip()
        if not raw or not WORKSPACE_PATTERN.fullmatch(raw):
            self._hint_label.configure(fg="#f38ba8")
            self._ws_entry.focus_set()
            return
        text = self._make_paste_text()
        self.clipboard_clear()
        self.clipboard_append(text)
        self._copy_btn.configure(text="\u2713 Copied", bg="#a6e3a1")
        if self._copy_reset_id is not None:
            self.after_cancel(self._copy_reset_id)
        self._copy_reset_id = self.after(
            2000, lambda: self._copy_btn.configure(
                text="Copy text", bg="#89b4fa",
            ),
        )

    def _center_window(self):
        self.update_idletasks()
        w = self.winfo_reqwidth()
        h = self.winfo_reqheight()
        x = (self.winfo_screenwidth() - w) // 2
        y = (self.winfo_screenheight() - h) // 2
        self.geometry(f"+{x}+{y}")


# ── Entry point ────────────────────────────────────────────────

def main():
    _ilog("main() starting")
    print()
    print("  Nimgir Installer")
    print("  " + "=" * 40)
    print()

    result = run_installation()

    for name, ok, detail in result.steps:
        icon = "[OK]  " if ok else "[FAIL]"
        print(f"  {icon} {name}: {detail}")
    print()

    if result.critical_ok:
        print("  Installation complete. Opening setup dialog...")
    else:
        print("  Installation had errors. Opening dialog...")
    print()

    _ilog("Creating NimgirInstaller Tkinter window")
    app = NimgirInstaller(result)
    _ilog("Starting mainloop")
    app.mainloop()
    _ilog("mainloop exited")


if __name__ == "__main__":
    try:
        main()
    except Exception as exc:
        import traceback
        _ilog(f"FATAL: {exc}")
        _ilog(traceback.format_exc())
        print()
        print(f"  FATAL ERROR: {exc}")
        print()
        traceback.print_exc()
        print()
        print(f"  Log file: {_INSTALL_LOG}")
        print()
        input("  Press Enter to close...")
        sys.exit(1)
